Remove suspend timer when resuming app
[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 <sys/stat.h>
26 #include <unistd.h>
27 #include <malloc.h>
28 #include <locale.h>
29 #include <libintl.h>
30 #include <linux/limits.h>
31 #include <glib.h>
32 #include <gio/gio.h>
33 #include <sys/time.h>
34 #include <dlfcn.h>
35 #include <vconf.h>
36 #include <aul.h>
37 #include <bundle_internal.h>
38 #include <sensor_internal.h>
39 #include <ttrace.h>
40 #include <system_info.h>
41
42 #include "appcore_base.h"
43 #include "appcore_base_private.h"
44 #include "appcore_watchdog.h"
45 #include "appcore_base_control.h"
46
47 #define PATH_LOCALE "locale"
48 #define RESOURCED_FREEZER_PATH "/Org/Tizen/ResourceD/Freezer"
49 #define RESOURCED_FREEZER_INTERFACE "org.tizen.resourced.freezer"
50 #define RESOURCED_FREEZER_SIGNAL "FreezerState"
51 #define SQLITE_FLUSH_MAX (1024 * 1024)
52
53 typedef struct _appcore_base_context {
54         appcore_base_ops ops;
55         void *data;
56         int argc;
57         char **argv;
58         unsigned int tid;
59         bool suspended_state;
60         bool allowed_bg;
61         bool dirty;
62         guint sid;
63         int display_state;
64 } appcore_base_context;
65
66 typedef struct _appcore_base_event_node {
67         int type;
68         appcore_base_event_cb cb;
69         void *data;
70         void *prev_event;
71 } appcore_base_event_node;
72
73 typedef struct _appcore_base_rotation {
74         int conn;
75         int lock;
76         int ref;
77         enum appcore_base_rm rm;
78         int charger_status;
79         bool initialized;
80 } appcore_base_rotation;
81
82 struct lang_info_s {
83         char *parent;
84         GList *list;
85 };
86
87 static appcore_base_context __context;
88 static GList *__events;
89 static GDBusConnection *__bus;
90 static guint __suspend_dbus_handler_initialized;
91 static char *__locale_dir;
92 static appcore_base_rotation __rotation;
93
94 appcore_base_tizen_profile_t appcore_base_get_tizen_profile(void)
95 {
96         static appcore_base_tizen_profile_t profile = TIZEN_PROFILE_UNKNOWN;
97         char *profile_name = NULL;
98
99         if (__builtin_expect(profile != TIZEN_PROFILE_UNKNOWN, 1))
100                 return profile;
101
102         system_info_get_platform_string("http://tizen.org/feature/profile",
103                         &profile_name);
104         if (profile_name == NULL)
105                 return profile;
106
107         switch (*profile_name) {
108         case 'm':
109         case 'M':
110                 profile = TIZEN_PROFILE_MOBILE;
111                 break;
112         case 'w':
113         case 'W':
114                 profile = TIZEN_PROFILE_WEARABLE;
115                 break;
116         case 't':
117         case 'T':
118                 profile = TIZEN_PROFILE_TV;
119                 break;
120         case 'i':
121         case 'I':
122                 profile = TIZEN_PROFILE_IVI;
123                 break;
124         default:
125                 profile = TIZEN_PROFILE_COMMON;
126                 break;
127         }
128         free(profile_name);
129
130         return profile;
131 }
132
133 static int __compare_event(void *prev_event, void *curr_event, int type)
134 {
135         char *curr_strval;
136         char *prev_strval;
137         int curr_intval;
138         int prev_intval;
139         int ret = -1;
140
141         switch (type) {
142         case APPCORE_BASE_EVENT_LOW_MEMORY:
143         case APPCORE_BASE_EVENT_LOW_BATTERY:
144         case APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED:
145         case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
146                 prev_intval = GPOINTER_TO_INT(prev_event);
147                 curr_intval = *(int *)curr_event;
148                 if (prev_intval == curr_intval)
149                         ret = 0;
150                 break;
151         case APPCORE_BASE_EVENT_LANG_CHANGE:
152         case APPCORE_BASE_EVENT_REGION_CHANGE:
153                 prev_strval = prev_event;
154                 curr_strval = curr_event;
155                 if (prev_strval && curr_strval &&
156                                 !strcmp(prev_strval, curr_strval))
157                         ret = 0;
158                 break;
159         default:
160                 break;
161         }
162
163         return ret;
164 }
165
166 static void __unset_prev_event(void **prev_event, int type)
167 {
168         if (type == APPCORE_BASE_EVENT_LANG_CHANGE ||
169                         type == APPCORE_BASE_EVENT_REGION_CHANGE)
170                 free(*prev_event);
171         *prev_event = NULL;
172 }
173
174 static void __set_prev_event(void **prev_event, void *curr_event, int type)
175 {
176         int curr_intval;
177         char *curr_strval;
178
179         switch (type) {
180         case APPCORE_BASE_EVENT_LOW_MEMORY:
181         case APPCORE_BASE_EVENT_LOW_BATTERY:
182         case APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED:
183         case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
184                 curr_intval = *(int *)curr_event;
185                 *prev_event = GINT_TO_POINTER(curr_intval);
186                 break;
187         case APPCORE_BASE_EVENT_LANG_CHANGE:
188         case APPCORE_BASE_EVENT_REGION_CHANGE:
189                 curr_strval = curr_event;
190                 if (curr_strval)
191                         *prev_event = strdup(curr_strval);
192                 break;
193         default:
194                 break;
195         }
196 }
197
198 static void __invoke_callback(void *event, int type)
199 {
200         GList *iter = __events;
201
202         while (iter) {
203                 appcore_base_event_node *node = iter->data;
204                 iter = g_list_next(iter);
205
206                 if (node->type != type)
207                         continue;
208
209                 if (__compare_event(node->prev_event, event, type)) {
210                         node->cb(event, node->data);
211                         __unset_prev_event(&node->prev_event, type);
212                         __set_prev_event(&node->prev_event, event, type);
213                 }
214         }
215 }
216
217 static bool __exist_callback(int type)
218 {
219         GList *iter = __events;
220
221         while (iter) {
222                 appcore_base_event_node *node = iter->data;
223
224                 if (node->type == type)
225                         return true;
226
227                 iter = g_list_next(iter);
228         }
229
230         return false;
231 }
232
233 static enum appcore_base_rm __get_rm(sensor_data_t data)
234 {
235         int event;
236         enum appcore_base_rm rm;
237
238         if (data.value_count <= 0) {
239                 _ERR("Failed to get sensor data");
240                 return APPCORE_BASE_RM_UNKNOWN;
241         }
242
243         event = data.values[0];
244         switch (event) {
245         case AUTO_ROTATION_DEGREE_0:
246                 rm = APPCORE_BASE_RM_PORTRAIT_NORMAL;
247                 break;
248         case AUTO_ROTATION_DEGREE_90:
249                 rm = APPCORE_BASE_RM_LANDSCAPE_NORMAL;
250                 break;
251         case AUTO_ROTATION_DEGREE_180:
252                 rm = APPCORE_BASE_RM_PORTRAIT_REVERSE;
253                 break;
254         case AUTO_ROTATION_DEGREE_270:
255                 rm = APPCORE_BASE_RM_LANDSCAPE_REVERSE;
256                 break;
257         default:
258                 rm = APPCORE_BASE_RM_UNKNOWN;
259                 break;
260         }
261
262         return rm;
263 }
264
265 static void __lock_cb(keynode_t *node, void *user_data)
266 {
267         bool r;
268         sensor_data_t data;
269         enum appcore_base_rm rm;
270
271         __rotation.lock = !vconf_keynode_get_bool(node);
272         if (__rotation.lock) {
273                 _DBG("Rotation locked");
274                 rm = APPCORE_BASE_RM_PORTRAIT_NORMAL;
275         } else {
276                 _DBG("Rotation unlocked");
277                 r = sensord_get_data(__rotation.conn, AUTO_ROTATION_SENSOR, &data);
278                 if (!r) {
279                         _ERR("Failed to get sensor data");
280                         return;
281                 }
282
283                 rm = __get_rm(data);
284                 if (rm == APPCORE_BASE_RM_UNKNOWN) {
285                         _ERR("Unknown mode");
286                         return;
287                 }
288         }
289
290         if (__rotation.rm == rm)
291                 return;
292
293         _DBG("Rotation: %d -> %d", __rotation.rm, rm);
294         __rotation.rm = rm;
295         __invoke_callback((void *)&__rotation.rm, APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED);
296 }
297
298 static void __auto_rotation_changed_cb(sensor_t sensor, unsigned int event_type,
299                 sensor_data_t *data, void *user_data)
300 {
301         enum appcore_base_rm rm;
302
303         if (data == NULL)
304                 return;
305
306         if (__rotation.lock)
307                 return;
308
309         if (event_type != AUTO_ROTATION_CHANGE_STATE_EVENT)
310                 return;
311
312         rm = __get_rm(*data);
313         if (rm == APPCORE_BASE_RM_UNKNOWN) {
314                 _ERR("Unknown mode");
315                 return;
316         }
317
318         _DBG("Rotation: %d -> %d", __rotation.rm, rm);
319         __rotation.rm = rm;
320         __invoke_callback((void *)&__rotation.rm, APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED);
321 }
322
323 static void __fini_rotation(void)
324 {
325         if (!__rotation.initialized)
326                 return;
327
328         vconf_ignore_key_changed(VCONFKEY_SETAPPL_AUTO_ROTATE_SCREEN_BOOL, __lock_cb);
329         sensord_unregister_event(__rotation.conn, AUTO_ROTATION_CHANGE_STATE_EVENT);
330         sensord_stop(__rotation.conn);
331         sensord_disconnect(__rotation.conn);
332
333         __rotation.lock = 0;
334         __rotation.initialized = false;
335 }
336
337 static void __init_rotation(void)
338 {
339         sensor_t sensor;
340         int lock;
341         bool r;
342
343         if (__rotation.initialized)
344                 return;
345
346         sensor = sensord_get_sensor(AUTO_ROTATION_SENSOR);
347         __rotation.conn = sensord_connect(sensor);
348         if (__rotation.conn < 0) {
349                 _ERR("Failed to connect sensord");
350                 return;
351         }
352
353         r = sensord_register_event(__rotation.conn, AUTO_ROTATION_CHANGE_STATE_EVENT,
354                         SENSOR_INTERVAL_NORMAL, 0, __auto_rotation_changed_cb, NULL);
355         if (!r) {
356                 _ERR("Failed to register auto rotation change event");
357                 sensord_disconnect(__rotation.conn);
358                 return;
359         }
360
361         r = sensord_start(__rotation.conn, 0);
362         if (!r) {
363                 _ERR("Failed to start sensord");
364                 sensord_unregister_event(__rotation.conn, AUTO_ROTATION_CHANGE_STATE_EVENT);
365                 sensord_disconnect(__rotation.conn);
366                 return;
367         }
368
369         lock = 0;
370         vconf_get_bool(VCONFKEY_SETAPPL_AUTO_ROTATE_SCREEN_BOOL, &lock);
371         vconf_notify_key_changed(VCONFKEY_SETAPPL_AUTO_ROTATE_SCREEN_BOOL, __lock_cb, NULL);
372
373         __rotation.lock = !lock;
374         __rotation.initialized = true;
375 }
376
377 static void __charger_status_changed_cb(keynode_t *keynode, void *user_data)
378 {
379         if (TIZEN_FEATURE_CHARGER_STATUS) {
380                 __rotation.charger_status = vconf_keynode_get_int(keynode);
381                 if (__rotation.charger_status) {
382                         if (__rotation.ref)
383                                 __init_rotation();
384                 } else {
385                         if (__rotation.ref)
386                                 __fini_rotation();
387                 }
388                 _DBG("charger status(%d)", __rotation.charger_status);
389         }
390 }
391
392 static void __unregister_rotation_changed_event(void)
393 {
394         if (!__rotation.ref)
395                 return;
396
397         __rotation.ref--;
398         if (__rotation.ref > 1)
399                 return;
400
401         __fini_rotation();
402         if (TIZEN_FEATURE_CHARGER_STATUS) {
403                 vconf_ignore_key_changed(VCONFKEY_SYSMAN_CHARGER_STATUS,
404                                 __charger_status_changed_cb);
405         }
406
407         __rotation.ref = 0;
408 }
409
410 static void __register_rotation_changed_event(void)
411 {
412         if (__rotation.ref) {
413                 __rotation.ref++;
414                 return;
415         }
416
417         if (TIZEN_FEATURE_CHARGER_STATUS) {
418                 vconf_get_int(VCONFKEY_SYSMAN_CHARGER_STATUS,
419                                 &__rotation.charger_status);
420                 vconf_notify_key_changed(VCONFKEY_SYSMAN_CHARGER_STATUS,
421                                 __charger_status_changed_cb, NULL);
422                 if (__rotation.charger_status)
423                         __init_rotation();
424         } else {
425                 __init_rotation();
426         }
427
428         __rotation.ref++;
429 }
430
431 static void __on_low_memory(keynode_t *key, void *data)
432 {
433         int val;
434
435         val = vconf_keynode_get_int(key);
436
437         if (val >= VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) {
438                 __invoke_callback(&val, APPCORE_BASE_EVENT_LOW_MEMORY);
439                 malloc_trim(0);
440         }
441 }
442
443 static void __on_low_battery(keynode_t *key, void *data)
444 {
445         int val;
446
447         val = vconf_keynode_get_int(key);
448
449         if (val <= VCONFKEY_SYSMAN_BAT_CRITICAL_LOW)
450                 __invoke_callback(&val, APPCORE_BASE_EVENT_LOW_BATTERY);
451 }
452
453 static void __destroy_lang_info(gpointer data)
454 {
455         struct lang_info_s *info = (struct lang_info_s *)data;
456
457         if (info == NULL)
458                 return;
459
460         if (info->list)
461                 g_list_free_full(info->list, free);
462         if (info->parent)
463                 free(info->parent);
464         free(info);
465 }
466
467 static struct lang_info_s *__create_lang_info(const char *lang)
468 {
469         struct lang_info_s *info;
470
471         info = calloc(1, sizeof(struct lang_info_s));
472         if (info == NULL) {
473                 _ERR("Out of memory");
474                 return NULL;
475         }
476
477         info->parent = strdup(lang);
478         if (info->parent == NULL) {
479                 _ERR("Out of memory");
480                 free(info);
481                 return NULL;
482         }
483
484         return info;
485 }
486
487 static gint __compare_langs(gconstpointer a, gconstpointer b)
488 {
489         if (!a || !b)
490                 return -1;
491
492         return strcmp(a, b);
493 }
494
495 static char *__get_string_before(const char *str, const char *delim)
496 {
497         char *new_str;
498         char *dup_str;
499         char *token;
500
501         dup_str = strdup(str);
502         if (dup_str == NULL)
503                 return NULL;
504
505         token = strtok(dup_str, delim);
506         if (token == NULL) {
507                 free(dup_str);
508                 return NULL;
509         }
510
511         new_str = strdup(token);
512         free(dup_str);
513
514         return new_str;
515 }
516
517 static GHashTable *__get_lang_table(void)
518 {
519         GHashTable *table;
520         DIR *dp;
521         struct dirent *dentry;
522         char buf[PATH_MAX];
523         struct stat stat_buf;
524         int ret;
525         char *parent_lang;
526         struct lang_info_s *info;
527
528         if (__locale_dir == NULL || __locale_dir[0] == '\0')
529                 return NULL;
530
531         table = g_hash_table_new_full(g_str_hash, g_str_equal,
532                         NULL, __destroy_lang_info);
533         if (table == NULL) {
534                 _ERR("Out of memory");
535                 return NULL;
536         }
537
538         dp = opendir(__locale_dir);
539         if (dp == NULL) {
540                 g_hash_table_destroy(table);
541                 return NULL;
542         }
543
544         while ((dentry = readdir(dp)) != NULL) {
545                 if (!strcmp(dentry->d_name, ".") ||
546                                 !strcmp(dentry->d_name, ".."))
547                         continue;
548
549                 snprintf(buf, sizeof(buf), "%s/%s",
550                                 __locale_dir, dentry->d_name);
551                 ret = stat(buf, &stat_buf);
552                 if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
553                         continue;
554
555                 parent_lang = __get_string_before(dentry->d_name, "_");
556                 if (parent_lang == NULL) {
557                         _ERR("Out of memory");
558                         break;
559                 }
560
561                 info = g_hash_table_lookup(table, parent_lang);
562                 if (info == NULL) {
563                         info = __create_lang_info(parent_lang);
564                         if (info == NULL) {
565                                 free(parent_lang);
566                                 break;
567                         }
568                         g_hash_table_insert(table, info->parent, info);
569                 }
570                 info->list = g_list_append(info->list, strdup(dentry->d_name));
571                 free(parent_lang);
572         }
573         closedir(dp);
574
575         return table;
576 }
577
578 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
579 {
580         struct lang_info_s *info;
581         GList *found;
582         char *parent_lang = NULL;
583         char *extract_lang;
584
585         if (lang == NULL)
586                 return list;
587
588         list = g_list_append(list, strdup(lang));
589
590         extract_lang = __get_string_before(lang, ".");
591         if (extract_lang == NULL)
592                 return list;
593
594         found = g_list_find_custom(list, extract_lang, __compare_langs);
595         if (found) {
596                 list = g_list_remove_link(list, found);
597                 list = g_list_concat(list, found);
598                 goto end;
599         }
600
601         parent_lang = __get_string_before(extract_lang, "_");
602         if (parent_lang == NULL)
603                 goto end;
604
605         info = g_hash_table_lookup(table, parent_lang);
606         if (info == NULL)
607                 goto end;
608
609         found = g_list_find_custom(info->list, extract_lang, __compare_langs);
610         if (found) {
611                 info->list = g_list_remove_link(info->list, found);
612                 list = g_list_concat(list, found);
613                 goto end;
614         }
615
616         found = g_list_find_custom(info->list, parent_lang, __compare_langs);
617         if (found) {
618                 info->list = g_list_remove_link(info->list, found);
619                 list = g_list_concat(list, found);
620                 goto end;
621         }
622
623         found = g_list_first(info->list);
624         if (found) {
625                 info->list = g_list_remove_link(info->list, found);
626                 list = g_list_concat(list, found);
627         }
628
629 end:
630         if (extract_lang)
631                 free(extract_lang);
632         if (parent_lang)
633                 free(parent_lang);
634
635         return list;
636 }
637
638 static GList *__split_language(const char *lang)
639 {
640         GList *list = NULL;
641         char *dup_lang;
642         char *token;
643
644         dup_lang = strdup(lang);
645         if (dup_lang == NULL) {
646                 _ERR("Out of memory");
647                 return NULL;
648         }
649
650         token = strtok(dup_lang, ":");
651         while (token != NULL) {
652                 list = g_list_append(list, strdup(token));
653                 token = strtok(NULL, ":");
654         }
655         free(dup_lang);
656
657         return list;
658 }
659
660 static GList *__append_default_langs(GList *list)
661 {
662         const char *langs[] = {"en_US", "en_GB", "en"};
663         unsigned int i;
664         GList *found;
665
666         for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
667                 found = g_list_find_custom(list, langs[i], __compare_langs);
668                 if (found == NULL)
669                         list = g_list_append(list, strdup(langs[i]));
670         }
671
672         return list;
673 }
674
675 static char *__get_language(const char *lang)
676 {
677         GHashTable *table;
678         GList *list;
679         GList *lang_list = NULL;
680         GList *iter;
681         char *language;
682         char buf[LINE_MAX] = {'\0'};
683         size_t n;
684
685         list = __split_language(lang);
686         if (list == NULL)
687                 return NULL;
688
689         table = __get_lang_table();
690         if (table == NULL) {
691                 g_list_free_full(list, free);
692                 return NULL;
693         }
694
695         iter = g_list_first(list);
696         while (iter) {
697                 language = (char *)iter->data;
698                 lang_list = __append_langs(language, lang_list, table);
699                 iter = g_list_next(iter);
700         }
701         g_list_free_full(list, free);
702         g_hash_table_destroy(table);
703
704         lang_list = __append_default_langs(lang_list);
705         iter = g_list_first(lang_list);
706         while (iter) {
707                 language = (char *)iter->data;
708                 if (language) {
709                         if (buf[0] == '\0') {
710                                 snprintf(buf, sizeof(buf), "%s", language);
711                         } else {
712                                 n = sizeof(buf) - strlen(buf) - 1;
713                                 strncat(buf, ":", n);
714                                 n = sizeof(buf) - strlen(buf) - 1;
715                                 strncat(buf, language, n);
716                         }
717                 }
718                 iter = g_list_next(iter);
719         }
720         g_list_free_full(lang_list, free);
721
722         return strdup(buf);
723 }
724
725 static void __update_lang(void)
726 {
727         char *language;
728         char *lang;
729         char *r;
730
731         lang = vconf_get_str(VCONFKEY_LANGSET);
732         if (lang) {
733                 /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */
734                 language = __get_language(lang);
735                 if (language) {
736                         _DBG("*****language(%s)", language);
737                         setenv("LANGUAGE", language, 1);
738                         free(language);
739                 } else {
740                         setenv("LANGUAGE", lang, 1);
741                 }
742                 setenv("LANG", lang, 1);
743                 setenv("LC_MESSAGES", lang, 1);
744                 setenv("LC_ALL", lang, 1);
745                 r = setlocale(LC_ALL, "");
746                 if (r == NULL) {
747                         r = setlocale(LC_ALL, "en_US.UTF-8");
748                         if (r != NULL) {
749                                 _DBG("*****appcore setlocale=%s\n", r);
750                         } else {
751                                 _DBG("*****appcore setlocale=\"C\"");
752                                 setenv("LC_ALL", "C", 1);
753                                 r = setlocale(LC_ALL, "");
754                                 if (r == NULL)
755                                         _ERR("failed to setlocale");
756                         }
757                 }
758                 free(lang);
759         }
760 }
761
762 static void __update_region(void)
763 {
764         char *region;
765         char *r;
766
767         region = vconf_get_str(VCONFKEY_REGIONFORMAT);
768         if (region) {
769                 setenv("LC_CTYPE", region, 1);
770                 setenv("LC_NUMERIC", region, 1);
771                 setenv("LC_TIME", region, 1);
772                 setenv("LC_COLLATE", region, 1);
773                 setenv("LC_MONETARY", region, 1);
774                 setenv("LC_PAPER", region, 1);
775                 setenv("LC_NAME", region, 1);
776                 setenv("LC_ADDRESS", region, 1);
777                 setenv("LC_TELEPHONE", region, 1);
778                 setenv("LC_MEASUREMENT", region, 1);
779                 setenv("LC_IDENTIFICATION", region, 1);
780                 r = setlocale(LC_ALL, "");
781                 if (r != NULL) {
782                         _DBG("*****appcore setlocale=%s\n", r);
783                 } else {
784                         _DBG("*****appcore setlocale=\"C\"");
785                         setenv("LC_ALL", "C", 1);
786                         r = setlocale(LC_ALL, "");
787                         if (r == NULL)
788                                 _ERR("failed to setlocale");
789                 }
790
791                 free(region);
792         }
793 }
794
795 static void __on_language_change(keynode_t *key, void *data)
796 {
797         char *val;
798
799         if (__context.sid) {
800                 g_source_remove(__context.sid);
801                 __context.sid = 0;
802         }
803
804         val = vconf_keynode_get_str(key);
805
806         __update_lang();
807         __invoke_callback((void *)val, APPCORE_BASE_EVENT_LANG_CHANGE);
808 }
809
810 static void __on_region_change(keynode_t *key, void *data)
811 {
812         char *val;
813         const char *name;
814
815         name = vconf_keynode_get_name(key);
816         if (name == NULL)
817                 return;
818
819         if (strcmp(name, VCONFKEY_REGIONFORMAT) &&
820                         strcmp(name, VCONFKEY_REGIONFORMAT_TIME1224))
821                 return;
822
823         val = vconf_get_str(VCONFKEY_REGIONFORMAT);
824
825         __update_region();
826         __invoke_callback((void *)val, APPCORE_BASE_EVENT_REGION_CHANGE);
827         free(val);
828 }
829
830 static gboolean __invoke_lang_change(gpointer data)
831 {
832         const char *lang;
833
834         __context.sid = 0;
835
836         lang = getenv("LANG");
837         if (!lang)
838                 return G_SOURCE_REMOVE;
839
840         __invoke_callback((void *)lang, APPCORE_BASE_EVENT_LANG_CHANGE);
841
842         return G_SOURCE_REMOVE;
843 }
844
845 static void __verify_language(void)
846 {
847         char *lang;
848         const char *env_lang;
849
850         env_lang = getenv("LANG");
851         if (!env_lang)
852                 return;
853
854         lang = vconf_get_str(VCONFKEY_LANGSET);
855         if (!lang)
856                 return;
857
858         if (strcmp(env_lang, lang) != 0) {
859                 _INFO("LANG(%s), LANGSET(%s)", env_lang, lang);
860                 __context.sid = g_idle_add(__invoke_lang_change, NULL);
861         }
862
863         free(lang);
864 }
865
866 static gboolean __flush_memory(gpointer data)
867 {
868         int suspend = APPCORE_BASE_SUSPENDED_STATE_WILL_ENTER_SUSPEND;
869
870         if (__context.ops.trim_memory)
871                 __context.ops.trim_memory(__context.data);
872
873         __context.tid = 0;
874
875         if (!__context.allowed_bg && !__context.suspended_state) {
876                 _DBG("[__SUSPEND__] flush case");
877                 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
878                 __context.suspended_state = true;
879         }
880
881         return FALSE;
882 }
883
884 static void __add_suspend_timer(void)
885 {
886         __context.tid = g_timeout_add_seconds(5, __flush_memory, NULL);
887 }
888
889 static void __remove_suspend_timer(void)
890 {
891         if (__context.tid > 0) {
892                 g_source_remove(__context.tid);
893                 __context.tid = 0;
894         }
895 }
896
897 static void __on_receive_suspend_signal(GDBusConnection *connection,
898                                         const gchar *sender_name,
899                                         const gchar *object_path,
900                                         const gchar *interface_name,
901                                         const gchar *signal_name,
902                                         GVariant *parameters,
903                                         gpointer user_data)
904 {
905         gint suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
906         gint pid;
907         gint status;
908
909         if (g_strcmp0(signal_name, RESOURCED_FREEZER_SIGNAL) == 0) {
910                 g_variant_get(parameters, "(ii)", &status, &pid);
911                 if (pid == getpid() && status == 0) {
912                         if (!__context.allowed_bg && __context.suspended_state) {
913                                 __remove_suspend_timer();
914                                 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
915                                 __context.suspended_state = false;
916                                 __add_suspend_timer();
917                         }
918                 }
919         }
920 }
921
922 static int __init_suspend_dbus_handler(void)
923 {
924         GError *err = NULL;
925
926         if (__suspend_dbus_handler_initialized)
927                 return 0;
928
929         if (!__bus) {
930                 __bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
931                 if (!__bus) {
932                         _ERR("Failed to connect to the D-BUS daemon: %s",
933                                                 err->message);
934                         g_error_free(err);
935                         return -1;
936                 }
937         }
938
939         __suspend_dbus_handler_initialized = g_dbus_connection_signal_subscribe(
940                                                 __bus,
941                                                 NULL,
942                                                 RESOURCED_FREEZER_INTERFACE,
943                                                 RESOURCED_FREEZER_SIGNAL,
944                                                 RESOURCED_FREEZER_PATH,
945                                                 NULL,
946                                                 G_DBUS_SIGNAL_FLAGS_NONE,
947                                                 __on_receive_suspend_signal,
948                                                 NULL,
949                                                 NULL);
950         if (__suspend_dbus_handler_initialized == 0) {
951                 _ERR("g_dbus_connection_signal_subscribe() is failed.");
952                 return -1;
953         }
954
955         _DBG("[__SUSPEND__] suspend signal initialized");
956
957         return 0;
958 }
959
960 static void __fini_suspend_dbus_handler(void)
961 {
962         if (__bus == NULL)
963                 return;
964
965         if (__suspend_dbus_handler_initialized) {
966                 g_dbus_connection_signal_unsubscribe(__bus,
967                                 __suspend_dbus_handler_initialized);
968                 __suspend_dbus_handler_initialized = 0;
969         }
970
971         g_object_unref(__bus);
972         __bus = NULL;
973 }
974
975 static gboolean __init_suspend(gpointer data)
976 {
977         __init_suspend_dbus_handler();
978         return G_SOURCE_REMOVE;
979 }
980
981 static int __get_locale_resource_dir(char *locale_dir, int size)
982 {
983         const char *res_path;
984
985         res_path = aul_get_app_resource_path();
986         if (res_path == NULL) {
987                 _ERR("Failed to get resource path");
988                 return -1;
989         }
990
991         snprintf(locale_dir, size, "%s" PATH_LOCALE, res_path);
992         if (access(locale_dir, R_OK) != 0)
993                 _DBG("%s does not exist", locale_dir);
994
995         return 0;
996 }
997
998 static int __get_app_name(const char *appid, char **name)
999 {
1000         char *name_token = NULL;
1001
1002         if (appid == NULL)
1003                 return -1;
1004
1005         /* com.vendor.name -> name */
1006         name_token = strrchr(appid, '.');
1007         if (name_token == NULL)
1008                 return -1;
1009
1010         name_token++;
1011
1012         *name = strdup(name_token);
1013         if (*name == NULL)
1014                 return -1;
1015
1016         return 0;
1017 }
1018
1019 static int __set_i18n(const char *domain, const char *dir)
1020 {
1021         char *r;
1022
1023         if (domain == NULL) {
1024                 errno = EINVAL;
1025                 return -1;
1026         }
1027
1028         if (dir) {
1029                 if (__locale_dir)
1030                         free(__locale_dir);
1031                 __locale_dir = strdup(dir);
1032         }
1033
1034         __update_lang();
1035         __update_region();
1036
1037         r = setlocale(LC_ALL, "");
1038         /* if locale is not set properly, try to set "en_US" again */
1039         if (r == NULL) {
1040                 r = setlocale(LC_ALL, "en_US.UTF-8");
1041                 if (r != NULL)
1042                         _DBG("*****appcore setlocale=%s\n", r);
1043         }
1044         if (r == NULL) {
1045                 _ERR("appcore: setlocale() error");
1046                 _DBG("*****appcore setlocale=\"C\"");
1047                 setenv("LC_ALL", "C", 1);
1048                 r = setlocale(LC_ALL, "");
1049                 if (r == NULL)
1050                         _ERR("failed to setlocale");
1051         }
1052
1053         r = bindtextdomain(domain, dir);
1054         if (r == NULL)
1055                 _ERR("appcore: bindtextdomain() error");
1056
1057         r = textdomain(domain);
1058         if (r == NULL)
1059                 _ERR("appcore: textdomain() error");
1060
1061         return 0;
1062 }
1063
1064 EXPORT_API int appcore_base_on_set_i18n(void)
1065 {
1066         int r;
1067         char locale_dir[PATH_MAX];
1068         char appid[PATH_MAX];
1069         char *name = NULL;
1070
1071         r = aul_app_get_appid_bypid(getpid(), appid, PATH_MAX);
1072         if (r < 0) {
1073                 _ERR("Failed to get application ID - pid(%d)", getpid());
1074                 return -1;
1075         }
1076
1077         r = __get_app_name(appid, &name);
1078         if (r < 0)
1079                 return -1;
1080
1081         r = __get_locale_resource_dir(locale_dir, sizeof(locale_dir));
1082         if (r < 0) {
1083                 free(name);
1084                 return -1;
1085         }
1086
1087         r = __set_i18n(name, locale_dir);
1088         if (r < 0) {
1089                 free(name);
1090                 return -1;
1091         }
1092
1093         free(name);
1094
1095         return 0;
1096 }
1097
1098 EXPORT_API int appcore_base_set_i18n(const char *domain_name, const char *dir_name)
1099 {
1100         return __set_i18n(domain_name, dir_name);
1101 }
1102
1103 static void __set_default_events(void)
1104 {
1105         int r;
1106
1107         vconf_notify_key_changed(VCONFKEY_LANGSET, __on_language_change, NULL);
1108         r = vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change, NULL);
1109         if (r == 0)
1110                 vconf_notify_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change, NULL);
1111         vconf_notify_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory, NULL);
1112 }
1113
1114 static void __unset_default_events(void)
1115 {
1116         int r;
1117
1118         vconf_ignore_key_changed(VCONFKEY_LANGSET, __on_language_change);
1119         r = vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, __on_region_change);
1120         if (r == 0)
1121                 vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, __on_region_change);
1122         vconf_ignore_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory);
1123 }
1124
1125 EXPORT_API int appcore_base_init(appcore_base_ops ops, int argc, char **argv, void *data)
1126 {
1127         int i;
1128         int r;
1129
1130         __context.ops = ops;
1131         __context.argc = argc;
1132         __context.argv = argv;
1133         __context.data = data;
1134         __context.tid = 0;
1135         __context.suspended_state = false;
1136         __context.allowed_bg = false;
1137
1138         if (__context.ops.init)
1139                 __context.ops.init(argc, argv, data);
1140
1141         if (TIZEN_FEATURE_BACKGROUND_MANAGEMENT)
1142                 g_idle_add(__init_suspend, NULL);
1143
1144         if (!__context.dirty) {
1145                 __context.dirty = true;
1146
1147                 for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
1148                         if (__exist_callback(i)) {
1149                                 if (__context.ops.set_event)
1150                                         __context.ops.set_event(i, __context.data);
1151                         }
1152                 }
1153         }
1154
1155         __verify_language();
1156         __set_default_events();
1157         if (__context.ops.set_i18n)
1158                 __context.ops.set_i18n(__context.data);
1159
1160         if (__context.ops.create) {
1161                 traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:CREATE");
1162                 r = __context.ops.create(__context.data);
1163                 traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1164                 if (r < 0) {
1165                         aul_status_update(STATUS_DYING);
1166                         return 0;
1167                 }
1168         }
1169
1170         if (__context.ops.run)
1171                 __context.ops.run(__context.data);
1172
1173         return 0;
1174 }
1175
1176 EXPORT_API void appcore_base_fini(void)
1177 {
1178         int i;
1179
1180         aul_status_update(STATUS_DYING);
1181         appcore_watchdog_disable();
1182         if (__context.ops.terminate) {
1183                 traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:TERMINATE");
1184                 __context.ops.terminate(__context.data);
1185                 traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1186         }
1187
1188         for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
1189                 if (__exist_callback(i)) {
1190                         if (__context.ops.unset_event)
1191                                 __context.ops.unset_event(i, __context.data);
1192                 }
1193         }
1194
1195         appcore_base_control_fini();
1196         __unset_default_events();
1197
1198         if (__context.sid) {
1199                 g_source_remove(__context.sid);
1200                 __context.sid = 0;
1201         }
1202
1203         __remove_suspend_timer();
1204
1205         g_list_free_full(__events, free);
1206         __events = NULL;
1207
1208         if (TIZEN_FEATURE_BACKGROUND_MANAGEMENT)
1209                 __fini_suspend_dbus_handler();
1210
1211         if (__locale_dir) {
1212                 free(__locale_dir);
1213                 __locale_dir = NULL;
1214         }
1215
1216         __context.dirty = false;
1217
1218         if (__context.ops.finish)
1219                 __context.ops.finish();
1220 }
1221
1222 EXPORT_API int appcore_base_flush_memory(void)
1223 {
1224         if (__context.ops.trim_memory)
1225                 __context.ops.trim_memory(__context.data);
1226
1227         return 0;
1228 }
1229
1230 EXPORT_API int appcore_base_on_receive(aul_type type, bundle *b)
1231 {
1232         const char *bg;
1233         int dummy = 0;
1234
1235         switch (type) {
1236         case AUL_START:
1237                 _DBG("[APP %d]     AUL event: AUL_START", getpid());
1238                 if (TIZEN_FEATURE_BACKGROUND_MANAGEMENT) {
1239                         bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
1240                         if (bg && !strcmp(bg, "ALLOWED_BG")) {
1241                                 _DBG("[__SUSPEND__] allowed background");
1242                                 __context.allowed_bg = true;
1243                                 __remove_suspend_timer();
1244                         }
1245                 }
1246
1247                 if (__context.ops.control) {
1248                         traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:RESET");
1249                         __context.ops.control(b, __context.data);
1250                         traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1251                 }
1252                 break;
1253         case AUL_RESUME:
1254                 _DBG("[APP %d]     AUL event: AUL_RESUME", getpid());
1255                 if (TIZEN_FEATURE_BACKGROUND_MANAGEMENT) {
1256                         bg = bundle_get_val(b, AUL_K_ALLOWED_BG);
1257                         if (bg && !strcmp(bg, "ALLOWED_BG")) {
1258                                 _DBG("[__SUSPEND__] allowed background");
1259                                 __context.allowed_bg = true;
1260                                 __remove_suspend_timer();
1261                         }
1262                 }
1263                 break;
1264         case AUL_TERMINATE:
1265                 _DBG("[APP %d]     AUL event: AUL_TERMINATE", getpid());
1266                 aul_status_update(STATUS_DYING);
1267                 if (!__context.allowed_bg)
1268                         __remove_suspend_timer();
1269
1270                 if (__context.ops.exit)
1271                         __context.ops.exit(__context.data);
1272                 break;
1273         case AUL_TERMINATE_INST:
1274         case AUL_TERMINATE_BG_INST:
1275         case AUL_TERMINATE_BGAPP:
1276                 _DBG("[APP %d]     AUL event: %d", getpid(), type);
1277                 if (!__context.allowed_bg)
1278                         __remove_suspend_timer();
1279                 break;
1280         case AUL_WAKE:
1281                 _DBG("[APP %d]     AUL event: AUL_WAKE", getpid());
1282                 if (TIZEN_FEATURE_BACKGROUND_MANAGEMENT) {
1283                         if (!__context.allowed_bg &&
1284                                         __context.suspended_state) {
1285                                 int suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
1286                                 __remove_suspend_timer();
1287                                 __invoke_callback((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
1288                                 __context.suspended_state = false;
1289                         }
1290                 }
1291                 break;
1292         case AUL_SUSPEND:
1293                 _DBG("[APP %d]     AUL event: AUL_SUSPEND", getpid());
1294                 if (TIZEN_FEATURE_BACKGROUND_MANAGEMENT) {
1295                         if (!__context.allowed_bg &&
1296                                         !__context.suspended_state) {
1297                                 __remove_suspend_timer();
1298                                 __flush_memory(NULL);
1299                         }
1300                 }
1301                 break;
1302         case AUL_UPDATE_REQUESTED:
1303                 _DBG("[APP %d]     AUL event: AUL_UPDATE_REQUESTED", getpid());
1304                 __invoke_callback((void *)&dummy, APPCORE_BASE_EVENT_UPDATE_REQUESTED);
1305                 break;
1306         default:
1307                 _DBG("[APP %d]     AUL event: %d", getpid(), type);
1308                 /* do nothing */
1309                 break;
1310         }
1311
1312         return 0;
1313 }
1314
1315 EXPORT_API int appcore_base_on_create(void)
1316 {
1317         int r;
1318
1319         r = aul_launch_init(__context.ops.receive, __context.data);
1320         if (r < 0) {
1321                 _ERR("Aul init failed: %d", r);
1322                 return -1;
1323         }
1324
1325         r = aul_launch_argv_handler(__context.argc, __context.argv);
1326         if (r < 0) {
1327                 _ERR("Aul argv handler failed: %d", r);
1328                 return -1;
1329         }
1330
1331         return 0;
1332 }
1333
1334 EXPORT_API int appcore_base_on_control(bundle *b)
1335 {
1336         appcore_base_control_invoke(b);
1337
1338         return 0;
1339 }
1340
1341 EXPORT_API int appcore_base_on_terminate()
1342 {
1343         aul_finalize();
1344
1345         return 0;
1346 }
1347
1348 EXPORT_API void appcore_base_on_set_event(enum appcore_base_event event)
1349 {
1350         switch (event) {
1351         case APPCORE_BASE_EVENT_LOW_BATTERY:
1352                 vconf_notify_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery, NULL);
1353                 break;
1354         case APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED:
1355                 __register_rotation_changed_event();
1356                 break;
1357         case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
1358                 break;
1359
1360         default:
1361                 break;
1362         }
1363
1364 }
1365
1366 EXPORT_API void appcore_base_on_unset_event(enum appcore_base_event event)
1367 {
1368         switch (event) {
1369         case APPCORE_BASE_EVENT_LOW_BATTERY:
1370                 vconf_ignore_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery);
1371                 break;
1372         case APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED:
1373                 __unregister_rotation_changed_event();
1374                 break;
1375         case APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE:
1376                 break;
1377         default:
1378                 break;
1379         }
1380 }
1381
1382 EXPORT_API int appcore_base_on_trim_memory(void)
1383 {
1384         int (*sqlite3_free_heap_memory)(int);
1385
1386         sqlite3_free_heap_memory = dlsym(RTLD_DEFAULT,
1387                         "sqlite3_release_memory");
1388         if (sqlite3_free_heap_memory)
1389                 sqlite3_free_heap_memory(SQLITE_FLUSH_MAX);
1390
1391         malloc_trim(0);
1392
1393         return 0;
1394 }
1395
1396 EXPORT_API appcore_base_event_h appcore_base_add_event(enum appcore_base_event event,
1397                 appcore_base_event_cb cb, void *data)
1398 {
1399         appcore_base_event_node *node;
1400
1401         if (__context.dirty && !__exist_callback(event)) {
1402                 if (__context.ops.set_event)
1403                         __context.ops.set_event(event, __context.data);
1404         }
1405
1406         node = malloc(sizeof(appcore_base_event_node));
1407
1408         if (node == NULL)
1409                 return NULL;
1410
1411         node->cb = cb;
1412         node->type = event;
1413         node->data = data;
1414
1415         if (event == APPCORE_BASE_EVENT_LANG_CHANGE ||
1416                         event == APPCORE_BASE_EVENT_REGION_CHANGE)
1417                 node->prev_event = NULL;
1418         else
1419                 node->prev_event = GINT_TO_POINTER(-1);
1420         __events = g_list_append(__events, node);
1421
1422         return node;
1423 }
1424
1425 EXPORT_API int appcore_base_remove_event(appcore_base_event_h handle)
1426 {
1427         appcore_base_event_node *node = handle;
1428         enum appcore_base_event event;
1429
1430         if (!node || !g_list_find(__events, node))
1431                 return -1;
1432
1433         event = node->type;
1434         __events = g_list_remove(__events, node);
1435         __unset_prev_event(&node->prev_event, event);
1436         free(node);
1437         if (__context.dirty && !__exist_callback(event)) {
1438                 if (__context.ops.unset_event)
1439                         __context.ops.unset_event(event, __context.data);
1440         }
1441
1442         return 0;
1443 }
1444
1445 EXPORT_API int appcore_base_raise_event(void *event, enum appcore_base_event type)
1446 {
1447         __invoke_callback(event, type);
1448         return 0;
1449 }
1450
1451 EXPORT_API int appcore_base_get_rotation_state(enum appcore_base_rm *curr)
1452 {
1453         if (curr == NULL)
1454                 return -1;
1455
1456         if (!__rotation.ref)
1457                 return -1;
1458
1459         *curr = __rotation.rm;
1460         return 0;
1461 }
1462
1463 EXPORT_API bool appcore_base_is_bg_allowed(void)
1464 {
1465         return __context.allowed_bg;
1466 }
1467
1468 EXPORT_API bool appcore_base_is_suspended(void)
1469 {
1470         return __context.suspended_state;
1471 }
1472
1473 EXPORT_API void appcore_base_toggle_suspended_state(void)
1474 {
1475         __context.suspended_state ^= __context.suspended_state;
1476 }
1477
1478 EXPORT_API void appcore_base_exit(void)
1479 {
1480         if (__context.ops.exit)
1481                 __context.ops.exit(__context.data);
1482 }
1483
1484 EXPORT_API void appcore_base_add_suspend_timer(void)
1485 {
1486         __add_suspend_timer();
1487 }
1488
1489 EXPORT_API void appcore_base_remove_suspend_timer(void)
1490 {
1491         __remove_suspend_timer();
1492 }
1493
1494 EXPORT_API void appcore_base_set_display_state(int display_state)
1495 {
1496         __context.display_state = display_state;
1497 }
1498
1499 EXPORT_API int appcore_base_get_display_state(void)
1500 {
1501         return __context.display_state;
1502 }
1503
1504 static int __on_receive(aul_type type, bundle *b, void *data)
1505 {
1506         return appcore_base_on_receive(type, b);
1507 }
1508
1509 static int __on_create(void *data)
1510 {
1511         return appcore_base_on_create();
1512 }
1513
1514 static int __on_control(bundle *b, void *data)
1515 {
1516         return appcore_base_on_control(b);
1517 }
1518
1519 static int __on_terminate(void *data)
1520 {
1521         return appcore_base_on_terminate();
1522 }
1523
1524 static int __on_set_i18n(void *data)
1525 {
1526         return appcore_base_on_set_i18n();
1527 }
1528
1529 static void __on_set_event(enum appcore_base_event event, void *data)
1530 {
1531         appcore_base_on_set_event(event);
1532 }
1533
1534 static void __on_unset_event(enum appcore_base_event event, void *data)
1535 {
1536         appcore_base_on_unset_event(event);
1537 }
1538
1539 static void __on_trim_memory(void *data)
1540 {
1541         appcore_base_on_trim_memory();
1542 }
1543
1544 EXPORT_API appcore_base_ops appcore_base_get_default_ops(void)
1545 {
1546         appcore_base_ops ops;
1547
1548         ops.create = __on_create;
1549         ops.control = __on_control;
1550         ops.terminate = __on_terminate;
1551         ops.receive = __on_receive;
1552         ops.set_i18n = __on_set_i18n;
1553         ops.init = NULL;
1554         ops.finish = NULL;
1555         ops.run = NULL;
1556         ops.exit = NULL;
1557         ops.set_event = __on_set_event;
1558         ops.unset_event = __on_unset_event;
1559         ops.trim_memory = __on_trim_memory;
1560
1561         return ops;
1562 }