Add abstract app core APIs
[platform/core/appfw/app-core.git] / src / base / appcore_base.c
1 /*
2  * Copyright (c) 2016 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
18 #define _GNU_SOURCE
19
20 #include <stdbool.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <malloc.h>
27 #include <locale.h>
28 #include <libintl.h>
29 #include <linux/limits.h>
30 #include <glib.h>
31 #include <gio/gio.h>
32 #include <sys/time.h>
33 #include <dlfcn.h>
34 #include <vconf.h>
35 #include <aul.h>
36 #include <bundle_internal.h>
37 #include "appcore_base.h"
38 #include "appcore_base_private.h"
39
40 #define PATH_LOCALE "locale"
41 #define RESOURCED_FREEZER_PATH "/Org/Tizen/Resourced/Freezer"
42 #define RESOURCED_FREEZER_INTERFACE "org.tizen.resourced.freezer"
43 #define RESOURCED_FREEZER_SIGNAL "FreezerState"
44
45 typedef struct _appcore_base_context {
46         appcore_base_ops ops;
47         void *data;
48         int argc;
49         char **argv;
50         unsigned int tid;
51         bool suspended_state;
52         bool allowed_bg;
53 } appcore_base_context;
54
55 typedef struct _appcore_base_event_node {
56         int type;
57         appcore_base_event_cb cb;
58         void *data;
59 } appcore_base_event_node;
60
61 static appcore_base_context __context;
62 static GList *__events;
63 static GDBusConnection *__bus;
64 static guint __suspend_dbus_handler_initialized;
65
66 static void __invoke_callback(void *event, int type)
67 {
68         GList *iter = __events;
69
70         while (iter) {
71                 appcore_base_event_node *node = iter->data;
72
73                 if (node->type == type)
74                         node->cb(event, node->data);
75                 iter = g_list_next(iter);
76         }
77 }
78
79 static bool __exist_callback(int type)
80 {
81         GList *iter = __events;
82
83         while (iter) {
84                 appcore_base_event_node *node = iter->data;
85
86                 if (node->type == type)
87                         return true;
88
89                 iter = g_list_next(iter);
90         }
91
92         return false;
93 }
94
95 static void __on_low_memory(keynode_t *key, void *data)
96 {
97         int val;
98
99         val = vconf_keynode_get_int(key);
100
101         if (val >= VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) {
102                 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_MEMORY);
103                 malloc_trim(0);
104         }
105 }
106
107 static void __on_low_battery(keynode_t *key, void *data)
108 {
109         int val;
110
111         val = vconf_keynode_get_int(key);
112
113         if (val <= VCONFKEY_SYSMAN_BAT_CRITICAL_LOW)
114                 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_BATTERY);
115 }
116
117 static void __update_lang(void)
118 {
119         char language[32];
120         char *lang;
121         char *r;
122
123         lang = vconf_get_str(VCONFKEY_LANGSET);
124         if (lang) {
125                 snprintf(language, sizeof(language), "%s:en_US:en_GB:en", lang);
126                 setenv("LANGUAGE", language, 1);
127                 setenv("LANG", lang, 1);
128                 setenv("LC_MESSAGES", lang, 1);
129                 r = setlocale(LC_ALL, "");
130                 if (r == NULL) {
131                         r = setlocale(LC_ALL, lang);
132                         if (r != NULL)
133                                 _DBG("*****appcore setlocale=%s\n", r);
134                 }
135                 free(lang);
136         }
137 }
138
139 static void __update_region(void)
140 {
141         char *region;
142         char *r;
143
144         region = vconf_get_str(VCONFKEY_REGIONFORMAT);
145         if (region) {
146                 setenv("LC_CTYPE", region, 1);
147                 setenv("LC_NUMERIC", region, 1);
148                 setenv("LC_TIME", region, 1);
149                 setenv("LC_COLLATE", region, 1);
150                 setenv("LC_MONETARY", region, 1);
151                 setenv("LC_PAPER", region, 1);
152                 setenv("LC_NAME", region, 1);
153                 setenv("LC_ADDRESS", region, 1);
154                 setenv("LC_TELEPHONE", region, 1);
155                 setenv("LC_MEASUREMENT", region, 1);
156                 setenv("LC_IDENTIFICATION", region, 1);
157                 r = setlocale(LC_ALL, "");
158                 if (r != NULL)
159                         _DBG("*****appcore setlocale=%s\n", r);
160
161                 free(region);
162         }
163 }
164
165 static void __on_language_change(keynode_t *key, void *data)
166 {
167         char *val;
168
169         val = vconf_keynode_get_str(key);
170
171         __update_lang();
172         __invoke_callback((void *)val, APPCORE_BASE_EVENT_LANG_CHANGE);
173 }
174
175 static void __on_region_change(keynode_t *key, void *data)
176 {
177         char *val = NULL;
178         const char *name;
179
180         name = vconf_keynode_get_name(key);
181         if (!strcmp(name, VCONFKEY_REGIONFORMAT))
182                 val = vconf_keynode_get_str(key);
183
184         __update_region();
185         __invoke_callback((void *)val, APPCORE_BASE_EVENT_REGION_CHANGE);
186 }
187
188 static gboolean __flush_memory(gpointer data)
189 {
190         int suspend = APPCORE_BASE_SUSPENDED_STATE_WILL_ENTER_SUSPEND;
191
192         appcore_base_flush_memory();
193         __context.tid = 0;
194
195         if (!__context.allowed_bg && !__context.suspended_state) {
196                 _DBG("[__SUSPEND__] flush case");
197                 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
198                 __context.suspended_state = true;
199         }
200
201         return FALSE;
202 }
203
204 static void __add_suspend_timer(void)
205 {
206         __context.tid = g_timeout_add_seconds(5, __flush_memory, NULL);
207 }
208
209 static void __remove_suspend_timer(void)
210 {
211         if (__context.tid > 0) {
212                 g_source_remove(__context.tid);
213                 __context.tid = 0;
214         }
215 }
216
217 static void __on_receive_suspend_signal(GDBusConnection *connection,
218                                         const gchar *sender_name,
219                                         const gchar *object_path,
220                                         const gchar *interface_name,
221                                         const gchar *signal_name,
222                                         GVariant *parameters,
223                                         gpointer user_data)
224 {
225         gint suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
226         gint pid;
227         gint status;
228
229         if (g_strcmp0(signal_name, RESOURCED_FREEZER_SIGNAL) == 0) {
230                 g_variant_get(parameters, "(ii)", &status, &pid);
231                 if (pid == getpid() && status == 0) {
232                         if (!__context.allowed_bg && __context.suspended_state) {
233                                 __remove_suspend_timer();
234                                 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
235                                 __context.suspended_state = false;
236                                 __add_suspend_timer();
237                         }
238                 }
239         }
240 }
241
242 static int __init_suspend_dbus_handler(void)
243 {
244         GError *err = NULL;
245
246         if (__suspend_dbus_handler_initialized)
247                 return 0;
248
249         if (!__bus) {
250                 __bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
251                 if (!__bus) {
252                         _ERR("Failed to connect to the D-BUS daemon: %s",
253                                                 err->message);
254                         g_error_free(err);
255                         return -1;
256                 }
257         }
258
259         __suspend_dbus_handler_initialized = g_dbus_connection_signal_subscribe(
260                                                 __bus,
261                                                 NULL,
262                                                 RESOURCED_FREEZER_INTERFACE,
263                                                 RESOURCED_FREEZER_SIGNAL,
264                                                 RESOURCED_FREEZER_PATH,
265                                                 NULL,
266                                                 G_DBUS_SIGNAL_FLAGS_NONE,
267                                                 __on_receive_suspend_signal,
268                                                 NULL,
269                                                 NULL);
270         if (__suspend_dbus_handler_initialized == 0) {
271                 _ERR("g_dbus_connection_signal_subscribe() is failed.");
272                 return -1;
273         }
274
275         _DBG("[__SUSPEND__] suspend signal initialized");
276
277         return 0;
278 }
279
280 static void __fini_suspend_dbus_handler(void)
281 {
282         if (__bus == NULL)
283                 return;
284
285         if (__suspend_dbus_handler_initialized) {
286                 g_dbus_connection_signal_unsubscribe(__bus,
287                                 __suspend_dbus_handler_initialized);
288                 __suspend_dbus_handler_initialized = 0;
289         }
290
291         g_object_unref(__bus);
292         __bus = NULL;
293 }
294
295 static int __get_locale_resource_dir(char *locale_dir, int size)
296 {
297         const char *res_path;
298
299         res_path = aul_get_app_resource_path();
300         if (res_path == NULL) {
301                 _ERR("Failed to get resource path");
302                 return -1;
303         }
304
305         snprintf(locale_dir, size, "%s" PATH_LOCALE, res_path);
306         if (access(locale_dir, R_OK) != 0)
307                 return -1;
308
309         return 0;
310 }
311
312 static int __get_app_name(const char *appid, char **name)
313 {
314         char *name_token = NULL;
315
316         if (appid == NULL)
317                 return -1;
318
319         /* com.vendor.name -> name */
320         name_token = strrchr(appid, '.');
321         if (name_token == NULL)
322                 return -1;
323
324         name_token++;
325
326         *name = strdup(name_token);
327         if (*name == NULL)
328                 return -1;
329
330         return 0;
331 }
332
333 static int __set_i18n(const char *domain, const char *dir)
334 {
335         char *r;
336         char *lan;
337
338         if (domain == NULL) {
339                 errno = EINVAL;
340                 return -1;
341         }
342
343         __update_lang();
344         __update_region();
345
346         r = setlocale(LC_ALL, "");
347         /* if locale is not set properly, try again to set as language base */
348         if (r == NULL) {
349                 lan = vconf_get_str(VCONFKEY_LANGSET);
350                 if (lan != NULL) {
351                         r = setlocale(LC_ALL, lan);
352                         _DBG("*****appcore setlocale=%s\n", r);
353                         free(lan);
354                 }
355         }
356         if (r == NULL)
357                 _ERR("appcore: setlocale() error");
358
359         r = bindtextdomain(domain, dir);
360         if (r == NULL)
361                 _ERR("appcore: bindtextdomain() error");
362
363         r = textdomain(domain);
364         if (r == NULL)
365                 _ERR("appcore: textdomain() error");
366
367         return 0;
368 }
369
370 EXPORT_API int appcore_base_on_set_i18n(void)
371 {
372         int r;
373         char locale_dir[PATH_MAX];
374         char appid[PATH_MAX];
375         char *name = NULL;
376
377         r = aul_app_get_appid_bypid(getpid(), appid, PATH_MAX);
378         if (r < 0)
379                 return -1;
380
381         r = __get_app_name(appid, &name);
382         if (r < 0)
383                 return -1;
384
385         r = __get_locale_resource_dir(locale_dir, sizeof(locale_dir));
386         if (r < 0) {
387                 free(name);
388                 return -1;
389         }
390
391         r = __set_i18n(name, locale_dir);
392         if (r < 0) {
393                 free(name);
394                 return -1;
395         }
396
397         free(name);
398
399         return 0;
400 }
401
402 EXPORT_API int appcore_base_init(appcore_base_ops ops, int argc, char **argv, void *data)
403 {
404         __context.ops = ops;
405         __context.argc = argc;
406         __context.argv = argv;
407         __context.data = data;
408         __context.tid = 0;
409         __context.suspended_state = false;
410         __context.allowed_bg = false;
411
412         if (__context.ops.set_i18n)
413                 __context.ops.set_i18n(__context.data);
414
415         __init_suspend_dbus_handler();
416
417         if (__context.ops.create && __context.ops.create(__context.data) < 0) {
418                 aul_status_update(STATUS_DYING);
419                 return 0;
420         }
421
422         if (__context.ops.run)
423                 __context.ops.run(__context.data);
424
425         return 0;
426 }
427
428 EXPORT_API void appcore_base_fini(void)
429 {
430         int i;
431
432         for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
433                 if (__exist_callback(i)) {
434                         if (__context.ops.unset_event)
435                                 __context.ops.unset_event(i, __context.data);
436                 }
437         }
438
439         g_list_free_full(__events, free);
440         __events = NULL;
441         __fini_suspend_dbus_handler();
442
443         aul_status_update(STATUS_DYING);
444         if (__context.ops.terminate)
445                 __context.ops.terminate(__context.data);
446
447 }
448
449 EXPORT_API int appcore_base_flush_memory(void)
450 {
451         malloc_trim(0);
452         return 0;
453 }
454
455 EXPORT_API int appcore_base_on_receive(aul_type type, bundle *b)
456 {
457         int ret;
458         const char **tep_path;
459         int len = 0;
460         int i;
461         const char *bg;
462         int dummy = 0;
463
464         switch (type) {
465         case AUL_START:
466                 _DBG("[APP %d]     AUL event: AUL_START", getpid());
467                 tep_path = bundle_get_str_array(b, AUL_TEP_PATH, &len);
468                 if (tep_path) {
469                         for (i = 0; i < len; i++) {
470                                 ret = aul_check_tep_mount(tep_path[i]);
471                                 if (ret == -1) {
472                                         _ERR("mount request not completed within 1 sec");
473                                         exit(-1);
474                                 }
475                         }
476                 }
477
478                 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
479                 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
480                         _DBG("[__SUSPEND__] allowed background");
481                         __context.allowed_bg = true;
482                         __remove_suspend_timer();
483                 }
484
485                 if (__context.ops.control)
486                        __context.ops.control(b, __context.data);
487                 break;
488         case AUL_RESUME:
489                 _DBG("[APP %d]     AUL event: AUL_RESUME", getpid());
490                 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
491                 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
492                         _DBG("[__SUSPEND__] allowed background");
493                         __context.allowed_bg = true;
494                         __remove_suspend_timer();
495                 }
496                 break;
497         case AUL_TERMINATE:
498                 _DBG("[APP %d]     AUL event: AUL_TERMINATE", getpid());
499                 aul_status_update(STATUS_DYING);
500                 if (!__context.allowed_bg)
501                         __remove_suspend_timer();
502
503                 if (__context.ops.exit)
504                         __context.ops.exit(__context.data);
505                 break;
506         case AUL_TERMINATE_BGAPP:
507                 _DBG("[APP %d]     AUL event: AUL_TERMINATE_BGAPP", getpid());
508                 if (!__context.allowed_bg)
509                         __remove_suspend_timer();
510                 break;
511         case AUL_WAKE:
512                 _DBG("[APP %d]     AUL event: AUL_WAKE", getpid());
513                 if (!__context.allowed_bg && __context.suspended_state) {
514                         int suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
515                         __remove_suspend_timer();
516                         __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
517                         __context.suspended_state = false;
518                 }
519                 break;
520         case AUL_SUSPEND:
521                 _DBG("[APP %d]     AUL event: AUL_SUSPEND", getpid());
522                 if (!__context.allowed_bg && !__context.suspended_state) {
523                         __remove_suspend_timer();
524                         __flush_memory(NULL);
525                 }
526                 break;
527         case AUL_UPDATE_REQUESTED:
528                 _DBG("[APP %d]     AUL event: AUL_UPDATE_REQUESTED", getpid());
529                 __invoke_callback((void *)&dummy, APPCORE_BASE_EVENT_UPDATE_REQUESTED);
530                 break;
531         default:
532                 _DBG("[APP %d]     AUL event: %d", getpid(), type);
533                 /* do nothing */
534                 break;
535         }
536
537         return 0;
538 }
539
540 EXPORT_API int appcore_base_on_create(void)
541 {
542         int r;
543         r = aul_launch_init(__context.ops.receive, NULL);
544         if (r < 0) {
545                 _ERR("Aul init failed: %d", r);
546                 return -1;
547         }
548
549         r = aul_launch_argv_handler(__context.argc, __context.argv);
550         if (r < 0) {
551                 _ERR("Aul argv handler failed: %d", r);
552                 return -1;
553         }
554
555         return 0;
556 }
557
558 EXPORT_API int appcore_base_on_control(bundle *b)
559 {
560         return 0;
561 }
562
563 EXPORT_API int appcore_base_on_terminate()
564 {
565         aul_finalize();
566         if (__context.ops.exit)
567                 __context.ops.exit(__context.data);
568
569         return 0;
570 }
571
572 EXPORT_API void appcore_base_on_set_event(enum appcore_base_event event)
573 {
574         int r;
575
576         switch (event) {
577         case APPCORE_BASE_EVENT_LOW_MEMORY:
578                 vconf_notify_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory, NULL);
579                 break;
580         case APPCORE_BASE_EVENT_LOW_BATTERY:
581                 vconf_notify_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery, NULL);
582                 break;
583         case APPCORE_BASE_EVENT_LANG_CHANGE:
584                 vconf_notify_key_changed(VCONFKEY_LANGSET, __on_language_change, NULL);
585                 break;
586         case APPCORE_BASE_EVENT_REGION_CHANGE:
587                 r = vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change, NULL);
588                 if (r < 0)
589                         break;
590
591                 vconf_notify_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change, NULL);
592                 break;
593         case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
594                 break;
595
596         default:
597                 break;
598         }
599
600 }
601
602 EXPORT_API void appcore_base_on_unset_event(enum appcore_base_event event)
603 {
604         int r;
605
606         switch (event) {
607         case APPCORE_BASE_EVENT_LOW_MEMORY:
608                 vconf_ignore_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory);
609                 break;
610         case APPCORE_BASE_EVENT_LOW_BATTERY:
611                 vconf_ignore_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery);
612                 break;
613         case APPCORE_BASE_EVENT_LANG_CHANGE:
614                 vconf_ignore_key_changed(VCONFKEY_LANGSET, __on_language_change);
615                 break;
616         case APPCORE_BASE_EVENT_REGION_CHANGE:
617                 r = vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change);
618                 if (r < 0)
619                         break;
620                 vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change);
621                 break;
622         case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
623                 break;
624         default:
625                 break;
626         }
627 }
628
629 EXPORT_API appcore_base_event_h appcore_base_add_event(enum appcore_base_event event,
630                 appcore_base_event_cb cb, void *data)
631 {
632         appcore_base_event_node *node;
633
634         if (!__exist_callback(event)) {
635                 if (__context.ops.set_event)
636                         __context.ops.set_event(event, __context.data);
637         }
638
639         node = malloc(sizeof(appcore_base_event_node));
640
641         if (node == NULL)
642                 return NULL;
643
644         node->cb = cb;
645         node->type = event;
646         node->data = data;
647         __events = g_list_append(__events, node);
648
649         return node;
650 }
651
652 EXPORT_API int appcore_base_remove_event(appcore_base_event_h handle)
653 {
654         appcore_base_event_node *node = handle;
655         enum appcore_base_event event;
656
657         event = node->type;
658         __events = g_list_remove(__events, node);
659         free(node);
660         if (!__exist_callback(event)) {
661                 if (__context.ops.unset_event)
662                         __context.ops.unset_event(event, __context.data);
663         }
664
665         return 0;
666 }
667
668 EXPORT_API int appcore_base_raise_event(void *event, enum appcore_base_event type)
669 {
670         __invoke_callback(event, type);
671         return 0;
672 }
673
674 EXPORT_API int appcore_base_get_rotation_state(enum appcore_base_rm *curr)
675 {
676         return 0;
677 }
678
679 EXPORT_API bool appcore_base_is_bg_allowed(void)
680 {
681         return __context.allowed_bg;
682 }
683
684 EXPORT_API bool appcore_base_is_suspended(void)
685 {
686         return __context.suspended_state;
687 }
688
689 EXPORT_API void appcore_base_toggle_suspended_state(void)
690 {
691         __context.suspended_state ^= __context.suspended_state;
692 }
693
694 static int __on_receive(aul_type type, bundle *b, void *data)
695 {
696         return appcore_base_on_receive(type, b);
697 }
698
699 static int __on_create(void *data)
700 {
701         return appcore_base_on_create();
702 }
703
704 static int __on_control(bundle *b, void *data)
705 {
706         return appcore_base_on_control(b);
707 }
708
709 static int __on_terminate(void *data)
710 {
711         return appcore_base_on_terminate();
712 }
713
714 static int __on_set_i18n(void *data)
715 {
716         return appcore_base_on_set_i18n();
717 }
718
719 static void __on_set_event(enum appcore_base_event event, void *data)
720 {
721         return appcore_base_on_set_event(event);
722 }
723
724 static void __on_unset_event(enum appcore_base_event event, void *data)
725 {
726         return appcore_base_on_unset_event(event);
727 }
728
729 EXPORT_API appcore_base_ops appcore_base_get_default_ops(void)
730 {
731         appcore_base_ops ops;
732
733         ops.create = __on_create;
734         ops.control = __on_control;
735         ops.terminate = __on_terminate;
736         ops.receive = __on_receive;
737         ops.set_i18n = __on_set_i18n;
738         ops.run = NULL;
739         ops.exit = NULL;
740         ops.set_event = __on_set_event;
741         ops.unset_event = __on_unset_event;
742
743         return ops;
744 }
745
746