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 {
54 } appcore_base_context;
56 typedef struct _appcore_base_event_node {
58 appcore_base_event_cb cb;
60 } appcore_base_event_node;
62 static appcore_base_context __context;
63 static GList *__events;
64 static GDBusConnection *__bus;
65 static guint __suspend_dbus_handler_initialized;
66 static char *__locale_dir;
68 static void __invoke_callback(void *event, int type)
70 GList *iter = __events;
73 appcore_base_event_node *node = iter->data;
75 if (node->type == type)
76 node->cb(event, node->data);
77 iter = g_list_next(iter);
81 static bool __exist_callback(int type)
83 GList *iter = __events;
86 appcore_base_event_node *node = iter->data;
88 if (node->type == type)
91 iter = g_list_next(iter);
97 static void __on_low_memory(keynode_t *key, void *data)
101 val = vconf_keynode_get_int(key);
103 if (val >= VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) {
104 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_MEMORY);
109 static void __on_low_battery(keynode_t *key, void *data)
113 val = vconf_keynode_get_int(key);
115 if (val <= VCONFKEY_SYSMAN_BAT_CRITICAL_LOW)
116 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_BATTERY);
119 static void __free_children_langs(gpointer data)
121 GList *list = (GList *)data;
126 g_list_free_full(list, (GDestroyNotify)free);
129 static gint __compare_langs(gconstpointer a, gconstpointer b)
134 static GHashTable *__get_lang_table(void)
138 struct dirent *dentry;
140 struct stat stat_buf;
146 if (__locale_dir == NULL || __locale_dir[0] == '\0')
149 table = g_hash_table_new_full(g_str_hash, g_str_equal,
150 free, __free_children_langs);
152 _ERR("Out of memory");
156 dp = opendir(__locale_dir);
158 g_hash_table_destroy(table);
162 while ((dentry = readdir(dp)) != NULL) {
163 if (!strcmp(dentry->d_name, ".") ||
164 !strcmp(dentry->d_name, ".."))
167 snprintf(buf, sizeof(buf), "%s/%s",
168 __locale_dir, dentry->d_name);
169 ret = stat(buf, &stat_buf);
170 if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
173 dup_lang = strdup(dentry->d_name);
174 if (dup_lang == NULL) {
175 _ERR("Out of memory");
179 token = strtok(dup_lang, "_");
185 list = (GList *)g_hash_table_lookup(table, token);
187 list = g_list_append(list, strdup(dentry->d_name));
188 g_hash_table_insert(table, strdup(token), list);
190 list = g_list_append(list, strdup(dentry->d_name));
199 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
211 found = g_list_find_custom(g_list_first(list), lang,
214 list = g_list_remove_link(list, found);
217 list = g_list_append(list, strdup(lang));
221 dup_lang = strdup(lang);
222 if (dup_lang == NULL)
225 token = strtok(dup_lang, "_");
231 child_list = g_hash_table_lookup(table, token);
232 if (child_list == NULL) {
237 found = g_list_find_custom(g_list_first(child_list),
238 lang, __compare_langs);
240 list = g_list_append(list, strdup(lang));
241 child_list = g_list_remove_link(child_list, found);
248 found = g_list_find_custom(g_list_first(child_list),
249 token, __compare_langs);
251 list = g_list_append(list, strdup(token));
252 child_list = g_list_remove_link(child_list, found);
260 child_iter = g_list_first(child_list);
262 child_lang = (char *)child_iter->data;
263 child_iter = g_list_next(child_iter);
265 list = g_list_append(list, strdup(child_lang));
266 child_list = g_list_remove(child_list, child_lang);
275 static GList *__split_language(const char *lang)
281 dup_lang = strdup(lang);
282 if (dup_lang == NULL) {
283 _ERR("Out of memory");
287 token = strtok(dup_lang, ":");
288 while (token != NULL) {
289 list = g_list_append(list, strdup(token));
290 token = strtok(NULL, ":");
297 static char *__get_language(const char *lang)
301 GList *lang_list = NULL;
304 char buf[LINE_MAX] = {'\0'};
307 list = __split_language(lang);
311 table = __get_lang_table();
313 g_list_free_full(list, free);
317 iter = g_list_first(list);
319 language = (char *)iter->data;
320 lang_list = __append_langs(language, lang_list, table);
321 iter = g_list_next(iter);
323 g_list_free_full(list, free);
324 g_hash_table_destroy(table);
326 iter = g_list_first(lang_list);
328 language = (char *)iter->data;
330 if (buf[0] == '\0') {
331 snprintf(buf, sizeof(buf), "%s", language);
333 n = sizeof(buf) - strlen(buf) - 1;
334 strncat(buf, ":", n);
335 n = sizeof(buf) - strlen(buf) - 1;
336 strncat(buf, language, n);
339 iter = g_list_next(iter);
341 g_list_free_full(lang_list, free);
343 n = sizeof(buf) - strlen(buf) - 1;
344 strncat(buf, ":", n);
345 n = sizeof(buf) - strlen(buf) - 1;
346 strncat(buf, "en_US:en_GB:en", n);
351 static void __update_lang(void)
357 lang = vconf_get_str(VCONFKEY_LANGSET);
359 /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */
360 language = __get_language(lang);
362 _DBG("*****language(%s)", language);
363 setenv("LANGUAGE", language, 1);
366 setenv("LANGUAGE", language, 1);
368 setenv("LANG", lang, 1);
369 setenv("LC_MESSAGES", lang, 1);
370 r = setlocale(LC_ALL, "");
372 r = setlocale(LC_ALL, lang);
374 _DBG("*****appcore setlocale=%s\n", r);
380 static void __update_region(void)
385 region = vconf_get_str(VCONFKEY_REGIONFORMAT);
387 setenv("LC_CTYPE", region, 1);
388 setenv("LC_NUMERIC", region, 1);
389 setenv("LC_TIME", region, 1);
390 setenv("LC_COLLATE", region, 1);
391 setenv("LC_MONETARY", region, 1);
392 setenv("LC_PAPER", region, 1);
393 setenv("LC_NAME", region, 1);
394 setenv("LC_ADDRESS", region, 1);
395 setenv("LC_TELEPHONE", region, 1);
396 setenv("LC_MEASUREMENT", region, 1);
397 setenv("LC_IDENTIFICATION", region, 1);
398 r = setlocale(LC_ALL, "");
400 _DBG("*****appcore setlocale=%s\n", r);
406 static void __on_language_change(keynode_t *key, void *data)
410 val = vconf_keynode_get_str(key);
413 __invoke_callback((void *)val, APPCORE_BASE_EVENT_LANG_CHANGE);
416 static void __on_region_change(keynode_t *key, void *data)
421 name = vconf_keynode_get_name(key);
422 if (!strcmp(name, VCONFKEY_REGIONFORMAT))
423 val = vconf_keynode_get_str(key);
426 __invoke_callback((void *)val, APPCORE_BASE_EVENT_REGION_CHANGE);
429 static gboolean __flush_memory(gpointer data)
431 int suspend = APPCORE_BASE_SUSPENDED_STATE_WILL_ENTER_SUSPEND;
433 appcore_base_flush_memory();
436 if (!__context.allowed_bg && !__context.suspended_state) {
437 _DBG("[__SUSPEND__] flush case");
438 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
439 __context.suspended_state = true;
445 static void __add_suspend_timer(void)
447 __context.tid = g_timeout_add_seconds(5, __flush_memory, NULL);
450 static void __remove_suspend_timer(void)
452 if (__context.tid > 0) {
453 g_source_remove(__context.tid);
458 static void __on_receive_suspend_signal(GDBusConnection *connection,
459 const gchar *sender_name,
460 const gchar *object_path,
461 const gchar *interface_name,
462 const gchar *signal_name,
463 GVariant *parameters,
466 gint suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
470 if (g_strcmp0(signal_name, RESOURCED_FREEZER_SIGNAL) == 0) {
471 g_variant_get(parameters, "(ii)", &status, &pid);
472 if (pid == getpid() && status == 0) {
473 if (!__context.allowed_bg && __context.suspended_state) {
474 __remove_suspend_timer();
475 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
476 __context.suspended_state = false;
477 __add_suspend_timer();
483 static int __init_suspend_dbus_handler(void)
487 if (__suspend_dbus_handler_initialized)
491 __bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
493 _ERR("Failed to connect to the D-BUS daemon: %s",
500 __suspend_dbus_handler_initialized = g_dbus_connection_signal_subscribe(
503 RESOURCED_FREEZER_INTERFACE,
504 RESOURCED_FREEZER_SIGNAL,
505 RESOURCED_FREEZER_PATH,
507 G_DBUS_SIGNAL_FLAGS_NONE,
508 __on_receive_suspend_signal,
511 if (__suspend_dbus_handler_initialized == 0) {
512 _ERR("g_dbus_connection_signal_subscribe() is failed.");
516 _DBG("[__SUSPEND__] suspend signal initialized");
521 static void __fini_suspend_dbus_handler(void)
526 if (__suspend_dbus_handler_initialized) {
527 g_dbus_connection_signal_unsubscribe(__bus,
528 __suspend_dbus_handler_initialized);
529 __suspend_dbus_handler_initialized = 0;
532 g_object_unref(__bus);
536 static int __get_locale_resource_dir(char *locale_dir, int size)
538 const char *res_path;
540 res_path = aul_get_app_resource_path();
541 if (res_path == NULL) {
542 _ERR("Failed to get resource path");
546 snprintf(locale_dir, size, "%s" PATH_LOCALE, res_path);
547 if (access(locale_dir, R_OK) != 0)
553 static int __get_app_name(const char *appid, char **name)
555 char *name_token = NULL;
560 /* com.vendor.name -> name */
561 name_token = strrchr(appid, '.');
562 if (name_token == NULL)
567 *name = strdup(name_token);
574 static int __set_i18n(const char *domain, const char *dir)
579 if (domain == NULL) {
587 r = setlocale(LC_ALL, "");
588 /* if locale is not set properly, try again to set as language base */
590 lan = vconf_get_str(VCONFKEY_LANGSET);
592 r = setlocale(LC_ALL, lan);
593 _DBG("*****appcore setlocale=%s\n", r);
598 _ERR("appcore: setlocale() error");
603 __locale_dir = strdup(dir);
606 r = bindtextdomain(domain, dir);
608 _ERR("appcore: bindtextdomain() error");
610 r = textdomain(domain);
612 _ERR("appcore: textdomain() error");
617 EXPORT_API int appcore_base_on_set_i18n(void)
620 char locale_dir[PATH_MAX];
621 char appid[PATH_MAX];
624 r = aul_app_get_appid_bypid(getpid(), appid, PATH_MAX);
628 r = __get_app_name(appid, &name);
632 r = __get_locale_resource_dir(locale_dir, sizeof(locale_dir));
638 r = __set_i18n(name, locale_dir);
649 EXPORT_API int appcore_base_init(appcore_base_ops ops, int argc, char **argv, void *data)
652 __context.argc = argc;
653 __context.argv = argv;
654 __context.data = data;
656 __context.suspended_state = false;
657 __context.allowed_bg = false;
659 if (__context.ops.set_i18n)
660 __context.ops.set_i18n(__context.data);
662 __init_suspend_dbus_handler();
664 if (__context.ops.create && __context.ops.create(__context.data) < 0) {
665 aul_status_update(STATUS_DYING);
669 if (__context.ops.run)
670 __context.ops.run(__context.data);
675 EXPORT_API void appcore_base_fini(void)
679 for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
680 if (__exist_callback(i)) {
681 if (__context.ops.unset_event)
682 __context.ops.unset_event(i, __context.data);
686 g_list_free_full(__events, free);
688 __fini_suspend_dbus_handler();
690 aul_status_update(STATUS_DYING);
691 if (__context.ops.terminate)
692 __context.ops.terminate(__context.data);
700 EXPORT_API int appcore_base_flush_memory(void)
706 EXPORT_API int appcore_base_on_receive(aul_type type, bundle *b)
709 const char **tep_path;
717 _DBG("[APP %d] AUL event: AUL_START", getpid());
718 tep_path = bundle_get_str_array(b, AUL_TEP_PATH, &len);
720 for (i = 0; i < len; i++) {
721 ret = aul_check_tep_mount(tep_path[i]);
723 _ERR("mount request not completed within 1 sec");
729 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
730 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
731 _DBG("[__SUSPEND__] allowed background");
732 __context.allowed_bg = true;
733 __remove_suspend_timer();
736 if (__context.ops.control)
737 __context.ops.control(b, __context.data);
740 _DBG("[APP %d] AUL event: AUL_RESUME", getpid());
741 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
742 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
743 _DBG("[__SUSPEND__] allowed background");
744 __context.allowed_bg = true;
745 __remove_suspend_timer();
749 _DBG("[APP %d] AUL event: AUL_TERMINATE", getpid());
750 aul_status_update(STATUS_DYING);
751 if (!__context.allowed_bg)
752 __remove_suspend_timer();
754 if (__context.ops.exit)
755 __context.ops.exit(__context.data);
757 case AUL_TERMINATE_BGAPP:
758 _DBG("[APP %d] AUL event: AUL_TERMINATE_BGAPP", getpid());
759 if (!__context.allowed_bg)
760 __remove_suspend_timer();
763 _DBG("[APP %d] AUL event: AUL_WAKE", getpid());
764 if (!__context.allowed_bg && __context.suspended_state) {
765 int suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
766 __remove_suspend_timer();
767 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
768 __context.suspended_state = false;
772 _DBG("[APP %d] AUL event: AUL_SUSPEND", getpid());
773 if (!__context.allowed_bg && !__context.suspended_state) {
774 __remove_suspend_timer();
775 __flush_memory(NULL);
778 case AUL_UPDATE_REQUESTED:
779 _DBG("[APP %d] AUL event: AUL_UPDATE_REQUESTED", getpid());
780 __invoke_callback((void *)&dummy, APPCORE_BASE_EVENT_UPDATE_REQUESTED);
783 _DBG("[APP %d] AUL event: %d", getpid(), type);
791 EXPORT_API int appcore_base_on_create(void)
794 r = aul_launch_init(__context.ops.receive, NULL);
796 _ERR("Aul init failed: %d", r);
800 r = aul_launch_argv_handler(__context.argc, __context.argv);
802 _ERR("Aul argv handler failed: %d", r);
809 EXPORT_API int appcore_base_on_control(bundle *b)
814 EXPORT_API int appcore_base_on_terminate()
817 if (__context.ops.exit)
818 __context.ops.exit(__context.data);
823 EXPORT_API void appcore_base_on_set_event(enum appcore_base_event event)
828 case APPCORE_BASE_EVENT_LOW_MEMORY:
829 vconf_notify_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory, NULL);
831 case APPCORE_BASE_EVENT_LOW_BATTERY:
832 vconf_notify_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery, NULL);
834 case APPCORE_BASE_EVENT_LANG_CHANGE:
835 vconf_notify_key_changed(VCONFKEY_LANGSET, __on_language_change, NULL);
837 case APPCORE_BASE_EVENT_REGION_CHANGE:
838 r = vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change, NULL);
842 vconf_notify_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change, NULL);
844 case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
853 EXPORT_API void appcore_base_on_unset_event(enum appcore_base_event event)
858 case APPCORE_BASE_EVENT_LOW_MEMORY:
859 vconf_ignore_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory);
861 case APPCORE_BASE_EVENT_LOW_BATTERY:
862 vconf_ignore_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery);
864 case APPCORE_BASE_EVENT_LANG_CHANGE:
865 vconf_ignore_key_changed(VCONFKEY_LANGSET, __on_language_change);
867 case APPCORE_BASE_EVENT_REGION_CHANGE:
868 r = vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change);
871 vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change);
873 case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
880 EXPORT_API appcore_base_event_h appcore_base_add_event(enum appcore_base_event event,
881 appcore_base_event_cb cb, void *data)
883 appcore_base_event_node *node;
885 if (!__exist_callback(event)) {
886 if (__context.ops.set_event)
887 __context.ops.set_event(event, __context.data);
890 node = malloc(sizeof(appcore_base_event_node));
898 __events = g_list_append(__events, node);
903 EXPORT_API int appcore_base_remove_event(appcore_base_event_h handle)
905 appcore_base_event_node *node = handle;
906 enum appcore_base_event event;
909 __events = g_list_remove(__events, node);
911 if (!__exist_callback(event)) {
912 if (__context.ops.unset_event)
913 __context.ops.unset_event(event, __context.data);
919 EXPORT_API int appcore_base_raise_event(void *event, enum appcore_base_event type)
921 __invoke_callback(event, type);
925 EXPORT_API int appcore_base_get_rotation_state(enum appcore_base_rm *curr)
930 EXPORT_API bool appcore_base_is_bg_allowed(void)
932 return __context.allowed_bg;
935 EXPORT_API bool appcore_base_is_suspended(void)
937 return __context.suspended_state;
940 EXPORT_API void appcore_base_toggle_suspended_state(void)
942 __context.suspended_state ^= __context.suspended_state;
945 static int __on_receive(aul_type type, bundle *b, void *data)
947 return appcore_base_on_receive(type, b);
950 static int __on_create(void *data)
952 return appcore_base_on_create();
955 static int __on_control(bundle *b, void *data)
957 return appcore_base_on_control(b);
960 static int __on_terminate(void *data)
962 return appcore_base_on_terminate();
965 static int __on_set_i18n(void *data)
967 return appcore_base_on_set_i18n();
970 static void __on_set_event(enum appcore_base_event event, void *data)
972 return appcore_base_on_set_event(event);
975 static void __on_unset_event(enum appcore_base_event event, void *data)
977 return appcore_base_on_unset_event(event);
980 EXPORT_API appcore_base_ops appcore_base_get_default_ops(void)
982 appcore_base_ops ops;
984 ops.create = __on_create;
985 ops.control = __on_control;
986 ops.terminate = __on_terminate;
987 ops.receive = __on_receive;
988 ops.set_i18n = __on_set_i18n;
991 ops.set_event = __on_set_event;
992 ops.unset_event = __on_unset_event;