Tizen 2.4.0 rev3 SDK Public 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)
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) {
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);
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)
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) {
848                                         if (__is_visible(pid)) {
849                                                 __set_fg_flag(pid, 1);
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);
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                                 return ac->pid;
944                         }
945                 }
946         }
947
948         return -1;
949 }
950
951 void app_group_remove_leader_pid(int lpid)
952 {
953         GList *list = (GList*)g_hash_table_lookup(app_group_hash,
954                       GINT_TO_POINTER(lpid));
955
956         if (list != NULL) {
957                 GList *next = g_list_next(list);
958
959                 if (next != NULL) {
960                         app_group_context_t *ac = (app_group_context_t*) list->data;
961                         free(ac);
962                         list = g_list_remove_link(list, list);
963
964                         ac = (app_group_context_t*) next->data;
965                         g_hash_table_insert(app_group_hash, GINT_TO_POINTER(ac->pid), next);
966                         g_hash_table_remove(app_group_hash, GINT_TO_POINTER(lpid));
967                 }
968         }
969 }
970
971 int app_group_can_start_app(const char *appid, bundle *b, gboolean *can_attach,
972                                 int *lpid, app_group_launch_mode *mode)
973 {
974         const char *val = NULL;
975         int caller_pid;
976         int caller_wid;
977
978         *can_attach = FALSE;
979         if (__can_attach_window(b, appid, mode)) {
980                 *can_attach = TRUE;
981
982                 val = bundle_get_val(b, AUL_K_ORG_CALLER_PID);
983                 if (val == NULL) {
984                         val = bundle_get_val(b, AUL_K_CALLER_PID);
985                 }
986
987                 if (val == NULL) {
988                         _E("no caller pid");
989                         return -1;
990                 }
991
992                 caller_pid = atoi(val);
993 #ifdef _APPFW_FEATURE_SEND_HOME_LAUNCH_SIGNAL
994                 if (home_appid != NULL) {
995                         char *caller_appid = _status_app_get_appid_bypid(caller_pid);
996
997                         if (caller_appid != NULL && strcmp(caller_appid, home_appid) == 0) {
998                                 _E("can't be attached to home app");
999                                 return -1;
1000                         }
1001                 }
1002 #endif
1003
1004                 *lpid = app_group_get_leader_pid(caller_pid);
1005                 if (*lpid != -1) {
1006                         caller_wid = app_group_get_window(caller_pid);
1007
1008                         if (caller_wid == 0) {
1009                                 _E("caller window wasn't ready");
1010                                 if (__can_be_leader(b))
1011                                         *can_attach = FALSE;
1012                                 else
1013                                         *can_attach = TRUE;
1014                         }
1015
1016                 } else {
1017                         _E("no lpid");
1018                         if (__can_be_leader(b))
1019                                 *can_attach = FALSE;
1020                         else
1021                                 return -1;
1022                 }
1023         }
1024
1025         return 0;
1026 }
1027
1028 void app_group_start_app(int pid, bundle *b, int lpid, gboolean can_attach,
1029                         app_group_launch_mode mode)
1030 {
1031         _E("app_group_start_app");
1032
1033         int caller_pid = __get_caller_pid(b);
1034         int can_shift = 0;
1035         int recycle = 0;
1036         const char *str;
1037
1038         str = bundle_get_val(b, AUL_SVC_K_SHIFT_WINDOW);
1039         if (str != NULL && strcmp(str, "true") == 0)
1040                 can_shift = 1;
1041
1042         str = bundle_get_val(b, AUL_SVC_K_RECYCLE);
1043         if (str != NULL && strcmp(str, "true") == 0)
1044                 recycle = 1;
1045
1046         if (can_attach)
1047                 __group_add(lpid, pid, 0, mode, caller_pid, 0, recycle);
1048         else
1049                 __group_add(pid, pid, 0, mode, caller_pid, can_shift, 0);
1050         app_group_set_hint(pid, b);
1051 }
1052
1053 int app_group_find_singleton(const char *appid, int *found_pid, int *found_lpid)
1054 {
1055         GHashTableIter iter;
1056         gpointer key = NULL;
1057         gpointer value = NULL;
1058         char *target = NULL;
1059
1060         g_hash_table_iter_init(&iter, app_group_hash);
1061         while (g_hash_table_iter_next(&iter, &key, &value)) {
1062                 GList *list = (GList*) value;
1063
1064                 while (list != NULL) {
1065                         app_group_context_t *ac = (app_group_context_t*) list->data;
1066
1067                         if (ac->launch_mode == APP_GROUP_LAUNCH_MODE_SINGLETON) {
1068                                 target = _status_app_get_appid_bypid(ac->pid);
1069
1070                                 if (appid != NULL && target != NULL && strcmp(appid, target) == 0) {
1071                                         *found_pid = ac->pid;
1072                                         *found_lpid = (int)key;
1073                                         return 0;
1074                                 }
1075                         }
1076                         list = g_list_next(list);
1077                 }
1078         }
1079
1080         return -1;
1081 }
1082
1083 int app_group_can_reroute(int pid)
1084 {
1085         GHashTableIter iter;
1086         gpointer key, value;
1087
1088         g_hash_table_iter_init(&iter, app_group_hash);
1089         while (g_hash_table_iter_next(&iter, &key, &value)) {
1090                 GList *list = (GList*) value;
1091                 GList *i = g_list_first(list);
1092
1093                 while (i != NULL) {
1094                         app_group_context_t *ac = (app_group_context_t*) i->data;
1095
1096                         if (ac->pid == pid) {
1097                                 return ac->reroute;
1098                         }
1099                         i = g_list_next(i);
1100                 }
1101         }
1102
1103         return 0;
1104 }
1105
1106 void app_group_lower(int pid, int *exit)
1107 {
1108         if (app_group_is_sub_app(pid)) {
1109                 if (__can_recycle(pid) && __can_reroute(pid)) {
1110                         app_group_context_t *ac = __get_context(pid);
1111                         if (ac) {
1112                                 if (ac->wid != 0)
1113                                         __detach_window(ac->wid);
1114                                 app_group_reroute(pid);
1115                                 ac = __context_dup(ac);
1116                                 __group_remove(pid);
1117
1118                                 if (ac)
1119                                         __do_recycle(ac);
1120
1121                         }
1122                         *exit = 0;
1123                 } else
1124                         *exit = 1;
1125                 return;
1126         }
1127
1128         GHashTableIter iter;
1129         gpointer key, value;
1130
1131         *exit = 0;
1132         g_hash_table_iter_init(&iter, app_group_hash);
1133         while (g_hash_table_iter_next(&iter, &key, &value)) {
1134                 GList *list = (GList*) value;
1135                 GList *i = g_list_first(list);
1136
1137                 while (i != NULL) {
1138                         app_group_context_t *ac = (app_group_context_t*) i->data;
1139
1140                         if (ac->pid == pid) {
1141                                 if (ac->can_shift) {
1142                                         __detach_window(ac->wid);
1143                                         ac->can_shift = 0;
1144                                         ecore_x_window_lower(ac->wid);
1145                                 }
1146                                 return;
1147                         }
1148                         i = g_list_next(i);
1149                 }
1150         }
1151 }
1152
1153 void app_group_restart_app(int pid, bundle *b)
1154 {
1155         if (b == NULL)
1156                 return;
1157
1158         GHashTableIter iter;
1159         gpointer key, value;
1160
1161         g_hash_table_iter_init(&iter, app_group_hash);
1162         while (g_hash_table_iter_next(&iter, &key, &value)) {
1163                 GList *list = (GList*) value;
1164                 GList *i = g_list_first(list);
1165
1166                 while (i != NULL) {
1167                         app_group_context_t *ac = (app_group_context_t*) i->data;
1168
1169                         if (ac->pid == pid) {
1170                                 const char *pid_str;
1171                                 ac->caller_pid = __get_caller_pid(b);
1172
1173                                 if (ac->can_shift) {
1174                                         if (ac->wid != 0)
1175                                                 __detach_window(ac->wid);
1176                                         ac->can_shift = 0;
1177                                 }
1178
1179                                 pid_str = bundle_get_val(b, AUL_SVC_K_SHIFT_WINDOW);
1180                                 if (pid_str != NULL && strcmp(pid_str, "true") == 0) {
1181                                         ac->can_shift = 1;
1182                                         if (ac->wid != 0) {
1183                                                 if (ac->caller_pid > 0) {
1184                                                         int cwid = app_group_get_window(ac->caller_pid);
1185
1186                                                         if (cwid != 0)
1187                                                                 __attach_window(cwid, ac->wid);
1188                                                         else
1189                                                                 _E("invalid caller wid");
1190
1191                                                 } else
1192                                                         _E("invalid caller pid");
1193
1194
1195                                         }
1196                                 }
1197                                 return;
1198                         }
1199                         i = g_list_next(i);
1200                 }
1201         }
1202 }
1203
1204 int app_group_find_pid_from_recycle_bin(const char *appid)
1205 {
1206         GList *iter = recycle_bin;
1207
1208         while (iter) {
1209                 app_group_context_t *ac = (app_group_context_t*) iter->data;
1210                 const char *appid_from_bin = _status_app_get_appid_bypid(ac->pid);
1211
1212                 if (appid && appid_from_bin && strcmp(appid, appid_from_bin) == 0) {
1213                         return ac->pid;
1214                 }
1215
1216                 iter = g_list_next(iter);
1217         }
1218
1219         return -1;
1220 }
1221
1222 void app_group_get_idle_pids(int *cnt, int **pids)
1223 {
1224         GList *iter = recycle_bin;
1225         int idle_cnt = g_list_length(iter);
1226
1227         if (idle_cnt <= 0) {
1228                 *cnt = 0;
1229                 *pids = NULL;
1230                 return;
1231         }
1232
1233         int *idle_pids = NULL;
1234
1235         idle_pids = malloc(sizeof(int) * idle_cnt);
1236         if (idle_pids == NULL) {
1237                 _E("Out-of-memory");
1238                 *cnt = 0;
1239                 *pids = NULL;
1240                 return;
1241         }
1242
1243         int i = 0;
1244         while (iter) {
1245                 app_group_context_t *ac = (app_group_context_t*) iter->data;
1246                 idle_pids[i] = ac->pid;
1247                 iter = g_list_next(iter);
1248                 i++;
1249         }
1250
1251         *cnt = idle_cnt;
1252         *pids = idle_pids;
1253 }
1254