power: Fixed a bug to use the wrong dbus path and interface for reboot.
[platform/core/api/device.git] / src / power.c
1 /*
2  * Copyright (c) 2011 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdbool.h>
21 #include <errno.h>
22 #include <glib.h>
23 #include <limits.h>
24 #include <tracker.h>
25
26 #include "power.h"
27 #include "display.h"
28 #include "common.h"
29 #include "dbus.h"
30
31 /**
32  * Parameters for device_power_request_lock()
33  */
34 #define STAY_CUR_STATE  0x1
35 #define GOTO_STATE_NOW  0x2
36 #define HOLD_KEY_BLOCK  0x4
37 #define STANDBY_MODE    0x8
38
39 /**
40  * Parameters for device_power_request_unlock()
41  */
42 #define PM_SLEEP_MARGIN 0x0 /**< keep guard time for unlock */
43 #define PM_RESET_TIMER  0x1 /**< reset timer for unlock */
44 #define PM_KEEP_TIMER   0x2 /**< keep timer for unlock */
45
46 #define METHOD_LOCK_STATE       "lockstate"
47 #define METHOD_UNLOCK_STATE     "unlockstate"
48 #define METHOD_CHANGE_STATE     "changestate"
49 #define METHOD_REBOOT           "Reboot"
50 #define METHOD_REBOOT_WITH_OPTION "RebootWithOption"
51
52 #define TYPE_REBOOT             "reboot"
53 #define REBOOT_REASON_NONE      ""
54
55 #define STR_STAYCURSTATE "staycurstate"
56 #define STR_GOTOSTATENOW "gotostatenow"
57
58 #define STR_HOLDKEYBLOCK "holdkeyblock"
59 #define STR_STANDBYMODE  "standbymode"
60 #define STR_NULL         "NULL"
61
62 #define STR_SLEEP_MARGIN "sleepmargin"
63 #define STR_RESET_TIMER  "resettimer"
64 #define STR_KEEP_TIMER   "keeptimer"
65
66 #define STR_LCD_OFF   "lcdoff"
67 #define STR_LCD_DIM   "lcddim"
68 #define STR_LCD_ON    "lcdon"
69
70 #define LOCK_CPU_TIMEOUT_MAX       (24*60*60*1000) /* milliseconds */
71 #define LOCK_CPU_PADDING_TIMEOUT   (20*1000) /* milliseconds */
72
73 #define POWER_CONF "/etc/deviced/device/power.conf"
74
75 static guint off_lock_timeout;
76 static guint padding_timeout;
77 static int prev_count;
78
79 static GList *request_id_list;
80
81 static struct _lock_timeout {
82         unsigned int release;
83         unsigned int padding;
84 } lock_timeout = {
85         .release = LOCK_CPU_TIMEOUT_MAX,
86         .padding = LOCK_CPU_PADDING_TIMEOUT,
87 };
88
89 static char *get_state_str(display_state_e state)
90 {
91         switch (state) {
92         case DISPLAY_STATE_NORMAL:
93                 return STR_LCD_ON;
94         case DISPLAY_STATE_SCREEN_DIM:
95                 return STR_LCD_DIM;
96         case DISPLAY_STATE_SCREEN_OFF:
97                 return STR_LCD_OFF;
98         default:
99                 break;
100         }
101         return NULL;
102 }
103
104 static void remove_off_lock_timeout(void)
105 {
106         if (!TIZEN_FEATURE_TRACKER)
107                 return;
108         if (off_lock_timeout) {
109                 _I("Power lock timeout handler removed");
110                 g_source_remove(off_lock_timeout);
111                 off_lock_timeout = 0;
112         }
113 }
114
115 static void remove_padding_timeout(void)
116 {
117         if (!TIZEN_FEATURE_TRACKER)
118                 return;
119         if (padding_timeout) {
120                 _I("Padding timeout handler removed");
121                 g_source_remove(padding_timeout);
122                 padding_timeout = 0;
123         }
124 }
125
126 static void notice_lock_expired_done(void *data, GVariant *result, GError *err)
127 {
128         int val, ret;
129         GList *l, *l_next;
130         char *id, *elem;
131         size_t len;
132
133         if (!result)
134                 return;
135
136         g_variant_get(result, "(si)", &id, &val);
137
138         elem = NULL;
139         len = strlen(id) + 1;
140         for (l = request_id_list, l_next = g_list_next(l);
141                         l && (elem = l->data) != NULL;
142                         l = l_next, l_next = g_list_next(l), elem = NULL) {
143                 if (!strncmp(id, elem, len))
144                         break;
145         }
146         if (!elem)
147                 return;
148
149         request_id_list = g_list_remove(request_id_list, elem);
150         free(elem);
151
152         if (val == 0) { /* Allow to lock cpu */
153                 _I("Continue Power Lock");
154                 return;
155         } else if (val == 1) { /* Release cpu lock */
156                 ret = device_power_release_lock(POWER_LOCK_CPU);
157                 if (ret != DEVICE_ERROR_NONE)
158                         _E("Failed to release power(CPU) (%d)", ret);
159         }
160 }
161
162 static char *get_new_request_id(pid_t pid)
163 {
164         char id[64];
165         snprintf(id, sizeof(id), "%d_%lu", pid, time(NULL));
166         return strdup(id);
167 }
168
169 static int notice_power_lock_expired(void)
170 {
171         int ret;
172         pid_t pid;
173         char *req_id;
174         char *param[1];
175
176         pid = getpid();
177
178         req_id = get_new_request_id(pid);
179         if (!req_id) {
180                 _E("Failed to get request id");
181                 return -ENOMEM;
182         }
183
184         param[0] = req_id;
185         ret = dbus_method_async_with_reply(
186                         DEVICED_BUS_NAME,
187                         DEVICED_PATH_DISPLAY,
188                         DEVICED_INTERFACE_DISPLAY,
189                         "LockTimeoutExpired",
190                         "s", param,
191                         notice_lock_expired_done,
192                         -1,
193                         NULL);
194         if (ret < 0) {
195                 _E("Failed to notice power lock expired (%d)", ret);
196                 return ret;
197         }
198
199         request_id_list = g_list_append(request_id_list, req_id);
200         _I("request ID %s is added", req_id);
201
202         return 0;
203 }
204
205 static gboolean padding_timeout_expired(gpointer data)
206 {
207         int ret, ref;
208         int count;
209
210         _I("Padding timeout expired");
211
212         remove_padding_timeout();
213
214         ret = tracker_get_power_lock_ref(&ref);
215         if (ret != TRACKER_ERROR_NONE) {
216                 _E("Failed to get reference count of power lock");
217                 goto out;
218         }
219
220         _I("reference count of power lock is (%d)", ref);
221         if (ref > 0) {
222                 _I("Power Lock continue (Reference count > 0 !!)");
223                 return G_SOURCE_REMOVE;
224         }
225
226         ret = tracker_get_power_lock_total(&count);
227         if (ret != TRACKER_ERROR_NONE) {
228                 _E("Failed to get total count of power lock(%d)", ret);
229                 goto out;
230         }
231
232         if (count != prev_count) {
233                 _I("Power Lock continue (Total reference count increased !!)");
234                 return G_SOURCE_REMOVE;
235         }
236
237 out:
238         ret = notice_power_lock_expired();
239         if (ret < 0) {
240                 _E("Failed to launch power lock expired popup (%d)", ret);
241                 return G_SOURCE_CONTINUE;
242         }
243
244         remove_off_lock_timeout();
245
246         return G_SOURCE_REMOVE;
247 }
248
249 static void add_padding_timeout(void)
250 {
251         guint id;
252
253         remove_padding_timeout();
254
255         _I("Padding timeout handler added");
256
257         id = g_timeout_add(lock_timeout.padding,
258                         padding_timeout_expired, NULL);
259         if (id)
260                 padding_timeout = id;
261         else
262                 _E("Failed to add timeout for padding time");
263 }
264
265 static gboolean off_lock_timeout_expired(gpointer data)
266 {
267         int ret, ref;
268
269         _I("Power lock timeout expired");
270
271         ret = tracker_get_power_lock_ref(&ref);
272         if (ret != TRACKER_ERROR_NONE) {
273                 _E("Failed to get reference count of power lock(%d)", ret);
274                 remove_off_lock_timeout();
275                 return G_SOURCE_REMOVE;
276         }
277
278         _I("reference count of power lock is (%d)", ref);
279         if (ref > 0)
280                 goto out;
281
282         add_padding_timeout();
283
284         ret = tracker_get_power_lock_total(&prev_count);
285         if (ret != TRACKER_ERROR_NONE)
286                 _E("Failed to get total count of power lock(%d)", ret);
287
288 out:
289         return G_SOURCE_CONTINUE;
290 }
291
292 static void add_off_lock_timeout(void)
293 {
294         guint id;
295
296         if (!TIZEN_FEATURE_TRACKER)
297                 return;
298
299         remove_off_lock_timeout();
300         remove_padding_timeout();
301
302         _I("Power lock timeout handler added");
303
304         id = g_timeout_add(lock_timeout.release,
305                         off_lock_timeout_expired, NULL);
306         if (id)
307                 off_lock_timeout = id;
308         else
309                 _E("Failed to add Power Lock timeout handler");
310 }
311
312 static void lock_cb(void *data, GVariant *result, GError *err)
313 {
314         int ret;
315
316         if (!result) {
317                 _E("no message : %s", err->message); //LCOV_EXCL_LINE
318                 return;
319         }
320
321         g_variant_get(result, "(i)", &ret);
322         _D("%s-%s : %d", DEVICED_INTERFACE_DISPLAY, METHOD_LOCK_STATE, ret);
323
324         if (ret < 0)
325                 remove_off_lock_timeout();
326 }
327
328 static int lock_state(display_state_e state, unsigned int flag, int timeout_ms)
329 {
330         char *arr[4];
331         char str_timeout[32];
332         int ret;
333
334         arr[0] = get_state_str(state);
335         if (!arr[0])
336                 return -EINVAL;
337
338         if (flag & GOTO_STATE_NOW)
339                 arr[1] = STR_GOTOSTATENOW;
340         else
341                 arr[1] = STR_STAYCURSTATE;
342
343         if (flag & HOLD_KEY_BLOCK)
344                 arr[2] = STR_HOLDKEYBLOCK;
345         else if (flag & STANDBY_MODE)
346                 arr[2] = STR_STANDBYMODE;
347         else
348                 arr[2] = STR_NULL;
349
350         snprintf(str_timeout, sizeof(str_timeout), "%d", -1);
351         arr[3] = str_timeout;
352         ret = dbus_method_sync(DEVICED_BUS_NAME,
353                         DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
354                         METHOD_LOCK_STATE, "sssi", arr);
355         if (ret == -EACCES || ret == -ECOMM || ret == -EPERM)
356                 return -EACCES;
357
358         snprintf(str_timeout, sizeof(str_timeout), "%d", timeout_ms);
359         arr[3] = str_timeout;
360
361         return dbus_method_async_with_reply(DEVICED_BUS_NAME,
362                         DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
363                         METHOD_LOCK_STATE, "sssi", arr, lock_cb, -1, NULL);
364 }
365
366 static void unlock_cb(void *data, GVariant *result, GError *err)
367 {
368         int ret;
369
370         if (!result) {
371                 _E("no message : %s", err->message); //LCOV_EXCL_LINE
372                 return;
373         }
374
375         g_variant_get(result, "(i)", &ret);
376         _D("%s-%s : %d", DEVICED_INTERFACE_DISPLAY, METHOD_UNLOCK_STATE, ret);
377 }
378
379 static int unlock_state(display_state_e state, unsigned int flag)
380 {
381         char *arr[2];
382         int ret;
383
384         arr[0] = "";
385         arr[1] = "";
386         ret = dbus_method_sync(DEVICED_BUS_NAME,
387                         DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
388                         METHOD_UNLOCK_STATE, "ss", arr);
389         if (ret == -EACCES || ret == -ECOMM || ret == -EPERM)
390                 return -EACCES;
391
392         arr[0] = get_state_str(state);
393         if (!arr[0])
394                 return -EINVAL;
395
396         if (flag == PM_SLEEP_MARGIN)
397                 arr[1] = STR_SLEEP_MARGIN;
398         else if (flag == PM_RESET_TIMER)
399                 arr[1] = STR_RESET_TIMER;
400         else if (flag == PM_KEEP_TIMER)
401                 arr[1] = STR_KEEP_TIMER;
402         else
403                 return -EINVAL;
404
405         return dbus_method_async_with_reply(DEVICED_BUS_NAME,
406                         DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
407                         METHOD_UNLOCK_STATE, "ss", arr, unlock_cb, -1, NULL);
408 }
409
410 int device_power_request_lock(power_lock_e type, int timeout_ms)
411 {
412         int ret;
413
414         if (timeout_ms < 0)
415                 return DEVICE_ERROR_INVALID_PARAMETER;
416
417         if (type == POWER_LOCK_CPU) {
418                 ret = lock_state(DISPLAY_STATE_SCREEN_OFF, STAY_CUR_STATE, timeout_ms);
419                 if (ret == 0 &&
420                         (timeout_ms == 0 || timeout_ms > lock_timeout.release))
421                         add_off_lock_timeout();
422         } else if (type == POWER_LOCK_DISPLAY) {
423                 _W("DEPRECATION WARNING: This power lock enum is deprecated and will be removed from next release. Use efl_util_set_window_screen_mode() instead.");
424                 ret = lock_state(DISPLAY_STATE_NORMAL, STAY_CUR_STATE, timeout_ms);
425         } else if (type == POWER_LOCK_DISPLAY_DIM) {
426                 _W("DEPRECATION WARNING: This power lock enum is deprecated and will be removed from next release. Use efl_util_set_window_screen_mode() instead.");
427                 ret = lock_state(DISPLAY_STATE_SCREEN_DIM, STAY_CUR_STATE, timeout_ms);
428         } else
429                 return DEVICE_ERROR_INVALID_PARAMETER;
430
431         return errno_to_device_error(ret);
432 }
433
434 int device_power_release_lock(power_lock_e type)
435 {
436         int ret;
437
438         if (type == POWER_LOCK_CPU) {
439                 ret = unlock_state(DISPLAY_STATE_SCREEN_OFF, PM_SLEEP_MARGIN);
440                 if (ret == 0) {
441                         remove_off_lock_timeout();
442                         remove_padding_timeout();
443                 }
444         } else if (type == POWER_LOCK_DISPLAY) {
445                 _W("DEPRECATION WARNING: This power lock enum is deprecated and will be removed from next release. Use efl_util_set_window_screen_mode() instead.");
446                 ret = unlock_state(DISPLAY_STATE_NORMAL, PM_KEEP_TIMER);
447         } else if (type == POWER_LOCK_DISPLAY_DIM) {
448                 _W("DEPRECATION WARNING: This power lock enum is deprecated and will be removed from next release. Use efl_util_set_window_screen_mode() instead.");
449                 ret = unlock_state(DISPLAY_STATE_SCREEN_DIM, PM_KEEP_TIMER);
450         } else
451                 return DEVICE_ERROR_INVALID_PARAMETER;
452
453         return errno_to_device_error(ret);
454 }
455
456 int device_power_wakeup(bool dim)
457 {
458         _W("DEPRECATION WARNING: device_power_wakeup() is deprecated and will be removed from next release.");
459         if (dim)
460                 return device_display_change_state(DISPLAY_STATE_SCREEN_DIM);
461
462         return device_display_change_state(DISPLAY_STATE_NORMAL);
463 }
464
465 //LCOV_EXCL_START Not available to test(Reboot during TCT)
466 int device_power_reboot(const char *reason)
467 {
468         char *arr[2];
469         char *method, *sig;
470         int ret;
471
472         if (reason) {
473                 arr[0] = TYPE_REBOOT;
474                 arr[1] = (char *)reason;
475                 method = METHOD_REBOOT_WITH_OPTION;
476                 sig = "ss";
477         } else {
478                 arr[0] = REBOOT_REASON_NONE;
479                 method = METHOD_REBOOT;
480                 sig = "s";
481         }
482
483         ret = dbus_method_sync(DEVICED_BUS_NAME,
484                         DEVICED_PATH_REBOOT,
485                         DEVICED_INTERFACE_REBOOT,
486                         method, sig, arr);
487         return errno_to_device_error(ret);
488 }
489 //LCOV_EXCL_STOP
490
491 static int power_load_config(struct parse_result *result, void *data)
492 {
493         unsigned int value;
494
495         if (!result)
496                 return 0;
497
498         if (strncmp(result->section, "Power", 6))
499                 return 0;
500
501         value = strtoul(result->value, NULL, 10);
502         if (value == 0)
503                 return 0;
504
505         if (!strncmp(result->name, "LockTimeout", 12)) {
506                 _I("Power Lock Auto Release Timeout: %ld msec", value);
507                 lock_timeout.release = value;
508         } else if (!strncmp(result->name, "LockPaddingTimeout", 12)) {
509                 _I("Power Lock Auto Release Padding Timeout: %ld msec", value);
510                 lock_timeout.padding = value;
511         }
512
513         return 0;
514 }
515
516 static void __CONSTRUCTOR__ power_init(void)
517 {
518         int ret;
519
520         ret = config_parse(POWER_CONF, power_load_config, NULL);
521         if (ret < 0)
522                 _E("Failed to load config file (%d)", ret);
523 }