display: state-transition: Relocate default_trans()
[platform/core/system/deviced.git] / src / display / 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 "ambient-mode.h"
26 #include "device-interface.h"
27 #include "poll.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"
38
39 #define METHOD_APP_STATUS       "CheckAppStatus"
40 #define PID_MAX                 6
41 #define LOCK_TIME_WARNING       60      /* 60 seconds */
42
43 #define no_foreground_lock(st)   (check_lock_state(st) == false)
44
45 static GList *cond_head[S_END];
46 static int trans_condition;
47 static bool custom_holdkey_block = false;
48
49 bool check_lock_state(int state)
50 {
51         GList *elem;
52         PmLockNode *t;
53
54         SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
55                 if (t->app_background == false)
56                         return true;
57         }
58
59         return false;
60 }
61
62 bool pmlock_get_lock_state(enum state_t state)
63 {
64         return (bool)SYS_G_LIST_LENGTH(cond_head[state]);
65 }
66
67 enum state_t power_lock_type_to_pmlock(power_lock_e power_lock_type)
68 {
69         switch(power_lock_type) {
70         case POWER_LOCK_CPU:
71                 return S_LCDOFF;
72         case POWER_LOCK_DISPLAY:
73                 return S_LCDON;
74         case POWER_LOCK_DISPLAY_DIM:
75                 return S_LCDDIM;
76         default:
77                 return S_END;
78         }
79 }
80
81 static void refresh_app_cond(void)
82 {
83         trans_condition = 0;
84
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;
91 }
92
93 static void broadcast_pmlock_state_changed(enum state_t state)
94 {
95         int num_of_pmlock;
96
97         switch(state) {
98         case S_LCDON:
99         case S_LCDDIM:
100         case S_LCDOFF:
101                 break;
102         default:
103                 return;
104         }
105
106         num_of_pmlock = g_list_length(cond_head[state]);
107         if (num_of_pmlock > 1)
108                 return;
109
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));
115 }
116
117 static void broadcast_pmlock_expired(pid_t pid, enum state_t state, const char* appid, time_t locktime)
118 {
119         time_t now;
120         double diff;
121         int ret;
122         const char* state_name = NULL;
123
124         time(&now);
125         diff = difftime(now, locktime);
126
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);
130
131         ret = gdbus_signal_emit(NULL,
132                                                 DEVICED_PATH_DISPLAY,
133                                                 DEVICED_INTERFACE_DISPLAY,
134                                                 "pmlock_expired",
135                                                 g_variant_new("(ii)", pid, (int)diff));
136         if (ret < 0)
137                 _E("Failed to send dbus pmlock_expired");
138 }
139
140 static void default_pmlock_check_cb(GVariant *var, void *user_data, GError *err)
141 {
142         pid_t pid = 0;
143         int detected = 0;
144         char *app_id = NULL;
145         enum state_t state = (enum state_t) user_data;
146         PmLockNode *node;
147
148         if (!var)
149                 return;
150
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));
153                 goto out;
154         }
155
156         if (!detected)
157                 goto out;
158
159         node = find_node(state, pid);
160         if (!node)
161                 goto out;
162
163         broadcast_pmlock_expired(pid, state, app_id, node->time);
164
165 out:
166         if (app_id)
167                 g_free(app_id);
168         g_variant_unref(var);
169 }
170
171 static gboolean default_pmlock_check(void *data)
172 {
173         char *str;
174         PmLockNode *node;
175         enum state_t state;
176         pid_t pid;
177         int ret;
178
179         if (!data) {
180                 _E("Invalid parameter.");
181                 return G_SOURCE_REMOVE;
182         }
183
184         node = (PmLockNode *) data;
185         state = node->state;
186         pid = node->pid;
187
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;
191         }
192
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;
199         }
200
201         if (!is_app(pid)) {
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;
206         }
207
208         switch (state) {
209         case S_NORMAL:
210                 str = "normal";
211                 break;
212         case S_LCDDIM:
213                 str = "lcddim";
214                 break;
215         case S_LCDOFF:
216                 str = "lcdoff";
217                 break;
218         default:
219                 _E("Invalid state.");
220                 return G_SOURCE_REMOVE;
221         }
222
223         ret = gdbus_call_async_with_reply(RESOURCED_BUS_NAME,
224                 RESOURCED_PATH_PROCESS,
225                 RESOURCED_INTERFACE_PROCESS,
226                 METHOD_APP_STATUS,
227                 g_variant_new("(is)", pid, str),
228                 default_pmlock_check_cb, -1, (void *)(intptr_t)state);
229         if (ret < 0)
230                 _E("Failed to call dbus method");
231
232         return G_SOURCE_CONTINUE;
233 }
234
235
236 PmLockNode *find_node(enum state_t s_index, pid_t pid)
237 {
238         GList *elem;
239         PmLockNode *t = NULL;
240
241         SYS_G_LIST_FOREACH(cond_head[s_index], elem, t) {
242                 if (t->pid == pid)
243                         return t;
244         }
245
246         return NULL;
247 }
248
249 /* FIXME: ambient call should handled later, to be removed or changed plugin interface way */
250 static gboolean delete_state_cond_callback(void *data)
251 {
252         if (!data)
253                 return G_SOURCE_REMOVE;
254
255         PmLockNode *tmp = (PmLockNode *)data;
256         const char *state_name = NULL;
257         pid_t pid;
258         enum state_t state = tmp->state;
259
260
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);
265
266         if (pid == INTERNAL_LOCK_AMBIENT)
267                 ambient_check_invalid_state(pid);
268
269         tmp = find_node(state, pid);
270         del_node(state, tmp);
271         set_unlock_time(pid, state);
272
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);
279
280         if (state == S_LCDOFF)
281                 set_process_active(false, pid);
282
283         return G_SOURCE_REMOVE;
284 }
285
286 static void remove_pmlock_node_timeout_callback_id(PmLockNode *target_node)
287 {
288         if (!target_node)
289                 return;
290
291         if (target_node->timeout_id)
292                 g_source_remove(target_node->timeout_id);
293         target_node->timeout_id = 0;
294 }
295
296 static int add_delete_state_cond_callback(unsigned int timeout, PmLockNode *target_node)
297 {
298         if (!target_node)
299                 return -EINVAL;
300
301         if (target_node->state < S_NORMAL || target_node->state > S_LCDOFF)
302                 return -EINVAL;
303
304         if (timeout <= 0) {
305                 remove_pmlock_node_timeout_callback_id(target_node);
306                 return 0;
307         }
308
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");
313
314         return 0;
315 }
316
317 static void handle_existing_pmlock_node(bool holdkey_block, unsigned int timeout, PmLockNode *found_node)
318 {
319         time_t now;
320
321         remove_pmlock_node_timeout_callback_id(found_node);
322         if (timeout > 0)
323                 add_delete_state_cond_callback(timeout, found_node);
324
325         time(&now);
326         found_node->time = now;
327         found_node->holdkey_block = holdkey_block;
328 }
329
330 PmLockNode *display_lock_add_pmlock_node(enum state_t s_index, pid_t pid, bool holdkey_block, unsigned int timeout)
331 {
332         PmLockNode *n;
333         PmLockNode *found_node;
334         time_t now;
335
336         assert(g_display_plugin.config);
337
338         found_node = find_node(s_index, pid);
339         if (found_node) {
340                 handle_existing_pmlock_node(holdkey_block, timeout, found_node);
341                 return found_node;
342         }
343
344         n = (PmLockNode *) calloc(1, sizeof(PmLockNode));
345         if (n == NULL) {
346                 _E("Not enough memory, failed to alloc lock node.");
347                 return NULL;
348         }
349
350         time(&now);
351         n->state = s_index;
352         n->pid = pid;
353         add_delete_state_cond_callback(timeout, n);
354         n->time = now;
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);
361         }
362         n->broadcast_warning = true;
363
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);
370
371                 /* use default lock checker */
372                 if (!n->warning_id)
373                         n->warning_id = g_timeout_add_seconds(g_display_plugin.config->lockcheck_timeout,
374                                 default_pmlock_check, (gpointer)n);
375         }
376
377         SYS_G_LIST_APPEND(cond_head[s_index], n);
378
379         refresh_app_cond();
380
381         broadcast_pmlock_state_changed(s_index);
382
383         return n;
384 }
385
386 int del_node(enum state_t s_index, PmLockNode *n)
387 {
388         if (n == NULL)
389                 return 0;
390
391         SYS_G_LIST_REMOVE(cond_head[s_index], n);
392
393         /* delete timer */
394         remove_pmlock_node_timeout_callback_id(n);
395
396         if (n->warning_id) {
397                 g_source_remove(n->warning_id);
398                 n->warning_id = 0;
399         }
400         if (n->warning_param) {
401                 g_variant_unref(n->warning_param);
402                 n->warning_param = NULL;
403         }
404
405         broadcast_pmlock_state_changed(s_index);
406
407         free(n);
408         refresh_app_cond();
409         return 0;
410 }
411
412 int check_lock_condition(enum state_t state)
413 {
414         GList *elem;
415         PmLockNode *t = NULL;
416         int ret = false;
417         pid_t owner = getpid();
418         const char *state_name = NULL;
419
420         display_plugin_state_get_name(state, &state_name);
421         _D("check holdkey block : state of %s", state_name);
422
423         SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
424                 if (t->pid != owner && t->app_background == false) {
425                         ret = true;
426                         _I("state change was blocked by pid(%d)!", t->pid);
427                         break;
428                 }
429         }
430
431         return ret;
432 }
433
434 int check_holdkey_block(enum state_t state)
435 {
436         GList *elem;
437         PmLockNode *t = NULL;
438         int ret = 0;
439         const char *state_name = NULL;
440
441         display_plugin_state_get_name(state, &state_name);
442         _I("Check holdkey block: state of %s", state_name);
443
444         if (custom_holdkey_block == true) {
445                 _I("Custom hold key blocked");
446                 return 1;
447         }
448
449         SYS_G_LIST_FOREACH(cond_head[state], elem, t) {
450                 if (t->holdkey_block == true) {
451                         ret = 1;
452                         _I("Hold key blocked by pid(%d).", t->pid);
453                         break;
454                 }
455         }
456
457         return ret;
458 }
459
460 int delete_condition(enum state_t state)
461 {
462         GList *elem, *next;
463         PmLockNode *t = NULL;
464         const char *state_name = NULL;
465
466         display_plugin_state_get_name(state, &state_name);
467         _I("delete condition : state of %s", state_name);
468
469         if (!cond_head[state])
470                 return 0;
471
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);
478                 del_node(state, t);
479         }
480
481         SYS_G_LIST_FREE_LIST(cond_head[state]);
482         cond_head[state] = NULL;
483
484         broadcast_pmlock_state_changed(state);
485
486         return 0;
487 }
488
489 void print_node(int next)
490 {
491         int ret;
492         GList *elem;
493         PmLockNode *n;
494         char buf[30];
495         time_t now;
496         double diff;
497
498         if (next <= S_START || next >= S_END)
499                 return;
500
501         time(&now);
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;
506
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,
512                                                                                 "pmlock_over",
513                                                                                 g_variant_new("(i)", n->pid));
514                                 if (ret < 0)
515                                         _E("Failed to send dbus signal pmlock_over.");
516                                 n->broadcast_warning = false;
517                         }
518                         _W("Over(%.0f s) pid( %5d) lock time(%s)", diff, n->pid, buf);
519                 } else
520                         _I("Pid(%5d) lock time(%s)", n->pid, buf);
521         }
522 }
523
524 void makeup_trans_condition(void)
525 {
526         check_processes(S_NORMAL);
527         check_processes(S_LCDDIM);
528         check_processes(S_LCDOFF);
529         refresh_app_cond();
530 }
531
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)
535 {
536         GList *elem, *next;
537         PmLockNode *t = NULL;
538         int ret = 0;
539
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);
545                         ret = 1;
546                         set_unlock_time(t->pid, prohibit_state);
547                         del_node(prohibit_state, t);
548                 }
549         }
550
551         return ret;
552 }
553
554 int get_trans_condition(void)
555 {
556         return trans_condition;
557 }
558
559 GList *get_cond_head(enum state_t s_index)
560 {
561         return cond_head[s_index];
562 }
563
564 int display_app_background(void *data)
565 {
566         pid_t pid;
567         PmLockNode *node;
568
569         pid = *(pid_t *)data;
570
571         node = find_node(S_NORMAL, pid);
572         if (node) {
573                 node->app_background = true;
574                 _I("App(%d) goes background. LCD_NORMAL will be unlocked.", pid);
575         }
576
577         node = find_node(S_LCDDIM, pid);
578         if (node) {
579                 node->app_background = true;
580                 _I("App(%d) goes background. LCD_DIM will be unlocked.", pid);
581         }
582
583         return 0;
584 }
585
586 int display_app_foreground(void *data)
587 {
588         pid_t pid;
589         PmLockNode *node;
590
591         pid = *(pid_t *)data;
592
593         node = find_node(S_NORMAL, pid);
594         if (node) {
595                 node->app_background = false;
596                 _I("App(%d) goes foreground. LCD_NORMAL will be locked.", pid);
597         }
598
599         node = find_node(S_LCDDIM, pid);
600         if (node) {
601                 node->app_background = false;
602                 _I("App(%d) goes foreground. LCD_DIM will be locked.", pid);
603         }
604
605         return 0;
606 }
607
608 int display_app_terminated(void *data)
609 {
610         pid_t pid;
611         PmLockNode *node;
612         enum state_t state;
613         enum state_t cur = get_pm_cur_state();
614         bool current_unlocked = false;
615
616         pid = *(pid_t *)data;
617
618         /* release lock if it is holding locks */
619         for (state = S_START; state < S_END; ++state) {
620                 node = find_node(state, pid);
621                 if (node) {
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);
625                         if (state == cur)
626                                 current_unlocked = true;
627                 }
628         }
629
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
636          *
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);
640
641         return 0;
642 }
643
644 int display_lock_set_custom_holdkey_block(bool blocked)
645 {
646         custom_holdkey_block = blocked;
647         return 0;
648 }
649
650 int display_lock_get_custom_holdkey_block(bool *blocked)
651 {
652         if (!blocked)
653                 return -EINVAL;
654
655         *blocked = custom_holdkey_block;
656         return 0;
657 }