power: introduce power-state-manager 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 gboolean max_wait_expired_cb(void *data)
44 {
45         struct sleep_wait *sw;
46         GList *elem, *elem_next;
47
48         max_wait_timer = 0;
49
50         /* force confirmation */
51         if (g_list_length(active) != 0) {
52                 SYS_G_LIST_FOREACH_SAFE(active, elem, elem_next, sw) {
53                         if (kill(sw->pid, 0) != 0) {
54                                 _D("Remove not existing sleep wait of pid=%d(%s)", sw->pid, sw->comm);
55                                 active = g_list_remove(active, sw);
56                                 free(sw);
57                         } else { /* move to inactive manually */
58                                 _W("pid=%d(%s) hasn't comfirmed sleep wait", sw->pid, sw->comm);
59                                 active = g_list_remove(active, sw);
60                                 inactive = g_list_append(inactive, sw);
61                         }
62                 }
63         }
64
65         device_notify(DEVICE_NOTIFIER_POWER_SLEEP_WAIT_DONE, NULL);
66
67         return G_SOURCE_REMOVE;
68 }
69
70 /* It returns
71  *      0 if sleep_wait is found in inactive list,
72  * -EBUSY if sleep_wait is found in active list,
73  * -ESRCH if sleep_wait is not found in both list */
74 static int find_sleep_wait(pid_t pid, struct sleep_wait **sw)
75 {
76         struct sleep_wait *ret = NULL;
77         GList *elem;
78
79         if (!sw)
80                 return -EINVAL;
81
82         SYS_G_LIST_FOREACH(inactive, elem, ret) {
83                 if (ret->pid == pid) {
84                         *sw = ret;
85                         return 0;
86                 }
87         }
88
89         SYS_G_LIST_FOREACH(active, elem, ret) {
90                 if (ret->pid == pid) {
91                         *sw = ret;
92                         return -EBUSY;
93                 }
94         }
95
96         *sw = NULL;
97         return -ESRCH;
98 }
99
100 int add_sleep_wait(pid_t pid)
101 {
102         struct sleep_wait *sw;
103         int ret;
104
105         ret = find_sleep_wait(pid, &sw);
106         if (ret == -ESRCH) { /* new node */
107                 sw = calloc(1, sizeof(struct sleep_wait));
108                 if (!sw)
109                         return -ENOMEM;
110
111                 sw->pid = pid;
112                 get_command(pid, sw->comm, sizeof(sw->comm));
113                 inactive = g_list_append(inactive, sw);
114
115                 _D("pid=%d(%s) is added to sleep wait list", sw->pid, sw->comm);
116         } else if (ret == 0 || ret == -EBUSY) {
117                 _D("pid=%d(%s) has already been added to sleep wait list", sw->pid, sw->comm);
118         }
119
120         return ret;
121 }
122
123 void remove_sleep_wait(pid_t pid)
124 {
125         struct sleep_wait *sw = NULL;
126         int retval;
127
128         retval = find_sleep_wait(pid, &sw);
129         if (retval == -ESRCH)
130                 return;
131
132         if (sw) {
133                 _D("pid=%d(%s) requested removing sleep wait", sw->pid, sw->comm);
134
135                 /* remove from where it is contained */
136                 active = g_list_remove(active, sw);
137                 inactive = g_list_remove(inactive, sw);
138
139                 free(sw);
140
141                 /* wake unlock when active becomes empty by this operation
142                  * during waiting sleep confirmations */
143                 if (max_wait_timer != 0 && g_list_length(active) == 0) {
144                         g_source_remove(max_wait_timer);
145                         max_wait_timer = 0;
146
147                         device_notify(DEVICE_NOTIFIER_POWER_SLEEP_WAIT_DONE, NULL);
148                 }
149         }
150 }
151
152 int confirm_sleep_wait(pid_t pid, int id)
153 {
154         struct sleep_wait *sw;
155         int ret = 0;
156
157         ret = find_sleep_wait(pid, &sw);
158         if (ret == -ESRCH) {
159                 _E("pid=%d hasn't been added to sleep wait list", pid);
160                 return ret;
161         }
162
163         /* Multiple confirmation or
164          * confirm request after MAX_WAIT_SECOND */
165         if (ret == 0) {
166                 _D("pid=%d(%s) sleep wait has already been confirmed", sw->pid, sw->comm);
167                 return 0;
168         }
169
170         if (id != current_sleep_id) {
171                 _E("Confirm mismatches sleep_id, current=%d, confirm=%d", current_sleep_id, id);
172                 return -EINVAL;
173         }
174
175         /* Move sw from active to inactive list.
176          * Wake unlock if active becomes empty. */
177         if (ret == -EBUSY) {
178                 active = g_list_remove(active, sw);
179                 inactive = g_list_append(inactive, sw);
180                 _D("pid=%d(%s) confirmed sleep wait", sw->pid, sw->comm);
181
182                 if (g_list_length(active) == 0) {
183                         /* remove max_wait_timer and wake unlock immediately */
184                         if (max_wait_timer) {
185                                 g_source_remove(max_wait_timer);
186                                 max_wait_timer = 0;
187                         }
188
189                         device_notify(DEVICE_NOTIFIER_POWER_SLEEP_WAIT_DONE, NULL);
190                 }
191
192                 return 0;
193         }
194
195         /* fail */
196         _E("Failed to confirm sleep wait, %d", ret);
197
198         return ret;
199 }
200
201 void start_sleep_wait(int id)
202 {
203         if (max_wait_timer) {
204                 _E("Already sleep waiting");
205                 return;
206         }
207
208         switch_active_list();
209
210         /* no need to defer sleep. wake unlock */
211         if (g_list_length(active) == 0) {
212                 device_notify(DEVICE_NOTIFIER_POWER_SLEEP_WAIT_DONE, NULL);
213                 return;
214         }
215
216         current_sleep_id = id;
217
218         _D("Sleep wait is triggered, maximum wait timeout=%ds", MAX_WAIT_SECOND);
219         max_wait_timer = g_timeout_add_seconds(MAX_WAIT_SECOND, max_wait_expired_cb, NULL);
220 }
221
222 void stop_sleep_wait(void)
223 {
224         struct sleep_wait *sw;
225         GList *elem, *elem_next;
226
227         if (max_wait_timer == 0)
228                 return;
229
230         /* cancel sleep wait */
231         g_source_remove(max_wait_timer);
232         max_wait_timer = 0;
233
234         _D("Wake locked during sleep wait, cancel all sleep waits");
235
236         SYS_G_LIST_FOREACH_SAFE(active, elem, elem_next, sw) {
237                 if (kill(sw->pid, 0) != 0) {
238                         _D("Remove not existing sleep wait of %d(%s)", sw->pid, sw->comm);
239                         active = g_list_remove(active, sw);
240                         free(sw);
241                 } else { /* move to inactive manually */
242                         active = g_list_remove(active, sw);
243                         inactive = g_list_append(inactive, sw);
244                 }
245         }
246 }