diff options
-rw-r--r-- | healthd/Android.mk | 23 | ||||
-rw-r--r-- | healthd/BatteryMonitor.cpp | 354 | ||||
-rw-r--r-- | healthd/BatteryMonitor.h | 67 | ||||
-rw-r--r-- | healthd/BatteryPropertiesRegistrar.cpp | 81 | ||||
-rw-r--r-- | healthd/BatteryPropertiesRegistrar.h | 52 | ||||
-rw-r--r-- | healthd/healthd.cpp | 258 |
6 files changed, 835 insertions, 0 deletions
diff --git a/healthd/Android.mk b/healthd/Android.mk new file mode 100644 index 0000000..910afb2 --- /dev/null +++ b/healthd/Android.mk @@ -0,0 +1,23 @@ +# Copyright 2013 The Android Open Source Project + +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + healthd.cpp \ + BatteryMonitor.cpp \ + BatteryPropertiesRegistrar.cpp + +LOCAL_MODULE := healthd +LOCAL_MODULE_TAGS := optional +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 + +include $(BUILD_EXECUTABLE) + +endif diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp new file mode 100644 index 0000000..22f7713 --- /dev/null +++ b/healthd/BatteryMonitor.cpp @@ -0,0 +1,354 @@ +/* + * 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" + +#include "BatteryMonitor.h" +#include "BatteryPropertiesRegistrar.h" + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <batteryservice/BatteryService.h> +#include <cutils/klog.h> +#include <utils/String8.h> +#include <utils/Vector.h> + +#define POWER_SUPPLY_SUBSYSTEM "power_supply" +#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM + +namespace android { + +struct sysfsStringEnumMap { + char* s; + int val; +}; + +static int mapSysfsString(const char* str, + struct sysfsStringEnumMap map[]) { + for (int i = 0; map[i].s; i++) + if (!strcmp(str, map[i].s)) + return map[i].val; + + return -1; +} + +int BatteryMonitor::getBatteryStatus(const char* status) { + int ret; + struct sysfsStringEnumMap batteryStatusMap[] = { + { "Unknown", BATTERY_STATUS_UNKNOWN }, + { "Charging", BATTERY_STATUS_CHARGING }, + { "Discharging", BATTERY_STATUS_DISCHARGING }, + { "Not charging", BATTERY_STATUS_NOT_CHARGING }, + { "Full", BATTERY_STATUS_FULL }, + { NULL, 0 }, + }; + + ret = mapSysfsString(status, batteryStatusMap); + if (ret < 0) { + KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status); + ret = BATTERY_STATUS_UNKNOWN; + } + + return ret; +} + +int BatteryMonitor::getBatteryHealth(const char* status) { + int ret; + struct sysfsStringEnumMap batteryHealthMap[] = { + { "Unknown", BATTERY_HEALTH_UNKNOWN }, + { "Good", BATTERY_HEALTH_GOOD }, + { "Overheat", BATTERY_HEALTH_OVERHEAT }, + { "Dead", BATTERY_HEALTH_DEAD }, + { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE }, + { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE }, + { "Cold", BATTERY_HEALTH_COLD }, + { NULL, 0 }, + }; + + ret = mapSysfsString(status, batteryHealthMap); + if (ret < 0) { + KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status); + ret = BATTERY_HEALTH_UNKNOWN; + } + + return ret; +} + +int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) { + char *cp = NULL; + + if (path.isEmpty()) + return -1; + int fd = open(path.string(), O_RDONLY, 0); + if (fd == -1) { + KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string()); + return -1; + } + + ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size)); + if (count > 0) + cp = (char *)memrchr(buf, '\n', count); + + if (cp) + *cp = '\0'; + else + buf[0] = '\0'; + + close(fd); + return count; +} + +BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) { + const int SIZE = 128; + char buf[SIZE]; + int length = readFromFile(path, buf, SIZE); + BatteryMonitor::PowerSupplyType ret; + struct sysfsStringEnumMap supplyTypeMap[] = { + { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN }, + { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY }, + { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB", ANDROID_POWER_SUPPLY_TYPE_USB }, + { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS }, + { NULL, 0 }, + }; + + if (length <= 0) + return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN; + + ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap); + if (ret < 0) + ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN; + + return ret; +} + +bool BatteryMonitor::getBooleanField(const String8& path) { + const int SIZE = 16; + char buf[SIZE]; + + bool value = false; + if (readFromFile(path, buf, SIZE) > 0) { + if (buf[0] != '0') { + value = true; + } + } + + return value; +} + +int BatteryMonitor::getIntField(const String8& path) { + const int SIZE = 128; + char buf[SIZE]; + + int value = 0; + if (readFromFile(path, buf, SIZE) > 0) { + value = strtol(buf, NULL, 0); + } + return value; +} + +bool BatteryMonitor::update(void) { + struct BatteryProperties props; + + props.chargerAcOnline = false; + props.chargerUsbOnline = false; + props.chargerWirelessOnline = false; + props.batteryStatus = BATTERY_STATUS_UNKNOWN; + props.batteryHealth = BATTERY_HEALTH_UNKNOWN; + + if (!mBatteryPresentPath.isEmpty()) + props.batteryPresent = getBooleanField(mBatteryPresentPath); + else + props.batteryPresent = true; + + props.batteryLevel = getIntField(mBatteryCapacityPath); + props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000; + props.batteryTemperature = getIntField(mBatteryTemperaturePath); + + const int SIZE = 128; + char buf[SIZE]; + String8 btech; + + if (readFromFile(mBatteryStatusPath, buf, SIZE) > 0) + props.batteryStatus = getBatteryStatus(buf); + + if (readFromFile(mBatteryHealthPath, buf, SIZE) > 0) + props.batteryHealth = getBatteryHealth(buf); + + if (readFromFile(mBatteryTechnologyPath, buf, SIZE) > 0) + props.batteryTechnology = String8(buf); + + unsigned int i; + + for (i = 0; i < mChargerNames.size(); i++) { + String8 path; + path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, + mChargerNames[i].string()); + + if (readFromFile(path, buf, SIZE) > 0) { + if (buf[0] != '0') { + path.clear(); + path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, + mChargerNames[i].string()); + switch(readPowerSupplyType(path)) { + case ANDROID_POWER_SUPPLY_TYPE_AC: + props.chargerAcOnline = true; + break; + case ANDROID_POWER_SUPPLY_TYPE_USB: + props.chargerUsbOnline = true; + break; + case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: + props.chargerWirelessOnline = true; + break; + default: + KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n", + mChargerNames[i].string()); + } + } + } + } + + KLOG_INFO(LOG_TAG, "battery l=%d v=%d t=%s%d.%d h=%d st=%d chg=%s%s%s\n", + props.batteryLevel, props.batteryVoltage, + props.batteryTemperature < 0 ? "-" : "", + abs(props.batteryTemperature / 10), + abs(props.batteryTemperature % 10), props.batteryHealth, + props.batteryStatus, + props.chargerAcOnline ? "a" : "", + props.chargerUsbOnline ? "u" : "", + props.chargerWirelessOnline ? "w" : ""); + + if (mBatteryPropertiesRegistrar != NULL) + mBatteryPropertiesRegistrar->notifyListeners(props); + + return props.chargerAcOnline | props.chargerUsbOnline | + props.chargerWirelessOnline; +} + +void BatteryMonitor::init(bool nosvcmgr) { + String8 path; + + DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH); + if (dir == NULL) { + KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH); + } else { + struct dirent* entry; + + while ((entry = readdir(dir))) { + const char* name = entry->d_name; + + if (!strcmp(name, ".") || !strcmp(name, "..")) + continue; + + char buf[20]; + // Look for "type" file in each subdirectory + path.clear(); + path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name); + switch(readPowerSupplyType(path)) { + case ANDROID_POWER_SUPPLY_TYPE_AC: + case ANDROID_POWER_SUPPLY_TYPE_USB: + case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: + path.clear(); + path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path.string(), R_OK) == 0) + mChargerNames.add(String8(name)); + break; + + case ANDROID_POWER_SUPPLY_TYPE_BATTERY: + path.clear(); + path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryStatusPath = path; + path.clear(); + path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryHealthPath = path; + path.clear(); + path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryPresentPath = path; + path.clear(); + path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryCapacityPath = path; + + path.clear(); + path.appendFormat("%s/%s/voltage_now", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) { + mBatteryVoltagePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_vol", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryVoltagePath = path; + } + + path.clear(); + path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) { + mBatteryTemperaturePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_temp", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryTemperaturePath = path; + } + + path.clear(); + path.appendFormat("%s/%s/technology", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryTechnologyPath = path; + break; + + case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN: + break; + } + } + closedir(dir); + } + + if (!mChargerNames.size()) + KLOG_ERROR(LOG_TAG, "No charger supplies found\n"); + if (mBatteryStatusPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); + if (mBatteryHealthPath.isEmpty()) + KLOG_WARNING("BatteryHealthPath not found\n"); + if (mBatteryPresentPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); + if (mBatteryCapacityPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); + if (mBatteryVoltagePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); + if (mBatteryTemperaturePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); + if (mBatteryTechnologyPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); + + if (nosvcmgr == false) { + mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this); + mBatteryPropertiesRegistrar->publish(); + } +} + +}; // namespace android diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h new file mode 100644 index 0000000..df0920b --- /dev/null +++ b/healthd/BatteryMonitor.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef HEALTHD_BATTERYMONITOR_H +#define HEALTHD_BATTERYMONITOR_H + +#include <binder/IInterface.h> +#include <utils/String8.h> +#include <utils/Vector.h> + +#include "BatteryPropertiesRegistrar.h" + +namespace android { + +class BatteryPropertiesRegistrar; + +class BatteryMonitor { + public: + + enum PowerSupplyType { + ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0, + ANDROID_POWER_SUPPLY_TYPE_AC, + ANDROID_POWER_SUPPLY_TYPE_USB, + ANDROID_POWER_SUPPLY_TYPE_WIRELESS, + ANDROID_POWER_SUPPLY_TYPE_BATTERY + }; + + void init(bool nosvcmgr); + bool update(void); + + private: + String8 mBatteryStatusPath; + String8 mBatteryHealthPath; + String8 mBatteryPresentPath; + String8 mBatteryCapacityPath; + String8 mBatteryVoltagePath; + String8 mBatteryTemperaturePath; + String8 mBatteryTechnologyPath; + + Vector<String8> mChargerNames; + + sp<BatteryPropertiesRegistrar> mBatteryPropertiesRegistrar; + + int getBatteryStatus(const char* status); + int getBatteryHealth(const char* status); + int readFromFile(const String8& path, char* buf, size_t size); + PowerSupplyType readPowerSupplyType(const String8& path); + bool getBooleanField(const String8& path); + int getIntField(const String8& path); +}; + +}; // namespace android + +#endif // HEALTHD_BATTERY_MONTIOR_H diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp new file mode 100644 index 0000000..6a33ad8 --- /dev/null +++ b/healthd/BatteryPropertiesRegistrar.cpp @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#include "BatteryPropertiesRegistrar.h" +#include <batteryservice/BatteryService.h> +#include <batteryservice/IBatteryPropertiesListener.h> +#include <batteryservice/IBatteryPropertiesRegistrar.h> +#include <binder/IServiceManager.h> +#include <utils/Errors.h> +#include <utils/Mutex.h> +#include <utils/String16.h> + +namespace android { + +BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) { + mBatteryMonitor = monitor; +} + +void BatteryPropertiesRegistrar::publish() { + defaultServiceManager()->addService(String16("batterypropreg"), this); +} + +void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) { + Mutex::Autolock _l(mRegistrationLock); + for (size_t i = 0; i < mListeners.size(); i++) { + mListeners[i]->batteryPropertiesChanged(props); + } +} + +void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) { + { + Mutex::Autolock _l(mRegistrationLock); + // check whether this is a duplicate + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == listener->asBinder()) { + return; + } + } + + mListeners.add(listener); + listener->asBinder()->linkToDeath(this); + } + mBatteryMonitor->update(); +} + +void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) { + Mutex::Autolock _l(mRegistrationLock); + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == listener->asBinder()) { + mListeners[i]->asBinder()->unlinkToDeath(this); + mListeners.removeAt(i); + break; + } + } +} + +void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) { + Mutex::Autolock _l(mRegistrationLock); + + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == who) { + mListeners.removeAt(i); + break; + } + } +} + +} // namespace android diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h new file mode 100644 index 0000000..793ddad --- /dev/null +++ b/healthd/BatteryPropertiesRegistrar.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H +#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H + +#include "BatteryMonitor.h" + +#include <binder/IBinder.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> +#include <batteryservice/BatteryService.h> +#include <batteryservice/IBatteryPropertiesListener.h> +#include <batteryservice/IBatteryPropertiesRegistrar.h> + +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); + void binderDied(const wp<IBinder>& who); +}; + +}; // namespace android + +#endif // HEALTHD_BATTERYPROPERTIES_REGISTRAR_H diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp new file mode 100644 index 0000000..1d79097 --- /dev/null +++ b/healthd/healthd.cpp @@ -0,0 +1,258 @@ +/* + * 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" +#define KLOG_LEVEL 6 + +#include "BatteryMonitor.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.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> + +using namespace android; + +// Periodic chores intervals in seconds +#define PERIODIC_CHORES_INTERVAL_FAST (60 * 1) +#define PERIODIC_CHORES_INTERVAL_SLOW (60 * 10) + +#define POWER_SUPPLY_SUBSYSTEM "power_supply" + +// epoll events: uevent, wakealarm, binder +#define MAX_EPOLL_EVENTS 3 +static int uevent_fd; +static int wakealarm_fd; +static int binder_fd; + +// -1 for no epoll timeout +static int awake_poll_interval = -1; + +static int wakealarm_wake_interval = PERIODIC_CHORES_INTERVAL_FAST; + +static BatteryMonitor* gBatteryMonitor; + +static bool nosvcmgr; + +static void wakealarm_set_interval(int interval) { + struct itimerspec itval; + + if (wakealarm_fd == -1) + return; + + wakealarm_wake_interval = interval; + itval.it_interval.tv_sec = interval; + itval.it_interval.tv_nsec = 0; + itval.it_value.tv_sec = interval; + itval.it_value.tv_nsec = 0; + + if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1) + KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n"); +} + +static void battery_update(void) { + // Fast wake interval when on charger (watch for overheat); + // slow wake interval when on battery (watch for drained battery). + + int new_wake_interval = gBatteryMonitor->update() ? + PERIODIC_CHORES_INTERVAL_FAST : PERIODIC_CHORES_INTERVAL_SLOW; + + if (new_wake_interval != wakealarm_wake_interval) + wakealarm_set_interval(new_wake_interval); + + // During awake periods poll at fast rate. If wake alarm is set at fast + // rate then just use the alarm; if wake alarm is set at slow rate then + // poll at fast rate while awake and let alarm wake up at slow rate when + // asleep. + + awake_poll_interval = + new_wake_interval == PERIODIC_CHORES_INTERVAL_FAST ? + -1 : PERIODIC_CHORES_INTERVAL_FAST * 1000; +} + +static void periodic_chores() { + battery_update(); +} + +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"); +} + +#define UEVENT_MSG_LEN 1024 +static void uevent_event(void) { + char msg[UEVENT_MSG_LEN+2]; + char *cp; + int n; + + n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN); + if (n <= 0) + return; + if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ + return; + + msg[n] = '\0'; + msg[n+1] = '\0'; + cp = msg; + + while (*cp) { + if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { + battery_update(); + break; + } + + /* advance to after the next \0 */ + while (*cp++) + ; + } +} + +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; + } + + wakealarm_set_interval(PERIODIC_CHORES_INTERVAL_FAST); +} + +static void wakealarm_event(void) { + unsigned long long wakeups; + + if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) { + 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); + 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 (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++; + } + + while (1) { + struct epoll_event events[maxevents]; + int nevents; + + IPCThreadState::self()->flushCommands(); + nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval); + + if (nevents == -1) { + if (errno == EINTR) + continue; + KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n"); + break; + } + + for (int n = 0; n < nevents; ++n) { + if (events[n].data.ptr) + (*(void (*)())events[n].data.ptr)(); + } + } + + return; +} + +int main(int argc, char **argv) { + int ch; + + 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); + } + } + + wakealarm_init(); + uevent_init(); + binder_init(); + gBatteryMonitor = new BatteryMonitor(); + gBatteryMonitor->init(nosvcmgr); + + healthd_mainloop(); + return 0; +} |