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;
68 static appcore_base_context __context;
69 static GList *__events;
70 static GDBusConnection *__bus;
71 static guint __suspend_dbus_handler_initialized;
72 static char *__locale_dir;
74 static void __invoke_callback(void *event, int type)
76 GList *iter = __events;
79 appcore_base_event_node *node = iter->data;
81 if (node->type == type)
82 node->cb(event, node->data);
83 iter = g_list_next(iter);
87 static bool __exist_callback(int type)
89 GList *iter = __events;
92 appcore_base_event_node *node = iter->data;
94 if (node->type == type)
97 iter = g_list_next(iter);
103 static void __on_low_memory(keynode_t *key, void *data)
107 val = vconf_keynode_get_int(key);
109 if (val >= VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) {
110 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_MEMORY);
115 static void __on_low_battery(keynode_t *key, void *data)
119 val = vconf_keynode_get_int(key);
121 if (val <= VCONFKEY_SYSMAN_BAT_CRITICAL_LOW)
122 __invoke_callback(key, APPCORE_BASE_EVENT_LOW_BATTERY);
125 static void __destroy_lang_info(gpointer data)
127 struct lang_info_s *info = (struct lang_info_s *)data;
133 g_list_free_full(info->list, free);
139 static struct lang_info_s *__create_lang_info(const char *lang)
141 struct lang_info_s *info;
143 info = calloc(1, sizeof(struct lang_info_s));
145 _ERR("Out of memory");
149 info->parent = strdup(lang);
150 if (info->parent == NULL) {
151 _ERR("Out of memory");
159 static gint __compare_langs(gconstpointer a, gconstpointer b)
167 static char *__get_string_before(const char *str, const char *delim)
173 dup_str = strdup(str);
177 token = strtok(dup_str, delim);
183 new_str = strdup(token);
189 static GHashTable *__get_lang_table(void)
193 struct dirent *dentry;
195 struct stat stat_buf;
198 struct lang_info_s *info;
200 if (__locale_dir == NULL || __locale_dir[0] == '\0')
203 table = g_hash_table_new_full(g_str_hash, g_str_equal,
204 NULL, __destroy_lang_info);
206 _ERR("Out of memory");
210 dp = opendir(__locale_dir);
212 g_hash_table_destroy(table);
216 while ((dentry = readdir(dp)) != NULL) {
217 if (!strcmp(dentry->d_name, ".") ||
218 !strcmp(dentry->d_name, ".."))
221 snprintf(buf, sizeof(buf), "%s/%s",
222 __locale_dir, dentry->d_name);
223 ret = stat(buf, &stat_buf);
224 if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
227 parent_lang = __get_string_before(dentry->d_name, "_");
228 if (parent_lang == NULL) {
229 _ERR("Out of memory");
233 info = g_hash_table_lookup(table, parent_lang);
235 info = __create_lang_info(parent_lang);
240 g_hash_table_insert(table, info->parent, info);
242 info->list = g_list_append(info->list, strdup(dentry->d_name));
250 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
252 struct lang_info_s *info;
254 char *parent_lang = NULL;
260 extract_lang = __get_string_before(lang, ".");
261 if (extract_lang == NULL)
264 found = g_list_find_custom(list, extract_lang, __compare_langs);
266 list = g_list_remove_link(list, found);
267 list = g_list_concat(list, found);
271 parent_lang = __get_string_before(extract_lang, "_");
272 if (parent_lang == NULL)
275 info = g_hash_table_lookup(table, parent_lang);
279 found = g_list_find_custom(info->list, extract_lang, __compare_langs);
281 info->list = g_list_remove_link(info->list, found);
282 list = g_list_concat(list, found);
286 found = g_list_find_custom(info->list, parent_lang, __compare_langs);
288 info->list = g_list_remove_link(info->list, found);
289 list = g_list_concat(list, found);
293 found = g_list_first(info->list);
295 info->list = g_list_remove_link(info->list, found);
296 list = g_list_concat(list, found);
308 static GList *__split_language(const char *lang)
314 dup_lang = strdup(lang);
315 if (dup_lang == NULL) {
316 _ERR("Out of memory");
320 token = strtok(dup_lang, ":");
321 while (token != NULL) {
322 list = g_list_append(list, strdup(token));
323 token = strtok(NULL, ":");
330 static GList *__append_default_langs(GList *list)
332 const char *langs[] = {"en_US", "en_GB", "en"};
336 for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
337 found = g_list_find_custom(list, langs[i], __compare_langs);
339 list = g_list_append(list, strdup(langs[i]));
345 static char *__get_language(const char *lang)
349 GList *lang_list = NULL;
352 char buf[LINE_MAX] = {'\0'};
355 list = __split_language(lang);
359 table = __get_lang_table();
361 g_list_free_full(list, free);
365 iter = g_list_first(list);
367 language = (char *)iter->data;
368 lang_list = __append_langs(language, lang_list, table);
369 iter = g_list_next(iter);
371 g_list_free_full(list, free);
372 g_hash_table_destroy(table);
374 lang_list = __append_default_langs(lang_list);
375 iter = g_list_first(lang_list);
377 language = (char *)iter->data;
379 if (buf[0] == '\0') {
380 snprintf(buf, sizeof(buf), "%s", language);
382 n = sizeof(buf) - strlen(buf) - 1;
383 strncat(buf, ":", n);
384 n = sizeof(buf) - strlen(buf) - 1;
385 strncat(buf, language, n);
388 iter = g_list_next(iter);
390 g_list_free_full(lang_list, free);
395 static void __update_lang(void)
401 lang = vconf_get_str(VCONFKEY_LANGSET);
403 /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */
404 language = __get_language(lang);
406 _DBG("*****language(%s)", language);
407 setenv("LANGUAGE", language, 1);
410 setenv("LANGUAGE", lang, 1);
412 setenv("LANG", lang, 1);
413 setenv("LC_MESSAGES", lang, 1);
414 r = setlocale(LC_ALL, "");
416 r = setlocale(LC_ALL, lang);
418 _DBG("*****appcore setlocale=%s\n", r);
424 static void __update_region(void)
429 region = vconf_get_str(VCONFKEY_REGIONFORMAT);
431 setenv("LC_CTYPE", region, 1);
432 setenv("LC_NUMERIC", region, 1);
433 setenv("LC_TIME", region, 1);
434 setenv("LC_COLLATE", region, 1);
435 setenv("LC_MONETARY", region, 1);
436 setenv("LC_PAPER", region, 1);
437 setenv("LC_NAME", region, 1);
438 setenv("LC_ADDRESS", region, 1);
439 setenv("LC_TELEPHONE", region, 1);
440 setenv("LC_MEASUREMENT", region, 1);
441 setenv("LC_IDENTIFICATION", region, 1);
442 r = setlocale(LC_ALL, "");
444 _DBG("*****appcore setlocale=%s\n", r);
450 static void __on_language_change(keynode_t *key, void *data)
454 val = vconf_keynode_get_str(key);
457 __invoke_callback((void *)val, APPCORE_BASE_EVENT_LANG_CHANGE);
460 static void __on_region_change(keynode_t *key, void *data)
465 name = vconf_keynode_get_name(key);
466 if (!strcmp(name, VCONFKEY_REGIONFORMAT))
467 val = vconf_keynode_get_str(key);
470 __invoke_callback((void *)val, APPCORE_BASE_EVENT_REGION_CHANGE);
473 static gboolean __flush_memory(gpointer data)
475 int suspend = APPCORE_BASE_SUSPENDED_STATE_WILL_ENTER_SUSPEND;
477 appcore_base_flush_memory();
480 if (!__context.allowed_bg && !__context.suspended_state) {
481 _DBG("[__SUSPEND__] flush case");
482 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
483 __context.suspended_state = true;
489 static void __add_suspend_timer(void)
491 __context.tid = g_timeout_add_seconds(5, __flush_memory, NULL);
494 static void __remove_suspend_timer(void)
496 if (__context.tid > 0) {
497 g_source_remove(__context.tid);
502 static void __on_receive_suspend_signal(GDBusConnection *connection,
503 const gchar *sender_name,
504 const gchar *object_path,
505 const gchar *interface_name,
506 const gchar *signal_name,
507 GVariant *parameters,
510 gint suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
514 if (g_strcmp0(signal_name, RESOURCED_FREEZER_SIGNAL) == 0) {
515 g_variant_get(parameters, "(ii)", &status, &pid);
516 if (pid == getpid() && status == 0) {
517 if (!__context.allowed_bg && __context.suspended_state) {
518 __remove_suspend_timer();
519 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
520 __context.suspended_state = false;
521 __add_suspend_timer();
527 static int __init_suspend_dbus_handler(void)
531 if (__suspend_dbus_handler_initialized)
535 __bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
537 _ERR("Failed to connect to the D-BUS daemon: %s",
544 __suspend_dbus_handler_initialized = g_dbus_connection_signal_subscribe(
547 RESOURCED_FREEZER_INTERFACE,
548 RESOURCED_FREEZER_SIGNAL,
549 RESOURCED_FREEZER_PATH,
551 G_DBUS_SIGNAL_FLAGS_NONE,
552 __on_receive_suspend_signal,
555 if (__suspend_dbus_handler_initialized == 0) {
556 _ERR("g_dbus_connection_signal_subscribe() is failed.");
560 _DBG("[__SUSPEND__] suspend signal initialized");
565 static void __fini_suspend_dbus_handler(void)
570 if (__suspend_dbus_handler_initialized) {
571 g_dbus_connection_signal_unsubscribe(__bus,
572 __suspend_dbus_handler_initialized);
573 __suspend_dbus_handler_initialized = 0;
576 g_object_unref(__bus);
580 static int __get_locale_resource_dir(char *locale_dir, int size)
582 const char *res_path;
584 res_path = aul_get_app_resource_path();
585 if (res_path == NULL) {
586 _ERR("Failed to get resource path");
590 snprintf(locale_dir, size, "%s" PATH_LOCALE, res_path);
591 if (access(locale_dir, R_OK) != 0)
592 _DBG("%s does not exist", locale_dir);
597 static int __get_app_name(const char *appid, char **name)
599 char *name_token = NULL;
604 /* com.vendor.name -> name */
605 name_token = strrchr(appid, '.');
606 if (name_token == NULL)
611 *name = strdup(name_token);
618 static int __set_i18n(const char *domain, const char *dir)
623 if (domain == NULL) {
631 __locale_dir = strdup(dir);
637 r = setlocale(LC_ALL, "");
638 /* if locale is not set properly, try again to set as language base */
640 lan = vconf_get_str(VCONFKEY_LANGSET);
642 r = setlocale(LC_ALL, lan);
643 _DBG("*****appcore setlocale=%s\n", r);
648 _ERR("appcore: setlocale() error");
650 r = bindtextdomain(domain, dir);
652 _ERR("appcore: bindtextdomain() error");
654 r = textdomain(domain);
656 _ERR("appcore: textdomain() error");
661 EXPORT_API int appcore_base_on_set_i18n(void)
664 char locale_dir[PATH_MAX];
665 char appid[PATH_MAX];
668 r = aul_app_get_appid_bypid(getpid(), appid, PATH_MAX);
672 r = __get_app_name(appid, &name);
676 r = __get_locale_resource_dir(locale_dir, sizeof(locale_dir));
682 r = __set_i18n(name, locale_dir);
693 EXPORT_API int appcore_base_init(appcore_base_ops ops, int argc, char **argv, void *data)
698 __context.argc = argc;
699 __context.argv = argv;
700 __context.data = data;
702 __context.suspended_state = false;
703 __context.allowed_bg = false;
705 if (__context.ops.set_i18n)
706 __context.ops.set_i18n(__context.data);
708 __init_suspend_dbus_handler();
710 if (!__context.dirty) {
711 __context.dirty = true;
713 for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
714 if (__exist_callback(i)) {
715 if (__context.ops.set_event)
716 __context.ops.set_event(i, __context.data);
721 if (__context.ops.create && __context.ops.create(__context.data) < 0) {
722 aul_status_update(STATUS_DYING);
726 if (__context.ops.run)
727 __context.ops.run(__context.data);
732 EXPORT_API void appcore_base_fini(void)
736 aul_status_update(STATUS_DYING);
737 if (__context.ops.terminate)
738 __context.ops.terminate(__context.data);
740 for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
741 if (__exist_callback(i)) {
742 if (__context.ops.unset_event)
743 __context.ops.unset_event(i, __context.data);
747 g_list_free_full(__events, free);
749 __fini_suspend_dbus_handler();
756 __context.dirty = false;
759 EXPORT_API int appcore_base_flush_memory(void)
765 EXPORT_API int appcore_base_on_receive(aul_type type, bundle *b)
768 const char **tep_path;
776 _DBG("[APP %d] AUL event: AUL_START", getpid());
777 tep_path = bundle_get_str_array(b, AUL_TEP_PATH, &len);
779 for (i = 0; i < len; i++) {
780 ret = aul_check_tep_mount(tep_path[i]);
782 _ERR("mount request not completed within 1 sec");
788 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
789 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
790 _DBG("[__SUSPEND__] allowed background");
791 __context.allowed_bg = true;
792 __remove_suspend_timer();
795 if (__context.ops.control)
796 __context.ops.control(b, __context.data);
799 _DBG("[APP %d] AUL event: AUL_RESUME", getpid());
800 bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
801 if (bg && strncmp(bg, "ALLOWED_BG", strlen("ALLOWED_BG")) == 0) {
802 _DBG("[__SUSPEND__] allowed background");
803 __context.allowed_bg = true;
804 __remove_suspend_timer();
808 _DBG("[APP %d] AUL event: AUL_TERMINATE", getpid());
809 aul_status_update(STATUS_DYING);
810 if (!__context.allowed_bg)
811 __remove_suspend_timer();
813 if (__context.ops.exit)
814 __context.ops.exit(__context.data);
816 case AUL_TERMINATE_BGAPP:
817 _DBG("[APP %d] AUL event: AUL_TERMINATE_BGAPP", getpid());
818 if (!__context.allowed_bg)
819 __remove_suspend_timer();
822 _DBG("[APP %d] AUL event: AUL_WAKE", getpid());
823 if (!__context.allowed_bg && __context.suspended_state) {
824 int suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
825 __remove_suspend_timer();
826 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
827 __context.suspended_state = false;
831 _DBG("[APP %d] AUL event: AUL_SUSPEND", getpid());
832 if (!__context.allowed_bg && !__context.suspended_state) {
833 __remove_suspend_timer();
834 __flush_memory(NULL);
837 case AUL_UPDATE_REQUESTED:
838 _DBG("[APP %d] AUL event: AUL_UPDATE_REQUESTED", getpid());
839 __invoke_callback((void *)&dummy, APPCORE_BASE_EVENT_UPDATE_REQUESTED);
842 _DBG("[APP %d] AUL event: %d", getpid(), type);
850 EXPORT_API int appcore_base_on_create(void)
853 r = aul_launch_init(__context.ops.receive, NULL);
855 _ERR("Aul init failed: %d", r);
859 r = aul_launch_argv_handler(__context.argc, __context.argv);
861 _ERR("Aul argv handler failed: %d", r);
868 EXPORT_API int appcore_base_on_control(bundle *b)
873 EXPORT_API int appcore_base_on_terminate()
876 if (__context.ops.exit)
877 __context.ops.exit(__context.data);
882 EXPORT_API void appcore_base_on_set_event(enum appcore_base_event event)
887 case APPCORE_BASE_EVENT_LOW_MEMORY:
888 vconf_notify_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory, NULL);
890 case APPCORE_BASE_EVENT_LOW_BATTERY:
891 vconf_notify_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery, NULL);
893 case APPCORE_BASE_EVENT_LANG_CHANGE:
894 vconf_notify_key_changed(VCONFKEY_LANGSET, __on_language_change, NULL);
896 case APPCORE_BASE_EVENT_REGION_CHANGE:
897 r = vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change, NULL);
901 vconf_notify_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change, NULL);
903 case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
912 EXPORT_API void appcore_base_on_unset_event(enum appcore_base_event event)
917 case APPCORE_BASE_EVENT_LOW_MEMORY:
918 vconf_ignore_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory);
920 case APPCORE_BASE_EVENT_LOW_BATTERY:
921 vconf_ignore_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery);
923 case APPCORE_BASE_EVENT_LANG_CHANGE:
924 vconf_ignore_key_changed(VCONFKEY_LANGSET, __on_language_change);
926 case APPCORE_BASE_EVENT_REGION_CHANGE:
927 r = vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change);
930 vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change);
932 case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
939 EXPORT_API appcore_base_event_h appcore_base_add_event(enum appcore_base_event event,
940 appcore_base_event_cb cb, void *data)
942 appcore_base_event_node *node;
944 if (__context.dirty && !__exist_callback(event)) {
945 if (__context.ops.set_event)
946 __context.ops.set_event(event, __context.data);
949 node = malloc(sizeof(appcore_base_event_node));
957 __events = g_list_append(__events, node);
962 EXPORT_API int appcore_base_remove_event(appcore_base_event_h handle)
964 appcore_base_event_node *node = handle;
965 enum appcore_base_event event;
968 __events = g_list_remove(__events, node);
970 if (__context.dirty && !__exist_callback(event)) {
971 if (__context.ops.unset_event)
972 __context.ops.unset_event(event, __context.data);
978 EXPORT_API int appcore_base_raise_event(void *event, enum appcore_base_event type)
980 __invoke_callback(event, type);
984 EXPORT_API int appcore_base_get_rotation_state(enum appcore_base_rm *curr)
989 EXPORT_API bool appcore_base_is_bg_allowed(void)
991 return __context.allowed_bg;
994 EXPORT_API bool appcore_base_is_suspended(void)
996 return __context.suspended_state;
999 EXPORT_API void appcore_base_toggle_suspended_state(void)
1001 __context.suspended_state ^= __context.suspended_state;
1004 static int __on_receive(aul_type type, bundle *b, void *data)
1006 return appcore_base_on_receive(type, b);
1009 static int __on_create(void *data)
1011 return appcore_base_on_create();
1014 static int __on_control(bundle *b, void *data)
1016 return appcore_base_on_control(b);
1019 static int __on_terminate(void *data)
1021 return appcore_base_on_terminate();
1024 static int __on_set_i18n(void *data)
1026 return appcore_base_on_set_i18n();
1029 static void __on_set_event(enum appcore_base_event event, void *data)
1031 return appcore_base_on_set_event(event);
1034 static void __on_unset_event(enum appcore_base_event event, void *data)
1036 return appcore_base_on_unset_event(event);
1039 EXPORT_API appcore_base_ops appcore_base_get_default_ops(void)
1041 appcore_base_ops ops;
1043 ops.create = __on_create;
1044 ops.control = __on_control;
1045 ops.terminate = __on_terminate;
1046 ops.receive = __on_receive;
1047 ops.set_i18n = __on_set_i18n;
1050 ops.set_event = __on_set_event;
1051 ops.unset_event = __on_unset_event;