display: display-state-transition: Relocate fuctions related to transition timer...
[platform/core/system/deviced.git] / src / display / plugin-common / ambient-mode.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2017 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 #include <stdbool.h>
21 #include <fcntl.h>
22 #include <libsyscommon/libgdbus.h>
23
24 #include "util.h"
25 #include "core.h"
26 #include "display.h"
27 #include "display-ops.h"
28 #include "display-panel.h"
29 #include "display-state-transition.h"
30 #include "shared/device-notifier.h"
31 #include "shared/devices.h"
32 #include "shared/plugin.h"
33
34 #define ON              "on"
35 #define OFF             "off"
36
37 #define SIGNAL_HOMESCREEN       "HomeScreen"
38
39 #define SIGNAL_ALPMLCDOFF               "ALPMLCDOff"
40 #define SIGNAL_ALPM_ON                  "ALPMOn"
41 #define SIGNAL_ALPM_OFF                 "ALPMOff"
42
43 #define CLOCK_START                     "clockstart"
44 #define CLOCK_END                       "clockend"
45 #define CLOCK_CHANGED                   "clockchanged"
46 #define TIMEOUT_NONE                    (-1)
47 #define AMBIENT_CLOCK_WAITING_TIME      5000 /* ms */
48
49 static struct display_plugin *disp_plgn;
50 static int ambient_state;
51 static int ambient_condition;   /* Setting Value */
52 static pid_t ambient_pid;  /* Ambient Clock pid */
53 static unsigned int update_count;
54
55 void broadcast_ambient_state(int state)
56 {
57         int ret;
58         char *signal;
59
60         signal = (state == true ? SIGNAL_ALPM_ON : SIGNAL_ALPM_OFF);
61         ret = gdbus_signal_emit(NULL,
62                                                 DEVICED_PATH_DISPLAY,
63                                                 DEVICED_INTERFACE_DISPLAY,
64                                                 signal,
65                                                 NULL);
66         if (ret < 0)
67                 _E("Failed to send dbus signal(%s).", signal);
68 }
69
70 int ambient_get_condition(void)
71 {
72         return ambient_condition;
73 }
74
75 int ambient_get_state(void)
76 {
77         return ambient_state;
78 }
79
80 static void ambient_set_condition(keynode_t *key_nodes, void *data)
81 {
82         int state, val;
83
84         if (key_nodes == NULL) {
85                 _E("Wrong parameter, key_nodes is null.");
86                 return;
87         }
88
89         val = vconf_keynode_get_bool(key_nodes);
90         if (val != ambient_condition) {
91                 if (display_panel_get_dpms_cached_state() != DPMS_ON)
92                         if (disp_plgn->pm_lock_internal)
93                                 disp_plgn->pm_change_internal(INTERNAL_LOCK_PM, LCD_NORMAL);
94         }
95
96         ambient_condition = val;
97         _I("Ambient mode condition is %d.", ambient_condition);
98
99         state = (ambient_condition == 0 ? 0 : 1);
100         device_notify(DEVICE_NOTIFIER_DISPLAY_AMBIENT_CONDITION, (void *)&state);
101
102         set_dim_state(state);
103 }
104
105 int ambient_set_state(int on)
106 {
107         if (!ambient_condition)
108                 return 0;
109
110         broadcast_ambient_state(on);
111
112         update_count = 0;
113
114         /**
115          * When display is turned off before going to AOD, Generally pm receives
116          * clock signal or ALPMLCDOFF signal and decides to go to AOD or not.
117          * But a specific case, the signal is not received.
118          * So at that time deviced should turn off display to match the pair.
119          */
120         if (on) {
121                 if (disp_plgn->pm_lock_internal)
122                         disp_plgn->pm_lock_internal(INTERNAL_LOCK_AMBIENT,
123                                 LCD_OFF, STAY_CUR_STATE, AMBIENT_CLOCK_WAITING_TIME);
124         } else
125                 ambient_pid = 0;
126
127         _D("AMBIENT is %s.", (on ? ON : OFF));
128
129         ambient_state = on;
130
131         device_notify(DEVICE_NOTIFIER_DISPLAY_AMBIENT_STATE, (void *)&ambient_state);
132
133         return 0;
134 }
135
136 void ambient_check_invalid_state(pid_t pid)
137 {
138         if (pid != INTERNAL_LOCK_AMBIENT)
139                 return;
140
141         if (is_emulator()) {
142                 /* In emulator, deviced does not turn off the display. */
143                 if (display_panel_get_dpms_cached_state() == DPMS_ON)
144                         return;
145         }
146
147         if (ambient_get_state() == false)
148                 return;
149
150         _I("Invalid state. Ambient state is change to off.");
151
152         /* If lcd_power is on and ambient state is true
153          * when pm state is changed to sleep,
154          * deviced doesn't get the clock signal.
155          * deviced just turns off lcd in this case.
156          */
157
158         display_state_transition_reset_state_transition_timeout(TIMEOUT_NONE);
159         lcd_direct_control(DPMS_OFF, NORMAL_MODE);
160
161         broadcast_lcd_off_late(LCD_OFF_LATE_MODE);
162 }
163
164 static void ambient_start_clock(void)
165 {
166         if ((get_pm_cur_state() == S_NORMAL) ||
167             (get_pm_cur_state() == S_LCDDIM) ||
168             (ambient_state == false))
169                 return;
170
171         if (disp_plgn->pm_lock_internal)
172                 disp_plgn->pm_lock_internal(INTERNAL_LOCK_AMBIENT, LCD_OFF, STAY_CUR_STATE,
173                         AMBIENT_CLOCK_WAITING_TIME);
174 }
175
176 static void ambient_end_clock(pid_t pid)
177 {
178         if ((get_pm_cur_state() == S_NORMAL) ||
179             (get_pm_cur_state() == S_LCDDIM) ||
180             (ambient_state == false))
181                 return;
182
183         if (update_count == 0) {
184                 _D("lcd off");
185
186                 display_panel_set_panel_state_by_off_state(NORMAL_MODE);
187                 broadcast_lcd_off_late(LCD_OFF_LATE_MODE);
188                 if (disp_plgn->pm_unlock_internal)
189                         disp_plgn->pm_unlock_internal(INTERNAL_LOCK_AMBIENT, LCD_OFF, PM_SLEEP_MARGIN);
190         }
191
192         update_count++;
193         if (update_count == UINT_MAX)
194                 update_count = 1;
195         ambient_pid = pid;
196
197         _I("Enter real ambient state by process(%d).",
198             ambient_pid);
199 }
200
201 static void homescreen_signal_handler(GDBusConnection  *conn,
202         const gchar      *sender,
203         const gchar      *path,
204         const gchar      *iface,
205         const gchar      *name,
206         GVariant         *param,
207         gpointer          data)
208 {
209         char *screen = NULL;
210         pid_t pid;
211
212         if (!g_variant_get_safe(param, "(s)", &screen)) {
213                 _E("failed to get params from gvariant. expected:%s, type:%s", "(s)", g_variant_get_type_string(param));
214                 goto out;
215         }
216
217         if (screen == NULL) {
218                 _E("screen is null.");
219                 goto out;
220         }
221         _D("screen : %s", screen);
222
223         pid = gdbus_connection_get_sender_pid(conn, sender);
224
225         if (!strncmp(screen, CLOCK_START, strlen(CLOCK_START)))
226                 ambient_start_clock();
227         else if (!strncmp(screen, CLOCK_END, strlen(CLOCK_END)))
228                 ambient_end_clock(pid);
229
230 out:
231         if (screen)
232                 g_free(screen);
233 }
234
235 static void ambient_lcdoff_signal_handler(GDBusConnection  *conn,
236         const gchar      *sender,
237         const gchar      *path,
238         const gchar      *iface,
239         const gchar      *name,
240         GVariant         *param,
241         gpointer          data)
242 {
243         if (ambient_state == false) {
244                 _E("It is not alpm mode.");
245                 return;
246         }
247
248         if (disp_plgn->pm_lock_internal)
249                 disp_plgn->pm_lock_internal(INTERNAL_LOCK_AMBIENT, LCD_OFF, GOTO_STATE_NOW, 0);
250
251         _I("Display off in suspend state.");
252
253         ambient_set_state(false);
254         lcd_direct_control(DPMS_OFF, NORMAL_MODE);
255
256         broadcast_lcd_off_late(LCD_OFF_LATE_MODE);
257         if (disp_plgn->pm_unlock_internal)
258                 disp_plgn->pm_unlock_internal(INTERNAL_LOCK_AMBIENT, LCD_OFF, PM_SLEEP_MARGIN);
259 }
260
261 static void ambient_init(void *data)
262 {
263         int ret;
264
265         ret = vconf_get_bool("db/starter/always_on_display", &ambient_condition);
266         if (ret < 0) {
267                 _E("Failed to get initial AOD condition. AOD is not going to work.");
268                 return;
269         } else {
270                 _I("Ambient mode condition is %d.", ambient_condition);
271
272                 vconf_notify_key_changed("db/starter/always_on_display",
273                         ambient_set_condition, NULL);
274         }
275         ret = gdbus_signal_subscribe(NULL,
276                                 DEVICED_OBJECT_PATH,
277                                 DEVICED_INTERFACE_NAME,
278                                 SIGNAL_HOMESCREEN,
279                                 homescreen_signal_handler,
280                                 NULL, NULL);
281         if (ret <= 0)
282                 _E("Failed to register signal handler: %d", ret);
283
284         ret = gdbus_signal_subscribe(NULL,
285                                 DEVICED_OBJECT_PATH,
286                                 DEVICED_INTERFACE_NAME,
287                                 SIGNAL_ALPMLCDOFF,
288                                 ambient_lcdoff_signal_handler,
289                                 NULL, NULL);
290         if (ret <= 0)
291                 _E("Failed to register signal handler: %d", ret);
292 }
293
294 static void ambient_exit(void *data)
295 {
296         ambient_set_state(false);
297         vconf_ignore_key_changed("db/starter/always_on_display", ambient_set_condition);
298 }
299
300 static const struct display_ops ambient_ops = {
301         .name     = "ambient",
302         .init     = ambient_init,
303         .exit     = ambient_exit,
304 };
305
306 DISPLAY_OPS_REGISTER(&ambient_ops)
307
308 static void __CONSTRUCTOR__ initialize(void)
309 {
310         disp_plgn = get_var_display_plugin();
311         if (!disp_plgn)
312                 _E("Failed to get display plugin variable.");
313 }