Handle abnormal exit
[platform/core/appfw/appcore-widget.git] / src / widget_app.c
1 /*
2  * Copyright (c) 2015 - 2017 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 #include <stdlib.h>
19 #include <stdbool.h>
20
21 #include <bundle.h>
22 #include <bundle_internal.h>
23 #include <aul.h>
24 #include <dlog.h>
25 #include <glib.h>
26 #include <glib-object.h>
27 #include <stdlib.h>
28 #include <Elementary.h>
29 #include <widget_errno.h>
30 #include <widget_instance.h>
31 #include <aul_app_com.h>
32 #include <Ecore_Wayland.h>
33 #include <system_info.h>
34 #include <vconf.h>
35 #include <vconf-internal-keys.h>
36 #include <screen_connector_provider.h>
37 #include <appcore_multiwindow_base.h>
38
39 #include "widget_app.h"
40 #include "widget-log.h"
41 #include "widget-private.h"
42 #include "widget_app_internal.h"
43
44 #ifdef LOG_TAG
45 #undef LOG_TAG
46 #endif
47
48 #define LOG_TAG "CAPI_WIDGET_APPLICATION"
49 #define APP_TYPE_WIDGET "widgetapp"
50 #define STATUS_FOREGROUND "fg"
51 #define STATUS_BACKGROUND "bg"
52
53 struct widget_extra {
54         void *extra;
55         char *instance_id;
56         bundle *args;
57         char *content;
58         Evas_Object *win;
59 };
60
61 struct widget_class_context {
62         widget_instance_lifecycle_callback_s callback;
63         void *data;
64 };
65
66 struct widget_app_context {
67         widget_app_lifecycle_callback_s callback;
68         void *data;
69         bool dirty;
70 };
71
72 struct widget_foreach_context {
73         widget_context_cb callback;
74         void *data;
75 };
76
77 struct app_event_info {
78         app_event_type_e type;
79         void *value;
80 };
81
82 struct app_event_handler {
83         app_event_type_e type;
84         app_event_cb cb;
85         void *data;
86         void *raw;
87 };
88
89 struct _widget_context {
90         int dummy;
91 };
92
93 static int __app_event_converter[APPCORE_BASE_EVENT_MAX] = {
94         [APP_EVENT_LOW_MEMORY] = APPCORE_BASE_EVENT_LOW_MEMORY,
95         [APP_EVENT_LOW_BATTERY] = APPCORE_BASE_EVENT_LOW_BATTERY,
96         [APP_EVENT_LANGUAGE_CHANGED] = APPCORE_BASE_EVENT_LANG_CHANGE,
97         [APP_EVENT_DEVICE_ORIENTATION_CHANGED] = APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED,
98         [APP_EVENT_REGION_FORMAT_CHANGED] = APPCORE_BASE_EVENT_REGION_CHANGE,
99         [APP_EVENT_SUSPENDED_STATE_CHANGED] = APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE,
100 };
101
102 static struct widget_app_context __context;
103 static char *__appid;
104 static char *__package_id;
105 static bool __fg_signal;
106 char *_viewer_endpoint;
107
108 static bool __is_widget_feature_enabled(void)
109 {
110         static bool feature = false;
111         static bool retrieved = false;
112         int ret;
113
114         if (retrieved == true)
115                 return feature;
116
117         ret = system_info_get_platform_bool(
118                         "http://tizen.org/feature/shell.appwidget", &feature);
119         if (ret != SYSTEM_INFO_ERROR_NONE) {
120                 _E("failed to get system info"); /* LCOV_EXCL_LINE */
121                 return false; /* LCOV_EXCL_LINE */
122         }
123
124         retrieved = true;
125
126         return feature;
127 }
128
129 /* LCOV_EXCL_START */
130 static void __on_poweroff(keynode_t *key, void *data)
131 {
132         int val;
133
134         val = vconf_keynode_get_int(key);
135         switch (val) {
136         case VCONFKEY_SYSMAN_POWER_OFF_DIRECT:
137         case VCONFKEY_SYSMAN_POWER_OFF_RESTART:
138                 _I("power off changed: %d", val);
139                 widget_app_exit();
140                 break;
141         case VCONFKEY_SYSMAN_POWER_OFF_NONE:
142         case VCONFKEY_SYSMAN_POWER_OFF_POPUP:
143         default:
144                 /* DO NOTHING */
145                 break;
146         }
147 }
148 /* LCOV_EXCL_STOP */
149
150 static int __widget_app_create(void *data)
151 {
152         char pkgid[256] = {0, };
153
154         appcore_multiwindow_base_on_create();
155         app_get_id(&__appid);
156         if (aul_app_get_pkgid_bypid(getpid(), pkgid, sizeof(pkgid)) == 0)
157                 __package_id = strdup(pkgid);
158
159         if (!__package_id || !__appid) {
160                 _E("__package_id is NULL");
161                 return -1;
162         }
163
164         screen_connector_provider_init();
165         vconf_notify_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS, __on_poweroff, NULL);
166
167         if (__context.callback.create == NULL) {
168                 _E("__context.callback.create(is NULL");
169                 return -1;
170         }
171
172         if (__context.callback.create(__context.data) == NULL) {
173                 _E("app_create_cb() returns NULL");
174                 return -1;
175         }
176
177         _D("widget app is created");
178         return 0;
179 }
180
181 static int __widget_app_terminate(void *data)
182 {
183         if (__context.callback.terminate)
184                 __context.callback.terminate(__context.data);
185
186         vconf_ignore_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS, __on_poweroff);
187         screen_connector_provider_fini();
188
189         if (_viewer_endpoint) {
190                 free(_viewer_endpoint);
191                 _viewer_endpoint = NULL;
192         }
193
194         if (__package_id) {
195                 free(__package_id);
196                 __package_id = NULL;
197         }
198
199         if (__appid) {
200                 free(__appid);
201                 __appid = NULL;
202         }
203
204         appcore_multiwindow_base_on_terminate();
205
206         _D("widget app is terminated");
207         return 0;
208 }
209
210 static int __send_lifecycle_event(const char *class_id, const char *instance_id,
211         int status)
212 {
213         bundle *b = bundle_create();
214         int ret;
215
216         if (b == NULL) {
217                 _E("out of memory"); /* LCOV_EXCL_LINE */
218                 return -1; /* LCOV_EXCL_LINE */
219         }
220
221         bundle_add_str(b, AUL_K_WIDGET_ID, class_id);
222         bundle_add_str(b, AUL_K_WIDGET_INSTANCE_ID, instance_id);
223         bundle_add_byte(b, AUL_K_WIDGET_STATUS, &status, sizeof(int));
224         bundle_add_str(b, AUL_K_PKGID, __package_id);
225
226         _D("send lifecycle %s(%d)", instance_id, status);
227         ret = aul_app_com_send("widget.status", b);
228         if (ret < 0)
229                 _E("send lifecycle error:%d", ret); /* LCOV_EXCL_LINE */
230
231         bundle_free(b);
232
233         return ret;
234 }
235
236 static int __send_update_status(const char *class_id, const char *instance_id,
237         int status, bundle *extra)
238 {
239         bundle *b;
240         int lifecycle = -1;
241         bundle_raw *raw = NULL;
242         int len;
243
244         b = bundle_create();
245         if (!b) {
246                 _E("out of memory"); /* LCOV_EXCL_LINE */
247                 return -1; /* LCOV_EXCL_LINE */
248         }
249
250         bundle_add_str(b, AUL_K_WIDGET_ID, class_id);
251         bundle_add_str(b, AUL_K_WIDGET_INSTANCE_ID, instance_id);
252         bundle_add_byte(b, AUL_K_WIDGET_STATUS, &status, sizeof(int));
253
254         if (extra) {
255                 bundle_encode(extra, &raw, &len);
256                 bundle_add_str(b, WIDGET_K_CONTENT_INFO, (const char *)raw);
257                 aul_widget_instance_add(class_id, instance_id);
258         }
259
260         _D("send update %s(%d) to %s", instance_id, status, _viewer_endpoint);
261         aul_app_com_send(_viewer_endpoint, b);
262
263         switch (status) {
264         case WIDGET_INSTANCE_EVENT_CREATE:
265                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_CREATE;
266                 break;
267         case WIDGET_INSTANCE_EVENT_DESTROY:
268                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_DESTROY;
269                 break;
270         case WIDGET_INSTANCE_EVENT_PAUSE:
271                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_PAUSE;
272                 break;
273         case WIDGET_INSTANCE_EVENT_RESUME:
274                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_RESUME;
275                 break;
276         }
277
278         if (lifecycle > -1)
279                 __send_lifecycle_event(class_id, instance_id, lifecycle);
280
281         bundle_free(b);
282         if (raw)
283                 free(raw);
284
285         return 0;
286 }
287
288 static void __instance_resume(const char *class_id, const char *id, bundle *b)
289 {
290         appcore_multiwindow_base_instance_h cxt;
291
292         cxt = appcore_multiwindow_base_instance_find(id);
293
294         if (!cxt) {
295                 _E("context not found: %s", id);
296                 return;
297         }
298
299         appcore_multiwindow_base_instance_resume(cxt);
300
301         __send_update_status(class_id, id,
302                         WIDGET_INSTANCE_EVENT_RESUME, NULL);
303         if (!__fg_signal) {
304                 _D("Send fg signal to resourceD");
305                 aul_send_app_status_change_signal(getpid(),
306                                 __appid,
307                                 __package_id,
308                                 STATUS_FOREGROUND,
309                                 APP_TYPE_WIDGET);
310                 __fg_signal = true;
311         }
312 }
313
314 static void __instance_pause(const char *class_id, const char *id, bundle *b)
315 {
316         appcore_multiwindow_base_instance_h cxt;
317
318         cxt = appcore_multiwindow_base_instance_find(id);
319
320         if (!cxt) {
321                 _E("context not found: %s", id);
322                 return;
323         }
324
325         appcore_multiwindow_base_instance_pause(cxt);
326
327         if (__fg_signal) {
328                 _D("Send bg signal to resourceD");
329                 aul_send_app_status_change_signal(getpid(),
330                                 __appid,
331                                 __package_id,
332                                 STATUS_BACKGROUND,
333                                 APP_TYPE_WIDGET);
334                 __fg_signal = false;
335         }
336 }
337
338 static void __instance_resize(const char *class_id, const char *id, bundle *b)
339 {
340         appcore_multiwindow_base_instance_h cxt;
341         struct widget_class_context *class_cxt;
342         const appcore_multiwindow_base_class *cls;
343         struct widget_extra *we;
344         char *remain = NULL;
345         char *w_str = NULL;
346         char *h_str = NULL;
347         int w = 0;
348         int h = 0;
349
350         cxt = appcore_multiwindow_base_instance_find(id);
351
352         if (!cxt) {
353                 _E("context not found: %s", id);
354                 return;
355         }
356
357         cls = appcore_multiwindow_base_instance_get_class(cxt);
358         if (!cls)
359                 return;
360
361         class_cxt = cls->data;
362         if (!class_cxt) {
363                 _E("class is NULL");
364                 return;
365         }
366
367         we = appcore_multiwindow_base_instance_get_extra(cxt);
368         if (!we) {
369                 _E("widget extra is NULL");
370                 return;
371         }
372
373         bundle_get_str(b, WIDGET_K_WIDTH, &w_str);
374         bundle_get_str(b, WIDGET_K_HEIGHT, &h_str);
375
376         if (w_str)
377                 w = (int)g_ascii_strtoll(w_str, &remain, 10);
378
379         if (h_str)
380                 h = (int)g_ascii_strtoll(h_str, &remain, 10);
381
382         if (we->win)
383                 evas_object_resize(we->win, w, h);
384         else
385                 _E("unable to find window of %s", id);
386
387         if (class_cxt->callback.resize)
388                 class_cxt->callback.resize(cxt, w, h, class_cxt->data);
389         _D("%s is resized to %dx%d", id, w, h);
390         __send_update_status(class_id, id,
391                 WIDGET_INSTANCE_EVENT_SIZE_CHANGED, NULL);
392 }
393
394 static void __inst_cb(const char *class_id, const char *id,
395                 appcore_multiwindow_base_instance_h cxt, void *data)
396 {
397         struct widget_class_context *class_cxt;
398         const appcore_multiwindow_base_class *cls;
399         bundle *content = NULL;
400         char *content_raw = NULL;
401         char *force_str = NULL;
402         int force;
403         bundle *b = data;
404
405         if (!b) {
406                 _E("bundle is NULL");
407                 return;
408         }
409
410         cls = appcore_multiwindow_base_instance_get_class(cxt);
411         if (!cls) {
412                 _E("class is NULL");
413                 return;
414         }
415
416         class_cxt = cls->data;
417         if (!class_cxt) {
418                 _E("class context is NULL");
419                 return;
420         }
421
422         if (!class_cxt->callback.update) {
423                 _E("update callback is NULL");
424                 return;
425         }
426
427         bundle_get_str(b, WIDGET_K_FORCE, &force_str);
428
429         if (force_str && strcmp(force_str, "true") == 0)
430                 force = 1;
431         else
432                 force = 0;
433
434         bundle_get_str(b, WIDGET_K_CONTENT_INFO, &content_raw);
435
436         if (content_raw)
437                 content = bundle_decode((const bundle_raw *)content_raw, strlen(content_raw));
438         class_cxt->callback.update(cxt, content, force, class_cxt->data);
439         __send_update_status(class_id, id,
440                 WIDGET_INSTANCE_EVENT_UPDATE, NULL);
441         _D("updated:%s", id);
442
443         if (content)
444                 bundle_free(content);
445 }
446
447 static void __instance_update(const char *class_id, const char *id, bundle *b)
448 {
449         appcore_multiwindow_base_instance_h cxt;
450
451         if (!id) {
452                 appcore_multiwindow_base_instance_foreach(class_id, __inst_cb, b);
453                 return;
454         }
455
456         cxt = appcore_multiwindow_base_instance_find(id);
457
458         if (!cxt) {
459                 _E("context not found: %s", id);
460                 return;
461         }
462
463         __inst_cb(class_id, id, cxt, b);
464 }
465
466 static void __instance_create(const char *class_id, const char *id, bundle *b)
467 {
468         struct widget_extra *we;
469         char *content = NULL;
470
471         we = (struct widget_extra *)calloc(1, sizeof(struct widget_extra));
472         if (!we) {
473                 _E("Out of memory");
474                 return;
475         }
476
477         we->instance_id = strdup(id);
478         we->args = b;
479         appcore_multiwindow_base_instance_run(class_id, id, we);
480         we->args = NULL;
481         we->win = NULL;
482         bundle_get_str(b, WIDGET_K_CONTENT_INFO, &content);
483         if (content)
484                 we->content = strdup(content);
485
486 }
487
488 static void __check_empty_instance(void)
489 {
490         int cnt = appcore_multiwindow_base_instance_get_cnt();
491
492         if (cnt == 0)
493                 widget_app_exit();
494 }
495
496 static void __instance_destroy(const char *class_id, const char *id, bundle *b)
497 {
498         appcore_multiwindow_base_instance_h cxt;
499         struct widget_extra *we;
500
501         cxt = appcore_multiwindow_base_instance_find(id);
502         if (!cxt) {
503                 _E("could not find widget obj: %s, clear amd info", id);
504                 aul_widget_instance_del(class_id, id);
505                 return;
506         }
507
508         we = appcore_multiwindow_base_instance_get_extra(cxt);
509         we->args = b;
510         appcore_multiwindow_base_instance_exit(cxt);
511         free(we->instance_id);
512         free(we->content);
513         free(we);
514         __check_empty_instance();
515 }
516
517 static int __widget_app_control(bundle *b, void *data)
518 {
519         char *class_id = NULL;
520         char *id = NULL;
521         char *operation = NULL;
522
523         appcore_multiwindow_base_on_control(b);
524
525         bundle_get_str(b, WIDGET_K_CLASS, &class_id);
526         /* for previous version compatibility, use appid for default class id */
527         if (class_id == NULL)
528                 class_id = __appid;
529
530         bundle_get_str(b, AUL_K_WIDGET_INSTANCE_ID, &id);
531         bundle_get_str(b, WIDGET_K_OPERATION, &operation);
532
533         if (!operation) {
534                 _E("operation is NULL");
535                 return 0;
536         }
537
538         if (strcmp(operation, "create") == 0) {
539                 __instance_create(class_id, id, b);
540         } else if (strcmp(operation, "resize") == 0) {
541                 __instance_resize(class_id, id, b);
542         } else if (strcmp(operation, "update") == 0) {
543                 __instance_update(class_id, id, b);
544         } else if (strcmp(operation, "destroy") == 0) {
545                 __instance_destroy(class_id, id, b);
546         } else if (strcmp(operation, "resume") == 0) {
547                 __instance_resume(class_id, id, b);
548         } else if (strcmp(operation, "pause") == 0) {
549                 __instance_pause(class_id, id, b);
550         } else if (strcmp(operation, "terminate") == 0) {
551                 __instance_destroy(class_id, id, b);
552         }
553
554         return 0;
555 }
556
557 static void __inst_resume_cb(const char *class_id, const char *id,
558                 appcore_multiwindow_base_instance_h cxt, void *data)
559 {
560         __instance_resume(class_id, id, data);
561 }
562
563 static void __get_content(bundle *b)
564 {
565         char *instance_id = NULL;
566         appcore_multiwindow_base_instance_h cxt;
567         struct widget_extra *we;
568
569         bundle_get_str(b, AUL_K_WIDGET_INSTANCE_ID, &instance_id);
570         if (!instance_id) {
571                 _E("instance id is NULL");
572                 return;
573         }
574
575         cxt = appcore_multiwindow_base_instance_find(instance_id);
576         if (!cxt) {
577                 _E("could not find widget obj: %s", instance_id);
578                 return;
579         }
580
581         we = appcore_multiwindow_base_instance_get_extra(cxt);
582         if (!we) {
583                 _E("widget extra is NULL");
584                 return;
585         }
586
587         if (we->content) {
588                 bundle_add_str(b, AUL_K_WIDGET_CONTENT_INFO, we->content);
589                 _D("content info of %s found", instance_id);
590         } else {
591                 bundle_add_str(b, AUL_K_WIDGET_CONTENT_INFO, "");
592                 _D("empty content info added");
593         }
594 }
595
596 static int __widget_app_receive(aul_type type, bundle *b, void *data)
597 {
598         appcore_multiwindow_base_on_receive(type, b);
599
600         switch (type) {
601         case AUL_RESUME:
602                 appcore_multiwindow_base_instance_foreach_full(__inst_resume_cb, b);
603                 break;
604         case AUL_TERMINATE:
605                 widget_app_exit();
606                 break;
607         case AUL_WIDGET_CONTENT:
608                 __get_content(b);
609                 break;
610         default:
611                 break;
612         }
613
614         return 0;
615 }
616
617 EXPORT_API int widget_app_main(int argc, char **argv,
618                 widget_app_lifecycle_callback_s *callback, void *user_data)
619 {
620         bundle *kb;
621         char *viewer_endpoint = NULL;
622
623         if (!__is_widget_feature_enabled()) {
624                 _E("not supported"); /* LCOV_EXCL_LINE */
625                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
626         }
627
628         if (argc <= 0 || argv == NULL || callback == NULL)
629                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
630                                 __FUNCTION__, NULL);
631
632         if (__context.dirty) {
633                 _E("Already started");
634                 return widget_app_error(WIDGET_ERROR_FAULT, __FUNCTION__, NULL);
635         }
636
637         if (callback->create == NULL)
638                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
639                                 __FUNCTION__,
640                                 "widget_app_create_cb() callback must be "
641                                 "registered");
642
643         appcore_multiwindow_base_ops ops = appcore_multiwindow_base_get_default_ops();
644
645         /* override methods */
646         ops.base.create = __widget_app_create;
647         ops.base.control = __widget_app_control;
648         ops.base.terminate = __widget_app_terminate;
649         ops.base.receive = __widget_app_receive;
650         __context.callback = *callback;
651         __context.data = user_data;
652         kb = bundle_import_from_argv(argc, argv);
653         if (kb) {
654                 bundle_get_str(kb, WIDGET_K_ENDPOINT, &viewer_endpoint);
655                 if (viewer_endpoint) {
656                         _D("viewer endpoint :%s", viewer_endpoint);
657                         _viewer_endpoint = strdup(viewer_endpoint);
658                 } else {
659                         _E("endpoint is missing");
660                 }
661
662                 bundle_free(kb);
663         } else {
664                 _E("failed to get launch argv"); /* LCOV_EXCL_LINE */
665                 return widget_app_error(WIDGET_ERROR_FAULT, __FUNCTION__, NULL);
666         }
667
668         __context.dirty = true;
669         appcore_multiwindow_base_init(ops, argc, argv, NULL);
670         appcore_multiwindow_base_fini();
671         __context.dirty = false;
672
673         return WIDGET_ERROR_NONE;
674 }
675
676 EXPORT_API int widget_app_exit(void)
677 {
678         if (!__is_widget_feature_enabled()) {
679                 _E("not supported"); /* LCOV_EXCL_LINE */
680                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
681         }
682
683         appcore_multiwindow_base_exit();
684         aul_widget_notify_exit();
685
686         return WIDGET_ERROR_NONE;
687 }
688
689 static gboolean __finish_event_cb(gpointer user_data)
690 {
691         appcore_multiwindow_base_instance_h cxt = user_data;
692         bundle *b;
693         const char *id;
694         const char *class_id;
695
696         if (!cxt) {
697                 _E("user_data is NULL");
698                 return FALSE;
699         }
700
701         id = appcore_multiwindow_base_instance_get_id(cxt);
702         class_id = appcore_multiwindow_base_instance_get_class_id(cxt);
703         b = bundle_create();
704
705         if (!b) {
706                 _E("Out-of-memory");
707                 return FALSE;
708         }
709
710         bundle_add_str(b, WIDGET_K_OPERATION, "terminate");
711         __instance_destroy(class_id, id, b);
712         bundle_free(b);
713
714         return FALSE;
715 }
716
717 EXPORT_API int widget_app_terminate_context(widget_context_h context)
718 {
719         if (!__is_widget_feature_enabled()) {
720                 _E("not supported"); /* LCOV_EXCL_LINE */
721                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
722         }
723
724         if (context == NULL)
725                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
726                                 __FUNCTION__, NULL);
727
728         g_idle_add(__finish_event_cb, context);
729         return WIDGET_ERROR_NONE;
730 }
731
732 static void __inst_full_cb(const char *class_id, const char *id,
733                 appcore_multiwindow_base_instance_h cxt, void *data)
734 {
735         struct widget_foreach_context *foreach_context = data;
736
737         if (!data)
738                 return;
739
740         if (foreach_context->callback)
741                 foreach_context->callback(cxt, foreach_context->data);
742 }
743
744 EXPORT_API int widget_app_foreach_context(widget_context_cb cb, void *data)
745 {
746         struct widget_foreach_context foreach_context;
747
748         if (!__is_widget_feature_enabled()) {
749                 _E("not supported"); /* LCOV_EXCL_LINE */
750                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
751         }
752
753         if (!cb) {
754                 _E("callback is NULL");
755                 return WIDGET_ERROR_INVALID_PARAMETER;
756         }
757
758         foreach_context.callback = cb;
759         foreach_context.data = data;
760         appcore_multiwindow_base_instance_foreach_full(__inst_full_cb, &foreach_context);
761
762         return WIDGET_ERROR_NONE;
763 }
764
765 int __event_cb(void *event, void *data)
766 {
767         app_event_handler_h handler = data;
768
769         struct app_event_info app_event;
770
771         app_event.type = handler->type;
772         app_event.value = event;
773
774         if (handler->cb)
775                 handler->cb(&app_event, handler->data);
776
777         return 0;
778 }
779
780 EXPORT_API int widget_app_add_event_handler(app_event_handler_h *event_handler,
781                                         app_event_type_e event_type, app_event_cb callback,
782                                         void *user_data)
783 {
784         int r;
785         bool feature;
786         app_event_handler_h handler;
787
788         r = system_info_get_platform_bool(FEATURE_SHELL_APPWIDGET, &feature);
789         if (r < 0)
790                 return WIDGET_ERROR_FAULT;
791
792         if (!feature)
793                 return WIDGET_ERROR_NOT_SUPPORTED;
794
795         if (event_handler == NULL || callback == NULL)
796                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
797
798         if (event_type < APP_EVENT_LOW_MEMORY
799             || event_type > APP_EVENT_REGION_FORMAT_CHANGED)
800                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
801
802         if (event_type == APP_EVENT_DEVICE_ORIENTATION_CHANGED)
803                 return widget_app_error(WIDGET_ERROR_NOT_SUPPORTED, __FUNCTION__, NULL);
804
805
806         handler = calloc(1, sizeof(struct app_event_handler));
807         if (!handler)
808                 return widget_app_error(WIDGET_ERROR_OUT_OF_MEMORY, __FUNCTION__, "failed to create handler");
809
810         handler->type = event_type;
811         handler->cb = callback;
812         handler->data = user_data;
813         handler->raw = appcore_base_add_event(__app_event_converter[event_type], __event_cb, handler);
814         *event_handler = handler;
815
816         return WIDGET_ERROR_NONE;
817 }
818
819 EXPORT_API int widget_app_remove_event_handler(app_event_handler_h
820                                                 event_handler)
821 {
822         int r;
823         bool feature;
824
825         r = system_info_get_platform_bool(FEATURE_SHELL_APPWIDGET, &feature);
826         if (r < 0)
827                 return WIDGET_ERROR_FAULT;
828
829         if (!feature)
830                 return WIDGET_ERROR_NOT_SUPPORTED;
831
832         app_event_type_e type;
833
834         if (event_handler == NULL)
835                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
836
837         type = event_handler->type;
838         if (type < APP_EVENT_LOW_MEMORY || type > APP_EVENT_REGION_FORMAT_CHANGED)
839                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
840
841         r = appcore_base_remove_event(event_handler->raw);
842         if (r < 0)
843                 return widget_app_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, "invalid raw handler");
844
845         free(event_handler);
846
847         return WIDGET_ERROR_NONE;
848 }
849
850 EXPORT_API const char *widget_app_get_id(widget_context_h context)
851 {
852         if (!__is_widget_feature_enabled()) {
853                 _E("not supported"); /* LCOV_EXCL_LINE */
854                 set_last_result(WIDGET_ERROR_NOT_SUPPORTED); /* LCOV_EXCL_LINE */
855                 return NULL; /* LCOV_EXCL_LINE */
856         }
857
858         if (!context) {
859                 set_last_result(WIDGET_ERROR_INVALID_PARAMETER);
860                 return NULL;
861         }
862
863         set_last_result(WIDGET_ERROR_NONE);
864         return appcore_multiwindow_base_instance_get_id(context);
865 }
866
867 static void __win_del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
868 {
869         char *plug_id;
870         plug_id = evas_object_data_del(obj, "___PLUGID");
871         free(plug_id);
872 }
873
874 EXPORT_API int widget_app_get_elm_win(widget_context_h context,
875                                         Evas_Object **win)
876 {
877         Evas_Object *ret_win;
878         Ecore_Wl_Window *wl_win;
879         struct wl_surface *surface;
880         struct widget_extra *we;
881         char buffer[256];
882         int rots[3] = {0};
883         int win_id;
884         const char *id;
885         appcore_multiwindow_base_instance_h cxt;
886
887         if (!__is_widget_feature_enabled()) {
888                 _E("not supported"); /* LCOV_EXCL_LINE */
889                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
890         }
891
892         if (context == NULL || win == NULL)
893                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
894                                 __FUNCTION__, NULL);
895
896         cxt = (appcore_multiwindow_base_instance_h)context;
897         id = appcore_multiwindow_base_instance_get_id(cxt);
898         ret_win = elm_win_add(NULL, id, ELM_WIN_BASIC);
899         if (ret_win == NULL) {
900                 _E("failed to create window"); /* LCOV_EXCL_LINE */
901                 goto fault; /* LCOV_EXCL_LINE */
902         }
903
904         elm_win_wm_rotation_preferred_rotation_set(ret_win, -1);
905         elm_win_wm_rotation_available_rotations_set(ret_win, rots, 1);
906
907         wl_win = elm_win_wl_window_get(ret_win);
908         if (wl_win == NULL) {
909                 _E("failed to get wayland window"); /* LCOV_EXCL_LINE */
910                 goto fault;
911         }
912
913         surface = ecore_wl_window_surface_get(wl_win);
914         if (surface == NULL) {
915                 _E("failed to get surface"); /* LCOV_EXCL_LINE */
916                 goto fault; /* LCOV_EXCL_LINE */
917         }
918         screen_connector_provider_remote_enable(id, surface);
919
920         ecore_wl_window_class_name_set(wl_win, id);
921         elm_win_aux_hint_add(ret_win, "wm.policy.win.user.geometry", "1");
922
923         *win = ret_win;
924         we  = appcore_multiwindow_base_instance_get_extra(cxt);
925         we->win = ret_win;
926         win_id = ecore_wl_window_id_get(wl_win);
927
928         /* Set data to use in accessibility */
929         snprintf(buffer, sizeof(buffer), "%s:%d", id, getpid());
930         evas_object_data_set(ret_win, "___PLUGID", strdup(buffer));
931         evas_object_event_callback_add(ret_win, EVAS_CALLBACK_DEL, __win_del_cb, NULL);
932         appcore_multiwindow_base_window_bind(cxt, ret_win);
933
934         _D("window created: %d", win_id);
935
936         return WIDGET_ERROR_NONE;
937
938 fault:
939         if (ret_win)    /* LCOV_EXCL_LINE */
940                 evas_object_del(ret_win); /* LCOV_EXCL_LINE */
941
942         return WIDGET_ERROR_FAULT; /* LCOV_EXCL_LINE */
943 }
944
945 static void __instance_drop(appcore_multiwindow_base_instance_h cxt)
946 {
947         struct widget_extra *we;
948
949         we = appcore_multiwindow_base_instance_get_extra(cxt);
950         appcore_multiwindow_base_instance_drop(cxt);
951         free(we->instance_id);
952         free(we->content);
953         free(we);
954         __check_empty_instance();
955 }
956
957 static void __stub_create(appcore_multiwindow_base_instance_h context, void *data)
958 {
959         struct widget_class_context *cxt  = data;
960         struct widget_extra *we;
961         bundle *b;
962         bundle *content_info = NULL;
963         char *id = NULL;
964         char *class_id = NULL;
965         char *operation = NULL;
966         char *content = NULL;
967         char *w_str = NULL;
968         char *h_str = NULL;
969         char *remain = NULL;
970         int w = 0;
971         int h = 0;
972         int ret = -1;
973
974         appcore_multiwindow_base_class_on_create(context);
975         we  = appcore_multiwindow_base_instance_get_extra((appcore_multiwindow_base_instance_h)context);
976         b = we->args;
977
978         bundle_get_str(b, WIDGET_K_CLASS, &class_id);
979         /* for previous version compatibility, use appid for default class id */
980         if (class_id == NULL)
981                 class_id = __appid;
982
983         bundle_get_str(b, AUL_K_WIDGET_INSTANCE_ID, &id);
984         bundle_get_str(b, WIDGET_K_OPERATION, &operation);
985
986         if (!operation) {
987                 _E("no operation provided");
988                 return;
989         }
990
991         bundle_get_str(b, WIDGET_K_CONTENT_INFO, &content);
992         bundle_get_str(b, WIDGET_K_WIDTH, &w_str);
993         bundle_get_str(b, WIDGET_K_HEIGHT, &h_str);
994
995         if (w_str)
996                 w = (int)g_ascii_strtoll(w_str, &remain, 10);
997
998         if (h_str)
999                 h = (int)g_ascii_strtoll(h_str, &remain, 10);
1000
1001         if (content)
1002                 content_info = bundle_decode((const bundle_raw *)content, strlen(content));
1003
1004         if (cxt->callback.create)
1005                 ret = cxt->callback.create(context, content_info, w, h, cxt->data);
1006         _D("%s is created %d", id);
1007
1008         if (ret < 0) {
1009                 _W("Create callback returns error(%d)", ret);
1010                 ret = __send_update_status(class_id, id,
1011                                 WIDGET_INSTANCE_EVENT_CREATE_ABORTED, NULL);
1012                 __instance_drop(context);
1013         } else {
1014                 ret = __send_update_status(class_id, id,
1015                         WIDGET_INSTANCE_EVENT_CREATE, NULL);
1016
1017                 aul_widget_instance_add(class_id, id);
1018         }
1019
1020         if (content_info)
1021                 bundle_free(content_info);
1022 }
1023
1024 static void __stub_terminate(appcore_multiwindow_base_instance_h context, void *data)
1025 {
1026         struct widget_class_context *class_cxt  = data;
1027         struct widget_extra *we;
1028         bundle *b;
1029         char *operation = NULL;
1030         bundle *content_info;
1031         widget_app_destroy_type_e reason = WIDGET_APP_DESTROY_TYPE_TEMPORARY;
1032         int event = WIDGET_INSTANCE_EVENT_TERMINATE;
1033         const char *id;
1034         const char *class_id;
1035
1036         id = appcore_multiwindow_base_instance_get_id(context);
1037         class_id = appcore_multiwindow_base_instance_get_class_id(context);
1038         we  = appcore_multiwindow_base_instance_get_extra((appcore_multiwindow_base_instance_h)context);
1039         b = we->args;
1040
1041         if (b) {
1042                 bundle_get_str(b, WIDGET_K_OPERATION, &operation);
1043                 if (operation && strcmp(operation, "destroy") == 0)
1044                         reason = WIDGET_APP_DESTROY_TYPE_PERMANENT;
1045         }
1046
1047         if (we->content)
1048                 content_info = bundle_decode((const bundle_raw *)we->content, strlen(we->content));
1049         else
1050                 content_info = bundle_create();
1051
1052         if (class_cxt->callback.destroy)
1053                 class_cxt->callback.destroy(context, reason, content_info, class_cxt->data);
1054         _D("%s is destroyed %d", id, reason);
1055
1056         if (reason == WIDGET_APP_DESTROY_TYPE_PERMANENT) {
1057                 event = WIDGET_INSTANCE_EVENT_DESTROY;
1058                 aul_widget_instance_del(class_id, id);
1059         } else {
1060                 __send_update_status(class_id, id,
1061                                 WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, content_info);
1062         }
1063
1064         if (content_info)
1065                 bundle_free(content_info);
1066
1067         __send_update_status(class_id, id, event, NULL);
1068         appcore_multiwindow_base_class_on_terminate(context);
1069 }
1070
1071 static void __stub_pause(appcore_multiwindow_base_instance_h context, void *data)
1072 {
1073         struct widget_class_context *class_cxt  = data;
1074         const char *id;
1075         const char *class_id;
1076
1077         appcore_multiwindow_base_class_on_pause(context);
1078         id = appcore_multiwindow_base_instance_get_id(context);
1079         class_id = appcore_multiwindow_base_instance_get_class_id(context);
1080
1081         if (!class_cxt) {
1082                 _E("class context is NULL");
1083                 return;
1084         }
1085
1086         if (class_cxt->callback.pause)
1087                 class_cxt->callback.pause(context, class_cxt->data);
1088         _D("%s is paused", id);
1089         __send_update_status(class_id, id,
1090                 WIDGET_INSTANCE_EVENT_PAUSE, NULL);
1091 }
1092
1093 static void __stub_resume(appcore_multiwindow_base_instance_h context, void *data)
1094 {
1095         struct widget_class_context *class_cxt  = data;
1096         const char *id;
1097         const char *class_id;
1098
1099         appcore_multiwindow_base_class_on_resume(context);
1100         id = appcore_multiwindow_base_instance_get_id(context);
1101         class_id = appcore_multiwindow_base_instance_get_class_id(context);
1102
1103         if (!class_cxt) {
1104                 _E("class context is NULL");
1105                 return;
1106         }
1107
1108         if (class_cxt->callback.resume)
1109                 class_cxt->callback.resume(context, class_cxt->data);
1110         _D("%s is resumed", id);
1111         __send_update_status(class_id, id,
1112                 WIDGET_INSTANCE_EVENT_RESUME, NULL);
1113 }
1114
1115 EXPORT_API widget_class_h widget_app_class_add(widget_class_h widget_class,
1116                 const char *class_id,
1117                 widget_instance_lifecycle_callback_s callback, void *user_data)
1118 {
1119         appcore_multiwindow_base_class cls;
1120         struct widget_class_context *cxt;
1121
1122         if (!__is_widget_feature_enabled()) {
1123                 _E("not supported");
1124                 set_last_result(WIDGET_ERROR_NOT_SUPPORTED);
1125                 return NULL;
1126         }
1127
1128         if (!class_id) {
1129                 _E("class is is NULL");
1130                 set_last_result(WIDGET_ERROR_INVALID_PARAMETER);
1131                 return NULL;
1132         }
1133
1134         cxt = calloc(1, sizeof(struct widget_class_context));
1135
1136         if (!cxt) {
1137                 _E("failed to calloc : %s", __FUNCTION__);
1138                 set_last_result(WIDGET_ERROR_OUT_OF_MEMORY);
1139                 return NULL;
1140         }
1141
1142         cxt->callback = callback;
1143         cxt->data = user_data;
1144
1145         cls.id = strdup(class_id);
1146         cls.data = cxt;
1147         cls.create = __stub_create;
1148         cls.terminate = __stub_terminate;
1149         cls.pause = __stub_pause;
1150         cls.resume = __stub_resume;
1151
1152         appcore_multiwindow_base_class_add(cls);
1153         set_last_result(WIDGET_ERROR_NONE);
1154
1155         return (widget_class_h)cxt;
1156 }
1157
1158 EXPORT_API widget_class_h widget_app_class_create(
1159                 widget_instance_lifecycle_callback_s callback, void *user_data)
1160 {
1161         return widget_app_class_add(NULL, __appid, callback, user_data);
1162 }
1163
1164 EXPORT_API int widget_app_context_set_tag(widget_context_h context, void *tag)
1165 {
1166         struct widget_extra *we;
1167
1168         if (!__is_widget_feature_enabled()) {
1169                 _E("not supported"); /* LCOV_EXCL_LINE */
1170                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
1171         }
1172
1173         if (!context)
1174                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1175                                 __FUNCTION__, NULL);
1176
1177         we = appcore_multiwindow_base_instance_get_extra((appcore_multiwindow_base_instance_h)context);
1178         we->extra = tag;
1179
1180         return WIDGET_ERROR_NONE;
1181 }
1182
1183 EXPORT_API int widget_app_context_get_tag(widget_context_h context, void **tag)
1184 {
1185         struct widget_extra *we;
1186
1187         if (!__is_widget_feature_enabled()) {
1188                 _E("not supported"); /* LCOV_EXCL_LINE */
1189                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
1190         }
1191
1192         if (!context || !tag)
1193                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1194                                 __FUNCTION__, NULL);
1195
1196         we  = appcore_multiwindow_base_instance_get_extra((appcore_multiwindow_base_instance_h)context);
1197         if (we == NULL)
1198                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1199                                 __FUNCTION__, NULL);
1200         *tag = we->extra;
1201
1202         return WIDGET_ERROR_NONE;
1203 }
1204
1205 EXPORT_API int widget_app_context_set_content_info(widget_context_h context,
1206                 bundle *content_info)
1207 {
1208         int ret = 0;
1209         bundle_raw *raw = NULL;
1210         int len;
1211         const char *id;
1212         const char *class_id;
1213         struct widget_extra *we;
1214         appcore_multiwindow_base_instance_h cxt;
1215
1216         if (!__is_widget_feature_enabled()) {
1217                 _E("not supported"); /* LCOV_EXCL_LINE */
1218                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
1219         }
1220
1221         if (!context || !content_info)
1222                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1223                                 __FUNCTION__, NULL);
1224
1225         cxt = (appcore_multiwindow_base_instance_h)context;
1226         id = appcore_multiwindow_base_instance_get_id(cxt);
1227         class_id = appcore_multiwindow_base_instance_get_class_id(cxt);
1228         we  = appcore_multiwindow_base_instance_get_extra(cxt);
1229
1230         if (!class_id || !id || !we)
1231                 return widget_app_error(WIDGET_ERROR_FAULT, __FUNCTION__, NULL);
1232
1233         ret = __send_update_status(class_id, id,
1234                         WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, content_info);
1235
1236         if (we->content)
1237                 free(we->content);
1238
1239         bundle_encode(content_info, &raw, &len);
1240         if (raw)
1241                 we->content = strdup((const char *)raw);
1242         else
1243                 we->content = NULL;
1244
1245         free(raw);
1246         if (ret < 0) {
1247                 /* LCOV_EXCL_START */
1248                 _E("failed to send content info: %s of %s (%d)", id,
1249                                 class_id, ret);
1250                 return widget_app_error(WIDGET_ERROR_IO_ERROR, __FUNCTION__,
1251                                 NULL);
1252                 /* LCOV_EXCL_STOP */
1253         }
1254
1255         return WIDGET_ERROR_NONE;
1256 }
1257
1258 EXPORT_API int widget_app_context_set_title(widget_context_h context,
1259                 const char *title)
1260 {
1261         appcore_multiwindow_base_instance_h cxt;
1262         struct widget_extra *we;
1263
1264         if (!__is_widget_feature_enabled()) {
1265                 _E("not supported"); /* LCOV_EXCL_LINE */
1266                 return WIDGET_ERROR_NOT_SUPPORTED; /* LCOV_EXCL_LINE */
1267         }
1268
1269         if (!context || !title) {
1270                 _E("Invalid parameter %p %p", context, title);
1271                 return WIDGET_ERROR_INVALID_PARAMETER;
1272         }
1273
1274         cxt = (appcore_multiwindow_base_instance_h)context;
1275         we  = appcore_multiwindow_base_instance_get_extra(cxt);
1276
1277         if (we->win)
1278                 elm_win_title_set(we->win, title);
1279
1280         return WIDGET_ERROR_NONE;
1281 }