display: display-state-transition: Relocate fuctions related to transition timer...
[platform/core/system/deviced.git] / src / display / plugin-common / display-lock.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
5  *
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
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 /**
20  * @file        display-lock.c
21  * @brief       Manage each state's lock. Lock blocks state transition by timeout.
22  *
23  */
24 #include "shared/common.h"
25 #include "device-interface.h"
26 #include "poll.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"
36
37 #define METHOD_APP_STATUS       "CheckAppStatus"
38 #define PID_MAX                 6
39 #define LOCK_TIME_WARNING       60      /* 60 seconds */
40
41 #define no_foreground_lock(st)   (check_lock_state(st) == false)
42
43 static GList *cond_head[S_END];
44 static int trans_condition;
45 static bool custom_holdkey_block = false;
46
47 bool check_lock_state(int state)
48 {
49         GList *elem;
50         PmLockNode *t;
51
52         SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
53                 if (t->app_background == false)
54                         return true;
55         }
56
57         return false;
58 }
59
60 bool pmlock_get_lock_state(enum state_t state)
61 {
62         return (bool)SYS_G_LIST_LENGTH(cond_head[state]);
63 }
64
65 enum state_t power_lock_type_to_pmlock(power_lock_e power_lock_type)
66 {
67         switch(power_lock_type) {
68         case POWER_LOCK_CPU:
69                 return S_LCDOFF;
70         case POWER_LOCK_DISPLAY:
71                 return S_LCDON;
72         case POWER_LOCK_DISPLAY_DIM:
73                 return S_LCDDIM;
74         default:
75                 return S_END;
76         }
77 }
78
79 static void refresh_app_cond(void)
80 {
81         trans_condition = 0;
82
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;
89 }
90
91 static void broadcast_pmlock_state_changed(enum state_t state)
92 {
93         int num_of_pmlock;
94
95         switch(state) {
96         case S_LCDON:
97         case S_LCDDIM:
98         case S_LCDOFF:
99                 break;
100         default:
101                 return;
102         }
103
104         num_of_pmlock = g_list_length(cond_head[state]);
105         if (num_of_pmlock > 1)
106                 return;
107
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));
113 }
114
115 static void broadcast_pmlock_expired(pid_t pid, enum state_t state, const char* appid, time_t locktime)
116 {
117         time_t now;
118         double diff;
119         int ret;
120         const char* state_name = NULL;
121
122         time(&now);
123         diff = difftime(now, locktime);
124
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);
128
129         ret = gdbus_signal_emit(NULL,
130                                                 DEVICED_PATH_DISPLAY,
131                                                 DEVICED_INTERFACE_DISPLAY,
132                                                 "pmlock_expired",
133                                                 g_variant_new("(ii)", pid, (int)diff));
134         if (ret < 0)
135                 _E("Failed to send dbus pmlock_expired");
136 }
137
138 static void default_pmlock_check_cb(GVariant *var, void *user_data, GError *err)
139 {
140         pid_t pid = 0;
141         int detected = 0;
142         char *app_id = NULL;
143         enum state_t state = (enum state_t) user_data;
144         PmLockNode *node;
145
146         if (!var)
147                 return;
148
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));
151                 goto out;
152         }
153
154         if (!detected)
155                 goto out;
156
157         node = find_node(state, pid);
158         if (!node)
159                 goto out;
160
161         broadcast_pmlock_expired(pid, state, app_id, node->time);
162
163 out:
164         if (app_id)
165                 g_free(app_id);
166         g_variant_unref(var);
167 }
168
169 static gboolean default_pmlock_check(void *data)
170 {
171         char *str;
172         PmLockNode *node;
173         enum state_t state;
174         pid_t pid;
175         int ret;
176
177         if (!data) {
178                 _E("Invalid parameter.");
179                 return G_SOURCE_REMOVE;
180         }
181
182         node = (PmLockNode *) data;
183         state = node->state;
184         pid = node->pid;
185
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;
189         }
190
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;
197         }
198
199         if (!is_app(pid)) {
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;
204         }
205
206         switch (state) {
207         case S_NORMAL:
208                 str = "normal";
209                 break;
210         case S_LCDDIM:
211                 str = "lcddim";
212                 break;
213         case S_LCDOFF:
214                 str = "lcdoff";
215                 break;
216         default:
217                 _E("Invalid state.");
218                 return G_SOURCE_REMOVE;
219         }
220
221         ret = gdbus_call_async_with_reply(RESOURCED_BUS_NAME,
222                 RESOURCED_PATH_PROCESS,
223                 RESOURCED_INTERFACE_PROCESS,
224                 METHOD_APP_STATUS,
225                 g_variant_new("(is)", pid, str),
226                 default_pmlock_check_cb, -1, (void *)(intptr_t)state);
227         if (ret < 0)
228                 _E("Failed to call dbus method");
229
230         return G_SOURCE_CONTINUE;
231 }
232
233
234 PmLockNode *find_node(enum state_t s_index, pid_t pid)
235 {
236         GList *elem;
237         PmLockNode *t = NULL;
238
239         SYS_G_LIST_FOREACH(cond_head[s_index], elem, t) {
240                 if (t->pid == pid)
241                         return t;
242         }
243
244         return NULL;
245 }
246
247 PmLockNode *add_node(enum state_t s_index, pid_t pid, guint timeout_id,
248                 bool holdkey_block)
249 {
250         PmLockNode *n;
251         time_t now;
252
253         assert(g_display_plugin.config);
254
255         n = (PmLockNode *) calloc(1, sizeof(PmLockNode));
256         if (n == NULL) {
257                 _E("Not enough memory, failed to alloc lock node.");
258                 return NULL;
259         }
260
261         time(&now);
262         n->state = s_index;
263         n->pid = pid;
264         n->timeout_id = timeout_id;
265         n->time = now;
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);
272         }
273         n->broadcast_warning = true;
274
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);
281
282                 /* use default lock checker */
283                 if (!n->warning_id)
284                         n->warning_id = g_timeout_add_seconds(g_display_plugin.config->lockcheck_timeout,
285                                 default_pmlock_check, (gpointer)n);
286         }
287
288         SYS_G_LIST_APPEND(cond_head[s_index], n);
289
290         refresh_app_cond();
291
292         broadcast_pmlock_state_changed(s_index);
293
294         return n;
295 }
296
297 int del_node(enum state_t s_index, PmLockNode *n)
298 {
299         if (n == NULL)
300                 return 0;
301
302         SYS_G_LIST_REMOVE(cond_head[s_index], n);
303
304         /* delete timer */
305         if (n->timeout_id) {
306                 g_source_remove(n->timeout_id);
307                 n->timeout_id = 0;
308         }
309         if (n->warning_id) {
310                 g_source_remove(n->warning_id);
311                 n->warning_id = 0;
312         }
313         if (n->warning_param) {
314                 g_variant_unref(n->warning_param);
315                 n->warning_param = NULL;
316         }
317
318         broadcast_pmlock_state_changed(s_index);
319
320         free(n);
321         refresh_app_cond();
322         return 0;
323 }
324
325 int check_lock_condition(enum state_t state)
326 {
327         GList *elem;
328         PmLockNode *t = NULL;
329         int ret = false;
330         pid_t owner = getpid();
331         const char *state_name = NULL;
332
333         display_plugin_state_get_name(state, &state_name);
334         _D("check holdkey block : state of %s", state_name);
335
336         SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
337                 if (t->pid != owner && t->app_background == false) {
338                         ret = true;
339                         _I("state change was blocked by pid(%d)!", t->pid);
340                         break;
341                 }
342         }
343
344         return ret;
345 }
346
347 int check_holdkey_block(enum state_t state)
348 {
349         GList *elem;
350         PmLockNode *t = NULL;
351         int ret = 0;
352         const char *state_name = NULL;
353
354         display_plugin_state_get_name(state, &state_name);
355         _I("Check holdkey block: state of %s", state_name);
356
357         if (custom_holdkey_block == true) {
358                 _I("Custom hold key blocked");
359                 return 1;
360         }
361
362         SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
363                 if (t->holdkey_block == true) {
364                         ret = 1;
365                         _I("Hold key blocked by pid(%d).", t->pid);
366                         break;
367                 }
368         }
369
370         return ret;
371 }
372
373 int delete_condition(enum state_t state)
374 {
375         GList *elem, *next;
376         PmLockNode *t = NULL;
377         const char *state_name = NULL;
378
379         display_plugin_state_get_name(state, &state_name);
380         _I("delete condition : state of %s", state_name);
381
382         if (!cond_head[state])
383                 return 0;
384
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);
388                         t->timeout_id = 0;
389                 }
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);
394                 del_node(state, t);
395         }
396
397         SYS_G_LIST_FREE_LIST(cond_head[state]);
398         cond_head[state] = NULL;
399
400         broadcast_pmlock_state_changed(state);
401
402         return 0;
403 }
404
405 void print_node(int next)
406 {
407         int ret;
408         GList *elem;
409         PmLockNode *n;
410         char buf[30];
411         time_t now;
412         double diff;
413
414         if (next <= S_START || next >= S_END)
415                 return;
416
417         time(&now);
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;
422
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,
428                                                                                 "pmlock_over",
429                                                                                 g_variant_new("(i)", n->pid));
430                                 if (ret < 0)
431                                         _E("Failed to send dbus signal pmlock_over.");
432                                 n->broadcast_warning = false;
433                         }
434                         _W("Over(%.0f s) pid( %5d) lock time(%s)", diff, n->pid, buf);
435                 } else
436                         _I("Pid(%5d) lock time(%s)", n->pid, buf);
437         }
438 }
439
440 void makeup_trans_condition(void)
441 {
442         check_processes(S_NORMAL);
443         check_processes(S_LCDDIM);
444         check_processes(S_LCDOFF);
445         refresh_app_cond();
446 }
447
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)
451 {
452         GList *elem, *next;
453         PmLockNode *t = NULL;
454         int ret = 0;
455
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);
461                         ret = 1;
462                         set_unlock_time(t->pid, prohibit_state);
463                         del_node(prohibit_state, t);
464                 }
465         }
466
467         return ret;
468 }
469
470 int get_trans_condition(void)
471 {
472         return trans_condition;
473 }
474
475 GList *get_cond_head(enum state_t s_index)
476 {
477         return cond_head[s_index];
478 }
479
480 int display_app_background(void *data)
481 {
482         pid_t pid;
483         PmLockNode *node;
484
485         pid = *(pid_t *)data;
486
487         node = find_node(S_NORMAL, pid);
488         if (node) {
489                 node->app_background = true;
490                 _I("App(%d) goes background. LCD_NORMAL will be unlocked.", pid);
491         }
492
493         node = find_node(S_LCDDIM, pid);
494         if (node) {
495                 node->app_background = true;
496                 _I("App(%d) goes background. LCD_DIM will be unlocked.", pid);
497         }
498
499         return 0;
500 }
501
502 int display_app_foreground(void *data)
503 {
504         pid_t pid;
505         PmLockNode *node;
506
507         pid = *(pid_t *)data;
508
509         node = find_node(S_NORMAL, pid);
510         if (node) {
511                 node->app_background = false;
512                 _I("App(%d) goes foreground. LCD_NORMAL will be locked.", pid);
513         }
514
515         node = find_node(S_LCDDIM, pid);
516         if (node) {
517                 node->app_background = false;
518                 _I("App(%d) goes foreground. LCD_DIM will be locked.", pid);
519         }
520
521         return 0;
522 }
523
524 int display_app_terminated(void *data)
525 {
526         pid_t pid;
527         PmLockNode *node;
528         enum state_t state;
529         enum state_t cur = get_pm_cur_state();
530         bool current_unlocked = false;
531
532         pid = *(pid_t *)data;
533
534         /* release lock if it is holding locks */
535         for (state = S_START; state < S_END; ++state) {
536                 node = find_node(state, pid);
537                 if (node) {
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);
541                         if (state == cur)
542                                 current_unlocked = true;
543                 }
544         }
545
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
552          *
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);
556
557         return 0;
558 }
559
560 /* update transition condition for application requrements */
561 void update_lock_timer(PMMsg *data,
562                 PmLockNode *node, guint timeout_id)
563 {
564         time_t now;
565
566         if (data->timeout > 0) {
567                 time(&now);
568                 node->time = now;
569         }
570
571         if (node->timeout_id)
572                 g_source_remove(node->timeout_id);
573         node->timeout_id = timeout_id;
574 }
575
576 int display_lock_set_custom_holdkey_block(bool blocked)
577 {
578         custom_holdkey_block = blocked;
579         return 0;
580 }
581
582 int display_lock_get_custom_holdkey_block(bool *blocked)
583 {
584         if (!blocked)
585                 return -EINVAL;
586
587         *blocked = custom_holdkey_block;
588         return 0;
589 }
590