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 "ambient-mode.h"
26 #include "device-interface.h"
28 //#include "display.h"
29 #include "display-signal.h"
30 #include "lock-detector.h"
31 #include "display-lock.h"
32 #include "display-panel.h"
33 #include "display-plugin.h"
34 #include "display-config.h"
35 #include "display-state-transition.h"
36 #include "shared/log-macro.h"
37 #include "shared/apps.h"
39 #define METHOD_APP_STATUS "CheckAppStatus"
41 #define LOCK_TIME_WARNING 60 /* 60 seconds */
43 #define no_foreground_lock(st) (check_lock_state(st) == false)
45 static GList *cond_head[S_END];
46 static int trans_condition;
47 static bool custom_holdkey_block = false;
49 bool check_lock_state(int state)
54 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
55 if (t->app_background == false)
62 bool pmlock_get_lock_state(enum state_t state)
64 return (bool)SYS_G_LIST_LENGTH(cond_head[state]);
67 enum state_t power_lock_type_to_pmlock(power_lock_e power_lock_type)
69 switch(power_lock_type) {
72 case POWER_LOCK_DISPLAY:
74 case POWER_LOCK_DISPLAY_DIM:
81 static void refresh_app_cond(void)
85 if (check_lock_state(S_NORMAL))
86 trans_condition |= MASK_NORMAL;
87 if (check_lock_state(S_LCDDIM))
88 trans_condition |= MASK_DIM;
89 if (check_lock_state(S_LCDOFF))
90 trans_condition |= MASK_OFF;
93 static void broadcast_pmlock_state_changed(enum state_t state)
106 num_of_pmlock = g_list_length(cond_head[state]);
107 if (num_of_pmlock > 1)
110 gdbus_signal_emit(NULL,
111 DEVICED_PATH_DISPLAY,
112 DEVICED_INTERFACE_DISPLAY,
113 DEVICED_SIGNAL_POWER_LOCK_STATE_CHANGED,
114 g_variant_new("(ii)", state, num_of_pmlock));
117 static void broadcast_pmlock_expired(pid_t pid, enum state_t state, const char* appid, time_t locktime)
122 const char* state_name = NULL;
125 diff = difftime(now, locktime);
127 display_plugin_state_get_name(state, &state_name);
128 CRITICAL_LOG("%s(%d) has held %s lock for a long time(%.0f s).",
129 appid ? appid : "NULL", pid, state_name + 2, diff);
131 ret = gdbus_signal_emit(NULL,
132 DEVICED_PATH_DISPLAY,
133 DEVICED_INTERFACE_DISPLAY,
135 g_variant_new("(ii)", pid, (int)diff));
137 _E("Failed to send dbus pmlock_expired");
140 static void default_pmlock_check_cb(GVariant *var, void *user_data, GError *err)
145 enum state_t state = (enum state_t) user_data;
151 if (!g_variant_get_safe(var, "(iis)", &pid, &detected, &app_id)) {
152 _E("Failed to get params from gvariant. expected:%s, type:%s", "(iis)", g_variant_get_type_string(var));
159 node = find_node(state, pid);
163 broadcast_pmlock_expired(pid, state, app_id, node->time);
168 g_variant_unref(var);
171 static gboolean default_pmlock_check(void *data)
180 _E("Invalid parameter.");
181 return G_SOURCE_REMOVE;
184 node = (PmLockNode *) data;
188 if (state == S_LCDOFF && display_panel_get_dpms_cached_state() == DPMS_ON) {
189 _D("Lcd state is PM_LCD_POWER_ON");
190 return G_SOURCE_CONTINUE;
193 /* Stop checking lock if process had been terminated */
194 if (kill(pid, 0) == -1) {
195 node = find_node(state, pid);
196 del_node(state, node);
197 _I("Process %d not found. Stop checking lock.", pid);
198 return G_SOURCE_REMOVE;
202 /* For (non-killable) daemon,
203 * no need to ask resourced if it is abnormal lock */
204 broadcast_pmlock_expired(pid, state, NULL, node->time);
205 return G_SOURCE_CONTINUE;
219 _E("Invalid state.");
220 return G_SOURCE_REMOVE;
223 ret = gdbus_call_async_with_reply(RESOURCED_BUS_NAME,
224 RESOURCED_PATH_PROCESS,
225 RESOURCED_INTERFACE_PROCESS,
227 g_variant_new("(is)", pid, str),
228 default_pmlock_check_cb, -1, (void *)(intptr_t)state);
230 _E("Failed to call dbus method");
232 return G_SOURCE_CONTINUE;
236 PmLockNode *find_node(enum state_t s_index, pid_t pid)
239 PmLockNode *t = NULL;
241 SYS_G_LIST_FOREACH(cond_head[s_index], elem, t) {
249 /* FIXME: ambient call should handled later, to be removed or changed plugin interface way */
250 static gboolean delete_state_cond_callback(void *data)
253 return G_SOURCE_REMOVE;
255 PmLockNode *tmp = (PmLockNode *)data;
256 const char *state_name = NULL;
258 enum state_t state = tmp->state;
261 /* A passed data is a pid_t type data, not a 64bit data. */
262 pid = (pid_t)((intptr_t)tmp->pid);
263 display_plugin_state_get_name(state, &state_name);
264 _I("delete prohibit %s condition by timeout (%d)", state_name, pid);
266 if (pid == INTERNAL_LOCK_AMBIENT)
267 ambient_check_invalid_state(pid);
269 tmp = find_node(state, pid);
270 del_node(state, tmp);
271 set_unlock_time(pid, state);
273 /* Change state only when the two conditions below are satisfied.
274 * 1. There should be no running state-transition timer
275 * 2. Released lock is one of the pm_cur_state's lock
276 * This emulates already expired transition timer */
277 if (!display_state_transition_is_there_state_transition_timer() && (get_pm_cur_state() == state))
278 display_state_transition_do_state_transition(get_pm_cur_state(), EVENT_TIMEOUT);
280 if (state == S_LCDOFF)
281 set_process_active(false, pid);
283 return G_SOURCE_REMOVE;
286 static void remove_pmlock_node_timeout_callback_id(PmLockNode *target_node)
291 if (target_node->timeout_id)
292 g_source_remove(target_node->timeout_id);
293 target_node->timeout_id = 0;
296 static int add_delete_state_cond_callback(unsigned int timeout, PmLockNode *target_node)
301 if (target_node->state < S_NORMAL || target_node->state > S_LCDOFF)
305 remove_pmlock_node_timeout_callback_id(target_node);
309 assert(target_node->timeout_id == 0);
310 target_node->timeout_id = g_timeout_add(timeout, delete_state_cond_callback, (void*)target_node);
311 if (!(target_node->timeout_id))
312 _W("Failed to register display timer");
317 static void handle_existing_pmlock_node(bool holdkey_block, unsigned int timeout, PmLockNode *found_node)
321 remove_pmlock_node_timeout_callback_id(found_node);
323 add_delete_state_cond_callback(timeout, found_node);
326 found_node->time = now;
327 found_node->holdkey_block = holdkey_block;
330 PmLockNode *display_lock_add_pmlock_node(enum state_t s_index, pid_t pid, bool holdkey_block, unsigned int timeout)
333 PmLockNode *found_node;
336 assert(g_display_plugin.config);
338 found_node = find_node(s_index, pid);
340 handle_existing_pmlock_node(holdkey_block, timeout, found_node);
344 n = (PmLockNode *) calloc(1, sizeof(PmLockNode));
346 _E("Not enough memory, failed to alloc lock node.");
353 add_delete_state_cond_callback(timeout, n);
355 n->holdkey_block = holdkey_block;
356 /* LCDOFF lock should be maintained regardless of fg/bg state */
357 if (n->state == S_NORMAL || n->state == S_LCDDIM) {
358 n->app_background = is_app_background(n->pid);
359 if (n->app_background)
360 _W("App(%d) requested %d lock in background.", n->pid, n->state);
362 n->broadcast_warning = true;
364 if (pid < INTERNAL_LOCK_BASE) {
365 /* check if this lock node needs custom-defined lock checker.
366 * n->warning_id would be 0 if fails to register the checker,
367 * or there is no need to use that checker */
368 if (g_display_plugin.config->pmlock_check)
369 n->warning_id = g_display_plugin.config->pmlock_check(n);
371 /* use default lock checker */
373 n->warning_id = g_timeout_add_seconds(g_display_plugin.config->lockcheck_timeout,
374 default_pmlock_check, (gpointer)n);
377 SYS_G_LIST_APPEND(cond_head[s_index], n);
381 broadcast_pmlock_state_changed(s_index);
386 int del_node(enum state_t s_index, PmLockNode *n)
391 SYS_G_LIST_REMOVE(cond_head[s_index], n);
394 remove_pmlock_node_timeout_callback_id(n);
397 g_source_remove(n->warning_id);
400 if (n->warning_param) {
401 g_variant_unref(n->warning_param);
402 n->warning_param = NULL;
405 broadcast_pmlock_state_changed(s_index);
412 int check_lock_condition(enum state_t state)
415 PmLockNode *t = NULL;
417 pid_t owner = getpid();
418 const char *state_name = NULL;
420 display_plugin_state_get_name(state, &state_name);
421 _D("check holdkey block : state of %s", state_name);
423 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
424 if (t->pid != owner && t->app_background == false) {
426 _I("state change was blocked by pid(%d)!", t->pid);
434 int check_holdkey_block(enum state_t state)
437 PmLockNode *t = NULL;
439 const char *state_name = NULL;
441 display_plugin_state_get_name(state, &state_name);
442 _I("Check holdkey block: state of %s", state_name);
444 if (custom_holdkey_block == true) {
445 _I("Custom hold key blocked");
449 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
450 if (t->holdkey_block == true) {
452 _I("Hold key blocked by pid(%d).", t->pid);
460 int delete_condition(enum state_t state)
463 PmLockNode *t = NULL;
464 const char *state_name = NULL;
466 display_plugin_state_get_name(state, &state_name);
467 _I("delete condition : state of %s", state_name);
469 if (!cond_head[state])
472 SYS_G_LIST_FOREACH_SAFE(cond_head[state], elem, next, t) {
473 remove_pmlock_node_timeout_callback_id(t);
474 if (state == S_LCDOFF)
475 set_process_active(false, t->pid);
476 _I("delete node of pid(%d)", t->pid);
477 set_unlock_time(t->pid, state);
481 SYS_G_LIST_FREE_LIST(cond_head[state]);
482 cond_head[state] = NULL;
484 broadcast_pmlock_state_changed(state);
489 void print_node(int next)
498 if (next <= S_START || next >= S_END)
502 SYS_G_LIST_FOREACH(cond_head[next], elem, n) {
503 diff = difftime(now, n->time);
504 ctime_r(&n->time, buf);
505 buf[strlen(buf) - 1] = 0;
507 if (diff > LOCK_TIME_WARNING) {
508 if (diff > LOCK_TIME_WARNING * 60 && n->pid < INTERNAL_LOCK_BASE && n->broadcast_warning) {
509 ret = gdbus_signal_emit(NULL,
510 DEVICED_PATH_DISPLAY,
511 DEVICED_INTERFACE_DISPLAY,
513 g_variant_new("(i)", n->pid));
515 _E("Failed to send dbus signal pmlock_over.");
516 n->broadcast_warning = false;
518 _W("Over(%.0f s) pid( %5d) lock time(%s)", diff, n->pid, buf);
520 _I("Pid(%5d) lock time(%s)", n->pid, buf);
524 void makeup_trans_condition(void)
526 check_processes(S_NORMAL);
527 check_processes(S_LCDDIM);
528 check_processes(S_LCDOFF);
532 /* If a lock node in the given state had been dead, delete it form lock list,
533 * and returns 1, else returns 0 */
534 int check_processes(enum state_t prohibit_state)
537 PmLockNode *t = NULL;
540 SYS_G_LIST_FOREACH_SAFE(cond_head[prohibit_state], elem, next, t) {
541 if (t->pid == -1 || (t->pid < INTERNAL_LOCK_BASE && kill(t->pid, 0) == -1)) {
542 _E("%d process does not exist, delete the REQ"
543 " - prohibit state %d ",
544 t->pid, prohibit_state);
546 set_unlock_time(t->pid, prohibit_state);
547 del_node(prohibit_state, t);
554 int get_trans_condition(void)
556 return trans_condition;
559 GList *get_cond_head(enum state_t s_index)
561 return cond_head[s_index];
564 int display_app_background(void *data)
569 pid = *(pid_t *)data;
571 node = find_node(S_NORMAL, pid);
573 node->app_background = true;
574 _I("App(%d) goes background. LCD_NORMAL will be unlocked.", pid);
577 node = find_node(S_LCDDIM, pid);
579 node->app_background = true;
580 _I("App(%d) goes background. LCD_DIM will be unlocked.", pid);
586 int display_app_foreground(void *data)
591 pid = *(pid_t *)data;
593 node = find_node(S_NORMAL, pid);
595 node->app_background = false;
596 _I("App(%d) goes foreground. LCD_NORMAL will be locked.", pid);
599 node = find_node(S_LCDDIM, pid);
601 node->app_background = false;
602 _I("App(%d) goes foreground. LCD_DIM will be locked.", pid);
608 int display_app_terminated(void *data)
613 enum state_t cur = get_pm_cur_state();
614 bool current_unlocked = false;
616 pid = *(pid_t *)data;
618 /* release lock if it is holding locks */
619 for (state = S_START; state < S_END; ++state) {
620 node = find_node(state, pid);
622 _W("App=%d is terminated without releasing lockstate=%d. deviced unlocks it.", pid, state);
623 del_node(state, node);
624 set_unlock_time(pid, state);
626 current_unlocked = true;
630 /* Change state only when all three conditions below are satisfied.
631 * 1. the lock released due to the termination of the application
632 * must be a lock for the current state
633 * 2. after releasing the lock, there is no foreground lock
634 * for the current state
635 * 3. there should be no running state-transition timer
637 * This emulates already expired transition timer */
638 if (current_unlocked && no_foreground_lock(cur) && !display_state_transition_is_there_state_transition_timer())
639 display_state_transition_reset_state_transition_timeout(0);
644 int display_lock_set_custom_holdkey_block(bool blocked)
646 custom_holdkey_block = blocked;
650 int display_lock_get_custom_holdkey_block(bool *blocked)
655 *blocked = custom_holdkey_block;