tizen 2.4 release
[framework/appfw/aul-1.git] / am_daemon / amd_app_group.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <glib.h>
6 #include <aul.h>
7 #include <aul_svc.h>
8 #include <Ecore_X.h>
9 #include <bundle_internal.h>
10
11 #include "app_sock.h"
12 #include "simple_util.h"
13 #include "amd_app_group.h"
14 #include "amd_launch.h"
15 #include "amd_request.h"
16 #include "amd_status.h"
17 #include "app_signal.h"
18 #include "amd_appinfo.h"
19
20 #define APP_SVC_K_LAUNCH_MODE   "__APP_SVC_LAUNCH_MODE__"
21
22 static GHashTable *app_group_hash = NULL;
23 static int dead_pid = -1;
24 static int focused_leader_pid = -1;
25 static GList *recycle_bin = NULL;
26
27 extern struct appinfomgr *_laf;
28 extern char *home_appid;
29
30 typedef struct _app_group_context_t {
31         int pid;
32         int wid;
33         int status;
34         int fg;
35         int group_sig;
36         int can_be_leader;
37         int reroute;
38         int caller_pid;
39         int can_shift;
40         int recycle;
41         app_group_launch_mode launch_mode;
42 } app_group_context_t;
43
44 static void __attach_window(int parent_wid, int child_wid)
45 {
46         ecore_x_icccm_transient_for_set(child_wid, parent_wid);
47 }
48
49 static void __detach_window(int child_wid)
50 {
51         ecore_x_icccm_transient_for_unset(child_wid);
52 }
53
54 static gint __comp_pid(gconstpointer a, gconstpointer b)
55 {
56         app_group_context_t *ac1 = (app_group_context_t*) a;
57
58         return ac1->pid - (int)b;
59 }
60
61 static void __list_destroy_cb(gpointer data)
62 {
63         free(data);
64 }
65
66 static gboolean __hash_table_cb(gpointer key, gpointer value,
67                 gpointer user_data)
68 {
69         int pid = (int) user_data;
70         GList *list = (GList*) value;
71         GList *itr = g_list_first(list);
72
73         while (itr != NULL) {
74                 app_group_context_t *ac = (app_group_context_t*) itr->data;
75
76                 if (ac->pid == pid) {
77                         free(ac);
78                         list = g_list_remove_link(list, itr);
79                         if (g_list_length(list) == 0) {
80                                 g_list_free_full(list, __list_destroy_cb);
81                                 return TRUE;
82                         } else
83                                 return FALSE;
84                 }
85                 itr = g_list_next(itr);
86         }
87
88         return FALSE;
89 }
90
91 static GList* __find_removable_apps(int from)
92 {
93         int cnt;
94         int *pids = NULL;
95         GList *list = NULL;
96         gboolean found = FALSE;
97
98         app_group_get_leader_pids(&cnt, &pids);
99
100         int i, j;
101
102         for (i = 0; i < cnt; i++) {
103                 int *gpids = NULL;
104                 int gcnt;
105
106                 app_group_get_group_pids(pids[i], &gcnt, &gpids);
107                 for (j = 0; j < gcnt; j++) {
108                         if (gpids[j] == from) {
109                                 found = TRUE;
110                                 continue;
111                         }
112
113                         if (found) {
114                                 list = g_list_append(list, (gpointer) gpids[j]);
115                         }
116                 }
117
118                 if (gpids != NULL)
119                         free(gpids);
120
121                 if (found)
122                         break;
123         }
124
125         if (pids != NULL)
126                 free(pids);
127
128         return list;
129 }
130
131 static void __prepare_to_suspend_services(int pid)
132 {
133         int dummy;
134         SECURE_LOGD("[__SUSPEND__] pid: %d", pid);
135         __app_send_raw_with_noreply(pid, APP_SUSPEND, (unsigned char *)&dummy, sizeof(int));
136 }
137
138 static void __prepare_to_wake_services(int pid)
139 {
140         int dummy;
141         SECURE_LOGD("[__SUSPEND__] pid: %d", pid);
142         __app_send_raw_with_noreply(pid, APP_WAKE, (unsigned char *)&dummy, sizeof(int));
143 }
144
145 static void __set_fg_flag(int cpid, int flag, gboolean force)
146 {
147         int lpid = app_group_get_leader_pid(cpid);
148         GHashTableIter iter;
149         gpointer key, value;
150
151         g_hash_table_iter_init(&iter, app_group_hash);
152         while (g_hash_table_iter_next(&iter, &key, &value)) {
153                 GList *list = (GList*) value;
154                 GList *i = g_list_first(list);
155                 app_group_context_t *ac = (app_group_context_t*) i->data;
156
157                 if (ac->pid == lpid) {
158
159                         while (i != NULL) {
160                                 ac = (app_group_context_t*) i->data;
161
162                                 if (ac->fg != flag || force == TRUE) {
163                                         const char *appid = NULL;
164                                         const char *pkgid = NULL;
165                                         const struct appinfo *ai = NULL;
166
167                                         appid = _status_app_get_appid_bypid(ac->pid);
168                                         ai = appinfo_find(_laf, appid);
169                                         pkgid = appinfo_get_value(ai, AIT_PKGID);
170
171                                         if (flag) {
172                                                 _D("send_signal FG %s", appid);
173
174                                                 aul_send_app_status_change_signal(ac->pid, appid,
175                                                                                 pkgid,
176                                                                                 STATUS_FOREGROUND,
177                                                                                 APP_TYPE_UI);
178                                                 _status_find_service_apps(ac->pid, STATUS_VISIBLE, __prepare_to_wake_services);
179                                         } else {
180                                                 _D("send_signal BG %s", appid);
181                                                 aul_send_app_status_change_signal(ac->pid, appid,
182                                                                                 pkgid,
183                                                                                 STATUS_BACKGROUND,
184                                                                                 APP_TYPE_UI);
185                                                 _status_find_service_apps(ac->pid, STATUS_BG, __prepare_to_suspend_services);
186                                         }
187                                         ac->fg = flag;
188                                 }
189                                 i = g_list_next(i);
190                         }
191                         break;
192                 }
193         }
194 }
195
196 static gboolean __is_visible(int cpid)
197 {
198         int lpid = app_group_get_leader_pid(cpid);
199         GHashTableIter iter;
200         gpointer key, value;
201
202         g_hash_table_iter_init(&iter, app_group_hash);
203         while (g_hash_table_iter_next(&iter, &key, &value)) {
204                 GList *list = (GList*) value;
205                 GList *i = g_list_first(list);
206                 app_group_context_t *ac = (app_group_context_t*) i->data;
207
208                 if (ac->pid == lpid) {
209                         while (i != NULL) {
210                                 ac = (app_group_context_t*) i->data;
211
212                                 if (ac->status == STATUS_VISIBLE)
213                                         return TRUE;
214
215                                 i = g_list_next(i);
216                         }
217                         break;
218                 }
219         }
220
221         return FALSE;
222 }
223
224 static gboolean __can_attach_window(bundle *b, const char *appid, app_group_launch_mode *launch_mode)
225 {
226         char *str = NULL;
227         const char *mode = NULL;
228         const struct appinfo *ai = NULL;
229
230         ai = appinfo_find(_laf, appid);
231         mode = appinfo_get_value(ai, AIT_LAUNCH_MODE);
232
233         if (mode == NULL)
234                 *launch_mode = APP_GROUP_LAUNCH_MODE_SINGLE;
235         else if (strcmp(mode, "caller") == 0)
236                 *launch_mode = APP_GROUP_LAUNCH_MODE_CALLER;
237         else if (strcmp(mode, "single") == 0)
238                 *launch_mode = APP_GROUP_LAUNCH_MODE_SINGLE;
239         else if (strcmp(mode, "group") == 0)
240                 *launch_mode = APP_GROUP_LAUNCH_MODE_GROUP;
241         else if (strcmp(mode, "singleton") == 0)
242                 *launch_mode = APP_GROUP_LAUNCH_MODE_SINGLETON;
243
244         switch (*launch_mode) {
245                 case APP_GROUP_LAUNCH_MODE_CALLER:
246                 case APP_GROUP_LAUNCH_MODE_SINGLETON:
247                         _D("launch mode from db is caller or singleton");
248
249                         bundle_get_str(b, APP_SVC_K_LAUNCH_MODE, &str);
250                         if (str != NULL && strncmp(str, "group", 5) == 0) {
251                                 return TRUE;
252                         }
253                         break;
254
255                 case APP_GROUP_LAUNCH_MODE_GROUP:
256                         return TRUE;
257
258                 case APP_GROUP_LAUNCH_MODE_SINGLE:
259                         return FALSE;
260         }
261
262         return FALSE;
263 }
264
265 static gboolean __can_be_leader(bundle *b)
266 {
267         char *str = NULL;
268
269         bundle_get_str(b, AUL_SVC_K_CAN_BE_LEADER, &str);
270
271         if (str != NULL && strcmp(str, "true") == 0)
272                 return TRUE;
273
274         return FALSE;
275 }
276
277 static int __get_previous_pid(int pid)
278 {
279         GHashTableIter iter;
280         gpointer key, value;
281
282         g_hash_table_iter_init(&iter, app_group_hash);
283         while (g_hash_table_iter_next(&iter, &key, &value)) {
284                 GList *list = (GList*) value;
285                 GList *i = g_list_first(list);
286
287                 int previous_pid = -1;
288                 while (i != NULL) {
289                         app_group_context_t *ac = (app_group_context_t*) i->data;
290
291                         if (ac->pid == pid) {
292                                 return previous_pid;
293                         }
294                         previous_pid = ac->pid;
295                         i = g_list_next(i);
296                 }
297         }
298
299         return -1;
300 }
301
302 static int __get_caller_pid(bundle *kb)
303 {
304         const char *pid_str;
305         int pid;
306
307         pid_str = bundle_get_val(kb, AUL_K_ORG_CALLER_PID);
308         if(pid_str)
309                 goto end;
310
311         pid_str = bundle_get_val(kb, AUL_K_CALLER_PID);
312         if (pid_str == NULL)
313                 return -1;
314
315 end:
316         pid = atoi(pid_str);
317         if (pid <= 1)
318                 return -1;
319
320         return pid;
321 }
322
323 static app_group_context_t* __detach_context_from_recycle_bin(int pid)
324 {
325         GList *iter = recycle_bin;
326
327         while (iter) {
328                 app_group_context_t *ac = (app_group_context_t*) iter->data;
329
330                 if (ac->pid == pid) {
331                         recycle_bin = g_list_remove_link(recycle_bin, iter);
332                         return ac;
333                 }
334
335                 iter = g_list_next(iter);
336         }
337
338         return NULL;
339
340 }
341
342 static void __group_add(int leader_pid, int pid, int wid, app_group_launch_mode mode,
343                         int caller_pid, int can_shift, int recycle)
344 {
345         app_group_context_t *ac = NULL;
346
347         if ((ac = __detach_context_from_recycle_bin(pid)) == NULL) {
348                 ac = malloc(sizeof(app_group_context_t));
349
350                 if (ac == NULL) {
351                         _E("out of memory");
352                         return;
353                 }
354                 ac->pid = pid;
355                 ac->wid = wid;
356                 ac->fg = 0;
357                 ac->can_be_leader = 0;
358                 ac->reroute = 0;
359                 ac->launch_mode = mode;
360                 ac->caller_pid = caller_pid;
361                 ac->can_shift = can_shift;
362                 ac->recycle = recycle;
363         }
364
365         if (leader_pid == pid || ac->recycle)
366                 ac->group_sig = 1;
367         else
368                 ac->group_sig = 0;
369
370         dead_pid = -1;
371
372         GList *list = (GList*) g_hash_table_lookup(app_group_hash,
373                         GINT_TO_POINTER(leader_pid));
374         if (list != NULL) {
375                 if (g_list_find_custom(list, (gconstpointer)pid, __comp_pid) != NULL) {
376                         _E("pid exist");
377                         free(ac);
378                         return;
379                 }
380         }
381
382         list = g_list_append(list, ac);
383         g_hash_table_insert(app_group_hash, GINT_TO_POINTER(leader_pid), list);
384
385         if (ac->wid != 0)
386                 app_group_set_window(pid, ac->wid);
387 }
388
389 static void __group_remove(int pid)
390 {
391         int ppid = __get_previous_pid(pid);
392         g_hash_table_foreach_remove(app_group_hash, __hash_table_cb,
393                         GINT_TO_POINTER(pid));
394
395         if (ppid != -1) {
396                 app_group_set_status(ppid, -1, FALSE);
397         }
398 }
399
400 static app_group_context_t* __get_context(int pid)
401 {
402         GHashTableIter iter;
403         gpointer key, value;
404
405         g_hash_table_iter_init(&iter, app_group_hash);
406         while (g_hash_table_iter_next(&iter, &key, &value)) {
407                 GList *list = (GList*) value;
408                 GList *i = g_list_first(list);
409
410                 while (i != NULL) {
411                         app_group_context_t *ac = (app_group_context_t*) i->data;
412
413                         if (ac->pid == pid) {
414                                 return ac;
415                         }
416                         i = g_list_next(i);
417                 }
418         }
419
420         return NULL;
421 }
422
423 static int __can_recycle(int pid)
424 {
425         app_group_context_t *context = __get_context(pid);
426
427         if (context)
428                 return context->recycle;
429
430         return 0;
431 }
432
433 static int __can_reroute(int pid)
434 {
435         app_group_context_t *context = __get_context(pid);
436
437         if (context)
438                 return context->reroute;
439
440         return 0;
441 }
442
443 static app_group_context_t* __context_dup(const app_group_context_t *context)
444 {
445         app_group_context_t* dup;
446
447         if (!context) {
448                 _E("context is NULL.");
449                 return NULL;
450         }
451
452         dup = malloc(sizeof(app_group_context_t));
453         if (!dup) {
454                 _E("out of memory");
455                 return NULL;
456         }
457
458         memcpy(dup, context, sizeof(app_group_context_t));
459         return dup;
460 }
461
462 static void __do_recycle(app_group_context_t *context)
463 {
464         if (context->fg) {
465                 const char *appid = NULL;
466                 const char *pkgid = NULL;
467                 const struct appinfo *ai = NULL;
468
469                 appid = _status_app_get_appid_bypid(context->pid);
470                 ai = appinfo_find(_laf, appid);
471                 pkgid = appinfo_get_value(ai, AIT_PKGID);
472
473                 _D("send_signal BG %s", appid);
474                 aul_send_app_status_change_signal(context->pid, appid, pkgid,
475                                                 STATUS_BACKGROUND,
476                                                 APP_TYPE_UI);
477                 _status_find_service_apps(context->pid, STATUS_BG, __prepare_to_suspend_services);
478                 context->fg = 0;
479         }
480         recycle_bin = g_list_append(recycle_bin, context);
481         _revoke_temporary_permission(context->pid);
482 }
483
484 void app_group_init()
485 {
486         app_group_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
487                         NULL);
488 }
489
490 void app_group_remove(int pid)
491 {
492         __group_remove(pid);
493         app_group_context_t *context = __detach_context_from_recycle_bin(pid);
494
495         if (context)
496                 free(context);
497 }
498
499 void app_group_remove_from_recycle_bin(int pid)
500 {
501         app_group_context_t *context = __detach_context_from_recycle_bin(pid);
502
503         if (context)
504                 free(context);
505 }
506
507 int app_group_get_window(int pid)
508 {
509         app_group_context_t *context = __get_context(pid);
510
511         if (context)
512                 return context->wid;
513
514         return -1;
515 }
516
517 int app_group_set_window(int pid, int wid)
518 {
519         GHashTableIter iter;
520         gpointer key, value;
521
522         g_hash_table_iter_init(&iter, app_group_hash);
523         while (g_hash_table_iter_next(&iter, &key, &value)) {
524                 GList *list = (GList*) value;
525                 GList *i = g_list_first(list);
526
527                 int previous_wid = 0;
528                 while (i != NULL) {
529                         app_group_context_t *ac = (app_group_context_t*) i->data;
530
531                         if (ac->pid == pid) {
532                                 ac->wid = wid;
533                                 if (previous_wid != 0)
534                                         __attach_window(previous_wid, wid);
535
536                                 if (ac->can_shift && ac->caller_pid > 0) {
537                                         int caller_wid = app_group_get_window(ac->caller_pid);
538
539                                         if (caller_wid != 0)
540                                                 __attach_window(caller_wid, wid);
541                                 }
542
543                                 i = g_list_next(i);
544                                 if (i) {
545                                         ac = (app_group_context_t*) i->data;
546                                         if (ac->wid != 0)
547                                                 __attach_window(wid, ac->wid);
548                                 }
549
550                                 return 0;
551                         }
552                         previous_wid = ac->wid;
553                         i = g_list_next(i);
554                 }
555         }
556
557         return -1;
558 }
559
560 void app_group_clear_top(int pid)
561 {
562         GList *list = __find_removable_apps(pid);
563
564         if (list != NULL) {
565                 GList *itr = g_list_last(list);
566
567                 while (itr != NULL) {
568                         int p = (int)(itr->data);
569
570                         __detach_window(p);
571                         _term_sub_app(p);
572                         app_group_remove(p);
573                         itr = g_list_previous(itr);
574                 }
575                 g_list_free(list);
576         }
577 }
578
579 gboolean app_group_is_group_app(bundle* kb)
580 {
581         if (kb == NULL)
582                 return FALSE;
583
584         char *str = NULL;
585         const char *mode = NULL;
586         char *appid = NULL;
587         const struct appinfo *ai = NULL;
588
589         bundle_get_str(kb, AUL_K_PKG_NAME, &appid);
590
591         if (appid == NULL)
592                 return FALSE;
593
594         ai = appinfo_find(_laf, appid);
595         mode = appinfo_get_value(ai, AIT_LAUNCH_MODE);
596
597         if (mode != NULL && (strncmp(mode, "caller", 6) == 0 ||
598                                 strncmp(mode, "singleton", 9) == 0)) {
599                 bundle_get_str(kb, APP_SVC_K_LAUNCH_MODE, &str);
600
601                 if (str != NULL && strncmp(str, "group", 5) == 0) {
602                         return TRUE;
603                 }
604         } else if (mode != NULL && strncmp(mode, "group", 5) == 0) {
605                 return TRUE;
606         }
607
608         return FALSE;
609 }
610
611 void app_group_get_leader_pids(int *cnt, int **pids)
612 {
613         GHashTableIter iter;
614         gpointer key, value;
615
616         int size = g_hash_table_size(app_group_hash);
617         int *leader_pids;
618
619         if (size > 0) {
620                 leader_pids = (int*) malloc(sizeof(int) * size);
621                 if (leader_pids == NULL) {
622                         _E("out of memory");
623                         *cnt = 0;
624                         *pids = NULL;
625                         return;
626                 }
627
628                 g_hash_table_iter_init(&iter, app_group_hash);
629                 int i = 0;
630                 while (g_hash_table_iter_next(&iter, &key, &value)) {
631                         leader_pids[i] = (int) key;
632                         i++;
633                 }
634
635                 *cnt = size;
636                 *pids = leader_pids;
637         } else {
638                 *cnt = 0;
639                 *pids = NULL;
640         }
641 }
642
643 gboolean app_group_is_leader_pid(int pid)
644 {
645         int cnt;
646         int *pids = NULL;
647         int i;
648
649         app_group_get_leader_pids(&cnt, &pids);
650
651         for (i=0; i<cnt; i++) {
652                 if (pid == pids[i]) {
653                         free(pids);
654                         return TRUE;
655                 }
656         }
657
658         if (pids != NULL)
659                 free(pids);
660
661         return FALSE;
662 }
663
664 void app_group_get_group_pids(int leader_pid, int *cnt, int **pids)
665 {
666         GHashTableIter iter;
667         gpointer key, value;
668
669         g_hash_table_iter_init(&iter, app_group_hash);
670         while (g_hash_table_iter_next(&iter, &key, &value)) {
671                 if ((int) key == leader_pid) {
672                         GList *list = (GList*) value;
673                         GList *i = g_list_first(list);
674                         int size = g_list_length(list);
675
676                         if (size > 0) {
677                                 int *pid_array = (int*) malloc(sizeof(int) * size);
678                                 int j = 0;
679
680                                 if (pid_array == NULL) {
681                                         _E("out of memory");
682                                         *cnt = 0;
683                                         *pids = NULL;
684                                         return;
685                                 }
686
687                                 while (i != NULL) {
688                                         app_group_context_t *ac = (app_group_context_t*) i->data;
689
690                                         pid_array[j] = ac->pid;
691                                         i = g_list_next(i);
692                                         j++;
693                                 }
694
695                                 *cnt = size;
696                                 *pids = pid_array;
697                         } else {
698                                 *cnt = 0;
699                                 *pids = NULL;
700                         }
701                         return;
702                 }
703         }
704
705         *cnt = 0;
706         *pids = NULL;
707 }
708
709 gboolean app_group_is_sub_app(int pid)
710 {
711         GHashTableIter iter;
712         gpointer key, value;
713
714         g_hash_table_iter_init(&iter, app_group_hash);
715         while (g_hash_table_iter_next(&iter, &key, &value)) {
716                 GList *list = (GList*) value;
717                 GList *found = NULL;
718
719                 if (list != NULL) {
720                         if ((found = g_list_find_custom(list, (gconstpointer)pid, __comp_pid)) != NULL) {
721                                 if (g_list_first(list) == found)
722                                         return FALSE;
723                                 return TRUE;
724                         }
725                 }
726         }
727
728         return FALSE;
729 }
730
731 void app_group_reroute(int pid)
732 {
733         GHashTableIter iter;
734         gpointer key, value;
735
736         g_hash_table_iter_init(&iter, app_group_hash);
737         while (g_hash_table_iter_next(&iter, &key, &value)) {
738                 GList *list = (GList*) value;
739                 GList *found = NULL;
740                 GList *before = NULL;
741                 GList *after = NULL;
742
743                 if (list != NULL) {
744                         if ((found = g_list_find_custom(list, (gconstpointer)pid, __comp_pid)) != NULL) {
745                                 before = g_list_previous(found);
746                                 after = g_list_next(found);
747
748                                 if (before == NULL || after == NULL)
749                                         return;
750
751                                 _D("reroute");
752                                 app_group_context_t *ac1 = (app_group_context_t*) before->data;
753                                 app_group_context_t *ac2 = (app_group_context_t*) after->data;
754
755                                 __detach_window(ac2->wid);
756                                 __attach_window(ac1->wid, ac2->wid);
757                                 break;
758                         }
759                 }
760         }
761 }
762
763 int app_group_get_leader_pid(int pid)
764 {
765         GHashTableIter iter;
766         gpointer key, value;
767         int lpid = -1;
768         int again = 0;
769
770 repeat:
771         g_hash_table_iter_init(&iter, app_group_hash);
772         while (g_hash_table_iter_next(&iter, &key, &value)) {
773                 GList *list = (GList*) value;
774
775                 if (list != NULL) {
776                         if (g_list_find_custom(list, (gconstpointer)pid, __comp_pid) != NULL) {
777                                 lpid = (int)key;
778                                 break;
779                         }
780                 }
781         }
782
783         if (lpid == -1 && dead_pid == pid)
784                 lpid = focused_leader_pid;
785
786         if (lpid == -1 && again == 0) {
787                 pid = getpgid(pid);
788                 again = 1;
789                 goto repeat;
790         }
791
792         return lpid;
793 }
794
795 void app_group_set_dead_pid(int pid)
796 {
797         focused_leader_pid = app_group_get_leader_pid(pid);
798         dead_pid = pid;
799
800         if (dead_pid == focused_leader_pid) {
801            focused_leader_pid = -1;
802            dead_pid = -1;
803         }
804 }
805
806 int app_group_get_status(int pid)
807 {
808         GHashTableIter iter;
809         gpointer key, value;
810
811         g_hash_table_iter_init(&iter, app_group_hash);
812         while (g_hash_table_iter_next(&iter, &key, &value)) {
813                 GList *list = (GList*) value;
814                 GList *i = g_list_first(list);
815
816                 while (i != NULL) {
817                         app_group_context_t *ac = (app_group_context_t*) i->data;
818
819                         if (ac->pid == pid)
820                                 return  ac->status;
821
822                         i = g_list_next(i);
823                 }
824         }
825         return -1;
826 }
827
828 int app_group_set_status(int pid, int status, gboolean force)
829 {
830         GHashTableIter iter;
831         gpointer key, value;
832
833         g_hash_table_iter_init(&iter, app_group_hash);
834         while (g_hash_table_iter_next(&iter, &key, &value)) {
835                 GList *list = (GList*) value;
836                 GList *i = g_list_first(list);
837
838                 while (i != NULL) {
839                         app_group_context_t *ac = (app_group_context_t*) i->data;
840
841                         if (ac->pid == pid) {
842                                 if (status > 0)
843                                         ac->status = status;
844                                 GList *last = g_list_last(list);
845                                 app_group_context_t *last_ac = (app_group_context_t*) last->data;
846
847                                 if (last_ac->wid != 0 || status == STATUS_VISIBLE || force == TRUE) {
848                                         if (__is_visible(pid)) {
849                                                 __set_fg_flag(pid, 1, force);
850                                                 if (!ac->group_sig && (int)key != pid) {
851                                                         char *appid = NULL;
852                                                         const char *pkgid = NULL;
853                                                         const struct appinfo *ai = NULL;
854
855                                                         appid = _status_app_get_appid_bypid(pid);
856                                                         ai = appinfo_find(_laf, appid);
857                                                         pkgid = appinfo_get_value(ai, AIT_PKGID);
858
859                                                         _D("send group signal %d", pid);
860                                                         aul_send_app_group_signal((int)key, pid, pkgid);
861                                                         ac->group_sig = 1;
862                                                 }
863                                         } else
864                                                 __set_fg_flag(pid, 0, force);
865                                 }
866                                 return 0;
867                         }
868                         i = g_list_next(i);
869                 }
870         }
871         return -1;
872 }
873
874 int app_group_get_fg_flag(int pid)
875 {
876         GHashTableIter iter;
877         gpointer key, value;
878
879         g_hash_table_iter_init(&iter, app_group_hash);
880         while (g_hash_table_iter_next(&iter, &key, &value)) {
881                 GList *list = (GList*) value;
882                 GList *i = g_list_first(list);
883
884                 while (i != NULL) {
885                         app_group_context_t *ac = (app_group_context_t*) i->data;
886
887                         if (ac->pid == pid) {
888                                 return ac->fg;
889                         }
890                         i = g_list_next(i);
891                 }
892         }
893
894         return 0;
895 }
896
897 int app_group_set_hint(int pid, bundle *kb)
898 {
899         char *str_leader = NULL;
900         char *str_reroute = NULL;
901
902         if (kb == NULL)
903                 return -1;
904
905         bundle_get_str(kb, AUL_SVC_K_CAN_BE_LEADER, &str_leader);
906         bundle_get_str(kb, AUL_SVC_K_REROUTE, &str_reroute);
907
908         GHashTableIter iter;
909         gpointer key, value;
910
911         g_hash_table_iter_init(&iter, app_group_hash);
912         while (g_hash_table_iter_next(&iter, &key, &value)) {
913                 GList *list = (GList*) value;
914                 GList *i = g_list_first(list);
915
916                 while (i != NULL) {
917                         app_group_context_t *ac = (app_group_context_t*) i->data;
918
919                         if (ac->pid == pid) {
920                                 if (str_leader != NULL && strcmp(str_leader, "true") == 0)
921                                         ac->can_be_leader = 1;
922                                 if (str_reroute != NULL && strcmp(str_reroute, "true") == 0)
923                                         ac->reroute = 1;
924                                 return 0;
925                         }
926                         i = g_list_next(i);
927                 }
928         }
929
930         return -1;
931 }
932
933 int app_group_find_second_leader(int lpid)
934 {
935         GList *list = (GList*) g_hash_table_lookup(app_group_hash,
936                         GINT_TO_POINTER(lpid));
937         if (list != NULL) {
938                 list = g_list_next(list);
939
940                 if (list != NULL) {
941                         app_group_context_t *ac = (app_group_context_t*) list->data;
942                         if (ac->can_be_leader) {
943                                 _W("found the second leader, lpid: %d, pid: %d", lpid, ac->pid);
944                                 return ac->pid;
945                         }
946                 }
947         }
948
949         return -1;
950 }
951
952 void app_group_remove_leader_pid(int lpid)
953 {
954         GList *list = (GList*)g_hash_table_lookup(app_group_hash,
955                       GINT_TO_POINTER(lpid));
956
957         if (list != NULL) {
958                 GList *next = g_list_next(list);
959
960                 if (next != NULL) {
961                         app_group_context_t *ac = (app_group_context_t*) list->data;
962                         free(ac);
963                         list = g_list_remove_link(list, list);
964
965                         ac = (app_group_context_t*) next->data;
966                         g_hash_table_insert(app_group_hash, GINT_TO_POINTER(ac->pid), next);
967                         g_hash_table_remove(app_group_hash, GINT_TO_POINTER(lpid));
968                 }
969         }
970 }
971
972 int app_group_can_start_app(const char *appid, bundle *b, gboolean *can_attach,
973                                 int *lpid, app_group_launch_mode *mode)
974 {
975         const char *val = NULL;
976         int caller_pid;
977         int caller_wid;
978
979         *can_attach = FALSE;
980         if (__can_attach_window(b, appid, mode)) {
981                 *can_attach = TRUE;
982
983                 val = bundle_get_val(b, AUL_K_ORG_CALLER_PID);
984                 if (val == NULL) {
985                         val = bundle_get_val(b, AUL_K_CALLER_PID);
986                 }
987
988                 if (val == NULL) {
989                         _E("no caller pid");
990                         return -1;
991                 }
992
993                 caller_pid = atoi(val);
994 #ifdef _APPFW_FEATURE_SEND_HOME_LAUNCH_SIGNAL
995                 if (home_appid != NULL) {
996                         char *caller_appid = _status_app_get_appid_bypid(caller_pid);
997
998                         if (caller_appid != NULL && strcmp(caller_appid, home_appid) == 0) {
999                                 _E("can't be attached to home app");
1000                                 return -1;
1001                         }
1002                 }
1003 #endif
1004
1005                 *lpid = app_group_get_leader_pid(caller_pid);
1006                 if (*lpid != -1) {
1007                         caller_wid = app_group_get_window(caller_pid);
1008
1009                         if (caller_wid == 0) {
1010                                 _E("caller window wasn't ready");
1011                                 if (__can_be_leader(b))
1012                                         *can_attach = FALSE;
1013                                 else
1014                                         *can_attach = TRUE;
1015                         }
1016
1017                 } else {
1018                         _E("no lpid");
1019                         if (__can_be_leader(b))
1020                                 *can_attach = FALSE;
1021                         else
1022                                 return -1;
1023                 }
1024         }
1025
1026         return 0;
1027 }
1028
1029 void app_group_start_app(int pid, bundle *b, int lpid, gboolean can_attach,
1030                         app_group_launch_mode mode)
1031 {
1032         _E("app_group_start_app");
1033
1034         int caller_pid = __get_caller_pid(b);
1035         int can_shift = 0;
1036         int recycle = 0;
1037         const char *str;
1038
1039         str = bundle_get_val(b, AUL_SVC_K_SHIFT_WINDOW);
1040         if (str != NULL && strcmp(str, "true") == 0)
1041                 can_shift = 1;
1042
1043         str = bundle_get_val(b, AUL_SVC_K_RECYCLE);
1044         if (str != NULL && strcmp(str, "true") == 0)
1045                 recycle = 1;
1046
1047         if (can_attach)
1048                 __group_add(lpid, pid, 0, mode, caller_pid, 0, recycle);
1049         else
1050                 __group_add(pid, pid, 0, mode, caller_pid, can_shift, 0);
1051         app_group_set_hint(pid, b);
1052 }
1053
1054 int app_group_find_singleton(const char *appid, int *found_pid, int *found_lpid)
1055 {
1056         GHashTableIter iter;
1057         gpointer key = NULL;
1058         gpointer value = NULL;
1059         char *target = NULL;
1060
1061         g_hash_table_iter_init(&iter, app_group_hash);
1062         while (g_hash_table_iter_next(&iter, &key, &value)) {
1063                 GList *list = (GList*) value;
1064
1065                 while (list != NULL) {
1066                         app_group_context_t *ac = (app_group_context_t*) list->data;
1067
1068                         if (ac->launch_mode == APP_GROUP_LAUNCH_MODE_SINGLETON) {
1069                                 target = _status_app_get_appid_bypid(ac->pid);
1070
1071                                 if (appid != NULL && target != NULL && strcmp(appid, target) == 0) {
1072                                         *found_pid = ac->pid;
1073                                         *found_lpid = (int)key;
1074                                         return 0;
1075                                 }
1076                         }
1077                         list = g_list_next(list);
1078                 }
1079         }
1080
1081         return -1;
1082 }
1083
1084 int app_group_can_reroute(int pid)
1085 {
1086         GHashTableIter iter;
1087         gpointer key, value;
1088
1089         g_hash_table_iter_init(&iter, app_group_hash);
1090         while (g_hash_table_iter_next(&iter, &key, &value)) {
1091                 GList *list = (GList*) value;
1092                 GList *i = g_list_first(list);
1093
1094                 while (i != NULL) {
1095                         app_group_context_t *ac = (app_group_context_t*) i->data;
1096
1097                         if (ac->pid == pid) {
1098                                 return ac->reroute;
1099                         }
1100                         i = g_list_next(i);
1101                 }
1102         }
1103
1104         return 0;
1105 }
1106
1107 void app_group_lower(int pid, int *exit)
1108 {
1109         if (app_group_is_sub_app(pid)) {
1110                 if (__can_recycle(pid) && __can_reroute(pid)) {
1111                         app_group_context_t *ac = __get_context(pid);
1112                         if (ac) {
1113                                 if (ac->wid != 0)
1114                                         __detach_window(ac->wid);
1115                                 app_group_reroute(pid);
1116                                 ac = __context_dup(ac);
1117                                 __group_remove(pid);
1118
1119                                 if (ac)
1120                                         __do_recycle(ac);
1121
1122                         }
1123                         *exit = 0;
1124                 } else
1125                         *exit = 1;
1126                 return;
1127         }
1128
1129         GHashTableIter iter;
1130         gpointer key, value;
1131
1132         *exit = 0;
1133         g_hash_table_iter_init(&iter, app_group_hash);
1134         while (g_hash_table_iter_next(&iter, &key, &value)) {
1135                 GList *list = (GList*) value;
1136                 GList *i = g_list_first(list);
1137
1138                 while (i != NULL) {
1139                         app_group_context_t *ac = (app_group_context_t*) i->data;
1140
1141                         if (ac->pid == pid) {
1142                                 if (ac->can_shift) {
1143                                         __detach_window(ac->wid);
1144                                         ac->can_shift = 0;
1145                                         ecore_x_window_lower(ac->wid);
1146                                 }
1147                                 return;
1148                         }
1149                         i = g_list_next(i);
1150                 }
1151         }
1152 }
1153
1154 void app_group_restart_app(int pid, bundle *b)
1155 {
1156         if (b == NULL)
1157                 return;
1158
1159         GHashTableIter iter;
1160         gpointer key, value;
1161
1162         g_hash_table_iter_init(&iter, app_group_hash);
1163         while (g_hash_table_iter_next(&iter, &key, &value)) {
1164                 GList *list = (GList*) value;
1165                 GList *i = g_list_first(list);
1166
1167                 while (i != NULL) {
1168                         app_group_context_t *ac = (app_group_context_t*) i->data;
1169
1170                         if (ac->pid == pid) {
1171                                 const char *pid_str;
1172                                 ac->caller_pid = __get_caller_pid(b);
1173
1174                                 if (ac->can_shift) {
1175                                         if (ac->wid != 0)
1176                                                 __detach_window(ac->wid);
1177                                         ac->can_shift = 0;
1178                                 }
1179
1180                                 pid_str = bundle_get_val(b, AUL_SVC_K_SHIFT_WINDOW);
1181                                 if (pid_str != NULL && strcmp(pid_str, "true") == 0) {
1182                                         ac->can_shift = 1;
1183                                         if (ac->wid != 0) {
1184                                                 if (ac->caller_pid > 0) {
1185                                                         int cwid = app_group_get_window(ac->caller_pid);
1186
1187                                                         if (cwid != 0)
1188                                                                 __attach_window(cwid, ac->wid);
1189                                                         else
1190                                                                 _E("invalid caller wid");
1191
1192                                                 } else
1193                                                         _E("invalid caller pid");
1194
1195
1196                                         }
1197                                 }
1198                                 return;
1199                         }
1200                         i = g_list_next(i);
1201                 }
1202         }
1203 }
1204
1205 int app_group_find_pid_from_recycle_bin(const char *appid)
1206 {
1207         GList *iter = recycle_bin;
1208
1209         while (iter) {
1210                 app_group_context_t *ac = (app_group_context_t*) iter->data;
1211                 const char *appid_from_bin = _status_app_get_appid_bypid(ac->pid);
1212
1213                 if (appid && appid_from_bin && strcmp(appid, appid_from_bin) == 0) {
1214                         return ac->pid;
1215                 }
1216
1217                 iter = g_list_next(iter);
1218         }
1219
1220         return -1;
1221 }
1222
1223 void app_group_get_idle_pids(int *cnt, int **pids)
1224 {
1225         GList *iter = recycle_bin;
1226         int idle_cnt = g_list_length(iter);
1227
1228         if (idle_cnt <= 0) {
1229                 *cnt = 0;
1230                 *pids = NULL;
1231                 return;
1232         }
1233
1234         int *idle_pids = NULL;
1235
1236         idle_pids = malloc(sizeof(int) * idle_cnt);
1237         if (idle_pids == NULL) {
1238                 _E("Out-of-memory");
1239                 *cnt = 0;
1240                 *pids = NULL;
1241                 return;
1242         }
1243
1244         int i = 0;
1245         while (iter) {
1246                 app_group_context_t *ac = (app_group_context_t*) iter->data;
1247                 idle_pids[i] = ac->pid;
1248                 iter = g_list_next(iter);
1249                 i++;
1250         }
1251
1252         *cnt = idle_cnt;
1253         *pids = idle_pids;
1254 }
1255
1256 int app_group_get_next_caller_pid(int pid)
1257 {
1258         GHashTableIter iter;
1259         gpointer key, value;
1260
1261         g_hash_table_iter_init(&iter, app_group_hash);
1262         while (g_hash_table_iter_next(&iter, &key, &value)) {
1263                 GList *list = (GList*) value;
1264                 GList *i = g_list_first(list);
1265
1266                 while (i != NULL) {
1267                         app_group_context_t *ac = (app_group_context_t*) i->data;
1268
1269                         if (ac->pid == pid) {
1270                                 i = g_list_next(i);
1271                                 if (i == NULL)
1272                                         return -1;
1273
1274                                 ac = (app_group_context_t*) i->data;
1275                                 return ac->caller_pid;
1276                         }
1277                         i = g_list_next(i);
1278                 }
1279         }
1280
1281         return -1;
1282 }
1283
1284