power: support cpu lock/unlock for headless
[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 #include <sys/time.h>
26 #include <libsyscommon/libgdbus.h>
27 #include <libsyscommon/ini-parser.h>
28
29 #include "power.h"
30 #include "power-internal.h"
31 #include "display.h"
32 #include "display-internal.h"
33 #include "common.h"
34
35 /**
36  * Parameters for device_power_request_lock()
37  */
38 #define STAY_CUR_STATE  0x1
39 #define GOTO_STATE_NOW  0x2
40 #define HOLD_KEY_BLOCK  0x4
41 #define STANDBY_MODE    0x8
42
43 /**
44  * Parameters for device_power_request_unlock()
45  */
46 #define PM_SLEEP_MARGIN 0x0 /**< keep guard time for unlock */
47 #define PM_RESET_TIMER  0x1 /**< reset timer for unlock */
48 #define PM_KEEP_TIMER   0x2 /**< keep timer for unlock */
49
50 #define METHOD_LOCK_STATE           "lockstate"
51 #define METHOD_UNLOCK_STATE         "unlockstate"
52 #define METHOD_CHANGE_STATE         "changestate"
53 #define METHOD_POWEROFF             "PowerOff"
54 #define METHOD_POWEROFF_WITH_OPTION "PowerOffWithOption"
55
56 #define TYPE_POWEROFF           "poweroff"
57 #define TYPE_REBOOT             "reboot"
58 #define REBOOT_REASON_NONE      ""
59
60 #define STR_STAYCURSTATE "staycurstate"
61 #define STR_GOTOSTATENOW "gotostatenow"
62
63 #define STR_HOLDKEYBLOCK "holdkeyblock"
64 #define STR_STANDBYMODE  "standbymode"
65 #define STR_NULL         "NULL"
66
67 #define STR_SLEEP_MARGIN "sleepmargin"
68 #define STR_RESET_TIMER  "resettimer"
69 #define STR_KEEP_TIMER   "keeptimer"
70
71 #define STR_LCD_OFF   "lcdoff"
72 #define STR_LCD_DIM   "lcddim"
73 #define STR_LCD_ON    "lcdon"
74
75 #define LOCK_CPU_TIMEOUT_MAX       (24*60*60*1000) /* milliseconds */
76 #define LOCK_CPU_PADDING_TIMEOUT   (20*1000) /* milliseconds */
77
78 #define POWER_CONF "/etc/deviced/device/power.conf"
79
80 static guint off_lock_timeout;
81 static guint padding_timeout;
82 static int prev_count;
83
84 static GList *request_id_list;
85
86 static struct _lock_timeout {
87         unsigned int release;
88         unsigned int padding;
89 } lock_timeout = {
90         .release = LOCK_CPU_TIMEOUT_MAX,
91         .padding = LOCK_CPU_PADDING_TIMEOUT,
92 };
93 static bool power_locked_CPU;
94 static bool power_locked_DISPLAY;
95 static bool power_locked_DISPLAY_DIM;
96
97 static char *get_state_str(display_state_e state)
98 {
99         switch (state) {
100         case DISPLAY_STATE_NORMAL:
101                 return STR_LCD_ON;
102         case DISPLAY_STATE_SCREEN_DIM:
103                 return STR_LCD_DIM;
104         case DISPLAY_STATE_SCREEN_OFF:
105                 return STR_LCD_OFF;
106         default:
107                 break;
108         }
109         return NULL;
110 }
111
112 static void remove_off_lock_timeout(void)
113 {
114         if (!TIZEN_FEATURE_TRACKER)
115                 return;
116         if (off_lock_timeout) {
117                 _I("Power lock timeout handler removed"); //LCOV_EXCL_LINE Logs
118                 g_source_remove(off_lock_timeout);
119                 off_lock_timeout = 0;
120         }
121 }
122
123 static void remove_padding_timeout(void)
124 {
125         if (!TIZEN_FEATURE_TRACKER)
126                 return;
127         if (padding_timeout) {
128                 _I("Padding timeout handler removed"); //LCOV_EXCL_LINE Logs
129                 g_source_remove(padding_timeout);
130                 padding_timeout = 0;
131         }
132 }
133
134 //LCOV_EXCL_START Callback function
135 static void notice_lock_expired_done(GVariant *result, void *data, GError *err)
136 {
137         int val, ret_val;
138         GList *l, *l_next;
139         char *id, *elem;
140         size_t len;
141
142         if (!result)
143                 return;
144
145         g_variant_get(result, "(&si)", &id, &val);
146
147         elem = NULL;
148         len = strlen(id) + 1;
149         for (l = request_id_list, l_next = g_list_next(l);
150                         l && (elem = l->data) != NULL;
151                         l = l_next, l_next = g_list_next(l), elem = NULL) {
152                 if (!strncmp(id, elem, len))
153                         break;
154         }
155         if (!elem)
156                 return;
157
158         request_id_list = g_list_remove(request_id_list, elem);
159         free(elem);
160
161         if (val == 0) { /* Allow to lock cpu */
162                 _I("Continue Power Lock");
163                 return;
164         } else if (val == 1) { /* Release cpu lock */
165                 ret_val = device_power_release_lock(POWER_LOCK_CPU);
166                 if (ret_val != DEVICE_ERROR_NONE)
167                         _E("Failed to release power(CPU) (%d)", ret_val);
168         }
169 }
170 //LCOV_EXCL_STOP
171
172 //LCOV_EXCL_START Callback function
173 static char *get_new_request_id(pid_t pid)
174 {
175         char id[64];
176         snprintf(id, sizeof(id), "%d_%lu", pid, time(NULL));
177         return strdup(id);
178 }
179 //LCOV_EXCL_STOP
180
181 //LCOV_EXCL_START Callback function
182 static int notice_power_lock_expired(void)
183 {
184         int ret_dbus;
185         pid_t pid;
186         char *req_id;
187
188         pid = getpid();
189
190         req_id = get_new_request_id(pid);
191         if (!req_id) {
192                 _E("Failed to get request id");
193                 return -ENOMEM;
194         }
195
196         ret_dbus = gdbus_call_async_with_reply(
197                         DEVICED_BUS_NAME,
198                         DEVICED_PATH_DISPLAY,
199                         DEVICED_INTERFACE_DISPLAY,
200                         "LockTimeoutExpired",
201                         g_variant_new("(s)", req_id),
202                         notice_lock_expired_done,
203                         -1,
204                         NULL);
205         if (ret_dbus < 0) {
206                 _E("Failed to notice power lock expired (%d)", ret_dbus);
207                 free(req_id);
208                 return ret_dbus;
209         }
210
211         request_id_list = g_list_append(request_id_list, req_id);
212         _I("request ID %s is added", req_id);
213
214         return 0;
215 }
216 //LCOV_EXCL_STOP
217
218 //LCOV_EXCL_START Callback function
219 static gboolean padding_timeout_expired(gpointer data)
220 {
221         int ret_val, ref;
222         int count;
223
224         _I("Padding timeout expired");
225
226         remove_padding_timeout();
227
228         ret_val = tracker_get_ref_counter(TRACKER_TYPE_POWER_LOCK, &ref);
229         if (ret_val != TRACKER_ERROR_NONE) {
230                 _E("Failed to get reference count of power lock");
231                 goto out;
232         }
233
234         _I("reference count of power lock is (%d)", ref);
235         if (ref > 0) {
236                 _I("Power Lock continue (Reference count > 0 !!)");
237                 return G_SOURCE_REMOVE;
238         }
239
240         ret_val = tracker_get_tick(TRACKER_TYPE_POWER_LOCK, &count);
241         if (ret_val != TRACKER_ERROR_NONE) {
242                 _E("Failed to get total count of power lock(%d)", ret_val);
243                 goto out;
244         }
245
246         if (count != prev_count) {
247                 _I("Power Lock continue (Total reference count increased !!)");
248                 return G_SOURCE_REMOVE;
249         }
250
251 out:
252         ret_val = notice_power_lock_expired();
253         if (ret_val < 0) {
254                 _E("Failed to launch power lock expired popup (%d)", ret_val);
255                 return G_SOURCE_CONTINUE;
256         }
257
258         remove_off_lock_timeout();
259
260         return G_SOURCE_REMOVE;
261 }
262 //LCOV_EXCL_STOP
263
264 //LCOV_EXCL_START Callback function
265 static void add_padding_timeout(void)
266 {
267         guint id;
268
269         remove_padding_timeout();
270
271         _I("Padding timeout handler added");
272
273         id = g_timeout_add(lock_timeout.padding,
274                         padding_timeout_expired, NULL);
275         if (id)
276                 padding_timeout = id;
277         else
278                 _E("Failed to add timeout for padding time");
279 }
280 //LCOV_EXCL_STOP
281
282 //LCOV_EXCL_START Callback function
283 static gboolean off_lock_timeout_expired(gpointer data)
284 {
285         int ret_val, ref;
286
287         _I("Power lock timeout expired");
288
289         ret_val = tracker_get_ref_counter(TRACKER_TYPE_POWER_LOCK, &ref);
290         if (ret_val != TRACKER_ERROR_NONE) {
291                 _E("Failed to get reference count of power lock(%d)", ret_val);
292                 remove_off_lock_timeout();
293                 return G_SOURCE_REMOVE;
294         }
295
296         _I("reference count of power lock is (%d)", ref);
297         if (ref > 0)
298                 goto out;
299
300         add_padding_timeout();
301
302         ret_val = tracker_get_tick(TRACKER_TYPE_POWER_LOCK, &prev_count);
303         if (ret_val != TRACKER_ERROR_NONE)
304                 _E("Failed to get total count of power lock(%d)", ret_val);
305
306 out:
307         return G_SOURCE_CONTINUE;
308 }
309 //LCOV_EXCL_STOP
310
311 static void add_off_lock_timeout(void)
312 {
313         guint id;
314
315         if (!TIZEN_FEATURE_TRACKER)
316                 return;
317
318         remove_off_lock_timeout();
319         remove_padding_timeout();
320
321         _I("Power lock timeout handler added"); //LCOV_EXCL_LINE Logs
322
323         id = g_timeout_add(lock_timeout.release,
324                         off_lock_timeout_expired, NULL);
325         if (id)
326                 off_lock_timeout = id;
327         else
328                 _E("Failed to add Power Lock timeout handler"); //LCOV_EXCL_LINE Logs
329 }
330
331 static void lock_cb(GVariant *result, void *data, GError *err)
332 {
333         int ret_val;
334
335         if (!result) {
336 //LCOV_EXCL_START System Error
337                 _E("no message : %s", err->message);
338                 return;
339 //LCOV_EXCL_STOP
340         }
341
342         g_variant_get(result, "(i)", &ret_val);
343         _D("%s-%s : %d", DEVICED_INTERFACE_DISPLAY, METHOD_LOCK_STATE, ret_val);
344
345         if (ret_val < 0)
346                 remove_off_lock_timeout();
347 }
348
349 static int lock_state(display_state_e state, unsigned int flag, int timeout_ms)
350 {
351         char *arr[4];
352         int ret_dbus;
353         static int privilege = -1;
354
355         if (flag & GOTO_STATE_NOW)
356                 arr[1] = STR_GOTOSTATENOW;
357         else
358                 arr[1] = STR_STAYCURSTATE;
359
360         if (flag & HOLD_KEY_BLOCK)
361                 arr[2] = STR_HOLDKEYBLOCK;
362         else if (flag & STANDBY_MODE)
363                 arr[2] = STR_STANDBYMODE;
364         else
365                 arr[2] = STR_NULL;
366
367         if (privilege < 0) {
368                 arr[0] = "privilege check";
369
370                 ret_dbus = gdbus_call_sync_with_reply_int(DEVICED_BUS_NAME,
371                                 DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
372                                 METHOD_LOCK_STATE, g_variant_new("(sssi)", arr[0], arr[1], arr[2], timeout_ms),
373                                 NULL);
374 //LCOV_EXCL_START System Error
375                 if (ret_dbus < 0)
376                         return ret_dbus;
377 //LCOV_EXCL_STOP
378                 else
379                         privilege = 1;
380         }
381
382         arr[0] = get_state_str(state);
383         if (!arr[0])
384                 return -EINVAL;
385
386         return gdbus_call_async_with_reply(DEVICED_BUS_NAME,
387                         DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
388                         METHOD_LOCK_STATE, g_variant_new("(sssi)", arr[0], arr[1], arr[2], timeout_ms), lock_cb, -1, NULL);
389 }
390
391 static void unlock_cb(GVariant *result, void *data, GError *err)
392 {
393         int ret_val;
394
395         if (!result) {
396 //LCOV_EXCL_START System Error
397                 _E("no message : %s", err->message);
398                 return;
399 //LCOV_EXCL_STOP
400         }
401
402         g_variant_get(result, "(i)", &ret_val);
403         _D("%s-%s : %d", DEVICED_INTERFACE_DISPLAY, METHOD_UNLOCK_STATE, ret_val);
404 }
405
406 static int unlock_state(display_state_e state, unsigned int flag)
407 {
408         char *arr[2];
409         int ret_dbus;
410         static int privilege = -1;
411
412         if (flag == PM_SLEEP_MARGIN)
413                 arr[1] = STR_SLEEP_MARGIN;
414         else if (flag == PM_RESET_TIMER)
415                 arr[1] = STR_RESET_TIMER;
416         else if (flag == PM_KEEP_TIMER)
417                 arr[1] = STR_KEEP_TIMER;
418         else
419                 return -EINVAL;
420
421         if (privilege < 0) {
422                 arr[0] = "privilege check";
423
424                 ret_dbus = gdbus_call_sync_with_reply_int(DEVICED_BUS_NAME,
425                                 DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
426                                 METHOD_UNLOCK_STATE, g_variant_new("(ss)", arr[0], arr[1]),
427                                 NULL);
428 //LCOV_EXCL_START System Error
429                 if (ret_dbus < 0)
430                         return ret_dbus;
431 //LCOV_EXCL_STOP
432                 else
433                         privilege = 1;
434         }
435
436         arr[0] = get_state_str(state);
437         if (!arr[0])
438                 return -EINVAL;
439
440         return gdbus_call_async_with_reply(DEVICED_BUS_NAME,
441                         DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
442                         METHOD_UNLOCK_STATE, g_variant_new("(ss)", arr[0], arr[1]), unlock_cb, -1, NULL);
443 }
444
445 int device_power_request_lock(power_lock_e type, int timeout_ms)
446 {
447         int ret;
448         static long num_calls = 0;
449
450         _I("power_lock_e = %d", type);
451
452         if (!is_feature_display_supported() || !is_feature_display_state_supported()) {
453                 if ((type == POWER_LOCK_DISPLAY) || (type == POWER_LOCK_DISPLAY_DIM)) {
454                         return DEVICE_ERROR_NOT_SUPPORTED;
455                 } else if (type == POWER_LOCK_CPU) {
456                         /* in case of headless, request for cpu lock is handled
457                          * in power module of deviced, not in display module */
458                         ret = gdbus_call_sync_with_reply_int(DEVICED_BUS_NAME,
459                                 DEVICED_PATH_POWER, DEVICED_INTERFACE_POWER,
460                                 "LockCpu", g_variant_new("(i)", timeout_ms), NULL);
461                         return errno_to_device_error(ret);
462                 }
463         }
464
465         if (check_async_call_rate(&num_calls) < 0) {
466 //LCOV_EXCL_START System Error
467                 _E("Rejected by too frequent calls; %d (calls per sec.) limit is violated."
468                                 , CHECK_RATE_THRESHOLD);
469                 return DEVICE_ERROR_OPERATION_FAILED;
470 //LCOV_EXCL_STOP
471         }
472
473         if (timeout_ms < 0)
474                 return DEVICE_ERROR_INVALID_PARAMETER;
475
476         if (type == POWER_LOCK_CPU) {
477                 ret = lock_state(DISPLAY_STATE_SCREEN_OFF, STAY_CUR_STATE, timeout_ms);
478                 if (ret == 0 &&
479                         (timeout_ms == 0 || timeout_ms > lock_timeout.release))
480                         add_off_lock_timeout();
481                 power_locked_CPU = true;
482         } else if (type == POWER_LOCK_DISPLAY) {
483                 ret = lock_state(DISPLAY_STATE_NORMAL, STAY_CUR_STATE, timeout_ms);
484                 power_locked_DISPLAY = true;
485         }
486         else if (type == POWER_LOCK_DISPLAY_DIM) {
487                 ret = lock_state(DISPLAY_STATE_SCREEN_DIM, STAY_CUR_STATE, timeout_ms);
488                 power_locked_DISPLAY_DIM = true;
489         }
490         else
491                 return DEVICE_ERROR_INVALID_PARAMETER;
492
493         return errno_to_device_error(ret);
494 }
495
496 int device_power_release_lock(power_lock_e type)
497 {
498         int ret_val;
499
500         _I("power_lock_e = %d", type);
501
502         if (!is_feature_display_supported() || !is_feature_display_state_supported()) {
503                 if ((type == POWER_LOCK_DISPLAY) || (type == POWER_LOCK_DISPLAY_DIM)) {
504                         return DEVICE_ERROR_NOT_SUPPORTED;
505                 } else if (type == POWER_LOCK_CPU) {
506                         /* in case of headless, request for cpu unlock is handled
507                          * in power module of deviced, not in display module */
508                         ret_val = gdbus_call_sync_with_reply_int(DEVICED_BUS_NAME,
509                                 DEVICED_PATH_POWER, DEVICED_INTERFACE_POWER,
510                                 "UnlockCpu", NULL, NULL);
511                         return errno_to_device_error(ret_val);
512                 }
513         }
514
515         if (type == POWER_LOCK_CPU && power_locked_CPU) {
516                 ret_val = unlock_state(DISPLAY_STATE_SCREEN_OFF, PM_SLEEP_MARGIN);
517                 if (ret_val == 0) {
518                         remove_off_lock_timeout();
519                         remove_padding_timeout();
520                         power_locked_CPU = false;
521                 }
522         } else if (type == POWER_LOCK_DISPLAY && power_locked_DISPLAY) {
523                 ret_val = unlock_state(DISPLAY_STATE_NORMAL, PM_KEEP_TIMER);
524                 if (ret_val == 0) power_locked_DISPLAY = false;
525         }
526         else if (type == POWER_LOCK_DISPLAY_DIM && power_locked_DISPLAY_DIM) {
527                 ret_val = unlock_state(DISPLAY_STATE_SCREEN_DIM, PM_KEEP_TIMER);
528                 if (ret_val == 0) power_locked_DISPLAY_DIM = false;
529         }
530         else
531                 return DEVICE_ERROR_INVALID_PARAMETER;
532
533         return errno_to_device_error(ret_val);
534 }
535
536 int device_power_wakeup(bool dim)
537 {
538         int ret_val;
539
540         ret_val = is_feature_display_state_supported();
541         if (!ret_val)
542                 return DEVICE_ERROR_NOT_SUPPORTED;
543
544         if (dim)
545                 return device_display_change_state(DISPLAY_STATE_SCREEN_DIM);
546
547         return device_display_change_state(DISPLAY_STATE_NORMAL);
548 }
549
550 //LCOV_EXCL_START Not available to test (Reboot during TCT)
551 int device_power_reboot(const char *reason)
552 {
553         const char *method;
554         GVariant *param;
555         int ret_dbus;
556
557         if (reason) {
558                 method = METHOD_POWEROFF_WITH_OPTION;
559                 param = g_variant_new("(ss)", TYPE_REBOOT, reason);
560         } else {
561                 method = METHOD_POWEROFF;
562                 param = g_variant_new("(s)", TYPE_REBOOT);
563         }
564
565         ret_dbus = gdbus_call_sync_with_reply_int(DEVICED_BUS_NAME,
566                         DEVICED_PATH_POWEROFF,
567                         DEVICED_INTERFACE_POWEROFF,
568                         method, param, NULL);
569         return errno_to_device_error(ret_dbus);
570 }
571 //LCOV_EXCL_STOP
572
573 //LCOV_EXCL_START Not available to test (Reboot during TCT)
574 int device_power_poweroff(void)
575 {
576         int ret_dbus;
577
578         ret_dbus = gdbus_call_sync_with_reply_int(DEVICED_BUS_NAME,
579                         DEVICED_PATH_POWEROFF, DEVICED_INTERFACE_POWEROFF,
580                         METHOD_POWEROFF, g_variant_new("(s)", TYPE_POWEROFF),
581                         NULL);
582
583         return errno_to_device_error(ret_dbus);
584 }
585 //LCOV_EXCL_STOP
586
587 static int power_load_config(struct parse_result *result, void *data)
588 {
589         unsigned int value;
590
591         if (!result)
592                 return 0;
593
594         if (strncmp(result->section, "Power", 6))
595                 return 0;
596
597         value = strtoul(result->value, NULL, 10);
598         if (value == 0)
599                 return 0;
600
601         if (!strncmp(result->name, "LockTimeout", 12)) {
602                 _I("Power Lock Auto Release Timeout: %u msec", value);
603                 lock_timeout.release = value;
604         } else if (!strncmp(result->name, "LockPaddingTimeout", 12)) {
605                 _I("Power Lock Auto Release Padding Timeout: %u msec", value);
606                 lock_timeout.padding = value;
607         }
608
609         return 0;
610 }
611
612 static void __CONSTRUCTOR__ power_init(void)
613 {
614         int ret_val;
615
616         ret_val = config_parse(POWER_CONF, power_load_config, NULL);
617         if (ret_val < 0)
618                 _E("Failed to load config file (%d)", ret_val); //LCOV_EXCL_LINE Logs
619 }