Use header inclusion instead of extern keyword
[platform/core/appfw/appcore-agent.git] / src / service_app_main.c
1 /*
2  * Copyright (c) 2011 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 #include <stdlib.h>
18 #include <unistd.h>
19 #include <bundle.h>
20 #include <aul.h>
21 #include <dlog.h>
22 #include <vconf-internal-keys.h>
23 #include <app_common.h>
24 #include <Ecore.h>
25 #include <appcore_base.h>
26 #include <service_app.h>
27 #include <aul_job_scheduler.h>
28 #include <bundle_internal.h>
29 #include <glib.h>
30 #include <app_control_internal.h>
31
32 #include "service_app_extension.h"
33 #include "service_app_internal.h"
34
35 #ifdef LOG_TAG
36 #undef LOG_TAG
37 #endif
38
39 #define LOG_TAG "CAPI_APPFW_APPLICATION"
40
41 typedef enum {
42         APP_STATE_NOT_RUNNING,
43         APP_STATE_CREATING,
44         APP_STATE_RUNNING,
45 } app_state_e;
46
47 struct app_event_handler {
48         app_event_type_e type;
49         app_event_cb cb;
50         void *data;
51         void* raw;
52 };
53
54 struct app_event_info {
55         app_event_type_e type;
56         void *value;
57 };
58
59 struct service_app_context {
60         service_app_lifecycle_callback_s callback;
61         void *data;
62         GList *running_jobs;
63         GList *pending_jobs;
64         GList *job_handlers;
65 };
66
67 struct service_app_job_s {
68         char *job_id;
69         service_app_job_cb callback;
70         void *user_data;
71 };
72
73 struct job_s {
74         int job_status;
75         char *job_id;
76         bundle *job_data;
77         guint timer;
78         guint idler;
79 };
80
81 static struct service_app_context __context;
82 static app_state_e __app_state = APP_STATE_NOT_RUNNING;
83
84 static int __app_event_converter[APPCORE_BASE_EVENT_MAX] = {
85         [APP_EVENT_LOW_MEMORY] = APPCORE_BASE_EVENT_LOW_MEMORY,
86         [APP_EVENT_LOW_BATTERY] = APPCORE_BASE_EVENT_LOW_BATTERY,
87         [APP_EVENT_LANGUAGE_CHANGED] = APPCORE_BASE_EVENT_LANG_CHANGE,
88         [APP_EVENT_DEVICE_ORIENTATION_CHANGED] = APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED,
89         [APP_EVENT_REGION_FORMAT_CHANGED] = APPCORE_BASE_EVENT_REGION_CHANGE,
90         [APP_EVENT_SUSPENDED_STATE_CHANGED] = APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE,
91 };
92
93 static int __on_error(app_error_e error, const char *function, const char *description);
94 static void __destroy_job_handler(gpointer data);
95 static bool __exist_job_handler(const char *job_id);
96
97 static struct job_s *__create_job(int job_status, const char *job_id,
98                 bundle *job_data)
99 {
100         struct job_s *job;
101
102         job = calloc(1, sizeof(struct job_s));
103         if (job == NULL) {
104                 /* LCOV_EXCL_START */
105                 LOGE("Out of memory");
106                 return NULL;
107                 /* LCOV_EXCL_STOP */
108         }
109
110         job->job_id = strdup(job_id);
111         if (job->job_id == NULL) {
112                 /* LCOV_EXCL_START */
113                 LOGE("Out of memory");
114                 free(job);
115                 return NULL;
116                 /* LCOV_EXCL_STOP */
117         }
118
119         job->job_data = bundle_dup(job_data);
120         if (job->job_data == NULL) {
121                 /* LCOV_EXCL_START */
122                 LOGE("Out of memory");
123                 free(job->job_id);
124                 free(job);
125                 return NULL;
126                 /* LCOV_EXCL_STOP */
127         }
128
129         job->job_status = job_status;
130
131         return job;
132 }
133
134 static void __destroy_job(gpointer data)
135 {
136         struct job_s *job = (struct job_s *)data;
137
138         if (job == NULL)
139                 return;
140
141         if (job->idler)
142                 g_source_remove(job->idler); /* LCOV_EXCL_LINE */
143         if (job->timer)
144                 g_source_remove(job->timer); /* LCOV_EXCL_LINE */
145         if (job->job_data)
146                 bundle_free(job->job_data);
147         if (job->job_id)
148                 free(job->job_id);
149         free(job);
150 }
151
152 /* LCOV_EXCL_START */
153 static gboolean __pending_job_timeout_handler(gpointer data)
154 {
155         struct job_s *job = (struct job_s *)data;
156
157         LOGE("[__TIMEOUT__] Job(%s) Status(%d)", job->job_id, job->job_status);
158         __context.pending_jobs = g_list_remove(__context.pending_jobs, job);
159         job->timer = 0;
160         __destroy_job(job);
161
162         return G_SOURCE_REMOVE;
163 }
164 /* LCOV_EXCL_STOP */
165
166 static void __job_finish(void)
167 {
168         if (__context.running_jobs) {
169                 /* LCOV_EXCL_START */
170                 g_list_free_full(__context.running_jobs, __destroy_job);
171                 __context.running_jobs = NULL;
172                 /* LCOV_EXCL_STOP */
173         }
174
175         if (__context.pending_jobs) {
176                 /* LCOV_EXCL_START */
177                 g_list_free_full(__context.pending_jobs, __destroy_job);
178                 __context.pending_jobs = NULL;
179                 /* LCOV_EXCL_STOP */
180         }
181
182         if (__context.job_handlers) {
183                 g_list_free_full(__context.job_handlers, __destroy_job_handler);
184                 __context.job_handlers = NULL;
185         }
186 }
187
188 static int __service_app_create(void *data)
189 {
190         LOGW("service_app_create()");
191         appcore_base_on_create();
192
193         if (__context.callback.create == NULL ||
194                         __context.callback.create(__context.data) == false)
195                 return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__, "service_app_create_cb() returns false");
196
197         return APP_ERROR_NONE;
198 }
199
200 static int __service_app_terminate(void *data)
201 {
202         LOGW("service_app_terminate()");
203         if (__context.callback.terminate)
204                 __context.callback.terminate(__context.data);
205
206         appcore_base_on_terminate();
207
208         return APP_ERROR_NONE;
209 }
210
211 static int __service_app_control(bundle *b, void *data)
212 {
213         app_control_h app_control = NULL;
214         const char *job_id;
215
216         LOGW("service_app_control()");
217         appcore_base_on_control(b);
218
219         job_id = bundle_get_val(b, AUL_K_JOB_ID);
220         if (job_id) {
221                 LOGD("[__JOB__] Job(%s)", job_id);
222                 return 0;
223         }
224
225         if (app_control_create_event(b, &app_control) != 0)
226                 return -1;
227
228         if (__context.callback.app_control)
229                 __context.callback.app_control(app_control, __context.data);
230
231         app_control_destroy(app_control);
232
233         return 0;
234 }
235
236 static int __service_app_receive(aul_type type, bundle *b, void *data)
237 {
238         appcore_base_on_receive(type, b);
239
240         if (type == AUL_TERMINATE_BGAPP) {
241                 appcore_base_exit();
242                 return 0;
243         }
244
245         return 0;
246 }
247
248 static void __loop_init(int argc, char **argv, void *data)
249 {
250         ecore_init();
251 }
252
253 static void __loop_fini(void)
254 {
255         ecore_shutdown();
256 }
257
258 static void __loop_run(void *data)
259 {
260         ecore_main_loop_begin();
261 }
262
263 static void __exit_main_loop(void *data)
264 {
265         ecore_main_loop_quit();
266 }
267
268 static void __loop_exit(void *data)
269 {
270         ecore_main_loop_thread_safe_call_sync((Ecore_Data_Cb)__exit_main_loop, NULL);
271 }
272
273 static const char *__error_to_string(app_error_e error)
274 {
275         switch (error) {
276         case APP_ERROR_NONE:
277                 return "NONE";
278         case APP_ERROR_INVALID_PARAMETER:
279                 return "INVALID_PARAMETER";
280         case APP_ERROR_OUT_OF_MEMORY:
281                 return "OUT_OF_MEMORY";
282         case APP_ERROR_INVALID_CONTEXT:
283                 return "INVALID_CONTEXT";
284         case APP_ERROR_NO_SUCH_FILE:
285                 return "NO_SUCH_FILE";
286         case APP_ERROR_ALREADY_RUNNING:
287                 return "ALREADY_RUNNING";
288         default:
289                 return "UNKNOWN";
290         }
291 }
292
293 static int __on_error(app_error_e error, const char *function, const char *description)
294 {
295         if (description)
296                 LOGE("[%s] %s(0x%08x) : %s", function, __error_to_string(error), error, description);
297         else
298                 LOGE("[%s] %s(0x%08x)", function, __error_to_string(error), error);
299
300         return error;
301 }
302
303 EXPORT_API int service_app_main_ext(int argc, char **argv, service_app_lifecycle_callback_s *callback,
304                 service_app_loop_method_s *method, void *user_data)
305 {
306         int ret;
307         appcore_base_ops ops = appcore_base_get_default_ops();
308
309         if (argc < 1 || argv == NULL || callback == NULL || method == NULL)
310                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
311
312         if (callback->create == NULL)
313                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, "service_app_create_cb() callback must be registered");
314
315         if (__app_state != APP_STATE_NOT_RUNNING)
316                 return __on_error(APP_ERROR_ALREADY_RUNNING, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
317
318         /* override methods */
319         ops.create = __service_app_create;
320         ops.terminate = __service_app_terminate;
321         ops.control = __service_app_control;
322         ops.receive = __service_app_receive;
323         ops.run = method->run;
324         ops.exit = method->exit;
325         ops.init = method->init;
326         ops.finish = method->fini;
327
328         __context.callback = *callback;
329         __context.data = user_data;
330
331         __app_state = APP_STATE_CREATING;
332         ret = appcore_base_init(ops, argc, argv, NULL);
333         if (ret < 0) {
334                 /* LCOV_EXCL_START */
335                 __app_state = APP_STATE_NOT_RUNNING;
336                 return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__, NULL);
337                 /* LCOV_EXCL_STOP */
338         }
339
340         appcore_base_fini();
341         __job_finish();
342         __app_state = APP_STATE_NOT_RUNNING;
343         return APP_ERROR_NONE;
344 }
345
346 EXPORT_API int service_app_main(int argc, char **argv, service_app_lifecycle_callback_s *callback, void *user_data)
347 {
348         service_app_loop_method_s method = {
349                 .init = __loop_init,
350                 .fini = __loop_fini,
351                 .run = __loop_run,
352                 .exit = __loop_exit,
353         };
354
355         LOGW("service_app_main()");
356         return service_app_main_ext(argc, argv, callback, &method, user_data);
357 }
358
359 EXPORT_API void service_app_exit(void)
360 {
361         LOGW("service_app_exit()");
362         appcore_base_exit();
363 }
364
365 static int __event_cb(void *event, void *data)
366 {
367         app_event_handler_h handler = data;
368
369         struct app_event_info app_event;
370
371         app_event.type = handler->type;
372         app_event.value = event;
373
374         if (handler->cb)
375                 handler->cb(&app_event, handler->data);
376         return 0;
377 }
378
379 EXPORT_API int service_app_add_event_handler(app_event_handler_h *event_handler, app_event_type_e event_type, app_event_cb callback, void *user_data)
380 {
381         app_event_handler_h handler;
382
383         if (event_handler == NULL || callback == NULL)
384                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, "null parameter");
385
386         if (event_type < APP_EVENT_LOW_MEMORY || event_type > APP_EVENT_SUSPENDED_STATE_CHANGED ||
387                         event_type == APP_EVENT_DEVICE_ORIENTATION_CHANGED)
388                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, "invalid event type");
389
390         handler = calloc(1, sizeof(struct app_event_handler));
391         if (!handler)
392                 return __on_error(APP_ERROR_OUT_OF_MEMORY, __FUNCTION__, "insufficient memory"); /* LCOV_EXCL_LINE */
393
394         handler->type = event_type;
395         handler->cb = callback;
396         handler->data = user_data;
397         handler->raw = appcore_base_add_event(__app_event_converter[event_type], __event_cb, handler);
398
399         *event_handler = handler;
400
401         return APP_ERROR_NONE;
402 }
403
404 EXPORT_API int service_app_remove_event_handler(app_event_handler_h event_handler)
405 {
406         int ret;
407         app_event_type_e type;
408
409         if (event_handler == NULL)
410                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
411
412         type = event_handler->type;
413         if (type < APP_EVENT_LOW_MEMORY ||
414                         type > APP_EVENT_SUSPENDED_STATE_CHANGED ||
415                         type == APP_EVENT_DEVICE_ORIENTATION_CHANGED)
416                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
417
418         ret = appcore_base_remove_event(event_handler->raw);
419         free(event_handler);
420
421         if (ret < 0)
422                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
423
424         return APP_ERROR_NONE;
425 }
426
427 EXPORT_API void service_app_exit_without_restart(void)
428 {
429         aul_status_update_v2(STATUS_NORESTART);
430         appcore_base_exit();
431 }
432
433 static void __invoke_job_callback(int status, const char *job_id,
434                 bundle *job_data)
435 {
436         struct service_app_job_s *handle;
437         GList *iter;
438
439         iter = __context.job_handlers;
440         while (iter) {
441                 handle = (struct service_app_job_s *)iter->data;
442                 iter = iter->next;
443                 if (strcmp(handle->job_id, job_id) == 0) {
444                         handle->callback(status, job_id, job_data,
445                                         handle->user_data);
446                 }
447         }
448 }
449
450 static gboolean __handle_job(gpointer data)
451 {
452         struct job_s *job = (struct job_s *)data;
453
454         __context.running_jobs = g_list_remove(__context.running_jobs, job);
455
456         LOGD("[__JOB___] Job(%s) Status(%d) START",
457                         job->job_id, job->job_status);
458         if (job->job_status == SERVICE_APP_JOB_STATUS_START) {
459                 aul_job_scheduler_update_job_status(job->job_id,
460                                 JOB_STATUS_START);
461                 __invoke_job_callback(job->job_status, job->job_id,
462                                 job->job_data);
463         } else {
464                 __invoke_job_callback(job->job_status, job->job_id,
465                                 job->job_data);
466                 aul_job_scheduler_update_job_status(job->job_id,
467                                 JOB_STATUS_STOPPED);
468         }
469         LOGD("[__JOB__] Job(%s) Status(%d) END",
470                         job->job_id, job->job_status);
471
472         job->idler = 0;
473         __destroy_job(job);
474
475         return G_SOURCE_REMOVE;
476 }
477
478 static void __flush_pending_job(const char *job_id)
479 {
480         struct job_s *job;
481         GList *iter;
482
483         iter = __context.pending_jobs;
484         while (iter) {
485                 /* LCOV_EXCL_START */
486                 job = (struct job_s *)iter->data;
487                 iter = iter->next;
488                 if (strcmp(job->job_id, job_id) == 0) {
489                         __context.pending_jobs = g_list_remove(
490                                         __context.pending_jobs, job);
491
492                         if (job->timer) {
493                                 g_source_remove(job->timer);
494                                 job->timer = 0;
495                         }
496
497                         job->idler = g_idle_add(__handle_job, job);
498                         __context.running_jobs = g_list_append(
499                                         __context.running_jobs, job);
500                 }
501                 /* LCOV_EXCL_STOP */
502         }
503 }
504
505 static bool __exist_job_handler(const char *job_id)
506 {
507         struct service_app_job_s *handle;
508         GList *iter;
509
510         iter = __context.job_handlers;
511         while (iter) {
512                 handle = (struct service_app_job_s *)iter->data;
513                 if (strcmp(handle->job_id, job_id) == 0)
514                         return true;
515
516                 iter = iter->next;
517         }
518
519         return false;
520 }
521
522 static struct service_app_job_s *__create_job_handler(const char *job_id,
523                 service_app_job_cb callback, void *user_data)
524 {
525         struct service_app_job_s *handle;
526
527         handle = calloc(1, sizeof(struct service_app_job_s));
528         if (handle == NULL) {
529                 /* LCOV_EXCL_START */
530                 LOGE("Out of memory");
531                 return NULL;
532                 /* LCOV_EXCL_STOP */
533         }
534
535         handle->job_id = strdup(job_id);
536         if (handle->job_id == NULL) {
537                 /* LCOV_EXCL_START */
538                 LOGE("Out of memory");
539                 free(handle);
540                 return NULL;
541                 /* LCOV_EXCL_STOP */
542         }
543
544         handle->callback = callback;
545         handle->user_data = user_data;
546
547         return handle;
548 }
549
550 static void __destroy_job_handler(gpointer data)
551 {
552         struct service_app_job_s *handle = (struct service_app_job_s *)data;
553
554         if (handle == NULL)
555                 return;
556
557         if (handle->job_id)
558                 free(handle->job_id);
559         free(handle);
560 }
561
562 EXPORT_API service_app_job_h service_app_add_job_handler(const char *job_id,
563                 service_app_job_cb callback, void *user_data)
564 {
565         struct service_app_job_s *handle;
566
567         if (job_id == NULL || callback == NULL) {
568                 /* LCOV_EXCL_START */
569                 LOGE("Invalid parameter");
570                 return NULL;
571                 /* LCOV_EXCL_STOP */
572         }
573
574         handle = __create_job_handler(job_id, callback, user_data);
575         if (handle == NULL)
576                 return NULL;
577
578         __context.job_handlers = g_list_append(__context.job_handlers, handle);
579
580         __flush_pending_job(job_id);
581
582         return handle;
583 }
584
585 EXPORT_API int service_app_remove_job_handler(service_app_job_h handle)
586 {
587         if (handle == NULL ||
588                         g_list_index(__context.job_handlers, handle) < 0) {
589                 /* LCOV_EXCL_START */
590                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
591                                 NULL);
592                 /* LCOV_EXCL_STOP */
593         }
594
595         __context.job_handlers = g_list_remove(__context.job_handlers, handle);
596
597         __destroy_job_handler(handle);
598
599         return APP_ERROR_NONE;
600 }
601
602 EXPORT_API int service_app_job_raise(int job_status, const char *job_id,
603                 bundle *job_data)
604 {
605         struct job_s *job;
606
607         if (job_status < SERVICE_APP_JOB_STATUS_START ||
608                         job_status > SERVICE_APP_JOB_STATUS_STOP ||
609                         job_id == NULL || job_data == NULL) {
610                 /* LCOV_EXCL_START */
611                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
612                                 NULL);
613                 /* LCOV_EXCL_STOP */
614         }
615
616         job = __create_job(job_status, job_id, job_data);
617         if (job == NULL)
618                 return __on_error(APP_ERROR_OUT_OF_MEMORY, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
619
620         if (!__exist_job_handler(job_id)) {
621                 /* LCOV_EXCL_START */
622                 job->timer = g_timeout_add(5000, __pending_job_timeout_handler,
623                                 job);
624                 __context.pending_jobs = g_list_append(__context.pending_jobs,
625                                 job);
626                 /* LCOV_EXCL_STOP */
627         } else {
628                 job->idler = g_idle_add(__handle_job, job);
629                 __context.running_jobs = g_list_append(__context.running_jobs,
630                                 job);
631         }
632
633         return APP_ERROR_NONE;
634 }
635
636 static void __remove_running_job(const char *job_id)
637 {
638         struct job_s *job;
639         GList *iter;
640
641         iter = __context.running_jobs;
642         while (iter) {
643                 /* LCOV_EXCL_START */
644                 job = (struct job_s *)iter->data;
645                 iter = iter->next;
646                 if (strcmp(job->job_id, job_id) == 0) {
647                         __context.running_jobs = g_list_remove(
648                                         __context.running_jobs, job);
649                         __destroy_job(job);
650                 }
651                 /* LCOV_EXCL_STOP */
652         }
653 }
654
655 EXPORT_API int service_app_job_finished(const char *job_id)
656 {
657         int r;
658
659         if (job_id == NULL) {
660                 /* LCOV_EXCL_START */
661                 return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
662                                 NULL);
663                 /* LCOV_EXCL_STOP */
664         }
665
666         __remove_running_job(job_id);
667
668         r = aul_job_scheduler_update_job_status(job_id, JOB_STATUS_FINISHED);
669         if (r != AUL_R_OK) {
670                 /* LCOV_EXCL_START */
671                 return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__,
672                                 NULL);
673                 /* LCOV_EXCL_STOP */
674         }
675
676         return APP_ERROR_NONE;
677 }