power: introduce sleep-wait for iot-headless
[platform/core/system/deviced.git] / plugins / iot-headless / power / sleep-wait.c
1 #include <libsyscommon/list.h>
2 #include <unistd.h>
3
4 #include "shared/log.h"
5 #include "shared/device-notifier.h"
6 #include "shared/common.h"
7
8 #include "sleep-wait.h"
9
10 #define MAX_WAIT_SECOND    5 /* second */
11
12 struct sleep_wait {
13         pid_t pid;
14         char comm[128];
15         // int timeout;
16         // int timer_id;
17 };
18
19 /* inactive: This list contains sleep waits added by AddSleepWait dbus method.
20  * active: All entries of inactive list are moved to this list when sleep is requested.
21  *         Each one moves to inactive list on receiving ConfirmWait dbus method.
22  *         Sleep will be triggered when it becomes empty. */
23 static GList *inactive;
24 static GList *active;
25
26 static int max_wait_timer;
27 static int current_sleep_id;
28
29 static void switch_active_list(void)
30 {
31         GList *tmp;
32         int n;
33
34         n = g_list_length(active);
35         if (n != 0)
36                 _W("Sleep wait remains in active list, %d", n);
37
38         tmp = inactive;
39         inactive = active;
40         active = tmp;
41 }
42
43 static void request_wake_unlock(void)
44 {
45         device_notify(DEVICE_NOTIFIER_REQUEST_ENABLE_AUTOSLEEP, NULL);
46         device_notify(DEVICE_NOTIFIER_REQUEST_WAKE_UNLOCK, NULL);
47 }
48
49 static gboolean max_wait_expired_cb(void *data)
50 {
51         struct sleep_wait *sw;
52         GList *elem, *elem_next;
53
54         max_wait_timer = 0;
55
56         /* force confirmation */
57         if (g_list_length(active) != 0) {
58                 SYS_G_LIST_FOREACH_SAFE(active, elem, elem_next, sw) {
59                         if (kill(sw->pid, 0) != 0) {
60                                 _D("Remove not existing sleep wait of pid=%d(%s)", sw->pid, sw->comm);
61                                 active = g_list_remove(active, sw);
62                                 free(sw);
63                         } else { /* move to inactive manually */
64                                 _W("pid=%d(%s) hasn't comfirmed sleep wait", sw->pid, sw->comm);
65                                 active = g_list_remove(active, sw);
66                                 inactive = g_list_append(inactive, sw);
67                         }
68                 }
69         }
70
71         _D("Force wake unlock");
72         request_wake_unlock();
73
74         return G_SOURCE_REMOVE;
75 }
76
77 /* It returns
78  *      0 if sleep_wait is found in inactive list,
79  * -EBUSY if sleep_wait is found in active list,
80  * -ESRCH if sleep_wait is not found in both list */
81 static int find_sleep_wait(pid_t pid, struct sleep_wait **sw)
82 {
83         struct sleep_wait *ret = NULL;
84         GList *elem;
85
86         if (!sw)
87                 return -EINVAL;
88
89         SYS_G_LIST_FOREACH(inactive, elem, ret) {
90                 if (ret->pid == pid) {
91                         *sw = ret;
92                         return 0;
93                 }
94         }
95
96         SYS_G_LIST_FOREACH(active, elem, ret) {
97                 if (ret->pid == pid) {
98                         *sw = ret;
99                         return -EBUSY;
100                 }
101         }
102
103         *sw = NULL;
104         return -ESRCH;
105 }
106
107 int add_sleep_wait(pid_t pid)
108 {
109         struct sleep_wait *sw;
110         int ret;
111
112         ret = find_sleep_wait(pid, &sw);
113         if (ret == -ESRCH) { /* new node */
114                 sw = calloc(1, sizeof(struct sleep_wait));
115                 if (!sw)
116                         return -ENOMEM;
117
118                 sw->pid = pid;
119                 get_command(pid, sw->comm, sizeof(sw->comm));
120                 inactive = g_list_append(inactive, sw);
121
122                 _D("pid=%d(%s) is added to sleep wait list", sw->pid, sw->comm);
123         } else if (ret == 0 || ret == -EBUSY) {
124                 _D("pid=%d(%s) has already been added to sleep wait list", sw->pid, sw->comm);
125         }
126
127         return ret;
128 }
129
130 void remove_sleep_wait(pid_t pid)
131 {
132         struct sleep_wait *sw = NULL;
133         int retval;
134
135         retval = find_sleep_wait(pid, &sw);
136         if (retval == -ESRCH)
137                 return;
138
139         if (sw) {
140                 _D("pid=%d(%s) requested removing sleep wait", sw->pid, sw->comm);
141
142                 /* remove from where it is contained */
143                 active = g_list_remove(active, sw);
144                 inactive = g_list_remove(inactive, sw);
145
146                 free(sw);
147
148                 /* wake unlock when active becomes empty by this operation
149                  * during waiting sleep confirmations */
150                 if (max_wait_timer != 0 && g_list_length(active) == 0) {
151                         g_source_remove(max_wait_timer);
152                         max_wait_timer = 0;
153
154                         _D("All sleep waits have been confirmed, wake unlock");
155                         request_wake_unlock();
156                 }
157         }
158 }
159
160 int confirm_sleep_wait(pid_t pid, int id)
161 {
162         struct sleep_wait *sw;
163         int ret = 0;
164
165         ret = find_sleep_wait(pid, &sw);
166         if (ret == -ESRCH) {
167                 _E("pid=%d hasn't been added to sleep wait list", pid);
168                 return ret;
169         }
170
171         /* Multiple confirmation or
172          * confirm request after MAX_WAIT_SECOND */
173         if (ret == 0) {
174                 _D("pid=%d(%s) sleep wait has already been confirmed", sw->pid, sw->comm);
175                 return 0;
176         }
177
178         if (id != current_sleep_id) {
179                 _E("Confirm mismatches sleep_id, current=%d, confirm=%d", current_sleep_id, id);
180                 return -EINVAL;
181         }
182
183         /* Move sw from active to inactive list.
184          * Wake unlock if active becomes empty. */
185         if (ret == -EBUSY) {
186                 active = g_list_remove(active, sw);
187                 inactive = g_list_append(inactive, sw);
188                 _D("pid=%d(%s) confirmed sleep wait", sw->pid, sw->comm);
189
190                 if (g_list_length(active) == 0) {
191                         /* remove max_wait_timer and wake unlock immediately */
192                         if (max_wait_timer) {
193                                 g_source_remove(max_wait_timer);
194                                 max_wait_timer = 0;
195                         }
196
197                         _D("All sleep waits have been confirmed, wake unlock");
198                         request_wake_unlock();
199                 }
200
201                 return 0;
202         }
203
204         /* fail */
205         _E("Failed to confirm sleep wait, %d", ret);
206
207         return ret;
208 }
209
210 void start_sleep_wait(int id)
211 {
212         if (max_wait_timer) {
213                 _E("Already sleep waiting");
214                 return;
215         }
216
217         switch_active_list();
218
219         /* no need to defer sleep. wake unlock */
220         if (g_list_length(active) == 0) {
221                 request_wake_unlock();
222                 return;
223         }
224
225         current_sleep_id = id;
226
227         _D("Sleep wait is triggered, maximum wait timeout=%ds", MAX_WAIT_SECOND);
228         max_wait_timer = g_timeout_add_seconds(MAX_WAIT_SECOND, max_wait_expired_cb, NULL);
229 }
230
231 void stop_sleep_wait(void)
232 {
233         struct sleep_wait *sw;
234         GList *elem, *elem_next;
235
236         if (max_wait_timer == 0)
237                 return;
238
239         /* cancel sleep wait */
240         g_source_remove(max_wait_timer);
241         max_wait_timer = 0;
242
243         _D("Wake locked during sleep wait, cancel all sleep waits");
244
245         SYS_G_LIST_FOREACH_SAFE(active, elem, elem_next, sw) {
246                 if (kill(sw->pid, 0) != 0) {
247                         _D("Remove not existing sleep wait of %d(%s)", sw->pid, sw->comm);
248                         active = g_list_remove(active, sw);
249                         free(sw);
250                 } else { /* move to inactive manually */
251                         active = g_list_remove(active, sw);
252                         inactive = g_list_append(inactive, sw);
253                 }
254         }
255 }