Release version 0.60.2
[platform/core/appfw/aul-1.git] / src / aul_launch.c
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef _GNU_SOURCE
18 #define _GNU_SOURCE
19 #endif
20
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <bundle_internal.h>
30
31 #include "aul_api.h"
32 #include "aul_cmd.h"
33 #include "aul_util.h"
34 #include "aul.h"
35 #include "aul_sock.h"
36 #include "launch.h"
37 #include "aul_watch_control_internal.h"
38 #include "aul_worker.h"
39 #include "aul_watchdog.h"
40
41 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
42
43 #define K_SERVICE_THREAD "__K_SERVICE_THREAD"
44
45 #define AUL_CTOR __attribute__ ((constructor))
46 #define AUL_DTOR __attribute__ ((destructor))
47
48 typedef struct client_channel_s {
49         int fd;
50         pid_t pid;
51         uid_t uid;
52         GRecMutex mutex;
53 } client_channel_t;
54
55 struct aul_request_s {
56         int cmd;
57         int clifd;
58         bundle *b;
59 };
60
61 typedef struct aul_request_s *aul_request_h;
62
63 typedef void (*dispatcher)(aul_request_h req);
64
65 typedef struct aul_handler_s {
66         aul_handler_fn callback;
67         void *user_data;
68 } aul_handler;
69
70 typedef struct subapp_handler_s {
71         bool is_subapp;
72         subapp_fn callback;
73         void *user_data;
74 } subapp_handler;
75
76 typedef struct data_control_provider_handler_s {
77         data_control_provider_handler_fn callback;
78 } data_control_provider_handler;
79
80 typedef struct launch_context_s {
81         bool initialized;
82         aul_worker_h worker;
83         aul_handler aul;
84         subapp_handler subapp;
85         data_control_provider_handler dcp;
86         GList* clients;
87         GRecMutex init_mutex;
88         GRecMutex mutex;
89         GMainContext *tizen_context;
90         bool touch_argv_handler;
91 } launch_context;
92
93 static launch_context __context;
94
95 AUL_CTOR static void __aul_constructor(void)
96 {
97         g_rec_mutex_init(&__context.init_mutex);
98 }
99
100 AUL_DTOR static void __aul_destructor(void)
101 {
102         if (g_rec_mutex_trylock(&__context.init_mutex))
103                 g_rec_mutex_unlock(&__context.init_mutex);
104
105         g_rec_mutex_clear(&__context.init_mutex);
106 }
107
108 static void __destroy_client_channel(gpointer data)
109 {
110         client_channel_t *channel = data;
111
112         g_rec_mutex_lock(&channel->mutex);
113         g_rec_mutex_unlock(&channel->mutex);
114         g_rec_mutex_clear(&channel->mutex);
115
116         free(channel);
117 }
118
119 static client_channel_t *__create_client_channel(int fd, pid_t pid, uid_t uid)
120 {
121         client_channel_t *channel;
122
123         channel = calloc(1, sizeof(client_channel_t));
124         if (!channel) {
125                 _E("Out of memory");
126                 return NULL;
127         }
128
129         g_rec_mutex_init(&channel->mutex);
130
131         channel->fd = fd;
132         channel->pid = pid;
133         channel->uid = uid;
134
135         return channel;
136 }
137
138 static void __add_client_channel(client_channel_t *channel)
139 {
140         g_rec_mutex_lock(&__context.mutex);
141         __context.clients = g_list_append(__context.clients, channel);
142         g_rec_mutex_unlock(&__context.mutex);
143 }
144
145 static void __remove_client_channel(client_channel_t *channel)
146 {
147         g_rec_mutex_lock(&__context.mutex);
148         __context.clients = g_list_remove(__context.clients, channel);
149         g_rec_mutex_unlock(&__context.mutex);
150 }
151
152 static client_channel_t *__find_client_channel(int fd)
153 {
154         client_channel_t *channel;
155         GList *iter;
156
157         g_rec_mutex_lock(&__context.mutex);
158         iter = __context.clients;
159         while (iter) {
160                 channel = iter->data;
161                 if (channel->fd == fd) {
162                         g_rec_mutex_unlock(&__context.mutex);
163                         return channel;
164                 }
165
166                 iter = g_list_next(iter);
167         }
168         g_rec_mutex_unlock(&__context.mutex);
169
170         return NULL;
171 }
172
173 static void __invoke_aul_handler(aul_type type, bundle *b)
174 {
175         if (__context.aul.callback)
176                 __context.aul.callback(type, b, __context.aul.user_data);
177 }
178
179 static void __dispatch_app_start(aul_request_h req)
180 {
181         const char *str;
182
183         aul_watch_control_invoke(req->b);
184         __invoke_aul_handler(AUL_START, req->b);
185         str = bundle_get_val(req->b, AUL_K_DATA_CONTROL_TYPE);
186         if (str && !strcmp(str, "CORE")) {
187                 if (__context.dcp.callback)
188                         __context.dcp.callback(req->b, 0, NULL);
189         }
190 }
191
192 static void __dispatch_app_resume(aul_request_h req)
193 {
194         __invoke_aul_handler(AUL_RESUME, NULL);
195 }
196
197 static void __dispatch_app_term_by_pid(aul_request_h req)
198 {
199         __invoke_aul_handler(AUL_TERMINATE, NULL);
200 }
201
202 static void __dispatch_app_term_bgapp_by_pid(aul_request_h req)
203 {
204         __invoke_aul_handler(AUL_TERMINATE_BGAPP, NULL);
205 }
206
207 static void __dispatch_app_term_req_by_pid(aul_request_h req)
208 {
209         if (__context.subapp.is_subapp) {
210                 if (__context.subapp.callback)
211                         __context.subapp.callback(__context.subapp.user_data);
212         } else {
213                 __invoke_aul_handler(AUL_TERMINATE, NULL);
214         }
215 }
216
217 static void __dispatch_app_result(aul_request_h req)
218 {
219         const char *pid_str;
220         int pid = -1;
221
222         pid_str = bundle_get_val(req->b, AUL_K_CALLEE_PID);
223         if (pid_str)
224                 pid = atoi(pid_str);
225
226         app_result(req->cmd, req->b, pid);
227 }
228
229 static void __dispatch_app_pause_by_pid(aul_request_h req)
230 {
231         __invoke_aul_handler(AUL_PAUSE, req->b);
232 }
233
234 static void __dispatch_app_com_message(aul_request_h req)
235 {
236         app_com_recv(req->b);
237 }
238
239 static void __dispatch_app_wake(aul_request_h req)
240 {
241         __invoke_aul_handler(AUL_WAKE, req->b);
242 }
243
244 static void __dispatch_app_suspend(aul_request_h req)
245 {
246         __invoke_aul_handler(AUL_SUSPEND, req->b);
247 }
248
249 static void __dispatch_widget_get_content(aul_request_h req)
250 {
251         const char *widget_id;
252         const char *instance_id;
253         const char *content_info;
254         int fds[2] = { 0, };
255         int r;
256
257         r = aul_sock_recv_reply_sock_fd(req->clifd, &fds, 1);
258         if (r < 0) {
259                 _E("Failed to receive fds");
260                 return;
261         }
262
263         widget_id = bundle_get_val(req->b, AUL_K_WIDGET_ID);
264         if (!widget_id) {
265                 _E("Failed to get widget ID");
266                 aul_sock_send_raw_with_fd(fds[0], -EINVAL, 0, 0,
267                                 AUL_SOCK_NOREPLY);
268                 return;
269         }
270
271         instance_id = bundle_get_val(req->b, AUL_K_WIDGET_INSTANCE_ID);
272         if (!instance_id) {
273                 _E("Failed to get instance ID");
274                 aul_sock_send_raw_with_fd(fds[0], -EINVAL, 0, 0,
275                                 AUL_SOCK_NOREPLY);
276                 return;
277         }
278
279         __invoke_aul_handler(AUL_WIDGET_CONTENT, req->b);
280
281         content_info = bundle_get_val(req->b, AUL_K_WIDGET_CONTENT_INFO);
282         if (content_info) {
283                 r = aul_sock_send_raw_with_fd(fds[0], 0,
284                                 (unsigned char *)content_info,
285                                 strlen(content_info) + 1, AUL_SOCK_NOREPLY);
286         } else {
287                 r = aul_sock_send_raw_with_fd(fds[0], -ENOENT,
288                                 NULL, 0, AUL_SOCK_NOREPLY);
289         }
290
291         if (r < 0) {
292                 _E("Failed to send content info. fd(%d), result(%d)",
293                                 fds[0], r);
294         }
295 }
296
297 static void __dispatch_app_update_requested(aul_request_h req)
298 {
299         __invoke_aul_handler(AUL_UPDATE_REQUESTED, req->b);
300 }
301
302 static void __dispatch_app_term_inst(aul_request_h req)
303 {
304         __invoke_aul_handler(AUL_TERMINATE_INST, req->b);
305 }
306
307 static void __dispatch_app_resume_inst(aul_request_h req)
308 {
309         __invoke_aul_handler(AUL_RESUME, req->b);
310 }
311
312 static void __dispatch_app_pause_inst(aul_request_h req)
313 {
314         __invoke_aul_handler(AUL_PAUSE, req->b);
315 }
316
317 static void __dispatch_app_term_bg_inst(aul_request_h req)
318 {
319         __invoke_aul_handler(AUL_TERMINATE_BG_INST, req->b);
320 }
321
322 static void __dispatch_watchdog_enable(aul_request_h req)
323 {
324         const char *interval_str;
325         unsigned int interval;
326
327         interval_str = bundle_get_val(req->b, AUL_K_INTERVAL);
328         if (!interval_str) {
329                 _E("Invalid request");
330                 return;
331         }
332
333         interval = strtoul(interval_str, NULL, 10);
334         aul_watchdog_start(interval);
335 }
336
337 static void __dispatch_watchdog_disable(aul_request_h req)
338 {
339         aul_watchdog_stop();
340 }
341
342 static void __dispatch_app_connect(aul_request_h req)
343 {
344 }
345
346 static dispatcher __dispatcher[] = {
347         [APP_START] = __dispatch_app_start,
348         [APP_START_RES] = __dispatch_app_start,
349         [APP_START_ASYNC] = __dispatch_app_start,
350         [APP_START_RES_ASYNC] = __dispatch_app_start,
351         [APP_OPEN] = __dispatch_app_resume,
352         [APP_RESUME] = __dispatch_app_resume,
353         [APP_RESUME_BY_PID] = __dispatch_app_resume,
354         [APP_TERM_BY_PID] = __dispatch_app_term_by_pid,
355         [APP_TERM_BY_PID_ASYNC] = __dispatch_app_term_by_pid,
356         [APP_TERM_BY_PID_SYNC] = __dispatch_app_term_by_pid,
357         [APP_TERM_BGAPP_BY_PID] = __dispatch_app_term_bgapp_by_pid,
358         [APP_TERM_REQ_BY_PID] = __dispatch_app_term_req_by_pid,
359         [APP_RESULT] = __dispatch_app_result,
360         [APP_CANCEL] = __dispatch_app_result,
361         [APP_PAUSE_BY_PID] = __dispatch_app_pause_by_pid,
362         [APP_COM_MESSAGE] = __dispatch_app_com_message,
363         [APP_WAKE] = __dispatch_app_wake,
364         [APP_SUSPEND] = __dispatch_app_suspend,
365         [WIDGET_GET_CONTENT] = __dispatch_widget_get_content,
366         [APP_UPDATE_REQUESTED] = __dispatch_app_update_requested,
367         [APP_SEND_LAUNCH_REQUEST] = __dispatch_app_start,
368         [APP_SEND_LAUNCH_REQUEST_SYNC] = __dispatch_app_start,
369         [APP_TERM_INSTANCE_ASYNC] = __dispatch_app_term_inst,
370         [APP_RESUME_INSTANCE] = __dispatch_app_resume_inst,
371         [APP_PAUSE_INSTANCE] = __dispatch_app_pause_inst,
372         [APP_TERM_BG_INSTANCE] = __dispatch_app_term_bg_inst,
373         [WATCHDOG_ENABLE] = __dispatch_watchdog_enable,
374         [WATCHDOG_DISABLE] = __dispatch_watchdog_disable,
375         [APP_CONNECT] = __dispatch_app_connect,
376 };
377
378 static void __destroy_request(struct aul_request_s *req)
379 {
380         if (req->b)
381                 bundle_free(req->b);
382         free(req);
383 }
384
385 static struct aul_request_s *__create_request(int cmd, int clifd, bundle *b)
386 {
387         struct aul_request_s *req;
388
389         req = malloc(sizeof(struct aul_request_s));
390         if (!req) {
391                 _E("Out of memory");
392                 return NULL;
393         }
394
395         req->cmd = cmd;
396         req->clifd = clifd;
397         req->b = b;
398
399         return req;
400 }
401
402 static int __send_result(struct aul_request_s *req, int res)
403 {
404         client_channel_t *channel;
405         int ret;
406
407         if (req->cmd != WIDGET_GET_CONTENT && req->clifd >= 0) {
408                 channel = __find_client_channel(req->clifd);
409                 if (!channel) {
410                         _E("Failed to find client channel. fd(%d)", req->clifd);
411                         return -1;
412                 }
413
414                 g_rec_mutex_lock(&channel->mutex);
415                 ret = aul_sock_send_result_v2(req->clifd, res, false);
416                 g_rec_mutex_unlock(&channel->mutex);
417                 if (ret < 0) {
418                         _E("Failed to send result. cmd(%s:%d)",
419                                         aul_cmd_convert_to_string(req->cmd),
420                                         req->cmd);
421                         return ret;
422                 }
423         }
424
425         return 0;
426 }
427
428 static gboolean __dispatch_request(gpointer data)
429 {
430         struct aul_request_s *req = (struct aul_request_s *)data;
431         int ret;
432
433         if (!__context.initialized) {
434                 _W("Ignore request(%d)", req->cmd);
435                 __destroy_request(req);
436                 return G_SOURCE_REMOVE;
437         }
438
439         aul_worker_remove_anr_timer(__context.worker);
440
441         ret = __send_result(req, 0);
442         if (ret < 0) {
443                 __destroy_request(req);
444                 return G_SOURCE_REMOVE;
445         }
446
447         if (req->cmd >= APP_START && req->cmd < ARRAY_SIZE(__dispatcher) &&
448                         __dispatcher[req->cmd]) {
449                 _W("Command(%s:%d)",
450                                 aul_cmd_convert_to_string(req->cmd), req->cmd);
451                 __dispatcher[req->cmd](req);
452         } else {
453                 _E("Command(%s:%d) is not available",
454                                 aul_cmd_convert_to_string(req->cmd), req->cmd);
455         }
456
457         __destroy_request(req);
458
459         return G_SOURCE_REMOVE;
460 }
461
462 static guint __g_idle_add_full(GMainContext *context, gint priority,
463                 GSourceFunc func, gpointer data)
464 {
465         GSource *source;
466         guint tag;
467
468         source = g_idle_source_new();
469         if (!source)
470                 return 0;
471
472         g_source_set_callback(source, (GSourceFunc)func, data, NULL);
473         g_source_set_priority(source, priority);
474         tag = g_source_attach(source, context);
475         g_source_unref(source);
476
477         return tag;
478 }
479
480 static GMainContext *__get_glib_context(int cmd, bundle *b)
481 {
482         GMainContext *context;
483
484         if (b && bundle_get_type(b, K_SERVICE_THREAD) != BUNDLE_TYPE_NONE)
485                 return NULL;
486
487         switch (cmd) {
488         case APP_START:
489         case APP_START_RES:
490         case APP_START_ASYNC:
491         case APP_START_RES_ASYNC:
492         case APP_OPEN:
493         case APP_RESUME:
494         case APP_RESUME_BY_PID:
495         case APP_PAUSE_BY_PID:
496         case APP_SEND_LAUNCH_REQUEST:
497         case APP_SEND_LAUNCH_REQUEST_SYNC:
498         case APP_RESUME_INSTANCE:
499         case APP_PAUSE_INSTANCE:
500                 context = __context.tizen_context;
501                 break;
502         default:
503                 context = NULL;
504                 break;
505         }
506
507         return context;
508 }
509
510 static void __process_app_pkt(app_pkt_t *pkt, int clifd, aul_worker_h worker)
511 {
512         struct aul_request_s *req;
513         bundle *b = NULL;
514         char *timeout_str;
515         unsigned int timeout = 5000;
516         int ret;
517
518         if (pkt->opt & AUL_SOCK_BUNDLE) {
519                 b = bundle_decode(pkt->data, pkt->len);
520                 if (!b) {
521                         _E("Failed to decode the packet");
522                         return;
523                 }
524
525                 ret = bundle_get_str(b, AUL_K_SOCKET_TIMEOUT, &timeout_str);
526                 if (ret == BUNDLE_ERROR_NONE && isdigit(timeout_str[0])) {
527                         timeout = atoi(timeout_str);
528                         _I("timeout: %ums", timeout);
529                 }
530         }
531
532         req = __create_request(pkt->cmd, clifd, b);
533         if (!req) {
534                 bundle_free(b);
535                 return;
536         }
537
538         aul_worker_add_anr_timer(worker, pkt->cmd, timeout);
539         __g_idle_add_full(__get_glib_context(pkt->cmd, b), G_PRIORITY_DEFAULT,
540                         __dispatch_request, req);
541 }
542
543 static bool __received_event_cb(int fd, int condition, void *user_data)
544 {
545         aul_worker_h worker = user_data;
546         client_channel_t *channel;
547         app_pkt_t *pkt;
548         int ret;
549
550         channel = __find_client_channel(fd);
551         if (!channel) {
552                 _E("Failed to find client channel. fd(%d)", fd);
553                 return false;
554         }
555
556         if (condition & (AUL_IO_HUP | AUL_IO_ERR | AUL_IO_NVAL)) {
557                 _E("IO error occurred. condition(%d), fd(%d)", condition, fd);
558                 aul_worker_remove_io_job(worker, fd);
559                 __remove_client_channel(channel);
560                 __destroy_client_channel(channel);
561                 return true;
562         }
563
564         g_rec_mutex_lock(&channel->mutex);
565         ret = aul_sock_recv_reply_pkt_v2(fd, &pkt, false);
566         g_rec_mutex_unlock(&channel->mutex);
567         if (ret != 0) {
568                 _E("Failed to receive the packet. error(%d)", ret);
569                 return true;
570         }
571
572         __process_app_pkt(pkt, fd, worker);
573         free(pkt);
574
575         return true;
576 }
577
578 static bool __connected_event_cb(int fd, int condition, void *user_data)
579 {
580         int cond = AUL_IO_IN | AUL_IO_HUP | AUL_IO_ERR | AUL_IO_NVAL;
581         aul_worker_h worker = user_data;
582         client_channel_t *channel = NULL;
583         struct ucred cr;
584         int clifd;
585         app_pkt_t *pkt;
586         int ret;
587
588         pkt = aul_sock_recv_pkt(fd, &clifd, &cr);
589         if (!pkt) {
590                 _E("Failed to receive the packet");
591                 return true;
592         }
593
594         _W("pid(%d), clifd(%d), cmd(%d)", cr.pid, clifd, pkt->cmd);
595         if (pkt->cmd != WIDGET_GET_CONTENT) {
596                 if (pkt->opt & AUL_SOCK_NOREPLY) {
597                         close(clifd);
598                         clifd = -1;
599                 } else {
600                         channel = __create_client_channel(clifd,
601                                         cr.pid, cr.uid);
602                         if (!channel) {
603                                 free(pkt);
604                                 close(clifd);
605                                 return true;
606                         }
607
608                         __add_client_channel(channel);
609                 }
610         }
611
612         __process_app_pkt(pkt, clifd, worker);
613
614         if (pkt->cmd == WIDGET_GET_CONTENT)
615                 clifd = -1;
616
617         free(pkt);
618
619         if (clifd < 0)
620                 return true;
621
622         ret = aul_worker_add_io_job(worker, "client", clifd, cond, true,
623                         __received_event_cb, worker);
624         if (ret < 0) {
625                 _E("Failed to add io job. error(%d)", ret);
626                 __remove_client_channel(channel);
627                 __destroy_client_channel(channel);
628                 return true;
629         }
630
631         return true;
632 }
633
634 static void __finalize_context(void)
635 {
636         g_rec_mutex_lock(&__context.init_mutex);
637         if (!__context.initialized) {
638                 g_rec_mutex_unlock(&__context.init_mutex);
639                 return;
640         }
641
642         __context.touch_argv_handler = false;
643
644         aul_launch_worker_fini();
645
646         if (__context.tizen_context) {
647                 g_main_context_unref(__context.tizen_context);
648                 __context.tizen_context = NULL;
649         }
650
651         if (__context.clients) {
652                 g_list_free_full(__context.clients, __destroy_client_channel);
653                 __context.clients = NULL;
654         }
655
656         if (g_rec_mutex_trylock(&__context.mutex))
657                 g_rec_mutex_unlock(&__context.mutex);
658
659         g_rec_mutex_clear(&__context.mutex);
660
661         __context.initialized = false;
662         g_rec_mutex_unlock(&__context.init_mutex);
663 }
664
665 static GMainContext *__get_tizen_glib_context(void)
666 {
667         GMainContext *context;
668         const char *env;
669
670         env = getenv("TIZEN_GLIB_CONTEXT");
671         if (env)
672                 context = (GMainContext *)strtoul(env, NULL, 10);
673         else
674                 context = NULL;
675
676         return context;
677 }
678
679 static int __initialize_context(void)
680 {
681         GMainContext *context;
682         int ret;
683         int fd;
684
685         if (__context.initialized) {
686                 _E("Already initialized");
687                 context = __get_tizen_glib_context();
688                 if (context)
689                         __context.tizen_context = g_main_context_ref(context);
690
691                 return AUL_R_OK;
692         }
693
694         fd = aul_initialize();
695         if (fd < 0) {
696                 _E("Failed to initialize aul");
697                 return fd;
698         }
699
700         g_rec_mutex_init(&__context.mutex);
701
702         context = __get_tizen_glib_context();
703         if (context)
704                 __context.tizen_context = g_main_context_ref(context);
705         else
706                 __context.tizen_context = NULL;
707
708         if (aul_launch_worker_init() != AUL_R_OK) {
709                 __finalize_context();
710                 return AUL_R_ERROR;
711         }
712
713         ret = aul_worker_add_io_job(__context.worker, "server", fd, AUL_IO_IN,
714                         false, __connected_event_cb, __context.worker);
715         if (ret < 0) {
716                 __finalize_context();
717                 return ret;
718         }
719
720         __context.initialized = true;
721
722         return AUL_R_OK;
723 }
724
725 API int aul_launch_worker_init(void)
726 {
727         if (__context.worker != NULL)
728                 return AUL_R_OK;
729
730         __context.worker = aul_worker_create("aul+");
731         if (__context.worker == NULL)
732                 return AUL_R_ERROR;
733
734         return AUL_R_OK;
735 }
736
737 API void aul_launch_worker_fini(void)
738 {
739         if (__context.worker == NULL)
740                 return;
741
742         aul_worker_destroy(__context.worker);
743         __context.worker = NULL;
744 }
745
746 API int aul_launch_init(aul_handler_fn callback, void *user_data)
747 {
748         int ret;
749
750         g_rec_mutex_lock(&__context.init_mutex);
751         if (callback) {
752                 __context.aul.callback = callback;
753                 __context.aul.user_data = user_data;
754         }
755
756         ret = __initialize_context();
757         g_rec_mutex_unlock(&__context.init_mutex);
758
759         return ret;
760 }
761
762 API int aul_launch_fini(void)
763 {
764         __finalize_context();
765         return AUL_R_OK;
766 }
767
768 static gboolean __app_start_cb(gpointer data)
769 {
770         bundle *b = (bundle *)data;
771         struct aul_request_s req = {
772                 .cmd = APP_START,
773                 .clifd = 0,
774                 .b = b
775         };
776
777         __dispatch_app_start(&req);
778
779         if (req.b)
780                 bundle_free(req.b);
781
782         return G_SOURCE_REMOVE;
783 }
784
785 API int aul_launch_argv_handler(int argc, char **argv)
786 {
787         bundle *b;
788
789         if (!aul_is_initialized()) {
790                 _E("AUL is not initialized");
791                 return AUL_R_ENOINIT;
792         }
793
794         if (__context.touch_argv_handler) {
795                 _E("Already registered");
796                 return AUL_R_OK;
797         }
798
799         b = bundle_import_from_argv(argc, argv);
800         if (!b)
801                 _E("Bundle is nullptr");
802
803         if (!__g_idle_add_full(__get_glib_context(APP_START, b),
804                                 G_PRIORITY_HIGH, __app_start_cb, b)) {
805                 _E("Failed to add idler");
806                 return AUL_R_ERROR;
807         }
808
809         __context.touch_argv_handler = true;
810         return AUL_R_OK;
811 }
812
813 API int aul_launch_local(bundle *b)
814 {
815         if (!aul_is_initialized()) {
816                 _E("AUL is not initialized");
817                 return AUL_R_ENOINIT;
818         }
819
820         if (!b)
821                 _E("Bundle is nullptr");
822
823         if (!__g_idle_add_full(__get_glib_context(APP_START, b),
824                                 G_PRIORITY_DEFAULT, __app_start_cb, b)) {
825                 _E("Failed to add idler");
826                 return AUL_R_ERROR;
827         }
828
829         return AUL_R_OK;
830 }
831
832 int aul_resume_local(void)
833 {
834         if (!aul_is_initialized()) {
835                 _E("AUL is not initialized");
836                 return AUL_R_ENOINIT;
837         }
838
839         __dispatch_app_resume(NULL);
840
841         return AUL_R_OK;
842 }
843
844 API int aul_set_subapp(subapp_fn callback, void *user_data)
845 {
846         __context.subapp.is_subapp = true;
847         __context.subapp.callback = callback;
848         __context.subapp.user_data = user_data;
849
850         return AUL_R_OK;
851 }
852
853 API int aul_is_subapp(void)
854 {
855         return (int)__context.subapp.is_subapp;
856 }
857
858 API int aul_set_data_control_provider_cb(data_control_provider_handler_fn cb)
859 {
860         __context.dcp.callback = cb;
861
862         return AUL_R_OK;
863 }
864
865 API int aul_unset_data_control_provider_cb(void)
866 {
867         __context.dcp.callback = NULL;
868
869         return AUL_R_OK;
870 }