2 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <sys/types.h>
30 #include <linux/limits.h>
37 #include <bundle_internal.h>
38 #include "appcore_base.h"
39 #include "appcore_base_private.h"
41 #define PATH_LOCALE "locale"
42 #define RESOURCED_FREEZER_PATH "/Org/Tizen/Resourced/Freezer"
43 #define RESOURCED_FREEZER_INTERFACE "org.tizen.resourced.freezer"
44 #define RESOURCED_FREEZER_SIGNAL "FreezerState"
46 typedef struct _appcore_base_context {
55 } appcore_base_context;
57 typedef struct _appcore_base_event_node {
59 appcore_base_event_cb cb;
61 } appcore_base_event_node;
63 static appcore_base_context __context;
64 static GList *__events;
65 static GDBusConnection *__bus;
66 static guint __suspend_dbus_handler_initialized;
67 static char *__locale_dir;
69 static void __invoke_callback(void *event, int type)
71 GList *iter = __events;
74 appcore_base_event_node *node = iter->data;
76 if (node->type == type)
77 node->cb(event, node->data);
78 iter = g_list_next(iter);
82 static bool __exist_callback(int type)
84 GList *iter = __events;
87 appcore_base_event_node *node = iter->data;
89 if (node->type == type)
92 iter = g_list_next(iter);
98 static void __on_low_memory(keynode_t *key, void *data)
102 val = vconf_keynode_get_int(key);
104 if (val >= VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) {
105 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_MEMORY);
110 static void __on_low_battery(keynode_t *key, void *data)
114 val = vconf_keynode_get_int(key);
116 if (val <= VCONFKEY_SYSMAN_BAT_CRITICAL_LOW)
117 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_BATTERY);
120 static void __free_children_langs(gpointer data)
122 GList *list = (GList *)data;
127 g_list_free_full(list, (GDestroyNotify)free);
130 static gint __compare_langs(gconstpointer a, gconstpointer b)
135 static GHashTable *__get_lang_table(void)
139 struct dirent *dentry;
141 struct stat stat_buf;
147 if (__locale_dir == NULL || __locale_dir[0] == '\0')
150 table = g_hash_table_new_full(g_str_hash, g_str_equal,
151 free, __free_children_langs);
153 _ERR("Out of memory");
157 dp = opendir(__locale_dir);
159 g_hash_table_destroy(table);
163 while ((dentry = readdir(dp)) != NULL) {
164 if (!strcmp(dentry->d_name, ".") ||
165 !strcmp(dentry->d_name, ".."))
168 snprintf(buf, sizeof(buf), "%s/%s",
169 __locale_dir, dentry->d_name);
170 ret = stat(buf, &stat_buf);
171 if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
174 dup_lang = strdup(dentry->d_name);
175 if (dup_lang == NULL) {
176 _ERR("Out of memory");
180 token = strtok(dup_lang, "_");
186 list = (GList *)g_hash_table_lookup(table, token);
188 list = g_list_append(list, strdup(dentry->d_name));
189 g_hash_table_insert(table, strdup(token), list);
191 list = g_list_append(list, strdup(dentry->d_name));
200 static char *__get_string_before(const char *str, const char *delim)
206 dup_str = strdup(str);
210 token = strtok(dup_str, delim);
216 new_str = strdup(token);
222 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
235 found = g_list_find_custom(g_list_first(list), lang,
238 tmp = (char *)found->data;
239 list = g_list_remove(list, tmp);
240 list = g_list_append(list, tmp);
244 extract_lang = __get_string_before(lang, ".");
245 if (extract_lang == NULL)
248 parent_lang = __get_string_before(extract_lang, "_");
249 if (parent_lang == NULL) {
254 child_list = g_hash_table_lookup(table, parent_lang);
255 if (child_list == NULL) {
261 found = g_list_find_custom(g_list_first(child_list),
262 extract_lang, __compare_langs);
264 tmp = (char *)found->data;
265 child_list = g_list_remove(child_list, tmp);
266 list = g_list_append(list, tmp);
273 found = g_list_find_custom(g_list_first(child_list),
274 parent_lang, __compare_langs);
276 tmp = (char *)found->data;
277 child_list = g_list_remove(child_list, tmp);
278 list = g_list_append(list, tmp);
284 child_iter = g_list_first(child_list);
286 child_lang = (char *)child_iter->data;
287 child_iter = g_list_next(child_iter);
289 list = g_list_append(list, strdup(child_lang));
290 child_list = g_list_remove(child_list, child_lang);
299 static GList *__split_language(const char *lang)
305 dup_lang = strdup(lang);
306 if (dup_lang == NULL) {
307 _ERR("Out of memory");
311 token = strtok(dup_lang, ":");
312 while (token != NULL) {
313 list = g_list_append(list, strdup(token));
314 token = strtok(NULL, ":");
321 static GList *__append_default_langs(GList *list)
323 const char *langs[] = {"en_US", "en_GB", "en"};
327 for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
328 found = g_list_find_custom(g_list_first(list), langs[i],
331 list = g_list_append(list, strdup(langs[i]));
337 static char *__get_language(const char *lang)
341 GList *lang_list = NULL;
344 char buf[LINE_MAX] = {'\0'};
347 list = __split_language(lang);
351 table = __get_lang_table();
353 g_list_free_full(list, free);
357 iter = g_list_first(list);
359 language = (char *)iter->data;
360 lang_list = __append_langs(language, lang_list, table);
361 iter = g_list_next(iter);
363 g_list_free_full(list, free);
364 g_hash_table_destroy(table);
366 lang_list = __append_default_langs(lang_list);
367 iter = g_list_first(lang_list);
369 language = (char *)iter->data;
371 if (buf[0] == '\0') {
372 snprintf(buf, sizeof(buf), "%s", language);
374 n = sizeof(buf) - strlen(buf) - 1;
375 strncat(buf, ":", n);
376 n = sizeof(buf) - strlen(buf) - 1;
377 strncat(buf, language, n);
380 iter = g_list_next(iter);
382 g_list_free_full(lang_list, free);
387 static void __update_lang(void)
393 lang = vconf_get_str(VCONFKEY_LANGSET);
395 /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */
396 language = __get_language(lang);
398 _DBG("*****language(%s)", language);
399 setenv("LANGUAGE", language, 1);
402 setenv("LANGUAGE", lang, 1);
404 setenv("LANG", lang, 1);
405 setenv("LC_MESSAGES", lang, 1);
406 r = setlocale(LC_ALL, "");
408 r = setlocale(LC_ALL, lang);
410 _DBG("*****appcore setlocale=%s\n", r);
416 static void __update_region(void)
421 region = vconf_get_str(VCONFKEY_REGIONFORMAT);
423 setenv("LC_CTYPE", region, 1);
424 setenv("LC_NUMERIC", region, 1);
425 setenv("LC_TIME", region, 1);
426 setenv("LC_COLLATE", region, 1);
427 setenv("LC_MONETARY", region, 1);
428 setenv("LC_PAPER", region, 1);
429 setenv("LC_NAME", region, 1);
430 setenv("LC_ADDRESS", region, 1);
431 setenv("LC_TELEPHONE", region, 1);
432 setenv("LC_MEASUREMENT", region, 1);
433 setenv("LC_IDENTIFICATION", region, 1);
434 r = setlocale(LC_ALL, "");
436 _DBG("*****appcore setlocale=%s\n", r);
442 static void __on_language_change(keynode_t *key, void *data)
446 val = vconf_keynode_get_str(key);
449 __invoke_callback((void *)val, APPCORE_BASE_EVENT_LANG_CHANGE);
452 static void __on_region_change(keynode_t *key, void *data)
457 name = vconf_keynode_get_name(key);
458 if (!strcmp(name, VCONFKEY_REGIONFORMAT))
459 val = vconf_keynode_get_str(key);
462 __invoke_callback((void *)val, APPCORE_BASE_EVENT_REGION_CHANGE);
465 static gboolean __flush_memory(gpointer data)
467 int suspend = APPCORE_BASE_SUSPENDED_STATE_WILL_ENTER_SUSPEND;
469 appcore_base_flush_memory();
472 if (!__context.allowed_bg && !__context.suspended_state) {
473 _DBG("[__SUSPEND__] flush case");
474 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
475 __context.suspended_state = true;
481 static void __add_suspend_timer(void)
483 __context.tid = g_timeout_add_seconds(5, __flush_memory, NULL);
486 static void __remove_suspend_timer(void)
488 if (__context.tid > 0) {
489 g_source_remove(__context.tid);
494 static void __on_receive_suspend_signal(GDBusConnection *connection,
495 const gchar *sender_name,
496 const gchar *object_path,
497 const gchar *interface_name,
498 const gchar *signal_name,
499 GVariant *parameters,
502 gint suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
506 if (g_strcmp0(signal_name, RESOURCED_FREEZER_SIGNAL) == 0) {
507 g_variant_get(parameters, "(ii)", &status, &pid);
508 if (pid == getpid() && status == 0) {
509 if (!__context.allowed_bg && __context.suspended_state) {
510 __remove_suspend_timer();
511 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
512 __context.suspended_state = false;
513 __add_suspend_timer();
519 static int __init_suspend_dbus_handler(void)
523 if (__suspend_dbus_handler_initialized)
527 __bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
529 _ERR("Failed to connect to the D-BUS daemon: %s",
536 __suspend_dbus_handler_initialized = g_dbus_connection_signal_subscribe(
539 RESOURCED_FREEZER_INTERFACE,
540 RESOURCED_FREEZER_SIGNAL,
541 RESOURCED_FREEZER_PATH,
543 G_DBUS_SIGNAL_FLAGS_NONE,
544 __on_receive_suspend_signal,
547 if (__suspend_dbus_handler_initialized == 0) {
548 _ERR("g_dbus_connection_signal_subscribe() is failed.");
552 _DBG("[__SUSPEND__] suspend signal initialized");
557 static void __fini_suspend_dbus_handler(void)
562 if (__suspend_dbus_handler_initialized) {
563 g_dbus_connection_signal_unsubscribe(__bus,
564 __suspend_dbus_handler_initialized);
565 __suspend_dbus_handler_initialized = 0;
568 g_object_unref(__bus);
572 static int __get_locale_resource_dir(char *locale_dir, int size)
574 const char *res_path;
576 res_path = aul_get_app_resource_path();
577 if (res_path == NULL) {
578 _ERR("Failed to get resource path");
582 snprintf(locale_dir, size, "%s" PATH_LOCALE, res_path);
583 if (access(locale_dir, R_OK) != 0)
589 static int __get_app_name(const char *appid, char **name)
591 char *name_token = NULL;
596 /* com.vendor.name -> name */
597 name_token = strrchr(appid, '.');
598 if (name_token == NULL)
603 *name = strdup(name_token);
610 static int __set_i18n(const char *domain, const char *dir)
615 if (domain == NULL) {
623 __locale_dir = strdup(dir);
629 r = setlocale(LC_ALL, "");
630 /* if locale is not set properly, try again to set as language base */
632 lan = vconf_get_str(VCONFKEY_LANGSET);
634 r = setlocale(LC_ALL, lan);
635 _DBG("*****appcore setlocale=%s\n", r);
640 _ERR("appcore: setlocale() error");
642 r = bindtextdomain(domain, dir);
644 _ERR("appcore: bindtextdomain() error");
646 r = textdomain(domain);
648 _ERR("appcore: textdomain() error");
653 EXPORT_API int appcore_base_on_set_i18n(void)
656 char locale_dir[PATH_MAX];
657 char appid[PATH_MAX];
660 r = aul_app_get_appid_bypid(getpid(), appid, PATH_MAX);
664 r = __get_app_name(appid, &name);
668 r = __get_locale_resource_dir(locale_dir, sizeof(locale_dir));
674 r = __set_i18n(name, locale_dir);
685 EXPORT_API int appcore_base_init(appcore_base_ops ops, int argc, char **argv, void *data)
690 __context.argc = argc;
691 __context.argv = argv;
692 __context.data = data;
694 __context.suspended_state = false;
695 __context.allowed_bg = false;
697 if (__context.ops.set_i18n)
698 __context.ops.set_i18n(__context.data);
700 __init_suspend_dbus_handler();
702 if (!__context.dirty) {
703 __context.dirty = true;
705 for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
706 if (__exist_callback(i)) {
707 if (__context.ops.set_event)
708 __context.ops.set_event(i, __context.data);
713 if (__context.ops.create && __context.ops.create(__context.data) < 0) {
714 aul_status_update(STATUS_DYING);
718 if (__context.ops.run)
719 __context.ops.run(__context.data);
724 EXPORT_API void appcore_base_fini(void)
728 aul_status_update(STATUS_DYING);
729 if (__context.ops.terminate)
730 __context.ops.terminate(__context.data);
732 for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
733 if (__exist_callback(i)) {
734 if (__context.ops.unset_event)
735 __context.ops.unset_event(i, __context.data);
739 g_list_free_full(__events, free);
741 __fini_suspend_dbus_handler();
748 __context.dirty = false;
751 EXPORT_API int appcore_base_flush_memory(void)
757 EXPORT_API int appcore_base_on_receive(aul_type type, bundle *b)
760 const char **tep_path;
768 _DBG("[APP %d] AUL event: AUL_START", getpid());
769 tep_path = bundle_get_str_array(b, AUL_TEP_PATH, &len);
771 for (i = 0; i < len; i++) {
772 ret = aul_check_tep_mount(tep_path[i]);
774 _ERR("mount request not completed within 1 sec");
780 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
781 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
782 _DBG("[__SUSPEND__] allowed background");
783 __context.allowed_bg = true;
784 __remove_suspend_timer();
787 if (__context.ops.control)
788 __context.ops.control(b, __context.data);
791 _DBG("[APP %d] AUL event: AUL_RESUME", getpid());
792 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
793 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
794 _DBG("[__SUSPEND__] allowed background");
795 __context.allowed_bg = true;
796 __remove_suspend_timer();
800 _DBG("[APP %d] AUL event: AUL_TERMINATE", getpid());
801 aul_status_update(STATUS_DYING);
802 if (!__context.allowed_bg)
803 __remove_suspend_timer();
805 if (__context.ops.exit)
806 __context.ops.exit(__context.data);
808 case AUL_TERMINATE_BGAPP:
809 _DBG("[APP %d] AUL event: AUL_TERMINATE_BGAPP", getpid());
810 if (!__context.allowed_bg)
811 __remove_suspend_timer();
814 _DBG("[APP %d] AUL event: AUL_WAKE", getpid());
815 if (!__context.allowed_bg && __context.suspended_state) {
816 int suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
817 __remove_suspend_timer();
818 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
819 __context.suspended_state = false;
823 _DBG("[APP %d] AUL event: AUL_SUSPEND", getpid());
824 if (!__context.allowed_bg && !__context.suspended_state) {
825 __remove_suspend_timer();
826 __flush_memory(NULL);
829 case AUL_UPDATE_REQUESTED:
830 _DBG("[APP %d] AUL event: AUL_UPDATE_REQUESTED", getpid());
831 __invoke_callback((void *)&dummy, APPCORE_BASE_EVENT_UPDATE_REQUESTED);
834 _DBG("[APP %d] AUL event: %d", getpid(), type);
842 EXPORT_API int appcore_base_on_create(void)
845 r = aul_launch_init(__context.ops.receive, NULL);
847 _ERR("Aul init failed: %d", r);
851 r = aul_launch_argv_handler(__context.argc, __context.argv);
853 _ERR("Aul argv handler failed: %d", r);
860 EXPORT_API int appcore_base_on_control(bundle *b)
865 EXPORT_API int appcore_base_on_terminate()
868 if (__context.ops.exit)
869 __context.ops.exit(__context.data);
874 EXPORT_API void appcore_base_on_set_event(enum appcore_base_event event)
879 case APPCORE_BASE_EVENT_LOW_MEMORY:
880 vconf_notify_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory, NULL);
882 case APPCORE_BASE_EVENT_LOW_BATTERY:
883 vconf_notify_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery, NULL);
885 case APPCORE_BASE_EVENT_LANG_CHANGE:
886 vconf_notify_key_changed(VCONFKEY_LANGSET, __on_language_change, NULL);
888 case APPCORE_BASE_EVENT_REGION_CHANGE:
889 r = vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change, NULL);
893 vconf_notify_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change, NULL);
895 case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
904 EXPORT_API void appcore_base_on_unset_event(enum appcore_base_event event)
909 case APPCORE_BASE_EVENT_LOW_MEMORY:
910 vconf_ignore_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory);
912 case APPCORE_BASE_EVENT_LOW_BATTERY:
913 vconf_ignore_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery);
915 case APPCORE_BASE_EVENT_LANG_CHANGE:
916 vconf_ignore_key_changed(VCONFKEY_LANGSET, __on_language_change);
918 case APPCORE_BASE_EVENT_REGION_CHANGE:
919 r = vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change);
922 vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change);
924 case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
931 EXPORT_API appcore_base_event_h appcore_base_add_event(enum appcore_base_event event,
932 appcore_base_event_cb cb, void *data)
934 appcore_base_event_node *node;
936 if (__context.dirty && !__exist_callback(event)) {
937 if (__context.ops.set_event)
938 __context.ops.set_event(event, __context.data);
941 node = malloc(sizeof(appcore_base_event_node));
949 __events = g_list_append(__events, node);
954 EXPORT_API int appcore_base_remove_event(appcore_base_event_h handle)
956 appcore_base_event_node *node = handle;
957 enum appcore_base_event event;
960 __events = g_list_remove(__events, node);
962 if (__context.dirty && !__exist_callback(event)) {
963 if (__context.ops.unset_event)
964 __context.ops.unset_event(event, __context.data);
970 EXPORT_API int appcore_base_raise_event(void *event, enum appcore_base_event type)
972 __invoke_callback(event, type);
976 EXPORT_API int appcore_base_get_rotation_state(enum appcore_base_rm *curr)
981 EXPORT_API bool appcore_base_is_bg_allowed(void)
983 return __context.allowed_bg;
986 EXPORT_API bool appcore_base_is_suspended(void)
988 return __context.suspended_state;
991 EXPORT_API void appcore_base_toggle_suspended_state(void)
993 __context.suspended_state ^= __context.suspended_state;
996 static int __on_receive(aul_type type, bundle *b, void *data)
998 return appcore_base_on_receive(type, b);
1001 static int __on_create(void *data)
1003 return appcore_base_on_create();
1006 static int __on_control(bundle *b, void *data)
1008 return appcore_base_on_control(b);
1011 static int __on_terminate(void *data)
1013 return appcore_base_on_terminate();
1016 static int __on_set_i18n(void *data)
1018 return appcore_base_on_set_i18n();
1021 static void __on_set_event(enum appcore_base_event event, void *data)
1023 return appcore_base_on_set_event(event);
1026 static void __on_unset_event(enum appcore_base_event event, void *data)
1028 return appcore_base_on_unset_event(event);
1031 EXPORT_API appcore_base_ops appcore_base_get_default_ops(void)
1033 appcore_base_ops ops;
1035 ops.create = __on_create;
1036 ops.control = __on_control;
1037 ops.terminate = __on_terminate;
1038 ops.receive = __on_receive;
1039 ops.set_i18n = __on_set_i18n;
1042 ops.set_event = __on_set_event;
1043 ops.unset_event = __on_unset_event;