shared: Replace device_notifier_type with the libsyscommon
[platform/core/system/deviced.git] / plugins / wearable / display / auto-brightness.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 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 <stdio.h>
22 #include <math.h>
23 #include <limits.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #include <vconf.h>
30 #include <sensor.h>
31
32 #include "core.h"
33 #include "device-interface.h"
34 #include "display-info.h"
35 #include "display-ops.h"
36 #include "display-backlight.h"
37 #include "display-config.h"
38 #include "setting.h"
39 #include "shared/device-notifier.h"
40 #include "shared/log.h"
41 #include <libsyscommon/ini-parser.h>
42
43 #define MAX_SAMPLING_COUNT      3
44 #define MAX_FAULT               5
45 #define DEFAULT_AUTOMATIC_BRT   5
46 #define AUTOMATIC_DEVIDE_VAL    10
47 #define AUTOMATIC_DELAY_TIME    500     /* 0.5 sec */
48
49 #define RADIAN_VALUE            (57.2957)
50 #define ROTATION_90             90
51 #define WORKING_ANGLE_MIN       0
52 #define WORKING_ANGLE_MAX       20
53
54 static int (*_default_action) (int);
55 static guint alc_timeout_id = 0;
56 static guint update_timeout;
57 static sensor_listener_h light_listener;
58 static sensor_listener_h accel_listener;
59 static int fault_count;
60 static int automatic_brt = DEFAULT_AUTOMATIC_BRT;
61 static int min_brightness = PM_MIN_BRIGHTNESS;
62 static char *min_brightness_name = 0;
63
64 /* light sensor */
65 static float lmax, lmin;
66
67 static bool update_working_position(void)
68 {
69         sensor_event_s data;
70         int ret;
71         float x, y, z, pitch, realg;
72
73         assert(g_display_plugin.config);
74
75         if (!g_display_plugin.config->accel_sensor_on)
76                 return false;
77
78         ret = sensor_listener_read_data(accel_listener, &data);
79         if (ret != SENSOR_ERROR_NONE) {
80                 _E("Failed to get accelerometer data: %d", ret);
81                 return true;
82         }
83
84         x = data.values[0];
85         y = data.values[1];
86         z = data.values[2];
87
88         realg = (float)sqrt((x * x) + (y * y) + (z * z));
89         pitch = ROTATION_90 - abs((int) (asin(z / realg) * RADIAN_VALUE));
90
91         _D("Accel data x=%f y=%f z=%f pitch=%f", x, y, z, pitch);
92
93         if (pitch >= WORKING_ANGLE_MIN && pitch <= WORKING_ANGLE_MAX)
94                 return true;
95         return false;
96 }
97
98 static void alc_set_brightness(int setting, int value, float light)
99 {
100         static float old;
101         int position, tmp_value = 0, ret;
102
103         assert(g_display_plugin.config);
104
105         ret = display_backlight_get_brightness(&tmp_value);
106         if (ret < 0) {
107                 _E("Failed to get display brightness.");
108                 return;
109         }
110
111         if (value < min_brightness)
112                 value = min_brightness;
113
114         if (tmp_value != value) {
115                 if (!setting && min_brightness == PM_MIN_BRIGHTNESS &&
116                     g_display_plugin.config->accel_sensor_on == true) {
117                         position = update_working_position();
118                         if (!position && (old > light)) {
119                                 _D("It's not working position, "
120                                     "LCD isn't getting dark.");
121                                 return;
122                         }
123                 }
124                 int diff, step;
125
126                 diff = value - tmp_value;
127                 if (abs(diff) < g_display_plugin.config->brightness_change_step)
128                         step = (diff > 0 ? 1 : -1);
129                 else
130                         step = (int)ceil(diff /
131                             (float)g_display_plugin.config->brightness_change_step);
132
133                 _D("%d", step);
134                 while (tmp_value != value) {
135                         if (step == 0) break;
136
137                         tmp_value += step;
138                         if ((step > 0 && tmp_value > value) ||
139                             (step < 0 && tmp_value < value))
140                                 tmp_value = value;
141
142                         display_backlight_set_default_brightness(tmp_value);
143                         display_backlight_update_by_default_brightness();
144                 }
145                 _I("Load light data(%f) auto brt=%d min brightness=%d "
146                     "brightness=%d", light, automatic_brt, min_brightness, value);
147                 old = light;
148         }
149 }
150
151 static bool check_brightness_changed(int value)
152 {
153         int i;
154         static int values[MAX_SAMPLING_COUNT], count = 0;
155
156         if (count >= MAX_SAMPLING_COUNT || count < 0)
157                 count = 0;
158
159         values[count++] = value;
160
161         for (i = 0; i < MAX_SAMPLING_COUNT - 1; i++)
162                 if (values[i] != values[i+1])
163                         return false;
164         return true;
165 }
166
167 static bool alc_update_brt(bool setting)
168 {
169         int value = 0;
170         int ret = -1;
171         sensor_event_s light_data;
172         int index;
173         float light;
174
175         assert(g_display_plugin.config);
176
177         ret = sensor_listener_read_data(light_listener, &light_data);
178         if (ret != SENSOR_ERROR_NONE) {
179                 _E("Failed to read light sensor data: %d", ret);
180                 goto out;
181         }
182
183         index = light_data.value_count - 1;
184         if (index < 0 || light_data.values[index] < 0) {
185                 _E("Invalid light sensor data.");
186                 goto out;
187         }
188
189         light = light_data.values[index];
190         ret = display_backlight_get_brightness_by_light_sensor(
191                         lmax, lmin, light, &value);
192         if (ret == -ENOTSUP) {
193                 _E("Not supported to handle the light data.");
194                 goto out;
195         }
196
197         if (ret < 0 || value < PM_MIN_BRIGHTNESS || value > PM_MAX_BRIGHTNESS) {
198                 _E("Failed to load light data. light=%f value=%d: %d", light, value, ret);
199                 goto out;
200         }
201
202         fault_count = 0;
203
204         if (g_display_plugin.config->continuous_sampling &&
205                 !check_brightness_changed(value) &&
206                 !setting)
207                 return true;
208
209         alc_set_brightness(setting, value, light);
210
211         return true;
212
213 out:
214         fault_count++;
215
216         if ((fault_count > MAX_FAULT) && !(get_pm_status_flag() & PWROFF_FLAG)) {
217                 if (alc_timeout_id) {
218                         g_source_remove(alc_timeout_id);
219                         alc_timeout_id = 0;
220                 }
221                 ret = vconf_set_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
222                     SETTING_BRIGHTNESS_AUTOMATIC_OFF);
223                 if (ret < 0)
224                         _E("Failed to set vconf value for automatic brightness: %d", vconf_get_ext_errno());
225                 _E("Fault counts is over %d, disable automatic brightness.", MAX_FAULT);
226                 return false;
227         }
228         return true;
229 }
230
231 static gboolean alc_handler(void *data)
232 {
233         if (get_pm_cur_state() != DEVICED_DISPLAY_STATE_ON) {
234                 if (alc_timeout_id > 0)
235                         g_source_remove(alc_timeout_id);
236                 alc_timeout_id = 0;
237                 return G_SOURCE_REMOVE;
238         }
239
240         if (!alc_update_brt(false))
241                 return G_SOURCE_REMOVE;
242
243         if (alc_timeout_id != 0)
244                 return G_SOURCE_CONTINUE;
245
246         return G_SOURCE_REMOVE;
247 }
248
249 static int alc_action(int timeout)
250 {
251         assert(g_display_plugin.config);
252
253         /* sampling timer add */
254         if (alc_timeout_id == 0 && !(get_pm_status_flag() & PWRSV_FLAG)) {
255                 update_auto_brightness(true);
256
257                 alc_timeout_id =
258                     g_timeout_add_seconds(g_display_plugin.config->lightsensor_interval,
259                             alc_handler, NULL);
260         }
261
262         if (_default_action != NULL)
263                 return _default_action(timeout);
264
265         /* unreachable code */
266         return -1;
267 }
268
269 static int connect_sensor(void)
270 {
271         int ret;
272         sensor_h sensor;
273         sensor_h *list = NULL;
274         int cnt = 0;
275
276         assert(g_display_plugin.config);
277
278         _I("Connect with sensor fw.");
279         /* light sensor */
280         ret = sensor_get_sensor_list(SENSOR_LIGHT, &list, &cnt);
281         if (ret != SENSOR_ERROR_NONE) {
282                 _E("Failed to get light sensor list: %d", ret);
283                 goto error;
284         }
285
286         /* TODO
287          * Sensor apis will be fixed
288          * to provide which sensor is effective among sensors */
289         if (cnt == 3) /* three light sensors exist */
290                 sensor = list[2];
291         else
292                 sensor = list[0];
293
294         g_free(list);
295
296         ret = sensor_get_min_range(sensor, &lmin);
297         if (ret != SENSOR_ERROR_NONE) {
298                 _E("Failed to get light sensor min range: %d", ret);
299                 goto error;
300         }
301         ret = sensor_get_max_range(sensor, &lmax);
302         if (ret != SENSOR_ERROR_NONE) {
303                 _E("Failed to get light sensor max range: %d", ret);
304                 goto error;
305         }
306         _I("Light sensor min=%f max=%f", lmin, lmax);
307
308         ret = sensor_create_listener(sensor, &light_listener);
309         if (ret != SENSOR_ERROR_NONE) {
310                 _E("Failed to create listener(light).");
311                 goto error;
312         }
313         sensor_listener_set_option(light_listener, SENSOR_OPTION_ALWAYS_ON);
314         ret = sensor_listener_start(light_listener);
315         if (ret != SENSOR_ERROR_NONE) {
316                 _E("Failed to start light sensor.");
317                 sensor_destroy_listener(light_listener);
318                 light_listener = 0;
319                 goto error;
320         }
321
322         if (!g_display_plugin.config->accel_sensor_on)
323                 goto success;
324
325         /* accelerometer sensor */
326         ret = sensor_get_default_sensor(SENSOR_ACCELEROMETER, &sensor);
327         if (ret != SENSOR_ERROR_NONE) {
328                 _E("Failed to get default accel sensor.");
329                 goto error;
330         }
331         ret = sensor_create_listener(&sensor, &accel_listener);
332         if (ret != SENSOR_ERROR_NONE) {
333                 _E("Failed to create listener(accel).");
334                 goto error;
335         }
336         sensor_listener_set_option(accel_listener, SENSOR_OPTION_ALWAYS_ON);
337         ret = sensor_listener_start(accel_listener);
338         if (ret != SENSOR_ERROR_NONE) {
339                 _E("Failed to start accel sensor.");
340                 sensor_destroy_listener(accel_listener);
341                 accel_listener = 0;
342                 goto error;
343         }
344
345 success:
346         fault_count = 0;
347         return 0;
348
349 error:
350         if (light_listener > 0) {
351                 sensor_listener_stop(light_listener);
352                 sensor_destroy_listener(light_listener);
353                 light_listener = 0;
354         }
355         if (g_display_plugin.config->accel_sensor_on && accel_listener > 0) {
356                 sensor_listener_stop(accel_listener);
357                 sensor_destroy_listener(accel_listener);
358                 accel_listener = 0;
359         }
360         return -EIO;
361 }
362
363 static int disconnect_sensor(void)
364 {
365         assert(g_display_plugin.config);
366
367         _I("Disconnect with sensor fw.");
368         /* light sensor*/
369         if (light_listener > 0) {
370                 sensor_listener_stop(light_listener);
371                 sensor_destroy_listener(light_listener);
372                 light_listener = 0;
373         }
374
375         /* accelerometer sensor*/
376         if (g_display_plugin.config->accel_sensor_on && accel_listener > 0) {
377                 sensor_listener_stop(accel_listener);
378                 sensor_destroy_listener(accel_listener);
379                 accel_listener = 0;
380         }
381
382         if (_default_action != NULL) {
383                 state_st(DEVICED_DISPLAY_STATE_ON)->action = _default_action;
384                 _default_action = NULL;
385         }
386         if (alc_timeout_id > 0) {
387                 g_source_remove(alc_timeout_id);
388                 alc_timeout_id = 0;
389         }
390
391         return 0;
392 }
393
394 void set_brightness_changed_state(void)
395 {
396         if (get_pm_status_flag() & PWRSV_FLAG) {
397                 set_pm_status_flag(BRTCH_FLAG);
398                 _D("Brightness changed in low battery,"
399                     "escape dim state (light).");
400         }
401 }
402
403 static int set_autobrightness_state(int status)
404 {
405         int ret = -1;
406         int brt = -1;
407         int default_brt = -1;
408
409         assert(g_display_plugin.config);
410
411         if (status == SETTING_BRIGHTNESS_AUTOMATIC_ON) {
412                 if (connect_sensor() < 0)
413                         return -1;
414
415                 /* escape dim state if it's in low battery.*/
416                 set_brightness_changed_state();
417
418                 /* change alc action func */
419                 if (_default_action == NULL)
420                         _default_action = state_st(DEVICED_DISPLAY_STATE_ON)->action;
421                 state_st(DEVICED_DISPLAY_STATE_ON)->action = alc_action;
422
423                 update_auto_brightness(true);
424
425                 alc_timeout_id =
426                     g_timeout_add_seconds(g_display_plugin.config->lightsensor_interval,
427                             alc_handler, NULL);
428         } else if (status == SETTING_BRIGHTNESS_AUTOMATIC_PAUSE) {
429                 _I("Auto brightness paused.");
430                 disconnect_sensor();
431         } else {
432                 disconnect_sensor();
433                 /* escape dim state if it's in low battery.*/
434                 set_brightness_changed_state();
435
436                 ret = get_setting_brightness(&default_brt);
437                 if (ret != 0 || (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS)) {
438                         _I("Failed to read vconf value for brightness.");
439                         brt = g_display_plugin.config->pm_default_brightness;
440                         if (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS) {
441                                 ret = vconf_set_int(VCONFKEY_SETAPPL_LCD_BRIGHTNESS, brt);
442                                 if (ret < 0)
443                                         _E("Failed to set vconf value for lcd brightness: %d", vconf_get_ext_errno());
444                         }
445                         default_brt = brt;
446                 }
447
448                 display_backlight_set_default_brightness(default_brt);
449                 display_backlight_update_by_default_brightness();
450         }
451
452         return 0;
453 }
454
455 static void set_alc_function(keynode_t *key_nodes, void *data)
456 {
457         int status, ret;
458
459         if (key_nodes == NULL) {
460                 _E("Wrong parameter, key_nodes is null.");
461                 return;
462         }
463
464         status = vconf_keynode_get_int(key_nodes);
465
466         switch (status) {
467         case SETTING_BRIGHTNESS_AUTOMATIC_OFF:
468         case SETTING_BRIGHTNESS_AUTOMATIC_ON:
469         case SETTING_BRIGHTNESS_AUTOMATIC_PAUSE:
470                 ret = set_autobrightness_state(status);
471                 _D("Set auto brightness: %d", ret);
472                 break;
473         default:
474                 _E("Invalid value(%d)", status);
475         }
476 }
477
478 static void set_alc_automatic_brt(keynode_t *key_nodes, void *data)
479 {
480         if (key_nodes == NULL) {
481                 _E("Wrong parameter, key_nodes is null.");
482                 return;
483         }
484         automatic_brt = vconf_keynode_get_int(key_nodes) / AUTOMATIC_DEVIDE_VAL;
485         _D("Automatic brt(%d)", automatic_brt);
486
487         alc_update_brt(true);
488 }
489
490 static gboolean update_handler(void *data)
491 {
492         int ret, on;
493
494         update_timeout = 0;
495
496         if (get_pm_cur_state() != DEVICED_DISPLAY_STATE_ON)
497                 return G_SOURCE_REMOVE;
498
499         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &on);
500         if (ret < 0 || on != SETTING_BRIGHTNESS_AUTOMATIC_ON) {
501                 if (ret < 0)
502                         _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
503                 return G_SOURCE_REMOVE;
504         }
505
506         _D("Auto brightness is working.");
507         alc_update_brt(true);
508
509         return G_SOURCE_REMOVE;
510 }
511
512 void update_auto_brightness(bool update)
513 {
514         if (update_timeout) {
515                 g_source_remove(update_timeout);
516                 update_timeout = 0;
517         }
518
519         if (update) {
520                 update_timeout = g_timeout_add(AUTOMATIC_DELAY_TIME,
521                     update_handler, NULL);
522         }
523 }
524
525 static int prepare_lsensor(void *data)
526 {
527         int status, ret;
528         int brt;
529
530         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
531
532         if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
533                 set_autobrightness_state(status);
534         else if (ret < 0)
535                 _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
536
537         /* add auto_brt_setting change handler */
538         vconf_notify_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
539                                  set_alc_function, NULL);
540
541         ret = vconf_get_int(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS, &brt);
542         if (ret < 0) {
543                 brt = -1;
544                 _E("Failed to get vconf value for automatic lcd brightness: %d", vconf_get_ext_errno());
545         }
546         if (brt < PM_MIN_BRIGHTNESS || brt > PM_MAX_BRIGHTNESS) {
547                 _E("Failed to get automatic brightness.");
548         } else {
549                 automatic_brt = brt / AUTOMATIC_DEVIDE_VAL;
550                 _I("Automatic brt(%d) init success.", automatic_brt);
551         }
552
553         vconf_notify_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
554                                 set_alc_automatic_brt, NULL);
555
556         return 0;
557 }
558
559 static void update_brightness_direct(void)
560 {
561         int ret, status;
562
563         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
564         if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
565                 alc_update_brt(true);
566         else if (ret < 0)
567                 _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
568 }
569
570 static int set_autobrightness_min(int val, char *name)
571 {
572         if (!name)
573                 return -EINVAL;
574
575         if (val < PM_MIN_BRIGHTNESS || val > PM_MAX_BRIGHTNESS)
576                 return -EINVAL;
577
578         min_brightness = val;
579
580         if (min_brightness_name) {
581                 free(min_brightness_name);
582                 min_brightness_name = 0;
583         }
584         min_brightness_name = strndup(name, strlen(name));
585
586         update_brightness_direct();
587
588         _I("Auto brightness min value changed. min_brightness=%d min_brightness_name=%s",
589             min_brightness, min_brightness_name);
590
591         return 0;
592 }
593
594 static void reset_autobrightness_min(GDBusConnection *conn,
595         const gchar     *sender,
596         const gchar     *unique_name,
597         gpointer         data)
598 {
599         if (!sender)
600                 return;
601
602         if (!min_brightness_name)
603                 return;
604
605         if (strcmp(sender, min_brightness_name))
606                 return;
607
608         _I("Change to default min brightness. before=%d changed=%d brightness_name=%s", min_brightness,
609             PM_MIN_BRIGHTNESS, min_brightness_name);
610         min_brightness = PM_MIN_BRIGHTNESS;
611         if (min_brightness_name) {
612                 free(min_brightness_name);
613                 min_brightness_name = 0;
614         }
615
616         update_brightness_direct();
617 }
618
619 static int lcd_changed_cb(void *data)
620 {
621         int lcd_state;
622
623         if (!data)
624                 return 0;
625         lcd_state = *(int *)data;
626         if (lcd_state == DEVICED_DISPLAY_STATE_OFF && alc_timeout_id > 0) {
627                 g_source_remove(alc_timeout_id);
628                 alc_timeout_id = 0;
629         }
630
631         return 0;
632 }
633
634 static int delayed_init_done(void *data)
635 {
636         int state;
637
638         if (!data)
639                 return 0;
640
641         state = *(int *)data;
642         if (state != true)
643                 return 0;
644
645         /* get light data from sensor fw */
646         prepare_lsensor(NULL);
647
648         return 0;
649 }
650
651 static void exit_lsensor(void)
652 {
653         vconf_ignore_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
654             set_alc_function);
655
656         vconf_ignore_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
657             set_alc_automatic_brt);
658
659         set_autobrightness_state(SETTING_BRIGHTNESS_AUTOMATIC_OFF);
660 }
661
662 static void auto_brightness_init(void *data)
663 {
664         g_display_plugin.set_autobrightness_min = set_autobrightness_min;
665         g_display_plugin.reset_autobrightness_min = reset_autobrightness_min;
666
667         syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_LCD, lcd_changed_cb);
668         syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_DELAYED_INIT, delayed_init_done);
669 }
670
671 static void auto_brightness_exit(void *data)
672 {
673         exit_lsensor();
674
675         syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_LCD, lcd_changed_cb);
676         syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_DELAYED_INIT, delayed_init_done);
677 }
678
679 static const struct display_ops display_autobrightness_ops = {
680         .name     = "auto-brightness",
681         .init     = auto_brightness_init,
682         .exit     = auto_brightness_exit,
683 };
684
685 DISPLAY_OPS_REGISTER(&display_autobrightness_ops)