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