diff options
Diffstat (limited to 'healthd')
-rw-r--r-- | healthd/Android.mk | 52 | ||||
-rw-r--r-- | healthd/BatteryMonitor.cpp | 162 | ||||
-rw-r--r-- | healthd/BatteryMonitor.h | 12 | ||||
-rw-r--r-- | healthd/BatteryPropertiesRegistrar.cpp | 30 | ||||
-rw-r--r-- | healthd/BatteryPropertiesRegistrar.h | 9 | ||||
-rw-r--r-- | healthd/healthd.cpp | 266 | ||||
-rw-r--r-- | healthd/healthd.h | 27 | ||||
-rw-r--r-- | healthd/healthd_mode_android.cpp | 62 | ||||
-rw-r--r-- | healthd/healthd_mode_charger.cpp | 686 | ||||
-rw-r--r-- | healthd/images/battery_0.png | bin | 0 -> 1295 bytes | |||
-rw-r--r-- | healthd/images/battery_1.png | bin | 0 -> 1290 bytes | |||
-rw-r--r-- | healthd/images/battery_2.png | bin | 0 -> 1289 bytes | |||
-rw-r--r-- | healthd/images/battery_3.png | bin | 0 -> 1291 bytes | |||
-rw-r--r-- | healthd/images/battery_4.png | bin | 0 -> 1288 bytes | |||
-rw-r--r-- | healthd/images/battery_5.png | bin | 0 -> 1267 bytes | |||
-rw-r--r-- | healthd/images/battery_charge.png | bin | 0 -> 2475 bytes | |||
-rw-r--r-- | healthd/images/battery_fail.png | bin | 0 -> 1805 bytes |
17 files changed, 1154 insertions, 152 deletions
diff --git a/healthd/Android.mk b/healthd/Android.mk index 473d375..5cd5ce1 100644 --- a/healthd/Android.mk +++ b/healthd/Android.mk @@ -13,6 +13,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ healthd.cpp \ + healthd_mode_android.cpp \ + healthd_mode_charger.cpp \ BatteryMonitor.cpp \ BatteryPropertiesRegistrar.cpp @@ -22,9 +24,57 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc +LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS + +ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) +LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK +endif + +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND +endif + +LOCAL_C_INCLUDES := bootable/recovery + +LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpixelflinger_static libpng libz libutils libstdc++ libcutils liblog libm libc + +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_STATIC_LIBRARIES += libsuspend +endif + LOCAL_HAL_STATIC_LIBRARIES := libhealthd +# Symlink /charger to /sbin/healthd +LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \ + && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger + include $(BUILD_EXECUTABLE) + +define _add-charger-image +include $$(CLEAR_VARS) +LOCAL_MODULE := system_core_charger_$(notdir $(1)) +LOCAL_MODULE_STEM := $(notdir $(1)) +_img_modules += $$(LOCAL_MODULE) +LOCAL_SRC_FILES := $1 +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger +include $$(BUILD_PREBUILT) +endef + +_img_modules := +_images := +$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ + $(eval $(call _add-charger-image,$(_img)))) + +include $(CLEAR_VARS) +LOCAL_MODULE := charger_res_images +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := $(_img_modules) +include $(BUILD_PHONY_PACKAGE) + +_add-charger-image := +_img_modules := + endif diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 688c7ff..3e6a4b1 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -18,7 +18,6 @@ #include "healthd.h" #include "BatteryMonitor.h" -#include "BatteryPropertiesRegistrar.h" #include <dirent.h> #include <errno.h> @@ -28,6 +27,7 @@ #include <unistd.h> #include <batteryservice/BatteryService.h> #include <cutils/klog.h> +#include <utils/Errors.h> #include <utils/String8.h> #include <utils/Vector.h> @@ -170,7 +170,6 @@ int BatteryMonitor::getIntField(const String8& path) { } bool BatteryMonitor::update(void) { - struct BatteryProperties props; bool logthis; props.chargerAcOnline = false; @@ -178,23 +177,15 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline = false; props.batteryStatus = BATTERY_STATUS_UNKNOWN; props.batteryHealth = BATTERY_HEALTH_UNKNOWN; - props.batteryCurrentNow = INT_MIN; - props.batteryChargeCounter = INT_MIN; if (!mHealthdConfig->batteryPresentPath.isEmpty()) props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); else - props.batteryPresent = true; + props.batteryPresent = mBatteryDevicePresent; props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath); props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; - if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) - props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath); - - if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) - props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath); - props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath); const int SIZE = 128; @@ -244,7 +235,9 @@ bool BatteryMonitor::update(void) { if (logthis) { char dmesgline[256]; - snprintf(dmesgline, sizeof(dmesgline), + + if (props.batteryPresent) { + snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d", props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "", @@ -252,11 +245,16 @@ bool BatteryMonitor::update(void) { abs(props.batteryTemperature % 10), props.batteryHealth, props.batteryStatus); - if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { - char b[20]; + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + int c = getIntField(mHealthdConfig->batteryCurrentNowPath); + char b[20]; - snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000); - strlcat(dmesgline, b, sizeof(dmesgline)); + snprintf(b, sizeof(b), " c=%d", c / 1000); + strlcat(dmesgline, b, sizeof(dmesgline)); + } + } else { + snprintf(dmesgline, sizeof(dmesgline), + "battery none"); } KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline, @@ -265,14 +263,91 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline ? "w" : ""); } - if (mBatteryPropertiesRegistrar != NULL) - mBatteryPropertiesRegistrar->notifyListeners(props); - + healthd_mode_ops->battery_update(&props); return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline; } -void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { +status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) { + status_t ret = BAD_VALUE; + + switch(id) { + case BATTERY_PROP_CHARGE_COUNTER: + if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) { + val->valueInt = + getIntField(mHealthdConfig->batteryChargeCounterPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_NOW: + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + val->valueInt = + getIntField(mHealthdConfig->batteryCurrentNowPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_AVG: + if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + val->valueInt = + getIntField(mHealthdConfig->batteryCurrentAvgPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + default: + break; + } + + if (ret != NO_ERROR) + val->valueInt = INT_MIN; + + return ret; +} + +void BatteryMonitor::dumpState(int fd) { + int v; + char vs[128]; + + snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n", + props.chargerAcOnline, props.chargerUsbOnline, + props.chargerWirelessOnline); + write(fd, vs, strlen(vs)); + snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n", + props.batteryStatus, props.batteryHealth, props.batteryPresent); + write(fd, vs, strlen(vs)); + snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", + props.batteryLevel, props.batteryVoltage, + props.batteryTemperature); + write(fd, vs, strlen(vs)); + + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryCurrentNowPath); + snprintf(vs, sizeof(vs), "current now: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryCurrentAvgPath); + snprintf(vs, sizeof(vs), "current avg: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryChargeCounterPath); + snprintf(vs, sizeof(vs), "charge counter: %d\n", v); + write(fd, vs, strlen(vs)); + } +} + +void BatteryMonitor::init(struct healthd_config *hc) { String8 path; mHealthdConfig = hc; @@ -303,6 +378,8 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { break; case ANDROID_POWER_SUPPLY_TYPE_BATTERY: + mBatteryDevicePresent = true; + if (mHealthdConfig->batteryStatusPath.isEmpty()) { path.clear(); path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, @@ -358,6 +435,14 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { mHealthdConfig->batteryCurrentNowPath = path; } + if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/current_avg", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryCurrentAvgPath = path; + } + if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) { path.clear(); path.appendFormat("%s/%s/charge_counter", @@ -400,24 +485,25 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { if (!mChargerNames.size()) KLOG_ERROR(LOG_TAG, "No charger supplies found\n"); - if (mHealthdConfig->batteryStatusPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); - if (mHealthdConfig->batteryHealthPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); - if (mHealthdConfig->batteryPresentPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); - if (mHealthdConfig->batteryCapacityPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); - if (mHealthdConfig->batteryVoltagePath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); - if (mHealthdConfig->batteryTemperaturePath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); - if (mHealthdConfig->batteryTechnologyPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); - - if (nosvcmgr == false) { - mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this); - mBatteryPropertiesRegistrar->publish(); + if (!mBatteryDevicePresent) { + KLOG_INFO(LOG_TAG, "No battery devices found\n"); + hc->periodic_chores_interval_fast = -1; + hc->periodic_chores_interval_slow = -1; + } else { + if (mHealthdConfig->batteryStatusPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); + if (mHealthdConfig->batteryHealthPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); + if (mHealthdConfig->batteryPresentPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); + if (mHealthdConfig->batteryCapacityPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); + if (mHealthdConfig->batteryVoltagePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); + if (mHealthdConfig->batteryTemperaturePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); + if (mHealthdConfig->batteryTechnologyPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); } } diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h index ba291af..4866cc2 100644 --- a/healthd/BatteryMonitor.h +++ b/healthd/BatteryMonitor.h @@ -17,17 +17,15 @@ #ifndef HEALTHD_BATTERYMONITOR_H #define HEALTHD_BATTERYMONITOR_H +#include <batteryservice/BatteryService.h> #include <binder/IInterface.h> #include <utils/String8.h> #include <utils/Vector.h> #include "healthd.h" -#include "BatteryPropertiesRegistrar.h" namespace android { -class BatteryPropertiesRegistrar; - class BatteryMonitor { public: @@ -39,14 +37,16 @@ class BatteryMonitor { ANDROID_POWER_SUPPLY_TYPE_BATTERY }; - void init(struct healthd_config *hc, bool nosvcmgr); + void init(struct healthd_config *hc); bool update(void); + status_t getProperty(int id, struct BatteryProperty *val); + void dumpState(int fd); private: struct healthd_config *mHealthdConfig; Vector<String8> mChargerNames; - - sp<BatteryPropertiesRegistrar> mBatteryPropertiesRegistrar; + bool mBatteryDevicePresent; + struct BatteryProperties props; int getBatteryStatus(const char* status); int getBatteryHealth(const char* status); diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp index 6a33ad8..58da4ee 100644 --- a/healthd/BatteryPropertiesRegistrar.cpp +++ b/healthd/BatteryPropertiesRegistrar.cpp @@ -18,19 +18,20 @@ #include <batteryservice/BatteryService.h> #include <batteryservice/IBatteryPropertiesListener.h> #include <batteryservice/IBatteryPropertiesRegistrar.h> +#include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <private/android_filesystem_config.h> #include <utils/Errors.h> #include <utils/Mutex.h> #include <utils/String16.h> -namespace android { +#include "healthd.h" -BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) { - mBatteryMonitor = monitor; -} +namespace android { void BatteryPropertiesRegistrar::publish() { - defaultServiceManager()->addService(String16("batterypropreg"), this); + defaultServiceManager()->addService(String16("batteryproperties"), this); } void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) { @@ -53,7 +54,7 @@ void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesLis mListeners.add(listener); listener->asBinder()->linkToDeath(this); } - mBatteryMonitor->update(); + healthd_battery_update(); } void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) { @@ -67,6 +68,23 @@ void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesL } } +status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) { + return healthd_get_property(id, val); +} + +status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& args) { + IPCThreadState* self = IPCThreadState::self(); + const int pid = self->getCallingPid(); + const int uid = self->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission( + String16("android.permission.DUMP"), pid, uid)) + return PERMISSION_DENIED; + + healthd_dump_battery_state(fd); + return OK; +} + void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) { Mutex::Autolock _l(mRegistrationLock); diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h index 793ddad..8853874 100644 --- a/healthd/BatteryPropertiesRegistrar.h +++ b/healthd/BatteryPropertiesRegistrar.h @@ -17,10 +17,9 @@ #ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H #define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H -#include "BatteryMonitor.h" - #include <binder/IBinder.h> #include <utils/Mutex.h> +#include <utils/String16.h> #include <utils/Vector.h> #include <batteryservice/BatteryService.h> #include <batteryservice/IBatteryPropertiesListener.h> @@ -28,22 +27,20 @@ namespace android { -class BatteryMonitor; - class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar, public IBinder::DeathRecipient { public: - BatteryPropertiesRegistrar(BatteryMonitor* monitor); void publish(); void notifyListeners(struct BatteryProperties props); private: - BatteryMonitor* mBatteryMonitor; Mutex mRegistrationLock; Vector<sp<IBatteryPropertiesListener> > mListeners; void registerListener(const sp<IBatteryPropertiesListener>& listener); void unregisterListener(const sp<IBatteryPropertiesListener>& listener); + status_t getProperty(int id, struct BatteryProperty *val); + status_t dump(int fd, const Vector<String16>& args); void binderDied(const wp<IBinder>& who); }; diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 9b84c3e..8363c41 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -21,16 +21,17 @@ #include "BatteryMonitor.h" #include <errno.h> +#include <libgen.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <batteryservice/BatteryService.h> -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> #include <cutils/klog.h> #include <cutils/uevent.h> #include <sys/epoll.h> #include <sys/timerfd.h> +#include <utils/Errors.h> using namespace android; @@ -49,13 +50,17 @@ static struct healthd_config healthd_config = { .batteryTemperaturePath = String8(String8::kEmptyString), .batteryTechnologyPath = String8(String8::kEmptyString), .batteryCurrentNowPath = String8(String8::kEmptyString), + .batteryCurrentAvgPath = String8(String8::kEmptyString), .batteryChargeCounterPath = String8(String8::kEmptyString), }; +static int eventct; +static int epollfd; + #define POWER_SUPPLY_SUBSYSTEM "power_supply" -// epoll events: uevent, wakealarm, binder -#define MAX_EPOLL_EVENTS 3 +// epoll_create() parameter is actually unused +#define MAX_EPOLL_EVENTS 40 static int uevent_fd; static int wakealarm_fd; static int binder_fd; @@ -67,7 +72,80 @@ static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; static BatteryMonitor* gBatteryMonitor; -static bool nosvcmgr; +struct healthd_mode_ops *healthd_mode_ops; + +// Android mode + +extern void healthd_mode_android_init(struct healthd_config *config); +extern int healthd_mode_android_preparetowait(void); +extern void healthd_mode_android_battery_update( + struct android::BatteryProperties *props); + +// Charger mode + +extern void healthd_mode_charger_init(struct healthd_config *config); +extern int healthd_mode_charger_preparetowait(void); +extern void healthd_mode_charger_heartbeat(void); +extern void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props); + +// NOPs for modes that need no special action + +static void healthd_mode_nop_init(struct healthd_config *config); +static int healthd_mode_nop_preparetowait(void); +static void healthd_mode_nop_heartbeat(void); +static void healthd_mode_nop_battery_update( + struct android::BatteryProperties *props); + +static struct healthd_mode_ops android_ops = { + .init = healthd_mode_android_init, + .preparetowait = healthd_mode_android_preparetowait, + .heartbeat = healthd_mode_nop_heartbeat, + .battery_update = healthd_mode_android_battery_update, +}; + +static struct healthd_mode_ops charger_ops = { + .init = healthd_mode_charger_init, + .preparetowait = healthd_mode_charger_preparetowait, + .heartbeat = healthd_mode_charger_heartbeat, + .battery_update = healthd_mode_charger_battery_update, +}; + +static struct healthd_mode_ops recovery_ops = { + .init = healthd_mode_nop_init, + .preparetowait = healthd_mode_nop_preparetowait, + .heartbeat = healthd_mode_nop_heartbeat, + .battery_update = healthd_mode_nop_battery_update, +}; + +static void healthd_mode_nop_init(struct healthd_config *config) { +} + +static int healthd_mode_nop_preparetowait(void) { + return -1; +} + +static void healthd_mode_nop_heartbeat(void) { +} + +static void healthd_mode_nop_battery_update( + struct android::BatteryProperties *props) { +} + +int healthd_register_event(int fd, void (*handler)(uint32_t)) { + struct epoll_event ev; + + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { + KLOG_ERROR(LOG_TAG, + "epoll_ctl failed; errno=%d\n", errno); + return -1; + } + + eventct++; + return 0; +} static void wakealarm_set_interval(int interval) { struct itimerspec itval; @@ -89,7 +167,11 @@ static void wakealarm_set_interval(int interval) { KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n"); } -static void battery_update(void) { +status_t healthd_get_property(int id, struct BatteryProperty *val) { + return gBatteryMonitor->getProperty(id, val); +} + +void healthd_battery_update(void) { // Fast wake interval when on charger (watch for overheat); // slow wake interval when on battery (watch for drained battery). @@ -113,21 +195,17 @@ static void battery_update(void) { -1 : healthd_config.periodic_chores_interval_fast * 1000; } -static void periodic_chores() { - battery_update(); +void healthd_dump_battery_state(int fd) { + gBatteryMonitor->dumpState(fd); + fsync(fd); } -static void uevent_init(void) { - uevent_fd = uevent_open_socket(64*1024, true); - - if (uevent_fd >= 0) - fcntl(uevent_fd, F_SETFL, O_NONBLOCK); - else - KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); +static void periodic_chores() { + healthd_battery_update(); } #define UEVENT_MSG_LEN 1024 -static void uevent_event(void) { +static void uevent_event(uint32_t epevents) { char msg[UEVENT_MSG_LEN+2]; char *cp; int n; @@ -144,7 +222,7 @@ static void uevent_event(void) { while (*cp) { if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { - battery_update(); + healthd_battery_update(); break; } @@ -154,89 +232,56 @@ static void uevent_event(void) { } } -static void wakealarm_init(void) { - wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); - if (wakealarm_fd == -1) { - KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); +static void uevent_init(void) { + uevent_fd = uevent_open_socket(64*1024, true); + + if (uevent_fd < 0) { + KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); return; } - wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + if (healthd_register_event(uevent_fd, uevent_event)) + KLOG_ERROR(LOG_TAG, + "register for uevent events failed\n"); } -static void wakealarm_event(void) { +static void wakealarm_event(uint32_t epevents) { unsigned long long wakeups; if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) { - KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm_fd failed\n"); + KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n"); return; } periodic_chores(); } -static void binder_init(void) { - ProcessState::self()->setThreadPoolMaxThreadCount(0); - IPCThreadState::self()->disableBackgroundScheduling(true); - IPCThreadState::self()->setupPolling(&binder_fd); -} - -static void binder_event(void) { - IPCThreadState::self()->handlePolledCommands(); -} - -static void healthd_mainloop(void) { - struct epoll_event ev; - int epollfd; - int maxevents = 0; - - epollfd = epoll_create(MAX_EPOLL_EVENTS); - if (epollfd == -1) { - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_create failed; errno=%d\n", - errno); +static void wakealarm_init(void) { + wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); + if (wakealarm_fd == -1) { + KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); return; } - if (uevent_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = (void *)uevent_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n", - errno); - else - maxevents++; - } + if (healthd_register_event(wakealarm_fd, wakealarm_event)) + KLOG_ERROR(LOG_TAG, + "Registration of wakealarm event failed\n"); - if (wakealarm_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = (void *)wakealarm_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n", - errno); - else - maxevents++; - } - - if (binder_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr= (void *)binder_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n", - errno); - else - maxevents++; - } + wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); +} +static void healthd_mainloop(void) { while (1) { - struct epoll_event events[maxevents]; + struct epoll_event events[eventct]; int nevents; + int timeout = awake_poll_interval; + int mode_timeout; - IPCThreadState::self()->flushCommands(); - nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval); + mode_timeout = healthd_mode_ops->preparetowait(); + if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) + timeout = mode_timeout; + nevents = epoll_wait(epollfd, events, eventct, timeout); if (nevents == -1) { if (errno == EINTR) @@ -247,39 +292,70 @@ static void healthd_mainloop(void) { for (int n = 0; n < nevents; ++n) { if (events[n].data.ptr) - (*(void (*)())events[n].data.ptr)(); + (*(void (*)(int))events[n].data.ptr)(events[n].events); } if (!nevents) periodic_chores(); + + healthd_mode_ops->heartbeat(); } return; } +static int healthd_init() { + epollfd = epoll_create(MAX_EPOLL_EVENTS); + if (epollfd == -1) { + KLOG_ERROR(LOG_TAG, + "epoll_create failed; errno=%d\n", + errno); + return -1; + } + + healthd_mode_ops->init(&healthd_config); + healthd_board_init(&healthd_config); + wakealarm_init(); + uevent_init(); + gBatteryMonitor = new BatteryMonitor(); + gBatteryMonitor->init(&healthd_config); + return 0; +} + int main(int argc, char **argv) { int ch; + int ret; klog_set_level(KLOG_LEVEL); - - while ((ch = getopt(argc, argv, "n")) != -1) { - switch (ch) { - case 'n': - nosvcmgr = true; - break; - case '?': - default: - KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch); + healthd_mode_ops = &android_ops; + + if (!strcmp(basename(argv[0]), "charger")) { + healthd_mode_ops = &charger_ops; + } else { + while ((ch = getopt(argc, argv, "cr")) != -1) { + switch (ch) { + case 'c': + healthd_mode_ops = &charger_ops; + break; + case 'r': + healthd_mode_ops = &recovery_ops; + break; + case '?': + default: + KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n", + optopt); + exit(1); + } } } - healthd_board_init(&healthd_config); - wakealarm_init(); - uevent_init(); - binder_init(); - gBatteryMonitor = new BatteryMonitor(); - gBatteryMonitor->init(&healthd_config, nosvcmgr); + ret = healthd_init(); + if (ret) { + KLOG_ERROR("Initialization failed, exiting\n"); + exit(2); + } healthd_mainloop(); - return 0; + KLOG_ERROR("Main loop terminated, exiting\n"); + return 3; } diff --git a/healthd/healthd.h b/healthd/healthd.h index 5374fb1..23a54bf 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h @@ -18,6 +18,7 @@ #define _HEALTHD_H_ #include <batteryservice/BatteryService.h> +#include <utils/Errors.h> #include <utils/String8.h> // periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at @@ -61,9 +62,35 @@ struct healthd_config { android::String8 batteryTemperaturePath; android::String8 batteryTechnologyPath; android::String8 batteryCurrentNowPath; + android::String8 batteryCurrentAvgPath; android::String8 batteryChargeCounterPath; }; +// Global helper functions + +int healthd_register_event(int fd, void (*handler)(uint32_t)); +void healthd_battery_update(); +android::status_t healthd_get_property(int id, + struct android::BatteryProperty *val); +void healthd_dump_battery_state(int fd); + +struct healthd_mode_ops { + void (*init)(struct healthd_config *config); + int (*preparetowait)(void); + void (*heartbeat)(void); + void (*battery_update)(struct android::BatteryProperties *props); +}; + +extern struct healthd_mode_ops *healthd_mode_ops; + +// Charger mode + +void healthd_mode_charger_init(struct healthd_config *config); +int healthd_mode_charger_preparetowait(void); +void healthd_mode_charger_heartbeat(void); +void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props); + // The following are implemented in libhealthd_board to handle board-specific // behavior. // diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp new file mode 100644 index 0000000..4887c8c --- /dev/null +++ b/healthd/healthd_mode_android.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "healthd-android" + +#include "healthd.h" +#include "BatteryPropertiesRegistrar.h" + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <cutils/klog.h> +#include <sys/epoll.h> + +using namespace android; + +static int gBinderFd; +static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar; + +void healthd_mode_android_battery_update( + struct android::BatteryProperties *props) { + if (gBatteryPropertiesRegistrar != NULL) + gBatteryPropertiesRegistrar->notifyListeners(*props); + + return; +} + +int healthd_mode_android_preparetowait(void) { + IPCThreadState::self()->flushCommands(); + return -1; +} + +static void binder_event(uint32_t epevents) { + IPCThreadState::self()->handlePolledCommands(); +} + +void healthd_mode_android_init(struct healthd_config *config) { + ProcessState::self()->setThreadPoolMaxThreadCount(0); + IPCThreadState::self()->disableBackgroundScheduling(true); + IPCThreadState::self()->setupPolling(&gBinderFd); + + if (gBinderFd >= 0) { + if (healthd_register_event(gBinderFd, binder_event)) + KLOG_ERROR(LOG_TAG, + "Register for binder events failed\n"); + } + + gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(); + gBatteryPropertiesRegistrar->publish(); +} diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp new file mode 100644 index 0000000..33a179b --- /dev/null +++ b/healthd/healthd_mode_charger.cpp @@ -0,0 +1,686 @@ +/* + * Copyright (C) 2011-2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/input.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/epoll.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> +#include <time.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <linux/netlink.h> + +#include <batteryservice/BatteryService.h> +#include <cutils/android_reboot.h> +#include <cutils/klog.h> +#include <cutils/misc.h> + +#ifdef CHARGER_ENABLE_SUSPEND +#include <suspend/autosuspend.h> +#endif + +#include "minui/minui.h" + +#include "healthd.h" + +char *locale; + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#define MSEC_PER_SEC (1000LL) +#define NSEC_PER_MSEC (1000000LL) + +#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC) +#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC) +#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) + +#define BATTERY_FULL_THRESH 95 + +#define LAST_KMSG_PATH "/proc/last_kmsg" +#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops" +#define LAST_KMSG_MAX_SZ (32 * 1024) + +#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) +#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0) +#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0) + +struct key_state { + bool pending; + bool down; + int64_t timestamp; +}; + +struct frame { + const char *name; + int disp_time; + int min_capacity; + bool level_only; + + gr_surface surface; +}; + +struct animation { + bool run; + + struct frame *frames; + int cur_frame; + int num_frames; + + int cur_cycle; + int num_cycles; + + /* current capacity being animated */ + int capacity; +}; + +struct charger { + bool have_battery_state; + bool charger_connected; + int capacity; + int64_t next_screen_transition; + int64_t next_key_check; + int64_t next_pwr_check; + + struct key_state keys[KEY_MAX + 1]; + + struct animation *batt_anim; + gr_surface surf_unknown; +}; + +static struct frame batt_anim_frames[] = { + { + .name = "charger/battery_0", + .disp_time = 750, + .min_capacity = 0, + .level_only = false, + .surface = NULL, + }, + { + .name = "charger/battery_1", + .disp_time = 750, + .min_capacity = 20, + .level_only = false, + .surface = NULL, + }, + { + .name = "charger/battery_2", + .disp_time = 750, + .min_capacity = 40, + .level_only = false, + .surface = NULL, + }, + { + .name = "charger/battery_3", + .disp_time = 750, + .min_capacity = 60, + .level_only = false, + .surface = NULL, + }, + { + .name = "charger/battery_4", + .disp_time = 750, + .min_capacity = 80, + .level_only = true, + .surface = NULL, + }, + { + .name = "charger/battery_5", + .disp_time = 750, + .min_capacity = BATTERY_FULL_THRESH, + .level_only = false, + .surface = NULL, + }, +}; + +static struct animation battery_animation = { + .run = false, + .frames = batt_anim_frames, + .cur_frame = 0, + .num_frames = ARRAY_SIZE(batt_anim_frames), + .cur_cycle = 0, + .num_cycles = 3, + .capacity = 0, +}; + +static struct charger charger_state; + +static int char_width; +static int char_height; + +/* current time in milliseconds */ +static int64_t curr_time_ms(void) +{ + struct timespec tm; + clock_gettime(CLOCK_MONOTONIC, &tm); + return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC); +} + +static void clear_screen(void) +{ + gr_color(0, 0, 0, 255); + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); +}; + +#define MAX_KLOG_WRITE_BUF_SZ 256 + +static void dump_last_kmsg(void) +{ + char *buf; + char *ptr; + unsigned sz = 0; + int len; + + LOGI("\n"); + LOGI("*************** LAST KMSG ***************\n"); + LOGI("\n"); + buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz); + + if (!buf || !sz) { + buf = (char *)load_file(LAST_KMSG_PATH, &sz); + if (!buf || !sz) { + LOGI("last_kmsg not found. Cold reset?\n"); + goto out; + } + } + + len = min(sz, LAST_KMSG_MAX_SZ); + ptr = buf + (sz - len); + + while (len > 0) { + int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ); + char yoink; + char *nl; + + nl = (char *)memrchr(ptr, '\n', cnt - 1); + if (nl) + cnt = nl - ptr + 1; + + yoink = ptr[cnt]; + ptr[cnt] = '\0'; + klog_write(6, "<6>%s", ptr); + ptr[cnt] = yoink; + + len -= cnt; + ptr += cnt; + } + + free(buf); + +out: + LOGI("\n"); + LOGI("************* END LAST KMSG *************\n"); + LOGI("\n"); +} + +static int get_battery_capacity() +{ + return charger_state.capacity; +} + +#ifdef CHARGER_ENABLE_SUSPEND +static int request_suspend(bool enable) +{ + if (enable) + return autosuspend_enable(); + else + return autosuspend_disable(); +} +#else +static int request_suspend(bool enable) +{ + return 0; +} +#endif + +static int draw_text(const char *str, int x, int y) +{ + int str_len_px = gr_measure(str); + + if (x < 0) + x = (gr_fb_width() - str_len_px) / 2; + if (y < 0) + y = (gr_fb_height() - char_height) / 2; + gr_text(x, y, str, 0); + + return y + char_height; +} + +static void android_green(void) +{ + gr_color(0xa4, 0xc6, 0x39, 255); +} + +/* returns the last y-offset of where the surface ends */ +static int draw_surface_centered(struct charger *charger, gr_surface surface) +{ + int w; + int h; + int x; + int y; + + w = gr_get_width(surface); + h = gr_get_height(surface); + x = (gr_fb_width() - w) / 2 ; + y = (gr_fb_height() - h) / 2 ; + + LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y); + gr_blit(surface, 0, 0, w, h, x, y); + return y + h; +} + +static void draw_unknown(struct charger *charger) +{ + int y; + if (charger->surf_unknown) { + draw_surface_centered(charger, charger->surf_unknown); + } else { + android_green(); + y = draw_text("Charging!", -1, -1); + draw_text("?\?/100", -1, y + 25); + } +} + +static void draw_battery(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; + struct frame *frame = &batt_anim->frames[batt_anim->cur_frame]; + + if (batt_anim->num_frames != 0) { + draw_surface_centered(charger, frame->surface); + LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n", + batt_anim->cur_frame, frame->name, frame->min_capacity, + frame->disp_time); + } +} + +static void redraw_screen(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; + + clear_screen(); + + /* try to display *something* */ + if (batt_anim->capacity < 0 || batt_anim->num_frames == 0) + draw_unknown(charger); + else + draw_battery(charger); + gr_flip(); +} + +static void kick_animation(struct animation *anim) +{ + anim->run = true; +} + +static void reset_animation(struct animation *anim) +{ + anim->cur_cycle = 0; + anim->cur_frame = 0; + anim->run = false; +} + +static void update_screen_state(struct charger *charger, int64_t now) +{ + struct animation *batt_anim = charger->batt_anim; + int cur_frame; + int disp_time; + + if (!batt_anim->run || now < charger->next_screen_transition) + return; + + /* animation is over, blank screen and leave */ + if (batt_anim->cur_cycle == batt_anim->num_cycles) { + reset_animation(batt_anim); + charger->next_screen_transition = -1; + gr_fb_blank(true); + LOGV("[%lld] animation done\n", now); + if (!charger->charger_connected) + request_suspend(true); + return; + } + + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time; + + /* animation starting, set up the animation */ + if (batt_anim->cur_frame == 0) { + int batt_cap; + int ret; + + LOGV("[%lld] animation starting\n", now); + batt_cap = get_battery_capacity(); + if (batt_cap >= 0 && batt_anim->num_frames != 0) { + int i; + + /* find first frame given current capacity */ + for (i = 1; i < batt_anim->num_frames; i++) { + if (batt_cap < batt_anim->frames[i].min_capacity) + break; + } + batt_anim->cur_frame = i - 1; + + /* show the first frame for twice as long */ + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2; + } + + batt_anim->capacity = batt_cap; + } + + /* unblank the screen on first cycle */ + if (batt_anim->cur_cycle == 0) + gr_fb_blank(false); + + /* draw the new frame (@ cur_frame) */ + redraw_screen(charger); + + /* if we don't have anim frames, we only have one image, so just bump + * the cycle counter and exit + */ + if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) { + LOGV("[%lld] animation missing or unknown battery status\n", now); + charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME; + batt_anim->cur_cycle++; + return; + } + + /* schedule next screen transition */ + charger->next_screen_transition = now + disp_time; + + /* advance frame cntr to the next valid frame + * if necessary, advance cycle cntr, and reset frame cntr + */ + batt_anim->cur_frame++; + + /* if the frame is used for level-only, that is only show it when it's + * the current level, skip it during the animation. + */ + while (batt_anim->cur_frame < batt_anim->num_frames && + batt_anim->frames[batt_anim->cur_frame].level_only) + batt_anim->cur_frame++; + if (batt_anim->cur_frame >= batt_anim->num_frames) { + batt_anim->cur_cycle++; + batt_anim->cur_frame = 0; + + /* don't reset the cycle counter, since we use that as a signal + * in a test above to check if animation is over + */ + } +} + +static int set_key_callback(int code, int value, void *data) +{ + struct charger *charger = (struct charger *)data; + int64_t now = curr_time_ms(); + int down = !!value; + + if (code > KEY_MAX) + return -1; + + /* ignore events that don't modify our state */ + if (charger->keys[code].down == down) + return 0; + + /* only record the down even timestamp, as the amount + * of time the key spent not being pressed is not useful */ + if (down) + charger->keys[code].timestamp = now; + charger->keys[code].down = down; + charger->keys[code].pending = true; + if (down) { + LOGV("[%lld] key[%d] down\n", now, code); + } else { + int64_t duration = now - charger->keys[code].timestamp; + int64_t secs = duration / 1000; + int64_t msecs = duration - secs * 1000; + LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now, + code, secs, msecs); + } + + return 0; +} + +static void update_input_state(struct charger *charger, + struct input_event *ev) +{ + if (ev->type != EV_KEY) + return; + set_key_callback(ev->code, ev->value, charger); +} + +static void set_next_key_check(struct charger *charger, + struct key_state *key, + int64_t timeout) +{ + int64_t then = key->timestamp + timeout; + + if (charger->next_key_check == -1 || then < charger->next_key_check) + charger->next_key_check = then; +} + +static void process_key(struct charger *charger, int code, int64_t now) +{ + struct key_state *key = &charger->keys[code]; + int64_t next_key_check; + + if (code == KEY_POWER) { + if (key->down) { + int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; + if (now >= reboot_timeout) { + LOGI("[%lld] rebooting\n", now); + android_reboot(ANDROID_RB_RESTART, 0, 0); + } else { + /* if the key is pressed but timeout hasn't expired, + * make sure we wake up at the right-ish time to check + */ + set_next_key_check(charger, key, POWER_ON_KEY_TIME); + } + } else { + /* if the power key got released, force screen state cycle */ + if (key->pending) { + request_suspend(false); + kick_animation(charger->batt_anim); + } + } + } + + key->pending = false; +} + +static void handle_input_state(struct charger *charger, int64_t now) +{ + process_key(charger, KEY_POWER, now); + + if (charger->next_key_check != -1 && now > charger->next_key_check) + charger->next_key_check = -1; +} + +static void handle_power_supply_state(struct charger *charger, int64_t now) +{ + if (!charger->have_battery_state) + return; + + if (!charger->charger_connected) { + request_suspend(false); + if (charger->next_pwr_check == -1) { + charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; + LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n", + now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check); + } else if (now >= charger->next_pwr_check) { + LOGI("[%lld] shutting down\n", now); + android_reboot(ANDROID_RB_POWEROFF, 0, 0); + } else { + /* otherwise we already have a shutdown timer scheduled */ + } + } else { + /* online supply present, reset shutdown timer if set */ + if (charger->next_pwr_check != -1) { + LOGI("[%lld] device plugged in: shutdown cancelled\n", now); + kick_animation(charger->batt_anim); + } + charger->next_pwr_check = -1; + } +} + +void healthd_mode_charger_heartbeat() +{ + struct charger *charger = &charger_state; + int64_t now = curr_time_ms(); + int ret; + + handle_input_state(charger, now); + handle_power_supply_state(charger, now); + + /* do screen update last in case any of the above want to start + * screen transitions (animations, etc) + */ + update_screen_state(charger, now); +} + +void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props) +{ + struct charger *charger = &charger_state; + + charger->charger_connected = + props->chargerAcOnline || props->chargerUsbOnline || + props->chargerWirelessOnline; + charger->capacity = props->batteryLevel; + + if (!charger->have_battery_state) { + charger->have_battery_state = true; + charger->next_screen_transition = curr_time_ms() - 1; + reset_animation(charger->batt_anim); + kick_animation(charger->batt_anim); + } +} + +int healthd_mode_charger_preparetowait(void) +{ + struct charger *charger = &charger_state; + int64_t now = curr_time_ms(); + int64_t next_event = INT64_MAX; + int64_t timeout; + struct input_event ev; + int ret; + + LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\n", now, + charger->next_screen_transition, charger->next_key_check, + charger->next_pwr_check); + + if (charger->next_screen_transition != -1) + next_event = charger->next_screen_transition; + if (charger->next_key_check != -1 && charger->next_key_check < next_event) + next_event = charger->next_key_check; + if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event) + next_event = charger->next_pwr_check; + + if (next_event != -1 && next_event != INT64_MAX) + timeout = max(0, next_event - now); + else + timeout = -1; + + return (int)timeout; +} + +static int input_callback(int fd, unsigned int epevents, void *data) +{ + struct charger *charger = (struct charger *)data; + struct input_event ev; + int ret; + + ret = ev_get_input(fd, epevents, &ev); + if (ret) + return -1; + update_input_state(charger, &ev); + return 0; +} + +static void charger_event_handler(uint32_t epevents) +{ + int ret; + + ret = ev_wait(-1); + if (!ret) + ev_dispatch(); +} + +void healthd_mode_charger_init(struct healthd_config *config) +{ + int ret; + struct charger *charger = &charger_state; + int i; + int epollfd; + + dump_last_kmsg(); + + LOGI("--------------- STARTING CHARGER MODE ---------------\n"); + + gr_init(); + gr_font_size(&char_width, &char_height); + + ret = ev_init(input_callback, charger); + if (!ret) { + epollfd = ev_get_epollfd(); + healthd_register_event(epollfd, charger_event_handler); + } + + ret = res_create_surface("charger/battery_fail", &charger->surf_unknown); + if (ret < 0) { + LOGE("Cannot load image\n"); + charger->surf_unknown = NULL; + } + + charger->batt_anim = &battery_animation; + + for (i = 0; i < charger->batt_anim->num_frames; i++) { + struct frame *frame = &charger->batt_anim->frames[i]; + + ret = res_create_surface(frame->name, &frame->surface); + if (ret < 0) { + LOGE("Cannot load image %s\n", frame->name); + /* TODO: free the already allocated surfaces... */ + charger->batt_anim->num_frames = 0; + charger->batt_anim->num_cycles = 1; + break; + } + } + + ev_sync_key_state(set_key_callback, charger); + +#ifndef CHARGER_DISABLE_INIT_BLANK + gr_fb_blank(true); +#endif + + charger->next_screen_transition = -1; + charger->next_key_check = -1; + charger->next_pwr_check = -1; +} diff --git a/healthd/images/battery_0.png b/healthd/images/battery_0.png Binary files differnew file mode 100644 index 0000000..2347074 --- /dev/null +++ b/healthd/images/battery_0.png diff --git a/healthd/images/battery_1.png b/healthd/images/battery_1.png Binary files differnew file mode 100644 index 0000000..cd34620 --- /dev/null +++ b/healthd/images/battery_1.png diff --git a/healthd/images/battery_2.png b/healthd/images/battery_2.png Binary files differnew file mode 100644 index 0000000..3e4095e --- /dev/null +++ b/healthd/images/battery_2.png diff --git a/healthd/images/battery_3.png b/healthd/images/battery_3.png Binary files differnew file mode 100644 index 0000000..08c1551 --- /dev/null +++ b/healthd/images/battery_3.png diff --git a/healthd/images/battery_4.png b/healthd/images/battery_4.png Binary files differnew file mode 100644 index 0000000..3a678da --- /dev/null +++ b/healthd/images/battery_4.png diff --git a/healthd/images/battery_5.png b/healthd/images/battery_5.png Binary files differnew file mode 100644 index 0000000..d8dc40e --- /dev/null +++ b/healthd/images/battery_5.png diff --git a/healthd/images/battery_charge.png b/healthd/images/battery_charge.png Binary files differnew file mode 100644 index 0000000..b501933 --- /dev/null +++ b/healthd/images/battery_charge.png diff --git a/healthd/images/battery_fail.png b/healthd/images/battery_fail.png Binary files differnew file mode 100644 index 0000000..36fc254 --- /dev/null +++ b/healthd/images/battery_fail.png |