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>
30 #include <libsyscommon/common.h>
31 #include <system/syscommon-plugin-deviced-common-interface.h>
33 #include "lowbat-handler.h"
34 #include "battery-ops.h"
36 #include "battery-parser.h"
38 #include "shared/devices.h"
39 #include "shared/device-notifier.h"
40 #include "shared/common.h"
41 #include "shared/apps.h"
42 #include "shared/eventsystem.h"
43 #include "shared/plugin.h"
46 #include "display-lock.h"
47 #include "display-ops.h"
48 #include "display-plugin.h"
49 #include "display-state-transition.h"
50 #include "power/power.h"
51 #include "power/power-off.h"
52 #include "power-supply.h"
54 #define BATTERY_UNKNOWN -1
55 #define BATTERY_DISABLED 0
56 #define BATTERY_ENABLED 1
58 #define VCONF_KEY_BATTERY_WARNING_LEVEL "db/sysman/battery_warning_level"
60 #define MIN_INOW_VALUE -50
62 enum custom_level_control_type {
68 struct lowbat_process_entry {
71 int (*func) (void *data);
74 static struct battery_plugin *battery_plgn;
76 static struct battery_status *battery;
78 static int cur_bat_state = BATTERY_UNKNOWN;
79 static int cur_bat_capacity = BATTERY_UNKNOWN;
80 static int custom_warning_level = BATTERY_UNKNOWN;
81 static bool custom_warning_launch;
83 struct battery_config_info battery_info = {
84 .normal = BATTERY_NORMAL,
85 .warning = BATTERY_WARNING,
86 .critical = BATTERY_CRITICAL,
87 .poweroff = BATTERY_POWEROFF,
91 static int scenario_count;
93 static guint low_batt_sig_timer;
95 static int uevent_buffering = 0;
96 static int lowbat_monitor_init(void *data);
98 static int lowbat_initialized(void *data)
100 static int status = BATTERY_UNKNOWN;
105 status = *(int *)data;
109 static int lowbat_scenario(int old, int now, void *data)
112 struct lowbat_process_entry *scenario;
115 if (old == now && battery->charge_now)
117 SYS_G_LIST_FOREACH(lpe, n, scenario) {
118 if (old != scenario->old || now != scenario->now)
122 scenario->func(data);
129 static int lowbat_add_scenario(int old, int now, int (*func)(void *data))
131 struct lowbat_process_entry *scenario;
133 _D("Lowbat_add_scenario: old(%d) now(%d) func(%p)", old, now, func);
136 _E("Invalid func address.");
140 scenario = malloc(sizeof(struct lowbat_process_entry));
142 _E("Failed to malloc for notifier.");
148 scenario->func = func;
150 SYS_G_LIST_APPEND(lpe, scenario);
155 static int power_execute(int state)
157 if (syscommon_is_emulator()) {
158 CRITICAL_LOG("Poweroff by lowbattery is disabled at emulator.");
162 power_request_change_state(state, LOWBAT_POWEROFF_REASON);
167 static int delayed_init_done(void *data)
179 status = lowbat_initialized(NULL);
180 if (status == BATTERY_UNKNOWN)
181 lowbat_monitor_init(NULL);
189 if (battery->charge_now != CHARGER_CHARGING &&
190 battery->charge_full != CHARGING_FULL)
191 lowbat_popup(BAT_OPT_NONE);
193 _I("Skip low battery popup during charging.");
199 int lowbat_popup(int option)
201 static int launched_poweroff;
202 static int lowbat_popup_option;
205 if (option == BAT_OPT_NONE) {
212 if (option == BAT_OPT_POWEROFF)
213 launched_poweroff = 0;
216 case BAT_OPT_CRITICAL:
217 value = "lowbattery_critical";
219 case BAT_OPT_WARNING:
220 value = "lowbattery_warning";
222 case BAT_OPT_POWEROFF:
225 case BAT_OPT_ERR_TEMP_LOW:
226 value = "chargeerrlow";
228 case BAT_OPT_ERR_TEMP_HIGH:
229 value = "chargeerrhigh";
231 case BAT_OPT_ERR_CF_OPEN:
232 value = "battdisconnect";
238 lowbat_popup_option = option;
241 _D("Popup value=%s", value);
242 if (delayed_init_done(NULL)) {
244 if (launched_poweroff == 1) {
245 _I("Will be foreced power off.");
246 power_execute(DEVICED_POWER_STATE_POWEROFF);
250 if (lowbat_popup_option == BAT_OPT_POWEROFF)
251 launched_poweroff = 1;
253 if (battery_do_not_disturb()) {
254 _I("block LCD and %s Popup", value);
257 if (lowbat_popup_option == BAT_OPT_ERR_TEMP_LOW ||
258 lowbat_popup_option == BAT_OPT_ERR_TEMP_HIGH ||
259 lowbat_popup_option == BAT_OPT_ERR_CF_OPEN) {
260 display_state_transition_request_state_transition_with_option(DEVICED_EVENT_BATTERY_CAPACITY_LOW, LCD_DIM);
262 display_state_transition_request_state_transition_with_option(DEVICED_EVENT_BATTERY_CAPACITY_LOW, LCD_NORMAL);
264 if (lowbat_popup_option == BAT_OPT_ERR_TEMP_LOW ||
265 lowbat_popup_option == BAT_OPT_ERR_TEMP_HIGH ||
266 lowbat_popup_option == BAT_OPT_ERR_CF_OPEN)
267 return launch_system_app(APP_ABNORMAL,
268 2, APP_KEY_TYPE, value);
269 return launch_system_app(APP_DEFAULT,
270 2, APP_KEY_TYPE, value);
272 _D("Boot-animation running yet.");
277 static int battery_warning_low_act(void *data)
279 if (battery_plgn->lowbat_noti_launch)
280 battery_plgn->lowbat_noti_launch(cur_bat_capacity, BAT_OPT_WARNING);
282 (void)lowbat_popup(BAT_OPT_WARNING);
286 static int battery_critical_low_act(void *data)
288 if (battery_plgn->lowbat_noti_launch)
289 battery_plgn->lowbat_noti_launch(cur_bat_capacity, BAT_OPT_CRITICAL);
291 (void)lowbat_popup(BAT_OPT_CRITICAL);
295 static int battery_power_off_act(void *data)
297 CRITICAL_LOG("Low battery power off.");
298 return power_execute(DEVICED_POWER_STATE_POWEROFF);
301 int battery_charge_err_cf_act(void *data)
303 return lowbat_popup(BAT_OPT_ERR_CF_OPEN);
306 int battery_charge_err_low_act(void *data)
308 display_lock_request_lock_with_option(DEVICED_EVENT_BATTERY_HEALTH_OVERCOOL, LCD_OFF, STAY_CUR_STATE, 60000);
310 return lowbat_popup(BAT_OPT_ERR_TEMP_LOW);
313 int battery_charge_err_high_act(void *data)
315 display_lock_request_lock_with_option(DEVICED_EVENT_BATTERY_HEALTH_OVERHEAT, LCD_OFF, STAY_CUR_STATE, 60000);
317 return lowbat_popup(BAT_OPT_ERR_TEMP_HIGH);
320 static void lowbat_scenario_init(void)
324 ret = vconf_get_int(VCONF_KEY_BATTERY_WARNING_LEVEL, &custom_warning_level);
326 _E("Failed to get vconf value for battery warning level: %d", vconf_get_ext_errno());
328 if (custom_warning_level != BATTERY_UNKNOWN)
329 battery_info.warning = custom_warning_level;
331 lowbat_add_scenario(battery_info.normal, battery_info.warning, battery_warning_low_act);
332 lowbat_add_scenario(battery_info.normal, battery_info.critical, battery_critical_low_act);
333 lowbat_add_scenario(battery_info.normal, battery_info.poweroff, battery_power_off_act);
334 lowbat_add_scenario(battery_info.warning, battery_info.warning, battery_warning_low_act);
335 lowbat_add_scenario(battery_info.warning, battery_info.critical, battery_critical_low_act);
336 lowbat_add_scenario(battery_info.warning, battery_info.poweroff, battery_power_off_act);
337 lowbat_add_scenario(battery_info.critical, battery_info.critical, battery_critical_low_act);
338 lowbat_add_scenario(battery_info.critical, battery_info.poweroff, battery_power_off_act);
339 lowbat_add_scenario(battery_info.poweroff, battery_info.poweroff, battery_power_off_act);
342 static void battery_level_send_system_event(int bat_percent)
345 static const char *prev;
347 if (bat_percent >= battery_info.normal && battery->charge_full == CHARGING_FULL)
348 str = EVT_VAL_BATTERY_LEVEL_FULL;
349 else if (bat_percent > battery_info.warning)
350 str = EVT_VAL_BATTERY_LEVEL_HIGH;
351 else if (bat_percent > battery_info.critical)
352 str = EVT_VAL_BATTERY_LEVEL_LOW;
353 else if (bat_percent > battery_info.poweroff)
354 str = EVT_VAL_BATTERY_LEVEL_CRITICAL;
356 str = EVT_VAL_BATTERY_LEVEL_EMPTY;
358 if (battery_plgn->lowbat_noti_clean && prev)
359 battery_plgn->lowbat_noti_clean(prev, str);
366 _D("System_event: %s", str);
367 CRITICAL_LOG("Battery %s.", str);
368 event_system_send(SYS_EVENT_BATTERY_LEVEL_STATUS,
369 EVT_KEY_BATTERY_LEVEL_STATUS, str);
372 static void change_lowbat_level(int bat_percent)
376 ret = vconf_get_int(VCONFKEY_SYSMAN_BATTERY_LEVEL_STATUS, &prev);
378 _E("Failed to get vconf value for battery level status: %d", vconf_get_ext_errno());
382 if (bat_percent >= battery_info.normal) {
383 if (battery->charge_full == CHARGING_FULL)
384 now = VCONFKEY_SYSMAN_BAT_LEVEL_FULL;
386 now = VCONFKEY_SYSMAN_BAT_LEVEL_HIGH;
387 } else if (bat_percent > battery_info.warning)
388 now = VCONFKEY_SYSMAN_BAT_LEVEL_HIGH;
389 else if (bat_percent > battery_info.critical)
390 now = VCONFKEY_SYSMAN_BAT_LEVEL_LOW;
391 else if (bat_percent > battery_info.poweroff)
392 now = VCONFKEY_SYSMAN_BAT_LEVEL_CRITICAL;
394 now = VCONFKEY_SYSMAN_BAT_LEVEL_EMPTY;
397 ret = vconf_set_int(VCONFKEY_SYSMAN_BATTERY_LEVEL_STATUS, now);
399 _E("Failed to set vconf value for battery level status: %d", vconf_get_ext_errno());
403 static void lowbat_custom_popup(int online, int capacity)
405 static int custom_type = BATTERY_UNKNOWN;
407 if (custom_type == CUSTOM_CONTROL_NONE)
409 if (custom_warning_level == BATTERY_UNKNOWN ||
410 custom_warning_level == battery_info.warning)
413 if (custom_type == BATTERY_UNKNOWN) {
414 if (custom_warning_level < battery_info.warning)
415 custom_type = LOWER_THAN_WARNING;
416 else if (custom_warning_level > battery_info.warning)
417 custom_type = HIGHER_THAN_WARNING;
419 custom_type = CUSTOM_CONTROL_NONE;
420 _E("Custom type is not defined: %d", custom_warning_level);
425 if (custom_type == LOWER_THAN_WARNING && capacity <= battery_info.critical)
427 if (custom_type == HIGHER_THAN_WARNING && capacity <= battery_info.warning)
430 if (capacity > custom_warning_level) {
431 custom_warning_launch = false;
434 if (online == battery->charger_connected && custom_warning_launch)
436 if (battery->charger_connected == 1 && custom_warning_launch)
439 custom_warning_launch = true;
440 _I("Launch custom lowbattery warning popup. online(%d, %d), capacity(%d, %d)", online, battery->charger_connected, capacity, custom_warning_level);
441 (void)battery_warning_low_act(NULL);
444 void check_extreme_status(int status)
449 if (status <= VCONFKEY_SYSMAN_BAT_POWER_OFF ||
450 status > VCONFKEY_SYSMAN_BAT_FULL)
452 ret = vconf_get_int(VCONFKEY_PM_KEY_IGNORE, &extreme);
454 _E("Failed to get vconf value for pm key ignore: %d", vconf_get_ext_errno());
455 if (ret != 0 || extreme != 1)
458 ret = vconf_set_int(VCONFKEY_PM_KEY_IGNORE, 0);
460 _E("Failed to set vconf value for pm key ignore: %d", vconf_get_ext_errno());
463 _I("Release key ignore.");
466 static int lowbat_process(int bat_percent, void *ad)
469 static bool low_bat_old;
470 static int low_bat_skip_cnt = 0;
471 int new_bat_capacity;
473 int vconf_state = -1;
476 bool low_bat = false;
479 if (!battery_initialized)
482 new_bat_capacity = bat_percent;
483 change_lowbat_level(new_bat_capacity);
484 lowbat_custom_popup(online, new_bat_capacity);
485 battery_level_send_system_event(new_bat_capacity);
487 cur_bat_capacity = new_bat_capacity;
489 ret = vconf_get_int(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, &vconf_state);
491 _E("Failed to get vconf value for battery status low: %d", vconf_get_ext_errno());
495 if (new_bat_capacity <= battery_info.poweroff) {
496 new_bat_state = battery_info.poweroff;
497 if (vconf_state != VCONFKEY_SYSMAN_BAT_POWER_OFF)
498 status = VCONFKEY_SYSMAN_BAT_POWER_OFF;
499 } else if (new_bat_capacity <= battery_info.critical) {
500 new_bat_state = battery_info.critical;
501 if (vconf_state != VCONFKEY_SYSMAN_BAT_CRITICAL_LOW)
502 status = VCONFKEY_SYSMAN_BAT_CRITICAL_LOW;
503 } else if (new_bat_capacity <= battery_info.warning) {
504 new_bat_state = battery_info.warning;
505 if (vconf_state != VCONFKEY_SYSMAN_BAT_WARNING_LOW)
506 status = VCONFKEY_SYSMAN_BAT_WARNING_LOW;
508 new_bat_state = battery_info.normal;
509 if (new_bat_capacity == BATTERY_FULL) {
510 if (battery->charge_full) {
511 if (vconf_state != VCONFKEY_SYSMAN_BAT_FULL)
512 status = VCONFKEY_SYSMAN_BAT_FULL;
514 if (vconf_state != VCONFKEY_SYSMAN_BAT_NORMAL)
515 status = VCONFKEY_SYSMAN_BAT_NORMAL;
518 if (vconf_state != VCONFKEY_SYSMAN_BAT_NORMAL)
519 status = VCONFKEY_SYSMAN_BAT_NORMAL;
523 /* If the battery continues to run out even though it is being charged
524 * for a certain period of time, turn off the device. */
525 if (new_bat_capacity <= battery_info.poweroff && battery->charge_now == CHARGER_CHARGING) {
526 if (low_bat_skip_cnt >= RETRY_MAX) {
527 _I("Go to real poweroff inspite of charging (c:%d charge:%d online:%d current_now:%d)",
528 battery->capacity, battery->charge_now, battery->charger_connected, battery->current_now);
529 } else if (battery->current_now <= MIN_INOW_VALUE) {
532 low_bat_skip_cnt = 0;
535 low_bat_skip_cnt = 0;
540 display_setting_update_pm_setting(SETTING_LOW_BATT, status);
542 result = vconf_state;
544 check_extreme_status(status);
546 if (new_bat_capacity <= battery_info.warning)
549 if (low_bat_old != low_bat) {
550 syscommon_notifier_emit_notify(DEVICED_NOTIFIER_LOWBAT, (void *)&low_bat);
551 low_bat_old = low_bat;
554 if (battery->charger_connected == -1)
556 if (cur_bat_state == new_bat_state && online == battery->charger_connected)
558 if (new_bat_state <= battery_info.poweroff && battery->charger_connected && low_bat_skip_cnt < RETRY_MAX)
561 if (cur_bat_state == BATTERY_UNKNOWN)
562 cur_bat_state = battery_info.normal;
563 if (lowbat_scenario(cur_bat_state, new_bat_state, NULL))
564 _I("Cur(%d) new(%d) capacity(%d).", cur_bat_state, new_bat_state, bat_percent);
566 online = battery->charger_connected;
567 cur_bat_state = new_bat_state;
572 static int check_lowbat_percent(int *pct)
576 bat_percent = battery->capacity;
579 if (bat_percent > 100)
581 change_lowbat_level(bat_percent);
582 battery_level_send_system_event(bat_percent);
587 static int lowbat_monitor(void *data)
591 r = lowbat_initialized(NULL);
592 if (r != BATTERY_ENABLED)
593 return battery->capacity_level;
596 r = check_lowbat_percent(&bat_percent);
598 return battery->capacity_level;
600 bat_percent = *(int *)data;
601 return lowbat_process(bat_percent, NULL);
604 static int lowbat_monitor_init(void *data)
608 status = lowbat_initialized(NULL);
609 if (status != BATTERY_UNKNOWN)
612 status = BATTERY_ENABLED;
613 lowbat_initialized(&status);
615 /* it's called just this once. */
616 syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_POWER_SUPPLY, lowbat_monitor_init);
618 /* load battery configuration file */
619 battery_parser_load_config(&battery_info);
620 _I("Battery conf: %d %d %d %d", battery_info.normal, battery_info.warning,
621 battery_info.critical, battery_info.poweroff);
623 lowbat_scenario_init();
624 check_lowbat_percent(&battery->capacity);
625 lowbat_process(battery->capacity, NULL);
630 static GVariant * dbus_set_lowbat_level(GDBusConnection *conn,
631 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
632 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
638 g_variant_get(param, "(i)", &now);
640 status = lowbat_initialized(NULL);
641 if (status != BATTERY_ENABLED) {
642 _E("Invalid lowbat handler.");
647 if (now <= battery_info.warning && now != BATTERY_UNKNOWN) {
648 _E("Invalid level is requested: %d", now);
653 custom_warning_level = now;
654 custom_warning_launch = false;
656 ret = vconf_set_int(VCONF_KEY_BATTERY_WARNING_LEVEL, custom_warning_level);
658 _E("Failed to set vconf value for battery warning level: %d", vconf_get_ext_errno());
659 _D("Custom warning level is changed to %d.", custom_warning_level);
662 return g_variant_new("(i)", ret);
665 static GVariant * dbus_get_lowbat_level(GDBusConnection *conn,
666 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
667 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
669 _D("Warning level: %d", custom_warning_level);
670 return g_variant_new("(i)", custom_warning_level);
673 static const dbus_method_s dbus_methods[] = {
674 { "SetLowbatLevel", "i", "i", dbus_set_lowbat_level },
675 { "GetLowbatLevel", NULL, "i", dbus_get_lowbat_level },
676 /* Add methods here */
679 static const dbus_interface_u dbus_interface = {
681 .name = DEVICED_INTERFACE_BATTERY,
682 .methods = dbus_methods,
683 .nr_methods = ARRAY_SIZE(dbus_methods),
686 static void lowbat_init(void *data)
690 syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_DELAYED_INIT, delayed_init_done);
691 syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_POWER_SUPPLY, lowbat_monitor_init);
693 ret = gdbus_add_object(NULL, DEVICED_PATH_BATTERY, &dbus_interface);
695 _E("Failed to init dbus method: %d", ret);
698 static void lowbat_exit(void *data)
700 int status = BATTERY_DISABLED;
702 lowbat_initialized(&status);
703 if (low_batt_sig_timer) {
704 g_source_remove(low_batt_sig_timer);
705 low_batt_sig_timer = 0;
708 battery_parser_unload_config(&battery_info);
711 static gboolean low_battery_charging_status(void *data)
713 low_batt_sig_timer = 0;
714 battery->capacity_level = lowbat_monitor(data);
715 if (battery->capacity_level > 0 && old_battery.capacity_level != battery->capacity_level) {
716 if (vconf_set_int(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, battery->capacity_level) < 0)
717 _E("Fail to set level.");
718 if (power_supply_broadcast(CHARGE_LEVEL_SIGNAL, battery->capacity_level) < 0)
719 _E("power_supply_broadcast failed");
721 return G_SOURCE_REMOVE;
724 static int lowbat_execute(void *data)
726 int capacity = *(int *)data;
728 /* In battery kernel side, it has a requirement that battery charging event must get quickly.
729 * So deviced receives the discharging and charging event within 1.5 sec,
730 * added a timer to wait for the second signal, if there is no second signal then execute
731 * the lowbat_execute.
733 if (low_batt_sig_timer) {
734 g_source_remove(low_batt_sig_timer);
735 low_batt_sig_timer = 0;
738 /* Do lowbat_process immediately rather deferring it when poweroff is needed.
739 * This prevents poweroff from being delayed infinitely when the uevent continues
740 * to occur shorter than 1.5 seconds on poweroff capacity */
741 if (capacity <= battery_info.poweroff || !uevent_buffering)
742 low_battery_charging_status(data);
744 low_batt_sig_timer = g_timeout_add(1500, low_battery_charging_status, data);
749 void lowbat_enable_uevent_buffering(void)
751 uevent_buffering = 1;
754 void lowbat_disable_uevent_buffering(void)
756 uevent_buffering = 0;
759 static const struct device_ops lowbat_device_ops = {
760 DECLARE_NAME_LEN("lowbat"),
762 .execute = lowbat_execute,
766 DEVICE_OPS_REGISTER(&lowbat_device_ops)
768 static void __CONSTRUCTOR__ initialize(void)
770 battery_plgn = get_var_battery_plugin();
772 _E("Failed to get battery plugin variable.");
774 battery = get_var_battery_status();
776 _E("Failed to get battery status structure variable.");