From d3e3d72cf828c9bcc764ea94ef48f2cbaa4aed97 Mon Sep 17 00:00:00 2001 From: Hridya Valsaraju Date: Mon, 11 Dec 2017 17:31:00 -0800 Subject: [PATCH] Move android.hardware.health@2.0-impl to hardware/interfaces Move android.hardware.health@2.0-impl to hardware/interfaces from system/core/healthd. Bug: 70533363 63702641 Test: make -j32 Change-Id: I0e4468f2183a273b4f73606b04faff88dd7afb72 --- health/2.0/default/Android.bp | 28 ++ health/2.0/default/Health.cpp | 183 +++++++++++++ health/2.0/default/healthd_common.cpp | 275 ++++++++++++++++++++ health/2.0/default/include/health2/Health.h | 69 +++++ 4 files changed, 555 insertions(+) create mode 100644 health/2.0/default/Android.bp create mode 100644 health/2.0/default/Health.cpp create mode 100644 health/2.0/default/healthd_common.cpp create mode 100644 health/2.0/default/include/health2/Health.h diff --git a/health/2.0/default/Android.bp b/health/2.0/default/Android.bp new file mode 100644 index 0000000000..8eb02e0d76 --- /dev/null +++ b/health/2.0/default/Android.bp @@ -0,0 +1,28 @@ +cc_library_static { + name: "android.hardware.health@2.0-impl", + vendor_available: true, + srcs: [ + "Health.cpp", + "healthd_common.cpp", + ], + + cflags: ["-DHEALTHD_USE_HEALTH_2_0"], + + export_include_dirs: ["include"], + + shared_libs: [ + "libbase", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "liblog", + "libutils", + "libcutils", + "android.hardware.health@2.0", + ], + + static_libs: [ + "libbatterymonitor", + "android.hardware.health@1.0-convert", + ], +} diff --git a/health/2.0/default/Health.cpp b/health/2.0/default/Health.cpp new file mode 100644 index 0000000000..4710c90f97 --- /dev/null +++ b/health/2.0/default/Health.cpp @@ -0,0 +1,183 @@ +#define LOG_TAG "android.hardware.health@2.0-impl" +#include + +#include + +#include + +extern void healthd_battery_update_internal(bool); + +namespace android { +namespace hardware { +namespace health { +namespace V2_0 { +namespace implementation { + +sp Health::instance_; + +Health::Health(struct healthd_config* c) { + // TODO(b/69268160): remove when libhealthd is removed. + healthd_board_init(c); + battery_monitor_ = std::make_unique(); + battery_monitor_->init(c); +} + +// Methods from IHealth follow. +Return Health::registerCallback(const sp& callback) { + if (callback == nullptr) { + return Result::SUCCESS; + } + + { + std::lock_guard _lock(callbacks_lock_); + callbacks_.push_back(callback); + // unlock + } + + auto linkRet = callback->linkToDeath(this, 0u /* cookie */); + if (!linkRet.withDefault(false)) { + LOG(WARNING) << __func__ << "Cannot link to death: " + << (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description()); + // ignore the error + } + + return update(); +} + +bool Health::unregisterCallbackInternal(const sp& callback) { + if (callback == nullptr) return false; + + bool removed = false; + std::lock_guard _lock(callbacks_lock_); + for (auto it = callbacks_.begin(); it != callbacks_.end();) { + if (interfacesEqual(*it, callback)) { + it = callbacks_.erase(it); + removed = true; + } else { + ++it; + } + } + (void)callback->unlinkToDeath(this).isOk(); // ignore errors + return removed; +} + +Return Health::unregisterCallback(const sp& callback) { + return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND; +} + +template +void getProperty(const std::unique_ptr& monitor, int id, T defaultValue, + const std::function& callback) { + struct BatteryProperty prop; + T ret = defaultValue; + Result result = Result::SUCCESS; + status_t err = monitor->getProperty(static_cast(id), &prop); + if (err != OK) { + LOG(DEBUG) << "getProperty(" << id << ")" + << " fails: (" << err << ") " << strerror(-err); + } else { + ret = static_cast(prop.valueInt64); + } + switch (err) { + case OK: + result = Result::SUCCESS; + break; + case NAME_NOT_FOUND: + result = Result::NOT_SUPPORTED; + break; + default: + result = Result::UNKNOWN; + break; + } + callback(result, static_cast(ret)); +} + +Return Health::getChargeCounter(getChargeCounter_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, INT32_MIN, _hidl_cb); + return Void(); +} + +Return Health::getCurrentNow(getCurrentNow_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CURRENT_NOW, INT32_MIN, _hidl_cb); + return Void(); +} + +Return Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CURRENT_AVG, INT32_MIN, _hidl_cb); + return Void(); +} + +Return Health::getCapacity(getCapacity_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CAPACITY, INT32_MIN, _hidl_cb); + return Void(); +} + +Return Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, INT64_MIN, _hidl_cb); + return Void(); +} + +Return Health::getChargeStatus(getChargeStatus_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, _hidl_cb); + return Void(); +} + +Return Health::update() { + if (!healthd_mode_ops || !healthd_mode_ops->battery_update) { + LOG(WARNING) << "health@2.0: update: not initialized. " + << "update() should not be called in charger / recovery."; + return Result::UNKNOWN; + } + + // Retrieve all information and call healthd_mode_ops->battery_update, which calls + // notifyListeners. + bool chargerOnline = battery_monitor_->update(); + + // adjust uevent / wakealarm periods + healthd_battery_update_internal(chargerOnline); + + return Result::SUCCESS; +} + +void Health::notifyListeners(const HealthInfo& info) { + std::lock_guard _lock(callbacks_lock_); + for (auto it = callbacks_.begin(); it != callbacks_.end();) { + auto ret = (*it)->healthInfoChanged(info); + if (!ret.isOk() && ret.isDeadObject()) { + it = callbacks_.erase(it); + } else { + ++it; + } + } +} + +Return Health::debug(const hidl_handle& handle, const hidl_vec&) { + if (handle != nullptr && handle->numFds >= 1) { + int fd = handle->data[0]; + battery_monitor_->dumpState(fd); + fsync(fd); + } + return Void(); +} + +void Health::serviceDied(uint64_t /* cookie */, const wp& who) { + (void)unregisterCallbackInternal(who.promote()); +} + +sp Health::initInstance(struct healthd_config* c) { + if (instance_ == nullptr) { + instance_ = new Health(c); + } + return instance_; +} + +sp Health::getImplementation() { + CHECK(instance_ != nullptr); + return instance_; +} + +} // namespace implementation +} // namespace V2_0 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/health/2.0/default/healthd_common.cpp b/health/2.0/default/healthd_common.cpp new file mode 100644 index 0000000000..8ff409dd56 --- /dev/null +++ b/health/2.0/default/healthd_common.cpp @@ -0,0 +1,275 @@ +/* + * 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 "android.hardware.health@2.0-impl" +#define KLOG_LEVEL 6 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace android; + +// Periodic chores fast interval in seconds +#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1) +// Periodic chores fast interval in seconds +#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10) + +static struct healthd_config healthd_config = { + .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST, + .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW, + .batteryStatusPath = String8(String8::kEmptyString), + .batteryHealthPath = String8(String8::kEmptyString), + .batteryPresentPath = String8(String8::kEmptyString), + .batteryCapacityPath = String8(String8::kEmptyString), + .batteryVoltagePath = String8(String8::kEmptyString), + .batteryTemperaturePath = String8(String8::kEmptyString), + .batteryTechnologyPath = String8(String8::kEmptyString), + .batteryCurrentNowPath = String8(String8::kEmptyString), + .batteryCurrentAvgPath = String8(String8::kEmptyString), + .batteryChargeCounterPath = String8(String8::kEmptyString), + .batteryFullChargePath = String8(String8::kEmptyString), + .batteryCycleCountPath = String8(String8::kEmptyString), + .energyCounter = NULL, + .boot_min_cap = 0, + .screen_on = NULL, +}; + +static int eventct; +static int epollfd; + +#define POWER_SUPPLY_SUBSYSTEM "power_supply" + +// epoll_create() parameter is actually unused +#define MAX_EPOLL_EVENTS 40 +static int uevent_fd; +static int wakealarm_fd; + +// -1 for no epoll timeout +static int awake_poll_interval = -1; + +static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; + +using ::android::hardware::health::V2_0::implementation::Health; + +struct healthd_mode_ops* healthd_mode_ops = nullptr; + +int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) { + struct epoll_event ev; + + ev.events = EPOLLIN; + + if (wakeup == EVENT_WAKEUP_FD) ev.events |= 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; + + if (wakealarm_fd == -1) return; + + wakealarm_wake_interval = interval; + + if (interval == -1) interval = 0; + + 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"); +} + +void healthd_battery_update_internal(bool charger_online) { + // Fast wake interval when on charger (watch for overheat); + // slow wake interval when on battery (watch for drained battery). + + int new_wake_interval = charger_online ? healthd_config.periodic_chores_interval_fast + : healthd_config.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. + + if (healthd_config.periodic_chores_interval_fast == -1) + awake_poll_interval = -1; + else + awake_poll_interval = new_wake_interval == healthd_config.periodic_chores_interval_fast + ? -1 + : healthd_config.periodic_chores_interval_fast * 1000; +} + +static void healthd_battery_update(void) { + Health::getImplementation()->update(); +} + +static void periodic_chores() { + healthd_battery_update(); +} + +#define UEVENT_MSG_LEN 2048 +static void uevent_event(uint32_t /*epevents*/) { + 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)) { + healthd_battery_update(); + break; + } + + /* advance to after the next \0 */ + while (*cp++) + ; + } +} + +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; + } + + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD)) + KLOG_ERROR(LOG_TAG, "register for uevent events failed\n"); +} + +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"); + return; + } + + periodic_chores(); +} + +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 (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD)) + KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n"); + + wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); +} + +static void healthd_mainloop(void) { + int nevents = 0; + while (1) { + struct epoll_event events[eventct]; + int timeout = awake_poll_interval; + int mode_timeout; + + /* Don't wait for first timer timeout to run periodic chores */ + if (!nevents) periodic_chores(); + + healthd_mode_ops->heartbeat(); + + 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) 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 (*)(int))events[n].data.ptr)(events[n].events); + } + } + + 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); + wakealarm_init(); + uevent_init(); + + return 0; +} + +int healthd_main() { + int ret; + + klog_set_level(KLOG_LEVEL); + + if (!healthd_mode_ops) { + KLOG_ERROR("healthd ops not set, exiting\n"); + exit(1); + } + + ret = healthd_init(); + if (ret) { + KLOG_ERROR("Initialization failed, exiting\n"); + exit(2); + } + + healthd_mainloop(); + KLOG_ERROR("Main loop terminated, exiting\n"); + return 3; +} diff --git a/health/2.0/default/include/health2/Health.h b/health/2.0/default/include/health2/Health.h new file mode 100644 index 0000000000..ab42ae7958 --- /dev/null +++ b/health/2.0/default/include/health2/Health.h @@ -0,0 +1,69 @@ +#ifndef ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H +#define ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H + +#include +#include + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace health { +namespace V2_0 { +namespace implementation { + +using V1_0::BatteryStatus; +using V1_0::HealthInfo; + +using ::android::hidl::base::V1_0::IBase; + +struct Health : public IHealth, hidl_death_recipient { + public: + static sp initInstance(struct healthd_config* c); + // Should only be called by implementation itself (-impl, -service). + // Clients should not call this function. Instead, initInstance() initializes and returns the + // global instance that has fewer functions. + // TODO(b/62229583): clean up and hide these functions after update() logic is simplified. + static sp getImplementation(); + + Health(struct healthd_config* c); + + // TODO(b/62229583): clean up and hide these functions after update() logic is simplified. + void notifyListeners(const HealthInfo& info); + + // Methods from IHealth follow. + Return registerCallback(const sp& callback) override; + Return unregisterCallback(const sp& callback) override; + Return update() override; + Return getChargeCounter(getChargeCounter_cb _hidl_cb) override; + Return getCurrentNow(getCurrentNow_cb _hidl_cb) override; + Return getCurrentAverage(getCurrentAverage_cb _hidl_cb) override; + Return getCapacity(getCapacity_cb _hidl_cb) override; + Return getEnergyCounter(getEnergyCounter_cb _hidl_cb) override; + Return getChargeStatus(getChargeStatus_cb _hidl_cb) override; + + // Methods from ::android::hidl::base::V1_0::IBase follow. + Return debug(const hidl_handle& fd, const hidl_vec& args) override; + + void serviceDied(uint64_t cookie, const wp& /* who */) override; + + private: + static sp instance_; + + std::mutex callbacks_lock_; + std::vector> callbacks_; + std::unique_ptr battery_monitor_; + + bool unregisterCallbackInternal(const sp& cb); +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace health +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H