78120d05493547cf11ddfe465493a12b21664bf3
[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)
511 {
512         struct aul_request_s *req;
513         bundle *b = NULL;
514
515         if (pkt->opt & AUL_SOCK_BUNDLE) {
516                 b = bundle_decode(pkt->data, pkt->len);
517                 if (!b) {
518                         _E("Failed to decode the packet");
519                         return;
520                 }
521         }
522
523         req = __create_request(pkt->cmd, clifd, b);
524         if (!req) {
525                 bundle_free(b);
526                 return;
527         }
528
529         __g_idle_add_full(__get_glib_context(pkt->cmd, b), G_PRIORITY_DEFAULT,
530                         __dispatch_request, req);
531 }
532
533 static bool __received_event_cb(int fd, int condition, void *user_data)
534 {
535         aul_worker_h worker = user_data;
536         client_channel_t *channel;
537         app_pkt_t *pkt;
538         int ret;
539
540         channel = __find_client_channel(fd);
541         if (!channel) {
542                 _E("Failed to find client channel. fd(%d)", fd);
543                 return false;
544         }
545
546         if (condition & (AUL_IO_HUP | AUL_IO_ERR | AUL_IO_NVAL)) {
547                 _E("IO error occurred. condition(%d), fd(%d)", condition, fd);
548                 __remove_client_channel(channel);
549                 __destroy_client_channel(channel);
550                 return false;
551         }
552
553         g_rec_mutex_lock(&channel->mutex);
554         ret = aul_sock_recv_reply_pkt_v2(fd, &pkt, false);
555         g_rec_mutex_unlock(&channel->mutex);
556         if (ret != 0) {
557                 _E("Failed to receive the packet. error(%d)", ret);
558                 return true;
559         }
560
561         aul_worker_add_anr_timer(worker, pkt->cmd);
562         __process_app_pkt(pkt, fd);
563         free(pkt);
564
565         return true;
566 }
567
568 static bool __connected_event_cb(int fd, int condition, void *user_data)
569 {
570         int cond = AUL_IO_IN | AUL_IO_HUP | AUL_IO_ERR | AUL_IO_NVAL;
571         aul_worker_h worker = user_data;
572         client_channel_t *channel = NULL;
573         struct ucred cr;
574         int clifd;
575         app_pkt_t *pkt;
576         int ret;
577
578         pkt = aul_sock_recv_pkt(fd, &clifd, &cr);
579         if (!pkt) {
580                 _E("Failed to receive the packet");
581                 return true;
582         }
583
584         _W("pid(%d), clifd(%d), cmd(%d)", cr.pid, clifd, pkt->cmd);
585         if (pkt->cmd != WIDGET_GET_CONTENT) {
586                 if (pkt->opt & AUL_SOCK_NOREPLY) {
587                         close(clifd);
588                         clifd = -1;
589                 } else {
590                         channel = __create_client_channel(clifd,
591                                         cr.pid, cr.uid);
592                         if (!channel) {
593                                 free(pkt);
594                                 close(clifd);
595                                 return true;
596                         }
597
598                         __add_client_channel(channel);
599                 }
600         }
601
602         aul_worker_add_anr_timer(worker, pkt->cmd);
603         __process_app_pkt(pkt, clifd);
604
605         if (pkt->cmd == WIDGET_GET_CONTENT)
606                 clifd = -1;
607
608         free(pkt);
609
610         if (clifd < 0)
611                 return true;
612
613         ret = aul_worker_add_io_job(worker, "client", clifd, cond,
614                                 __received_event_cb, worker);
615         if (ret < 0) {
616                 _E("Failed to add io job. error(%d)", ret);
617                 __remove_client_channel(channel);
618                 __destroy_client_channel(channel);
619                 return true;
620         }
621
622         return true;
623 }
624
625 static void __finalize_context(void)
626 {
627         g_rec_mutex_lock(&__context.init_mutex);
628         if (!__context.initialized) {
629                 g_rec_mutex_unlock(&__context.init_mutex);
630                 return;
631         }
632
633         __context.touch_argv_handler = false;
634
635         if (__context.worker) {
636                 aul_worker_destroy(__context.worker);
637                 __context.worker = NULL;
638         }
639
640         if (__context.tizen_context) {
641                 g_main_context_unref(__context.tizen_context);
642                 __context.tizen_context = NULL;
643         }
644
645         if (__context.clients) {
646                 g_list_free_full(__context.clients, __destroy_client_channel);
647                 __context.clients = NULL;
648         }
649
650         if (g_rec_mutex_trylock(&__context.mutex))
651                 g_rec_mutex_unlock(&__context.mutex);
652
653         g_rec_mutex_clear(&__context.mutex);
654
655         __context.initialized = false;
656         g_rec_mutex_unlock(&__context.init_mutex);
657 }
658
659 static GMainContext *__get_tizen_glib_context(void)
660 {
661         GMainContext *context;
662         const char *env;
663
664         env = getenv("TIZEN_GLIB_CONTEXT");
665         if (env)
666                 context = (GMainContext *)strtoul(env, NULL, 10);
667         else
668                 context = NULL;
669
670         return context;
671 }
672
673 static int __initialize_context(void)
674 {
675         GMainContext *context;
676         int ret;
677         int fd;
678
679         if (__context.initialized) {
680                 _E("Already initialized");
681                 context = __get_tizen_glib_context();
682                 if (context)
683                         __context.tizen_context = g_main_context_ref(context);
684
685                 return AUL_R_OK;
686         }
687
688         fd = aul_initialize();
689         if (fd < 0) {
690                 _E("Failed to initialize aul");
691                 return fd;
692         }
693
694         g_rec_mutex_init(&__context.mutex);
695
696         context = __get_tizen_glib_context();
697         if (context)
698                 __context.tizen_context = g_main_context_ref(context);
699         else
700                 __context.tizen_context = NULL;
701
702         __context.worker = aul_worker_create("aul+");
703         if (!__context.worker) {
704                 __finalize_context();
705                 return AUL_R_ERROR;
706         }
707
708         ret = aul_worker_add_io_job(__context.worker, "server", fd, AUL_IO_IN,
709                         __connected_event_cb, __context.worker);
710         if (ret < 0) {
711                 __finalize_context();
712                 return ret;
713         }
714
715         __context.initialized = true;
716
717         return AUL_R_OK;
718 }
719
720 API int aul_launch_init(aul_handler_fn callback, void *user_data)
721 {
722         int ret;
723
724         g_rec_mutex_lock(&__context.init_mutex);
725         if (callback) {
726                 __context.aul.callback = callback;
727                 __context.aul.user_data = user_data;
728         }
729
730         ret = __initialize_context();
731         g_rec_mutex_unlock(&__context.init_mutex);
732
733         return ret;
734 }
735
736 API int aul_launch_fini(void)
737 {
738         __finalize_context();
739         return AUL_R_OK;
740 }
741
742 static gboolean __app_start_cb(gpointer data)
743 {
744         bundle *b = (bundle *)data;
745         struct aul_request_s req = {
746                 .cmd = APP_START,
747                 .clifd = 0,
748                 .b = b
749         };
750
751         __dispatch_app_start(&req);
752
753         if (req.b)
754                 bundle_free(req.b);
755
756         return G_SOURCE_REMOVE;
757 }
758
759 API int aul_launch_argv_handler(int argc, char **argv)
760 {
761         bundle *b;
762
763         if (!aul_is_initialized()) {
764                 _E("AUL is not initialized");
765                 return AUL_R_ENOINIT;
766         }
767
768         if (__context.touch_argv_handler) {
769                 _E("Already registered");
770                 return AUL_R_OK;
771         }
772
773         b = bundle_import_from_argv(argc, argv);
774         if (!b)
775                 _E("Bundle is nullptr");
776
777         if (!__g_idle_add_full(__get_glib_context(APP_START, b),
778                                 G_PRIORITY_HIGH, __app_start_cb, b)) {
779                 _E("Failed to add idler");
780                 return AUL_R_ERROR;
781         }
782
783         __context.touch_argv_handler = true;
784         return AUL_R_OK;
785 }
786
787 API int aul_launch_local(bundle *b)
788 {
789         if (!aul_is_initialized()) {
790                 _E("AUL is not initialized");
791                 return AUL_R_ENOINIT;
792         }
793
794         if (!b)
795                 _E("Bundle is nullptr");
796
797         if (!__g_idle_add_full(__get_glib_context(APP_START, b),
798                                 G_PRIORITY_DEFAULT, __app_start_cb, b)) {
799                 _E("Failed to add idler");
800                 return AUL_R_ERROR;
801         }
802
803         return AUL_R_OK;
804 }
805
806 int aul_resume_local(void)
807 {
808         if (!aul_is_initialized()) {
809                 _E("AUL is not initialized");
810                 return AUL_R_ENOINIT;
811         }
812
813         __dispatch_app_resume(NULL);
814
815         return AUL_R_OK;
816 }
817
818 API int aul_set_subapp(subapp_fn callback, void *user_data)
819 {
820         __context.subapp.is_subapp = true;
821         __context.subapp.callback = callback;
822         __context.subapp.user_data = user_data;
823
824         return AUL_R_OK;
825 }
826
827 API int aul_is_subapp(void)
828 {
829         return (int)__context.subapp.is_subapp;
830 }
831
832 API int aul_set_data_control_provider_cb(data_control_provider_handler_fn cb)
833 {
834         __context.dcp.callback = cb;
835
836         return AUL_R_OK;
837 }
838
839 API int aul_unset_data_control_provider_cb(void)
840 {
841         __context.dcp.callback = NULL;
842
843         return AUL_R_OK;
844 }