power: apply wait callback mechanism to all state transitions
[platform/core/system/deviced.git] / plugins / iot-headless / power / power-state-wait.c
1 #include <unistd.h>
2 #include <stdint.h>
3 #include <libsyscommon/list.h>
4 #include <device/power-internal.h>
5
6 #include "shared/log.h"
7 #include "shared/device-notifier.h"
8 #include "shared/common.h"
9
10 #include "power-state-wait.h"
11 #include "power-state-manager.h"
12
13 #define MAX_WAIT_SECOND    5 /* second */
14
15 struct proc_info {
16         pid_t pid;
17         char comm[128];
18         guint64 state_bitmap;
19         int killed;
20 };
21
22 struct change_state_wait {
23         struct proc_info *pi;
24         guint64 id;
25         enum psm_state state;
26 };
27
28 static GList *proc_list;
29 static GList *waiting_list;
30 static int max_wait_timer;
31 static void (*__change_state_wait_done) (void);
32 static enum psm_state waiting_state;
33
34 static void change_state_wait_done(void)
35 {
36         _D("%s wait done", psm_name[waiting_state]);
37
38         if (max_wait_timer) {
39                 g_source_remove(max_wait_timer);
40                 max_wait_timer = 0;
41         }
42
43         if (__change_state_wait_done) {
44                 __change_state_wait_done();
45         }
46 }
47
48 static gboolean max_wait_expired_cb(void *data)
49 {
50         struct change_state_wait *csw;
51         struct proc_info *pi;
52         GList *elem, *elem_next;
53
54         max_wait_timer = 0;
55
56         SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) {
57                 if (kill(csw->pi->pid, 0) != 0)
58                         csw->pi->killed = 1;
59                 else
60                         _E("pid=%d(%s) hasn't confirmed id=%ld(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]);
61                 waiting_list = g_list_remove_link(waiting_list, elem);
62                 g_list_free(elem);
63                 free(csw);
64         }
65
66         SYS_G_LIST_FOREACH_SAFE(proc_list, elem, elem_next, pi) {
67                 if (pi->killed) {
68                         _D("Clean up not existing pid=%d(%s)", pi->pid, pi->comm);
69                         proc_list = g_list_remove_link(proc_list, elem);
70                         g_list_free(elem);
71                         free(pi);
72                 }
73         }
74
75         change_state_wait_done();
76
77         return G_SOURCE_REMOVE;
78 }
79
80 int add_change_state_wait(pid_t pid, guint64 state)
81 {
82         struct proc_info *pi;
83         GList *elem;
84
85         _D("pid=%d added csw for %#lx", pid, state);
86
87         SYS_G_LIST_FOREACH(proc_list, elem, pi) {
88                 if (pi->pid == pid) {
89                         pi->state_bitmap |= state;
90                         return 0;
91                 }
92         }
93
94         pi = calloc(1, sizeof(struct proc_info));
95         if (!pi)
96                 return -ENOMEM;
97
98         pi->pid = pid;
99         pi->state_bitmap = state;
100         get_command(pid, pi->comm, sizeof(pi->comm));
101         SYS_G_LIST_APPEND(proc_list, pi);
102
103         return 0;
104 }
105
106 void remove_change_state_wait(pid_t pid, guint64 state)
107 {
108         struct proc_info *pi;
109         GList *elem, *elem_next;
110
111         _D("pid=%d removed csw for %#lx", pid, state);
112
113         SYS_G_LIST_FOREACH_SAFE(proc_list, elem, elem_next, pi) {
114                 if (pi->pid == pid) {
115                         pi->state_bitmap &= ~state;
116                         if (pi->state_bitmap == 0) {
117                                 proc_list = g_list_remove_link(proc_list, elem);
118                                 g_list_free(elem);
119                                 free(pi);
120                         }
121                 }
122         }
123 }
124
125 int confirm_change_state_wait(pid_t pid, guint64 id)
126 {
127         struct change_state_wait *csw;
128         GList *elem, *elem_next;
129
130         SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) {
131                 if (csw->pi->pid == pid && csw->id == id) {
132                         _D("pid=%d(%s) confirmed id=%lu(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]);
133                         waiting_list = g_list_remove_link(waiting_list, elem);
134                         g_list_free(elem);
135                         free(csw);
136
137                         if (SYS_G_LIST_LENGTH(waiting_list) == 0) {
138                                 change_state_wait_done();
139                                 return 0;
140                         }
141                 }
142         }
143
144         return 0;
145 }
146
147 static int check_proc_requested_wait_for_state(struct proc_info *pi, enum psm_state state)
148 {
149         if (!pi)
150                 return 0;
151
152         if (state == PSM_NORMAL)
153                 return (pi->state_bitmap & POWER_STATE_NORMAL);
154         if (state == PSM_SLEEP)
155                 return (pi->state_bitmap & POWER_STATE_SLEEP);
156         if (state == PSM_POWEROFF)
157                 return (pi->state_bitmap & POWER_STATE_POWEROFF);
158         if (state == PSM_REBOOT)
159                 return (pi->state_bitmap & POWER_STATE_REBOOT);
160
161         return 0;
162 }
163
164 int update_change_state_wait(guint64 id, const struct trans_info *ti, change_state_wait_done_cb callback)
165 {
166         struct proc_info *pi;
167         struct change_state_wait *csw;
168         GList *elem, *elem_next;
169         int n_waiting;
170
171         // initialize timer
172         if (max_wait_timer) {
173                 g_source_remove(max_wait_timer);
174                 max_wait_timer = 0;
175         }
176
177         // we are waiting for confirm of transition to the next state
178         waiting_state = ti->next;
179         _D("%s wait is triggered, id=%ld", psm_name[waiting_state], id);
180
181         // cancel all ongoing csw that is not a waiting for the next state
182         SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) {
183                 if (csw->state != waiting_state) {
184                         waiting_list = g_list_remove_link(waiting_list, elem);
185                         g_list_free(elem);
186                         _D("Cancel waiting: pid=%d(%s) for id=%ld(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]);
187                         free(csw);
188                 }
189         }
190
191         // add wait list
192         SYS_G_LIST_FOREACH(proc_list, elem, pi) {
193                 if (check_proc_requested_wait_for_state(pi, waiting_state)) {
194                         csw = calloc(1, sizeof(struct change_state_wait));
195                                 if (!csw)
196                                         continue;
197
198                         csw->pi = pi;
199                         csw->id = id;
200                         csw->state = waiting_state;
201                         SYS_G_LIST_APPEND(waiting_list, csw);
202                 }
203         }
204
205         n_waiting = SYS_G_LIST_LENGTH(waiting_list);
206         if (n_waiting == 0) {
207                 _D("There were no csw requests for %s, skip waiting", psm_name[waiting_state]);
208                 __change_state_wait_done = NULL;
209                 return 0;
210         }
211
212         __change_state_wait_done = callback;
213
214         _D("Expected number of wait confirm=%d", n_waiting);
215         max_wait_timer = g_timeout_add_seconds(MAX_WAIT_SECOND, max_wait_expired_cb, NULL);
216
217         return n_waiting;
218 }