4 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 * @file display-lock.c
21 * @brief Manage each state's lock. Lock blocks state transition by timeout.
24 #include "shared/common.h"
25 #include "device-interface.h"
27 //#include "display.h"
28 #include "display-signal.h"
29 #include "lock-detector.h"
30 #include "display-lock.h"
31 #include "display-panel.h"
32 #include "display-config.h"
33 #include "display-state-transition.h"
34 #include "shared/log-macro.h"
35 #include "shared/apps.h"
37 #define METHOD_APP_STATUS "CheckAppStatus"
39 #define LOCK_TIME_WARNING 60 /* 60 seconds */
41 #define no_foreground_lock(st) (check_lock_state(st) == false)
43 static GList *cond_head[S_END];
44 static int trans_condition;
45 static bool custom_holdkey_block = false;
47 bool check_lock_state(int state)
52 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
53 if (t->app_background == false)
60 bool pmlock_get_lock_state(enum state_t state)
62 return (bool)SYS_G_LIST_LENGTH(cond_head[state]);
65 enum state_t power_lock_type_to_pmlock(power_lock_e power_lock_type)
67 switch(power_lock_type) {
70 case POWER_LOCK_DISPLAY:
72 case POWER_LOCK_DISPLAY_DIM:
79 static void refresh_app_cond(void)
83 if (check_lock_state(S_NORMAL))
84 trans_condition |= MASK_NORMAL;
85 if (check_lock_state(S_LCDDIM))
86 trans_condition |= MASK_DIM;
87 if (check_lock_state(S_LCDOFF))
88 trans_condition |= MASK_OFF;
91 static void broadcast_pmlock_state_changed(enum state_t state)
104 num_of_pmlock = g_list_length(cond_head[state]);
105 if (num_of_pmlock > 1)
108 gdbus_signal_emit(NULL,
109 DEVICED_PATH_DISPLAY,
110 DEVICED_INTERFACE_DISPLAY,
111 DEVICED_SIGNAL_POWER_LOCK_STATE_CHANGED,
112 g_variant_new("(ii)", state, num_of_pmlock));
115 static void broadcast_pmlock_expired(pid_t pid, enum state_t state, const char* appid, time_t locktime)
120 const char* state_name = NULL;
123 diff = difftime(now, locktime);
125 display_plugin_state_get_name(state, &state_name);
126 CRITICAL_LOG("%s(%d) has held %s lock for a long time(%.0f s).",
127 appid ? appid : "NULL", pid, state_name + 2, diff);
129 ret = gdbus_signal_emit(NULL,
130 DEVICED_PATH_DISPLAY,
131 DEVICED_INTERFACE_DISPLAY,
133 g_variant_new("(ii)", pid, (int)diff));
135 _E("Failed to send dbus pmlock_expired");
138 static void default_pmlock_check_cb(GVariant *var, void *user_data, GError *err)
143 enum state_t state = (enum state_t) user_data;
149 if (!g_variant_get_safe(var, "(iis)", &pid, &detected, &app_id)) {
150 _E("Failed to get params from gvariant. expected:%s, type:%s", "(iis)", g_variant_get_type_string(var));
157 node = find_node(state, pid);
161 broadcast_pmlock_expired(pid, state, app_id, node->time);
166 g_variant_unref(var);
169 static gboolean default_pmlock_check(void *data)
178 _E("Invalid parameter.");
179 return G_SOURCE_REMOVE;
182 node = (PmLockNode *) data;
186 if (state == S_LCDOFF && display_panel_get_dpms_cached_state() == DPMS_ON) {
187 _D("Lcd state is PM_LCD_POWER_ON");
188 return G_SOURCE_CONTINUE;
191 /* Stop checking lock if process had been terminated */
192 if (kill(pid, 0) == -1) {
193 node = find_node(state, pid);
194 del_node(state, node);
195 _I("Process %d not found. Stop checking lock.", pid);
196 return G_SOURCE_REMOVE;
200 /* For (non-killable) daemon,
201 * no need to ask resourced if it is abnormal lock */
202 broadcast_pmlock_expired(pid, state, NULL, node->time);
203 return G_SOURCE_CONTINUE;
217 _E("Invalid state.");
218 return G_SOURCE_REMOVE;
221 ret = gdbus_call_async_with_reply(RESOURCED_BUS_NAME,
222 RESOURCED_PATH_PROCESS,
223 RESOURCED_INTERFACE_PROCESS,
225 g_variant_new("(is)", pid, str),
226 default_pmlock_check_cb, -1, (void *)(intptr_t)state);
228 _E("Failed to call dbus method");
230 return G_SOURCE_CONTINUE;
234 PmLockNode *find_node(enum state_t s_index, pid_t pid)
237 PmLockNode *t = NULL;
239 SYS_G_LIST_FOREACH(cond_head[s_index], elem, t) {
247 PmLockNode *add_node(enum state_t s_index, pid_t pid, guint timeout_id,
253 assert(g_display_plugin.config);
255 n = (PmLockNode *) calloc(1, sizeof(PmLockNode));
257 _E("Not enough memory, failed to alloc lock node.");
264 n->timeout_id = timeout_id;
266 n->holdkey_block = holdkey_block;
267 /* LCDOFF lock should be maintained regardless of fg/bg state */
268 if (n->state == S_NORMAL || n->state == S_LCDDIM) {
269 n->app_background = is_app_background(n->pid);
270 if (n->app_background)
271 _W("App(%d) requested %d lock in background.", n->pid, n->state);
273 n->broadcast_warning = true;
275 if (pid < INTERNAL_LOCK_BASE) {
276 /* check if this lock node needs custom-defined lock checker.
277 * n->warning_id would be 0 if fails to register the checker,
278 * or there is no need to use that checker */
279 if (g_display_plugin.config->pmlock_check)
280 n->warning_id = g_display_plugin.config->pmlock_check(n);
282 /* use default lock checker */
284 n->warning_id = g_timeout_add_seconds(g_display_plugin.config->lockcheck_timeout,
285 default_pmlock_check, (gpointer)n);
288 SYS_G_LIST_APPEND(cond_head[s_index], n);
292 broadcast_pmlock_state_changed(s_index);
297 int del_node(enum state_t s_index, PmLockNode *n)
302 SYS_G_LIST_REMOVE(cond_head[s_index], n);
306 g_source_remove(n->timeout_id);
310 g_source_remove(n->warning_id);
313 if (n->warning_param) {
314 g_variant_unref(n->warning_param);
315 n->warning_param = NULL;
318 broadcast_pmlock_state_changed(s_index);
325 int check_lock_condition(enum state_t state)
328 PmLockNode *t = NULL;
330 pid_t owner = getpid();
331 const char *state_name = NULL;
333 display_plugin_state_get_name(state, &state_name);
334 _D("check holdkey block : state of %s", state_name);
336 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
337 if (t->pid != owner && t->app_background == false) {
339 _I("state change was blocked by pid(%d)!", t->pid);
347 int check_holdkey_block(enum state_t state)
350 PmLockNode *t = NULL;
352 const char *state_name = NULL;
354 display_plugin_state_get_name(state, &state_name);
355 _I("Check holdkey block: state of %s", state_name);
357 if (custom_holdkey_block == true) {
358 _I("Custom hold key blocked");
362 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
363 if (t->holdkey_block == true) {
365 _I("Hold key blocked by pid(%d).", t->pid);
373 int delete_condition(enum state_t state)
376 PmLockNode *t = NULL;
377 const char *state_name = NULL;
379 display_plugin_state_get_name(state, &state_name);
380 _I("delete condition : state of %s", state_name);
382 if (!cond_head[state])
385 SYS_G_LIST_FOREACH_SAFE(cond_head[state], elem, next, t) {
386 if (t->timeout_id > 0) {
387 g_source_remove(t->timeout_id);
390 if (state == S_LCDOFF)
391 set_process_active(false, t->pid);
392 _I("delete node of pid(%d)", t->pid);
393 set_unlock_time(t->pid, state);
397 SYS_G_LIST_FREE_LIST(cond_head[state]);
398 cond_head[state] = NULL;
400 broadcast_pmlock_state_changed(state);
405 void print_node(int next)
414 if (next <= S_START || next >= S_END)
418 SYS_G_LIST_FOREACH(cond_head[next], elem, n) {
419 diff = difftime(now, n->time);
420 ctime_r(&n->time, buf);
421 buf[strlen(buf) - 1] = 0;
423 if (diff > LOCK_TIME_WARNING) {
424 if (diff > LOCK_TIME_WARNING * 60 && n->pid < INTERNAL_LOCK_BASE && n->broadcast_warning) {
425 ret = gdbus_signal_emit(NULL,
426 DEVICED_PATH_DISPLAY,
427 DEVICED_INTERFACE_DISPLAY,
429 g_variant_new("(i)", n->pid));
431 _E("Failed to send dbus signal pmlock_over.");
432 n->broadcast_warning = false;
434 _W("Over(%.0f s) pid( %5d) lock time(%s)", diff, n->pid, buf);
436 _I("Pid(%5d) lock time(%s)", n->pid, buf);
440 void makeup_trans_condition(void)
442 check_processes(S_NORMAL);
443 check_processes(S_LCDDIM);
444 check_processes(S_LCDOFF);
448 /* If a lock node in the given state had been dead, delete it form lock list,
449 * and returns 1, else returns 0 */
450 int check_processes(enum state_t prohibit_state)
453 PmLockNode *t = NULL;
456 SYS_G_LIST_FOREACH_SAFE(cond_head[prohibit_state], elem, next, t) {
457 if (t->pid == -1 || (t->pid < INTERNAL_LOCK_BASE && kill(t->pid, 0) == -1)) {
458 _E("%d process does not exist, delete the REQ"
459 " - prohibit state %d ",
460 t->pid, prohibit_state);
462 set_unlock_time(t->pid, prohibit_state);
463 del_node(prohibit_state, t);
470 int get_trans_condition(void)
472 return trans_condition;
475 GList *get_cond_head(enum state_t s_index)
477 return cond_head[s_index];
480 int display_app_background(void *data)
485 pid = *(pid_t *)data;
487 node = find_node(S_NORMAL, pid);
489 node->app_background = true;
490 _I("App(%d) goes background. LCD_NORMAL will be unlocked.", pid);
493 node = find_node(S_LCDDIM, pid);
495 node->app_background = true;
496 _I("App(%d) goes background. LCD_DIM will be unlocked.", pid);
502 int display_app_foreground(void *data)
507 pid = *(pid_t *)data;
509 node = find_node(S_NORMAL, pid);
511 node->app_background = false;
512 _I("App(%d) goes foreground. LCD_NORMAL will be locked.", pid);
515 node = find_node(S_LCDDIM, pid);
517 node->app_background = false;
518 _I("App(%d) goes foreground. LCD_DIM will be locked.", pid);
524 int display_app_terminated(void *data)
529 enum state_t cur = get_pm_cur_state();
530 bool current_unlocked = false;
532 pid = *(pid_t *)data;
534 /* release lock if it is holding locks */
535 for (state = S_START; state < S_END; ++state) {
536 node = find_node(state, pid);
538 _W("App=%d is terminated without releasing lockstate=%d. deviced unlocks it.", pid, state);
539 del_node(state, node);
540 set_unlock_time(pid, state);
542 current_unlocked = true;
546 /* Change state only when all three conditions below are satisfied.
547 * 1. the lock released due to the termination of the application
548 * must be a lock for the current state
549 * 2. after releasing the lock, there is no foreground lock
550 * for the current state
551 * 3. there should be no running state-transition timer
553 * This emulates already expired transition timer */
554 if (current_unlocked && no_foreground_lock(cur) && !display_state_transition_is_there_state_transition_timer())
555 display_state_transition_reset_state_transition_timeout(0);
560 /* update transition condition for application requrements */
561 void update_lock_timer(PMMsg *data,
562 PmLockNode *node, guint timeout_id)
566 if (data->timeout > 0) {
571 if (node->timeout_id)
572 g_source_remove(node->timeout_id);
573 node->timeout_id = timeout_id;
576 int display_lock_set_custom_holdkey_block(bool blocked)
578 custom_holdkey_block = blocked;
582 int display_lock_get_custom_holdkey_block(bool *blocked)
587 *blocked = custom_holdkey_block;