bad84078575dc3ae5c5bfece8980aca188375040
[platform/core/system/deviced.git] / plugins / wearable / display / display-handler.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2016 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 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <stdint.h>
23 #include <error.h>
24
25 #include "auto-brightness-sensorhub.h"
26 #include "shared/device-notifier.h"
27 #include "core.h"
28 #include "device-interface.h"
29 #include "poll.h"
30 #include "shared/common.h"
31 #include "shared/devices.h"
32 #include "shared/log.h"
33 #include "display-actor.h"
34 #include "display-info.h"
35 #include "display-panel.h"
36 #include "display-backlight.h"
37 #include "display-ops.h"
38 #include "display-info.h"
39
40 #define CHARGER_LCD_NODE "/sys/class/power_supply/battery/lcd"
41 #define SIGNAL_HOMESCREEN       "HomeScreen"
42 #define CLOCK_CHANGED           "clockchanged"
43 #define CLOCK_END                               "clockend"
44
45 enum charging_lcd_state {
46         CHARGING_LCD_OFF = 0,
47         CHARGING_LCD_ON = 1,
48 };
49
50 static struct display_backlight_ops *backlight_ops;
51 static guint autobrt_timer;
52 static int autobrtlevel;
53
54 static bool aod_clock_displayed; /* True while AOD screen is displayed */
55
56 static gboolean lcdon_from_aod_cb(gpointer data)
57 {
58         int level = (int)(intptr_t) data;
59
60         autobrt_timer = 0;
61
62         /* If it is still not turned on, do not apply auto brightness */
63         if (display_panel_get_dpms_cached_state() != DPMS_ON)
64                 return G_SOURCE_REMOVE;
65
66         display_backlight_change_brightness_by_dpms_state(DPMS_ON);
67         set_brightness_level(level);
68
69         /* lcdon is completed, aod disappered */
70         aod_clock_displayed = false;
71
72         return G_SOURCE_REMOVE;
73 }
74
75 /* For AOD state, there is race condition between
76  * HomeScreen dbus signal and AutoBrightnessChanged dbus method call.
77  * Cannot decide which one arrives first */
78 static GVariant *dbus_autobrightnesschanged(GDBusConnection *conn,
79         const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
80         GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
81 {
82         int ret = 0;
83         int level;
84
85         g_variant_get(param, "(i)", &level);
86
87         /* If received level is identical to autobrt timer reserved level, ignore it. */
88         if (autobrt_timer && autobrtlevel == level) {
89                 _D("Ignore autobrightness change request as same request(%d) is pending.", level);
90                 return g_variant_new("(i)", ret);
91         }
92
93         if (autobrt_timer) {
94                 g_source_remove(autobrt_timer);
95                 autobrt_timer = 0;
96         }
97
98         syscommon_notifier_emit_notify(DEVICE_NOTIFIER_LCD_AUTOBRT_SENSING, NULL);
99
100         /* When display state changes from AOD to LCDON, brightness change
101                 * effect seems sluggish because of heavy load of jobs for turning on
102                 * display. So delay this brightness change a bit to avoid this
103                 * heavy loaded time and therefore make it change smoothly. */
104         if (aod_clock_displayed) {
105                 autobrtlevel = level; /* reserve the level, defer applying */
106                 autobrt_timer = g_timeout_add(200, lcdon_from_aod_cb, (gpointer)(intptr_t) level);
107         } else { /* DEVICED_DISPLAY_STATE_ON state or LCDON from usual OFF state, not from AOD */
108                 set_brightness_level(level);
109         }
110
111         return g_variant_new("(i)", ret);
112 }
113
114 static void aod_change_signal(GDBusConnection  *conn,
115                                                         const gchar      *sender,
116                                                         const gchar      *path,
117                                                         const gchar      *iface,
118                                                         const gchar      *name,
119                                                         GVariant         *param,
120                                                         gpointer          data)
121
122 {
123         char *screen = NULL;
124
125         if (!g_variant_get_safe(param, "(s)", &screen)) {
126                 _E("failed to get params from gvariant. expected:%s, type:%s", "(s)", g_variant_get_type_string(param));
127                 goto out;
128         }
129
130         if (screen == NULL) {
131                 _E("screen is null.");
132                 goto out;
133         }
134
135         /* clock-viewer sends "clockchanged" as signal parameter when AOD->LCDON.
136          * On catching this signal, do change brightness immediately. */
137         if (!strcmp(screen, CLOCK_CHANGED)) {
138                 /* aod_clock_displayed can be false if this clockchanged siganl arrives after 200ms timeout.
139                  * In this situation, there is noting to do because all the
140                  * brightness-related task, which should have been done in this routine,
141                  * have been performed by timeout callback, lcdon_from_aod_cb(). */
142                 if (!aod_clock_displayed)
143                         return;
144
145                 display_backlight_change_brightness_by_dpms_state(DPMS_ON);
146                 if (autobrt_timer) { /* if there is reserved level, apply it */
147                         g_source_remove(autobrt_timer);
148                         autobrt_timer = 0;
149                         set_brightness_level(autobrtlevel);
150                 }
151                 /* lcdon is completed, aod disappered */
152                 aod_clock_displayed = false;
153         } else if (!strcmp(screen, CLOCK_END)) {
154                 /* lcdoff is completed, aod showed up */
155                 aod_clock_displayed = true;
156         }
157
158 out:
159         if (screen)
160                 g_free(screen);
161 }
162
163 static const dbus_method_s dbus_methods[] = {
164         { "AutoBrightnessChanged",   "i",   "i", dbus_autobrightnesschanged },
165 };
166
167 static const dbus_interface_u dbus_interface = {
168         .oh = NULL,
169         .name = DEVICED_INTERFACE_DISPLAY,
170         .methods = dbus_methods,
171         .nr_methods = ARRAY_SIZE(dbus_methods),
172 };
173
174 static int display_state_changed(void *data)
175 {
176         enum deviced_display_state state;
177         int ret = 0;
178
179         state = DATA_VALUE_INT(data);
180
181         if (state == DEVICED_DISPLAY_STATE_ON)
182                 ret = sys_set_int(CHARGER_LCD_NODE, CHARGING_LCD_ON);
183         else
184                 ret = sys_set_int(CHARGER_LCD_NODE, CHARGING_LCD_OFF);
185
186         if (ret < 0)
187                 _E("Can't write %s node %d.", CHARGER_LCD_NODE, state);
188
189         return 0;
190 }
191
192 static void display_handler_init(void *data)
193 {
194         int ret;
195
196         prepare_level_handler();
197         syscommon_notifier_subscribe_notify(DEVICE_NOTIFIER_LCD, display_state_changed);
198
199         aod_clock_displayed = false;
200         ret = gdbus_signal_subscribe(NULL,
201                                 DEVICED_OBJECT_PATH,
202                                 DEVICED_INTERFACE_NAME,
203                                 SIGNAL_HOMESCREEN,
204                                 aod_change_signal,
205                                 NULL, NULL);
206         if (ret <= 0)
207                 _E("Failed to register signal handler: %d", ret);
208
209         ret = sys_set_int(CHARGER_LCD_NODE, CHARGING_LCD_ON);
210         if (ret < 0)
211                 _E("Can't write %s node.", CHARGER_LCD_NODE);
212
213         ret = gdbus_add_object(NULL, DEVICED_PATH_DISPLAY, &dbus_interface);
214         if (ret < 0)
215                 _E("Failed to register dbus object.");
216 }
217
218 static const struct display_ops display_handler_ops = {
219         .name     = "dislay-handler",
220         .init     = display_handler_init,
221 };
222
223 DISPLAY_OPS_REGISTER(&display_handler_ops)
224
225 static void __CONSTRUCTOR__ initialize(void)
226 {
227         backlight_ops = get_var_backlight_ops();
228         if (!backlight_ops)
229                 _E("Failed to get backlight operator variable.");
230 }