4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
26 #include <eventsystem.h>
27 #include <hal/device/hal-battery.h>
28 #include <libsyscommon/libgdbus.h>
29 #include <libsyscommon/list.h>
31 #include "lowbat-handler.h"
32 #include "battery-ops.h"
36 #include "shared/devices.h"
37 #include "shared/device-notifier.h"
38 #include "shared/common.h"
39 #include "core/udev.h"
40 #include "shared/eventsystem.h"
41 #include "shared/plugin.h"
42 #include "display/setting.h"
43 #include "display/poll.h"
44 #include "display/display-ops.h"
45 #include "power/power-handler.h"
46 #include "apps/apps.h"
47 #include "power-supply.h"
49 #define BATTERY_UNKNOWN -1
50 #define BATTERY_DISABLED 0
51 #define BATTERY_ENABLED 1
53 #define VCONF_KEY_BATTERY_WARNING_LEVEL "db/sysman/battery_warning_level"
55 #define MIN_INOW_VALUE -50
57 enum custom_level_control_type {
63 struct lowbat_process_entry {
66 int (*func) (void *data);
69 static struct display_plugin *disp_plgn;
70 static struct battery_plugin *battery_plgn;
72 static struct battery_status *battery;
74 static int cur_bat_state = BATTERY_UNKNOWN;
75 static int cur_bat_capacity = BATTERY_UNKNOWN;
76 static int custom_warning_level = BATTERY_UNKNOWN;
77 static bool custom_warning_launch;
79 struct battery_config_info battery_info = {
80 .normal = BATTERY_NORMAL,
81 .warning = BATTERY_WARNING,
82 .critical = BATTERY_CRITICAL,
83 .poweroff = BATTERY_POWEROFF,
84 .realoff = BATTERY_REALOFF,
88 static int scenario_count;
90 static guint low_batt_sig_timer;
92 static int uevent_buffering = 0;
93 static int lowbat_monitor_init(void *data);
95 static int lowbat_initialized(void *data)
97 static int status = BATTERY_UNKNOWN;
102 status = *(int *)data;
106 static int lowbat_scenario(int old, int now, void *data)
109 struct lowbat_process_entry *scenario;
112 if (old == now && battery->charge_now)
114 SYS_G_LIST_FOREACH(lpe, n, scenario) {
115 if (old != scenario->old || now != scenario->now)
119 scenario->func(data);
126 static int lowbat_add_scenario(int old, int now, int (*func)(void *data))
128 struct lowbat_process_entry *scenario;
130 _D("Lowbat_add_scenario: old(%d) now(%d) func(%p)", old, now, func);
133 _E("Invalid func address.");
137 scenario = malloc(sizeof(struct lowbat_process_entry));
139 _E("Failed to malloc for notifier.");
145 scenario->func = func;
147 SYS_G_LIST_APPEND(lpe, scenario);
152 static int power_execute(void *data)
154 static const struct device_ops *ops;
157 CRITICAL_LOG("Poweroff by lowbattery is disabled at emulator.");
161 FIND_DEVICE_INT(ops, POWER_OPS_NAME);
163 return ops->execute(data);
166 static int delayed_init_done(void *data)
178 status = lowbat_initialized(NULL);
179 if (status == BATTERY_UNKNOWN)
180 lowbat_monitor_init(NULL);
188 if (battery->charge_now != CHARGER_CHARGING &&
189 battery->charge_full != CHARGING_FULL)
190 lowbat_popup(BAT_OPT_NONE);
192 _I("Skip low battery popup during charging.");
198 int lowbat_popup(int option)
200 static int launched_poweroff;
201 static int lowbat_popup_option;
204 if (option == BAT_OPT_NONE) {
211 if (option == BAT_OPT_POWEROFF)
212 launched_poweroff = 0;
215 case BAT_OPT_CRITICAL:
216 value = "lowbattery_critical";
218 case BAT_OPT_WARNING:
219 value = "lowbattery_warning";
221 case BAT_OPT_POWEROFF:
224 case BAT_OPT_ERR_TEMP_LOW:
225 value = "chargeerrlow";
227 case BAT_OPT_ERR_TEMP_HIGH:
228 value = "chargeerrhigh";
230 case BAT_OPT_ERR_CF_OPEN:
231 value = "battdisconnect";
237 lowbat_popup_option = option;
240 _D("Popup value=%s", value);
241 if (delayed_init_done(NULL)) {
243 if (launched_poweroff == 1) {
244 _I("Will be foreced power off.");
245 power_execute(POWER_POWEROFF);
249 if (lowbat_popup_option == BAT_OPT_POWEROFF)
250 launched_poweroff = 1;
252 if (battery_do_not_disturb()) {
253 _I("block LCD and %s Popup", value);
256 if (lowbat_popup_option == BAT_OPT_ERR_TEMP_LOW ||
257 lowbat_popup_option == BAT_OPT_ERR_TEMP_HIGH ||
258 lowbat_popup_option == BAT_OPT_ERR_CF_OPEN) {
259 if (disp_plgn->pm_change_internal)
260 disp_plgn->pm_change_internal(INTERNAL_LOCK_LOWBAT, LCD_DIM);
262 if (disp_plgn->pm_change_internal)
263 disp_plgn->pm_change_internal(INTERNAL_LOCK_LOWBAT, LCD_NORMAL);
265 if (lowbat_popup_option == BAT_OPT_ERR_TEMP_LOW ||
266 lowbat_popup_option == BAT_OPT_ERR_TEMP_HIGH ||
267 lowbat_popup_option == BAT_OPT_ERR_CF_OPEN)
268 return launch_system_app(APP_ABNORMAL,
269 2, APP_KEY_TYPE, value);
270 return launch_system_app(APP_DEFAULT,
271 2, APP_KEY_TYPE, value);
273 _D("Boot-animation running yet.");
278 static int battery_warning_low_act(void *data)
280 if (battery_plgn->lowbat_noti_launch)
281 battery_plgn->lowbat_noti_launch(cur_bat_capacity, BAT_OPT_WARNING);
283 (void)lowbat_popup(BAT_OPT_WARNING);
287 static int battery_critical_low_act(void *data)
289 if (battery_plgn->lowbat_noti_launch)
290 battery_plgn->lowbat_noti_launch(cur_bat_capacity, BAT_OPT_CRITICAL);
292 (void)lowbat_popup(BAT_OPT_CRITICAL);
296 int battery_power_off_act(void *data)
298 CRITICAL_LOG("Low battery power off.");
299 return power_execute(POWER_POWEROFF);
302 int battery_charge_err_cf_act(void *data)
304 return lowbat_popup(BAT_OPT_ERR_CF_OPEN);
307 int battery_charge_err_low_act(void *data)
309 if (disp_plgn->pm_lock_internal)
310 disp_plgn->pm_lock_internal(INTERNAL_LOCK_OVERCOOL, LCD_OFF, STAY_CUR_STATE, 60000);
311 return lowbat_popup(BAT_OPT_ERR_TEMP_LOW);
314 int battery_charge_err_high_act(void *data)
316 if (disp_plgn->pm_lock_internal)
317 disp_plgn->pm_lock_internal(INTERNAL_LOCK_OVERHEAT, LCD_OFF, STAY_CUR_STATE, 60000);
318 return lowbat_popup(BAT_OPT_ERR_TEMP_HIGH);
321 static void lowbat_scenario_init(void)
325 ret = vconf_get_int(VCONF_KEY_BATTERY_WARNING_LEVEL, &custom_warning_level);
327 _E("Failed to get vconf value for battery warning level: %d", vconf_get_ext_errno());
329 if (custom_warning_level != BATTERY_UNKNOWN)
330 battery_info.warning = custom_warning_level;
332 lowbat_add_scenario(battery_info.normal, battery_info.warning, battery_warning_low_act);
333 lowbat_add_scenario(battery_info.normal, battery_info.critical, battery_critical_low_act);
334 lowbat_add_scenario(battery_info.normal, battery_info.poweroff, battery_critical_low_act);
335 lowbat_add_scenario(battery_info.normal, battery_info.realoff, battery_power_off_act);
336 lowbat_add_scenario(battery_info.warning, battery_info.warning, battery_warning_low_act);
337 lowbat_add_scenario(battery_info.warning, battery_info.critical, battery_critical_low_act);
338 lowbat_add_scenario(battery_info.warning, battery_info.poweroff, battery_critical_low_act);
339 lowbat_add_scenario(battery_info.warning, battery_info.realoff, battery_power_off_act);
340 lowbat_add_scenario(battery_info.critical, battery_info.critical, battery_critical_low_act);
341 lowbat_add_scenario(battery_info.critical, battery_info.realoff, battery_power_off_act);
342 lowbat_add_scenario(battery_info.poweroff, battery_info.poweroff, battery_critical_low_act);
343 lowbat_add_scenario(battery_info.poweroff, battery_info.realoff, battery_power_off_act);
344 lowbat_add_scenario(battery_info.realoff, battery_info.realoff, battery_power_off_act);
347 static void battery_level_send_system_event(int bat_percent)
350 static const char *prev;
352 if (bat_percent >= battery_info.normal && battery->charge_full == CHARGING_FULL)
353 str = EVT_VAL_BATTERY_LEVEL_FULL;
354 else if (bat_percent > battery_info.warning)
355 str = EVT_VAL_BATTERY_LEVEL_HIGH;
356 else if (bat_percent > battery_info.critical)
357 str = EVT_VAL_BATTERY_LEVEL_LOW;
358 else if (bat_percent > battery_info.poweroff)
359 str = EVT_VAL_BATTERY_LEVEL_CRITICAL;
361 str = EVT_VAL_BATTERY_LEVEL_EMPTY;
363 if (battery_plgn->lowbat_noti_clean && prev)
364 battery_plgn->lowbat_noti_clean(prev, str);
371 _D("System_event: %s", str);
372 CRITICAL_LOG("Battery %s.", str);
373 event_system_send(SYS_EVENT_BATTERY_LEVEL_STATUS,
374 EVT_KEY_BATTERY_LEVEL_STATUS, str);
377 static void change_lowbat_level(int bat_percent)
381 ret = vconf_get_int(VCONFKEY_SYSMAN_BATTERY_LEVEL_STATUS, &prev);
383 _E("Failed to get vconf value for battery level status: %d", vconf_get_ext_errno());
387 if (bat_percent >= battery_info.normal) {
388 if (battery->charge_full == CHARGING_FULL)
389 now = VCONFKEY_SYSMAN_BAT_LEVEL_FULL;
391 now = VCONFKEY_SYSMAN_BAT_LEVEL_HIGH;
392 } else if (bat_percent > battery_info.warning)
393 now = VCONFKEY_SYSMAN_BAT_LEVEL_HIGH;
394 else if (bat_percent > battery_info.critical)
395 now = VCONFKEY_SYSMAN_BAT_LEVEL_LOW;
396 else if (bat_percent > battery_info.poweroff)
397 now = VCONFKEY_SYSMAN_BAT_LEVEL_CRITICAL;
399 now = VCONFKEY_SYSMAN_BAT_LEVEL_EMPTY;
402 ret = vconf_set_int(VCONFKEY_SYSMAN_BATTERY_LEVEL_STATUS, now);
404 _E("Failed to set vconf value for battery level status: %d", vconf_get_ext_errno());
408 static void lowbat_custom_popup(int online, int capacity)
410 static int custom_type = BATTERY_UNKNOWN;
412 if (custom_type == CUSTOM_CONTROL_NONE)
414 if (custom_warning_level == BATTERY_UNKNOWN ||
415 custom_warning_level == battery_info.warning)
418 if (custom_type == BATTERY_UNKNOWN) {
419 if (custom_warning_level < battery_info.warning)
420 custom_type = LOWER_THAN_WARNING;
421 else if (custom_warning_level > battery_info.warning)
422 custom_type = HIGHER_THAN_WARNING;
424 custom_type = CUSTOM_CONTROL_NONE;
425 _E("Custom type is not defined: %d", custom_warning_level);
430 if (custom_type == LOWER_THAN_WARNING && capacity <= battery_info.critical)
432 if (custom_type == HIGHER_THAN_WARNING && capacity <= battery_info.warning)
435 if (capacity > custom_warning_level) {
436 custom_warning_launch = false;
439 if (online == battery->charger_connected && custom_warning_launch)
441 if (battery->charger_connected == 1 && custom_warning_launch)
444 custom_warning_launch = true;
445 _I("Launch custom lowbattery warning popup. online(%d, %d), capacity(%d, %d)", online, battery->charger_connected, capacity, custom_warning_level);
446 (void)battery_warning_low_act(NULL);
449 void check_extreme_status(int status)
454 if (status <= VCONFKEY_SYSMAN_BAT_POWER_OFF ||
455 status > VCONFKEY_SYSMAN_BAT_FULL)
457 ret = vconf_get_int(VCONFKEY_PM_KEY_IGNORE, &extreme);
459 _E("Failed to get vconf value for pm key ignore: %d", vconf_get_ext_errno());
460 if (ret != 0 || extreme != 1)
463 ret = vconf_set_int(VCONFKEY_PM_KEY_IGNORE, 0);
465 _E("Failed to set vconf value for pm key ignore: %d", vconf_get_ext_errno());
468 _I("Release key ignore.");
471 static int lowbat_process(int bat_percent, void *ad)
474 static bool low_bat_old;
475 static int low_bat_skip_cnt = 0;
476 int new_bat_capacity;
478 int vconf_state = -1;
481 bool low_bat = false;
484 if (!battery_initialized)
487 new_bat_capacity = bat_percent;
488 change_lowbat_level(new_bat_capacity);
489 lowbat_custom_popup(online, new_bat_capacity);
490 battery_level_send_system_event(new_bat_capacity);
492 cur_bat_capacity = new_bat_capacity;
494 ret = vconf_get_int(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, &vconf_state);
496 _E("Failed to get vconf value for battery status low: %d", vconf_get_ext_errno());
500 if (new_bat_capacity <= battery_info.realoff) {
501 if (battery->charge_now == CHARGER_CHARGING || battery->charger_charging == CHARGER_DISABLED) {
502 _I("Skip lowbat poweroff during test (c:%d charge:%d online:%d charger_charging:%d)",
503 battery->capacity, battery->charge_now, battery->charger_connected, battery->charger_charging);
504 new_bat_state = battery_info.poweroff;
505 if (vconf_state != VCONFKEY_SYSMAN_BAT_POWER_OFF)
506 status = VCONFKEY_SYSMAN_BAT_POWER_OFF;
508 new_bat_state = battery_info.realoff;
509 if (vconf_state != VCONFKEY_SYSMAN_BAT_REAL_POWER_OFF)
510 status = VCONFKEY_SYSMAN_BAT_REAL_POWER_OFF;
512 } else if (new_bat_capacity <= battery_info.poweroff) {
513 new_bat_state = battery_info.poweroff;
514 if (vconf_state != VCONFKEY_SYSMAN_BAT_POWER_OFF)
515 status = VCONFKEY_SYSMAN_BAT_POWER_OFF;
516 } else if (new_bat_capacity <= battery_info.critical) {
517 new_bat_state = battery_info.critical;
518 if (vconf_state != VCONFKEY_SYSMAN_BAT_CRITICAL_LOW)
519 status = VCONFKEY_SYSMAN_BAT_CRITICAL_LOW;
520 } else if (new_bat_capacity <= battery_info.warning) {
521 new_bat_state = battery_info.warning;
522 if (vconf_state != VCONFKEY_SYSMAN_BAT_WARNING_LOW)
523 status = VCONFKEY_SYSMAN_BAT_WARNING_LOW;
525 new_bat_state = battery_info.normal;
526 if (new_bat_capacity == BATTERY_FULL) {
527 if (battery->charge_full) {
528 if (vconf_state != VCONFKEY_SYSMAN_BAT_FULL)
529 status = VCONFKEY_SYSMAN_BAT_FULL;
531 if (vconf_state != VCONFKEY_SYSMAN_BAT_NORMAL)
532 status = VCONFKEY_SYSMAN_BAT_NORMAL;
535 if (vconf_state != VCONFKEY_SYSMAN_BAT_NORMAL)
536 status = VCONFKEY_SYSMAN_BAT_NORMAL;
540 /* If the battery continues to run out even though it is being charged
541 * for a certain period of time, turn off the device. */
542 if (new_bat_capacity <= battery_info.realoff && battery->charge_now == CHARGER_CHARGING) {
543 if (low_bat_skip_cnt >= RETRY_MAX) {
544 new_bat_state = battery_info.realoff;
545 status = VCONFKEY_SYSMAN_BAT_REAL_POWER_OFF;
546 _I("Go to real poweroff inspite of charging (c:%d charge:%d online:%d charger_charging %d current now %d)",
547 battery->capacity, battery->charge_now, battery->charger_connected, battery->charger_charging, battery->current_now);
548 low_bat_skip_cnt = 0;
549 } else if (battery->current_now <= MIN_INOW_VALUE) {
552 low_bat_skip_cnt = 0;
555 low_bat_skip_cnt = 0;
560 if (disp_plgn->update_pm_setting)
561 disp_plgn->update_pm_setting(SETTING_LOW_BATT, status);
563 result = vconf_state;
565 check_extreme_status(status);
567 if (new_bat_capacity <= battery_info.warning)
570 if (low_bat_old != low_bat) {
571 device_notify(DEVICE_NOTIFIER_LOWBAT, (void *)&low_bat);
572 low_bat_old = low_bat;
575 if (battery->charger_connected == -1)
577 if (cur_bat_state == new_bat_state && online == battery->charger_connected)
580 online = battery->charger_connected;
581 if (cur_bat_state == BATTERY_UNKNOWN)
582 cur_bat_state = battery_info.normal;
583 if (lowbat_scenario(cur_bat_state, new_bat_state, NULL))
584 _I("Cur(%d) new(%d) capacity(%d).", cur_bat_state, new_bat_state, bat_percent);
585 cur_bat_state = new_bat_state;
590 static int check_lowbat_percent(int *pct)
594 bat_percent = battery->capacity;
597 if (bat_percent > 100)
599 change_lowbat_level(bat_percent);
600 battery_level_send_system_event(bat_percent);
605 static int lowbat_monitor(void *data)
609 r = lowbat_initialized(NULL);
610 if (r != BATTERY_ENABLED)
611 return battery->charging_level;
614 r = check_lowbat_percent(&bat_percent);
616 return battery->charging_level;
618 bat_percent = *(int *)data;
619 return lowbat_process(bat_percent, NULL);
622 static int lowbat_monitor_init(void *data)
626 status = lowbat_initialized(NULL);
627 if (status != BATTERY_UNKNOWN)
630 status = BATTERY_ENABLED;
631 lowbat_initialized(&status);
633 /* it's called just this once. */
634 unregister_notifier(DEVICE_NOTIFIER_POWER_SUPPLY, lowbat_monitor_init);
636 /* load battery configuration file */
637 battery_config_load(&battery_info);
638 _I("Battery conf: %d %d %d %d %d", battery_info.normal, battery_info.warning,
639 battery_info.critical, battery_info.poweroff, battery_info.realoff);
641 lowbat_scenario_init();
642 check_lowbat_percent(&battery->capacity);
643 lowbat_process(battery->capacity, NULL);
648 static GVariant * dbus_set_lowbat_level(GDBusConnection *conn,
649 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
650 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
656 g_variant_get(param, "(i)", &now);
658 status = lowbat_initialized(NULL);
659 if (status != BATTERY_ENABLED) {
660 _E("Invalid lowbat handler.");
665 if (now <= battery_info.warning && now != BATTERY_UNKNOWN) {
666 _E("Invalid level is requested: %d", now);
671 custom_warning_level = now;
672 custom_warning_launch = false;
674 ret = vconf_set_int(VCONF_KEY_BATTERY_WARNING_LEVEL, custom_warning_level);
676 _E("Failed to set vconf value for battery warning level: %d", vconf_get_ext_errno());
677 _D("Custom warning level is changed to %d.", custom_warning_level);
680 return g_variant_new("(i)", ret);
683 static GVariant * dbus_get_lowbat_level(GDBusConnection *conn,
684 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
685 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
687 _D("Warning level: %d", custom_warning_level);
688 return g_variant_new("(i)", custom_warning_level);
691 static const dbus_method_s dbus_methods[] = {
692 { "SetLowbatLevel", "i", "i", dbus_set_lowbat_level },
693 { "GetLowbatLevel", NULL, "i", dbus_get_lowbat_level },
694 /* Add methods here */
697 static const dbus_interface_u dbus_interface = {
699 .name = DEVICED_INTERFACE_BATTERY,
700 .methods = dbus_methods,
701 .nr_methods = ARRAY_SIZE(dbus_methods),
704 static int lowbat_probe(void *data)
706 static const struct device_ops *ops;
709 FIND_DEVICE_INT(ops, "power_supply");
710 ret = ops->probe(data);
712 _I("Support lowbat handler.");
717 static void lowbat_init(void *data)
721 register_notifier(DEVICE_NOTIFIER_DELAYED_INIT, delayed_init_done);
722 register_notifier(DEVICE_NOTIFIER_POWER_SUPPLY, lowbat_monitor_init);
724 ret = gdbus_add_object(NULL, DEVICED_PATH_BATTERY, &dbus_interface);
726 _E("Failed to init dbus method: %d", ret);
729 static void lowbat_exit(void *data)
731 int status = BATTERY_DISABLED;
733 lowbat_initialized(&status);
734 if (low_batt_sig_timer) {
735 g_source_remove(low_batt_sig_timer);
736 low_batt_sig_timer = 0;
740 static gboolean low_battery_charging_status(void *data)
742 low_batt_sig_timer = 0;
743 battery->charging_level = lowbat_monitor(data);
744 if (battery->charging_level > 0 && old_battery.charging_level != battery->charging_level) {
745 if (vconf_set_int(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, battery->charging_level) < 0)
746 _E("Fail to set level.");
747 if (power_supply_broadcast(CHARGE_LEVEL_SIGNAL, battery->charging_level) < 0)
748 _E("power_supply_broadcast failed");
750 return G_SOURCE_REMOVE;
753 static int lowbat_execute(void *data)
755 int capacity = *(int *)data;
757 /* In battery kernel side, it has a requirement that battery charging event must get quickly.
758 * So deviced receives the discharging and charging event within 1.5 sec,
759 * added a timer to wait for the second signal, if there is no second signal then execute
760 * the lowbat_execute.
762 if (low_batt_sig_timer) {
763 g_source_remove(low_batt_sig_timer);
764 low_batt_sig_timer = 0;
767 /* Do lowbat_process immediately rather deferring it when poweroff is needed.
768 * This prevents poweroff from being delayed infinitely when the uevent continues
769 * to occur shorter than 1.5 seconds on realoff capacity */
770 if (capacity <= battery_info.realoff || !uevent_buffering)
771 low_battery_charging_status(data);
773 low_batt_sig_timer = g_timeout_add(1500, low_battery_charging_status, data);
778 void lowbat_enable_uevent_buffering(void)
780 uevent_buffering = 1;
783 void lowbat_disable_uevent_buffering(void)
785 uevent_buffering = 0;
788 static const struct device_ops lowbat_device_ops = {
789 DECLARE_NAME_LEN("lowbat"),
790 .probe = lowbat_probe,
792 .execute = lowbat_execute,
796 DEVICE_OPS_REGISTER(&lowbat_device_ops)
798 static void __CONSTRUCTOR__ initialize(void)
800 disp_plgn = get_var_display_plugin();
802 _E("Failed to get display plugin variable.");
804 battery_plgn = get_var_battery_plugin();
806 _E("Failed to get battery plugin variable.");
808 battery = get_var_battery_status();
810 _E("Failed to get battery status structure variable.");