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_transition_timer() (get_transition_timer() == 0)
42 #define no_foreground_lock(st) (check_lock_state(st) == false)
44 static GList *cond_head[S_END];
45 static int trans_condition;
46 static bool custom_holdkey_block = false;
48 bool check_lock_state(int state)
53 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
54 if (t->app_background == false)
61 bool pmlock_get_lock_state(enum state_t state)
63 return (bool)SYS_G_LIST_LENGTH(cond_head[state]);
66 enum state_t power_lock_type_to_pmlock(power_lock_e power_lock_type)
68 switch(power_lock_type) {
71 case POWER_LOCK_DISPLAY:
73 case POWER_LOCK_DISPLAY_DIM:
80 static void refresh_app_cond(void)
84 if (check_lock_state(S_NORMAL))
85 trans_condition |= MASK_NORMAL;
86 if (check_lock_state(S_LCDDIM))
87 trans_condition |= MASK_DIM;
88 if (check_lock_state(S_LCDOFF))
89 trans_condition |= MASK_OFF;
92 static void broadcast_pmlock_state_changed(enum state_t state)
105 num_of_pmlock = g_list_length(cond_head[state]);
106 if (num_of_pmlock > 1)
109 gdbus_signal_emit(NULL,
110 DEVICED_PATH_DISPLAY,
111 DEVICED_INTERFACE_DISPLAY,
112 DEVICED_SIGNAL_POWER_LOCK_STATE_CHANGED,
113 g_variant_new("(ii)", state, num_of_pmlock));
116 static void broadcast_pmlock_expired(pid_t pid, enum state_t state, const char* appid, time_t locktime)
121 const char* state_name = NULL;
124 diff = difftime(now, locktime);
126 display_plugin_state_get_name(state, &state_name);
127 CRITICAL_LOG("%s(%d) has held %s lock for a long time(%.0f s).",
128 appid ? appid : "NULL", pid, state_name + 2, diff);
130 ret = gdbus_signal_emit(NULL,
131 DEVICED_PATH_DISPLAY,
132 DEVICED_INTERFACE_DISPLAY,
134 g_variant_new("(ii)", pid, (int)diff));
136 _E("Failed to send dbus pmlock_expired");
139 static void default_pmlock_check_cb(GVariant *var, void *user_data, GError *err)
144 enum state_t state = (enum state_t) user_data;
150 if (!g_variant_get_safe(var, "(iis)", &pid, &detected, &app_id)) {
151 _E("Failed to get params from gvariant. expected:%s, type:%s", "(iis)", g_variant_get_type_string(var));
158 node = find_node(state, pid);
162 broadcast_pmlock_expired(pid, state, app_id, node->time);
167 g_variant_unref(var);
170 static gboolean default_pmlock_check(void *data)
179 _E("Invalid parameter.");
180 return G_SOURCE_REMOVE;
183 node = (PmLockNode *) data;
187 if (state == S_LCDOFF && display_panel_get_dpms_cached_state() == DPMS_ON) {
188 _D("Lcd state is PM_LCD_POWER_ON");
189 return G_SOURCE_CONTINUE;
192 /* Stop checking lock if process had been terminated */
193 if (kill(pid, 0) == -1) {
194 node = find_node(state, pid);
195 del_node(state, node);
196 _I("Process %d not found. Stop checking lock.", pid);
197 return G_SOURCE_REMOVE;
201 /* For (non-killable) daemon,
202 * no need to ask resourced if it is abnormal lock */
203 broadcast_pmlock_expired(pid, state, NULL, node->time);
204 return G_SOURCE_CONTINUE;
218 _E("Invalid state.");
219 return G_SOURCE_REMOVE;
222 ret = gdbus_call_async_with_reply(RESOURCED_BUS_NAME,
223 RESOURCED_PATH_PROCESS,
224 RESOURCED_INTERFACE_PROCESS,
226 g_variant_new("(is)", pid, str),
227 default_pmlock_check_cb, -1, (void *)(intptr_t)state);
229 _E("Failed to call dbus method");
231 return G_SOURCE_CONTINUE;
235 PmLockNode *find_node(enum state_t s_index, pid_t pid)
238 PmLockNode *t = NULL;
240 SYS_G_LIST_FOREACH(cond_head[s_index], elem, t) {
248 PmLockNode *add_node(enum state_t s_index, pid_t pid, guint timeout_id,
254 assert(g_display_plugin.config);
256 n = (PmLockNode *) calloc(1, sizeof(PmLockNode));
258 _E("Not enough memory, failed to alloc lock node.");
265 n->timeout_id = timeout_id;
267 n->holdkey_block = holdkey_block;
268 /* LCDOFF lock should be maintained regardless of fg/bg state */
269 if (n->state == S_NORMAL || n->state == S_LCDDIM) {
270 n->app_background = is_app_background(n->pid);
271 if (n->app_background)
272 _W("App(%d) requested %d lock in background.", n->pid, n->state);
274 n->broadcast_warning = true;
276 if (pid < INTERNAL_LOCK_BASE) {
277 /* check if this lock node needs custom-defined lock checker.
278 * n->warning_id would be 0 if fails to register the checker,
279 * or there is no need to use that checker */
280 if (g_display_plugin.config->pmlock_check)
281 n->warning_id = g_display_plugin.config->pmlock_check(n);
283 /* use default lock checker */
285 n->warning_id = g_timeout_add_seconds(g_display_plugin.config->lockcheck_timeout,
286 default_pmlock_check, (gpointer)n);
289 SYS_G_LIST_APPEND(cond_head[s_index], n);
293 broadcast_pmlock_state_changed(s_index);
298 int del_node(enum state_t s_index, PmLockNode *n)
303 SYS_G_LIST_REMOVE(cond_head[s_index], n);
307 g_source_remove(n->timeout_id);
311 g_source_remove(n->warning_id);
314 if (n->warning_param) {
315 g_variant_unref(n->warning_param);
316 n->warning_param = NULL;
319 broadcast_pmlock_state_changed(s_index);
326 int check_lock_condition(enum state_t state)
329 PmLockNode *t = NULL;
331 pid_t owner = getpid();
332 const char *state_name = NULL;
334 display_plugin_state_get_name(state, &state_name);
335 _D("check holdkey block : state of %s", state_name);
337 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
338 if (t->pid != owner && t->app_background == false) {
340 _I("state change was blocked by pid(%d)!", t->pid);
348 int check_holdkey_block(enum state_t state)
351 PmLockNode *t = NULL;
353 const char *state_name = NULL;
355 display_plugin_state_get_name(state, &state_name);
356 _I("Check holdkey block: state of %s", state_name);
358 if (custom_holdkey_block == true) {
359 _I("Custom hold key blocked");
363 SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
364 if (t->holdkey_block == true) {
366 _I("Hold key blocked by pid(%d).", t->pid);
374 int delete_condition(enum state_t state)
377 PmLockNode *t = NULL;
378 const char *state_name = NULL;
380 display_plugin_state_get_name(state, &state_name);
381 _I("delete condition : state of %s", state_name);
383 if (!cond_head[state])
386 SYS_G_LIST_FOREACH_SAFE(cond_head[state], elem, next, t) {
387 if (t->timeout_id > 0) {
388 g_source_remove(t->timeout_id);
391 if (state == S_LCDOFF)
392 set_process_active(false, t->pid);
393 _I("delete node of pid(%d)", t->pid);
394 set_unlock_time(t->pid, state);
398 SYS_G_LIST_FREE_LIST(cond_head[state]);
399 cond_head[state] = NULL;
401 broadcast_pmlock_state_changed(state);
406 void print_node(int next)
415 if (next <= S_START || next >= S_END)
419 SYS_G_LIST_FOREACH(cond_head[next], elem, n) {
420 diff = difftime(now, n->time);
421 ctime_r(&n->time, buf);
422 buf[strlen(buf) - 1] = 0;
424 if (diff > LOCK_TIME_WARNING) {
425 if (diff > LOCK_TIME_WARNING * 60 && n->pid < INTERNAL_LOCK_BASE && n->broadcast_warning) {
426 ret = gdbus_signal_emit(NULL,
427 DEVICED_PATH_DISPLAY,
428 DEVICED_INTERFACE_DISPLAY,
430 g_variant_new("(i)", n->pid));
432 _E("Failed to send dbus signal pmlock_over.");
433 n->broadcast_warning = false;
435 _W("Over(%.0f s) pid( %5d) lock time(%s)", diff, n->pid, buf);
437 _I("Pid(%5d) lock time(%s)", n->pid, buf);
441 void makeup_trans_condition(void)
443 check_processes(S_NORMAL);
444 check_processes(S_LCDDIM);
445 check_processes(S_LCDOFF);
449 /* If a lock node in the given state had been dead, delete it form lock list,
450 * and returns 1, else returns 0 */
451 int check_processes(enum state_t prohibit_state)
454 PmLockNode *t = NULL;
457 SYS_G_LIST_FOREACH_SAFE(cond_head[prohibit_state], elem, next, t) {
458 if (t->pid == -1 || (t->pid < INTERNAL_LOCK_BASE && kill(t->pid, 0) == -1)) {
459 _E("%d process does not exist, delete the REQ"
460 " - prohibit state %d ",
461 t->pid, prohibit_state);
463 set_unlock_time(t->pid, prohibit_state);
464 del_node(prohibit_state, t);
471 int get_trans_condition(void)
473 return trans_condition;
476 GList *get_cond_head(enum state_t s_index)
478 return cond_head[s_index];
481 int display_app_background(void *data)
486 pid = *(pid_t *)data;
488 node = find_node(S_NORMAL, pid);
490 node->app_background = true;
491 _I("App(%d) goes background. LCD_NORMAL will be unlocked.", pid);
494 node = find_node(S_LCDDIM, pid);
496 node->app_background = true;
497 _I("App(%d) goes background. LCD_DIM will be unlocked.", pid);
503 int display_app_foreground(void *data)
508 pid = *(pid_t *)data;
510 node = find_node(S_NORMAL, pid);
512 node->app_background = false;
513 _I("App(%d) goes foreground. LCD_NORMAL will be locked.", pid);
516 node = find_node(S_LCDDIM, pid);
518 node->app_background = false;
519 _I("App(%d) goes foreground. LCD_DIM will be locked.", pid);
525 int display_app_terminated(void *data)
530 enum state_t cur = get_pm_cur_state();
531 bool current_unlocked = false;
533 pid = *(pid_t *)data;
535 /* release lock if it is holding locks */
536 for (state = S_START; state < S_END; ++state) {
537 node = find_node(state, pid);
539 _W("App=%d is terminated without releasing lockstate=%d. deviced unlocks it.", pid, state);
540 del_node(state, node);
541 set_unlock_time(pid, state);
543 current_unlocked = true;
547 /* Change state only when all three conditions below are satisfied.
548 * 1. the lock released due to the termination of the application
549 * must be a lock for the current state
550 * 2. after releasing the lock, there is no foreground lock
551 * for the current state
552 * 3. there should be no running state-transition timer
554 * This emulates already expired transition timer */
555 if (current_unlocked && no_foreground_lock(cur) && no_transition_timer())
561 /* update transition condition for application requrements */
562 void update_lock_timer(PMMsg *data,
563 PmLockNode *node, guint timeout_id)
567 if (data->timeout > 0) {
572 if (node->timeout_id)
573 g_source_remove(node->timeout_id);
574 node->timeout_id = timeout_id;
577 int display_lock_set_custom_holdkey_block(bool blocked)
579 custom_holdkey_block = blocked;
583 int display_lock_get_custom_holdkey_block(bool *blocked)
588 *blocked = custom_holdkey_block;