diff options
author | Yifan Hong <elsk@google.com> | 2017-09-19 17:57:50 -0700 |
---|---|---|
committer | Yifan Hong <elsk@google.com> | 2017-10-06 15:29:59 -0700 |
commit | 2763df8eaed8e6714e06e519ea3e654fc6ae9137 (patch) | |
tree | 9ce2a7fcf4321beb88af89d9a106b923f5c4f28c /healthd | |
parent | 0e5361be318619826c6cb5a9cc4b29e4268123a7 (diff) | |
download | core-2763df8eaed8e6714e06e519ea3e654fc6ae9137.tar.gz core-2763df8eaed8e6714e06e519ea3e654fc6ae9137.tar.bz2 core-2763df8eaed8e6714e06e519ea3e654fc6ae9137.zip |
Implement android.hardware.health@2.0.
healthd_common.cpp uses health@2.0 for health@2.0-service
and healthd.
charger and recovery will be fixed in a follow up change.
(They still use libbatterymonitor for now).
Test: adb logcat -e "health@2.0"
Test: adb shell lshal --debug
Change-Id: I9ff0691d467df37118d7f143693c1bcd17a5bcbf
Diffstat (limited to 'healthd')
-rw-r--r-- | healthd/Android.bp | 56 | ||||
-rw-r--r-- | healthd/Health.cpp | 163 | ||||
-rw-r--r-- | healthd/HealthService.cpp | 108 | ||||
-rw-r--r-- | healthd/android.hardware.health@2.0-service.rc | 4 | ||||
-rw-r--r-- | healthd/healthd_common.cpp | 116 | ||||
-rw-r--r-- | healthd/include/health2/Health.h | 61 |
6 files changed, 502 insertions, 6 deletions
diff --git a/healthd/Android.bp b/healthd/Android.bp index 7269b628c..d3488665e 100644 --- a/healthd/Android.bp +++ b/healthd/Android.bp @@ -18,3 +18,59 @@ cc_library_static { header_libs: ["libhealthd_headers"], export_header_lib_headers: ["libhealthd_headers"], } + +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", + ], +} + +cc_binary { + name: "android.hardware.health@2.0-service", + init_rc: ["android.hardware.health@2.0-service.rc"], + vendor: true, + relative_install_path: "hw", + srcs: ["HealthService.cpp"], + + cflags: ["-DHEALTH_INSTANCE_NAME=\"default\""], + + static_libs: [ + "android.hardware.health@2.0-impl", + "android.hardware.health@1.0-convert", + "libbatterymonitor", + ], + + shared_libs: [ + "libbase", + "libcutils", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "liblog", + "libutils", + "android.hardware.health@2.0", + ], +} diff --git a/healthd/Health.cpp b/healthd/Health.cpp new file mode 100644 index 000000000..74f3eec21 --- /dev/null +++ b/healthd/Health.cpp @@ -0,0 +1,163 @@ +#define LOG_TAG "Health" +#include <android-base/logging.h> + +#include <health2/Health.h> + +#include <hidl/HidlTransportSupport.h> + +extern void healthd_battery_update_internal(bool); + +namespace android { +namespace hardware { +namespace health { +namespace V2_0 { +namespace implementation { + +Health::Health(struct healthd_config* c) { + battery_monitor_ = std::make_unique<BatteryMonitor>(); + battery_monitor_->init(c); +} + +// Methods from IHealth follow. +Return<Result> Health::registerCallback(const sp<IHealthInfoCallback>& callback) { + if (callback == nullptr) { + return Result::SUCCESS; + } + + { + std::lock_guard<std::mutex> _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<IBase>& callback) { + if (callback == nullptr) return false; + + bool removed = false; + std::lock_guard<std::mutex> _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<Result> Health::unregisterCallback(const sp<IHealthInfoCallback>& callback) { + return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND; +} + +template<typename T> +void getProperty(const std::unique_ptr<BatteryMonitor>& monitor, int id, T defaultValue, + const std::function<void(Result, T)>& callback) { + struct BatteryProperty prop; + T ret = defaultValue; + Result result = Result::SUCCESS; + status_t err = monitor->getProperty(static_cast<int>(id), &prop); + if (err != OK) { + LOG(DEBUG) << "getProperty(" << id << ")" << " fails: (" << err << ") " << strerror(-err); + } else { + ret = static_cast<T>(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<T>(ret)); +} + +Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, INT32_MIN, _hidl_cb); + return Void(); +} + +Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CURRENT_NOW, INT32_MIN, _hidl_cb); + return Void(); +} + +Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CURRENT_AVG, INT32_MIN, _hidl_cb); + return Void(); +} + +Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_CAPACITY, INT32_MIN, _hidl_cb); + return Void(); +} + +Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, INT64_MIN, _hidl_cb); + return Void(); +} + +Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) { + getProperty(battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, _hidl_cb); + return Void(); +} + + +Return<Result> 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<std::mutex> _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<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) { + 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<IBase>& who) { + (void)unregisterCallbackInternal(who.promote()); +} + +// Methods from ::android::hidl::base::V1_0::IBase follow. + +} // namespace implementation +} // namespace V2_0 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/healthd/HealthService.cpp b/healthd/HealthService.cpp new file mode 100644 index 000000000..e8a1a85e2 --- /dev/null +++ b/healthd/HealthService.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2017 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. + */ + +// This is a reference implementation for health@2.0 HAL. A vendor +// can write its own HealthService.cpp with customized init and update functions. + +#define LOG_TAG "health@2.0/" HEALTH_INSTANCE_NAME +#include <android-base/logging.h> + +#include <android/hardware/health/1.0/types.h> +#include <hal_conversion.h> +#include <health2/Health.h> +#include <healthd/healthd.h> +#include <hidl/HidlTransportSupport.h> + +using android::hardware::IPCThreadState; +using android::hardware::configureRpcThreadpool; +using android::hardware::health::V1_0::HealthInfo; +using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo; +using android::hardware::health::V2_0::IHealth; +using android::hardware::health::V2_0::implementation::Health; + +// see healthd_common.cpp +android::sp<IHealth> gHealth; + +static int gBinderFd; + +extern int healthd_main(void); + +static void binder_event(uint32_t /*epevents*/) { + IPCThreadState::self()->handlePolledCommands(); +} + +// TODO(b/67463967): healthd_board_* functions should be removed in health@2.0 +int healthd_board_battery_update(struct android::BatteryProperties*) +{ + // return 0 to log periodic polled battery status to kernel log + return 0; +} + +void healthd_mode_service_2_0_init(struct healthd_config* config) { + LOG(INFO) << LOG_TAG << " Hal is starting up..."; + + // Implementation-defined init logic goes here. + // 1. config->periodic_chores_interval_* variables + // 2. config->battery*Path variables + // 3. config->energyCounter. In this implementation, energyCounter is not defined. + + configureRpcThreadpool(1, false /* callerWillJoin */); + IPCThreadState::self()->disableBackgroundScheduling(true); + IPCThreadState::self()->setupPolling(&gBinderFd); + + if (gBinderFd >= 0) { + if (healthd_register_event(gBinderFd, binder_event)) + LOG(ERROR) << LOG_TAG << ": Register for binder events failed"; + } + + gHealth = new ::android::hardware::health::V2_0::implementation::Health(config); + CHECK_EQ(gHealth->registerAsService(HEALTH_INSTANCE_NAME), android::OK) + << LOG_TAG << ": Failed to register HAL"; + + LOG(INFO) << LOG_TAG << ": Hal init done"; +} + +int healthd_mode_service_2_0_preparetowait(void) { + IPCThreadState::self()->flushCommands(); + return -1; +} + +void healthd_mode_service_2_0_heartbeat(void) { + // noop +} + +void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) { + + // Implementation-defined update logic goes here. An implementation + // can make modifications to prop before broadcasting it to all callbacks. + + HealthInfo info; + convertToHealthInfo(prop, info); + static_cast<Health*>(gHealth.get())->notifyListeners(info); +} + +static struct healthd_mode_ops healthd_mode_service_2_0_ops = { + .init = healthd_mode_service_2_0_init, + .preparetowait = healthd_mode_service_2_0_preparetowait, + .heartbeat = healthd_mode_service_2_0_heartbeat, + .battery_update = healthd_mode_service_2_0_battery_update, +}; + +int main() { + healthd_mode_ops = &healthd_mode_service_2_0_ops; + LOG(INFO) << LOG_TAG << ": Hal starting main loop..."; + return healthd_main(); +} diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc new file mode 100644 index 000000000..8b868687e --- /dev/null +++ b/healthd/android.hardware.health@2.0-service.rc @@ -0,0 +1,4 @@ +service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service + class hal + user system + group system diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp index 659991918..19e600f86 100644 --- a/healthd/healthd_common.cpp +++ b/healthd/healthd_common.cpp @@ -33,6 +33,10 @@ #include <sys/timerfd.h> #include <utils/Errors.h> +#ifdef HEALTHD_USE_HEALTH_2_0 +#include <health2/Health.h> +#endif + using namespace android; #ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST @@ -84,9 +88,13 @@ static int awake_poll_interval = -1; static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; -static BatteryMonitor* gBatteryMonitor; +#ifndef HEALTHD_USE_HEALTH_2_0 +static BatteryMonitor* gBatteryMonitor = nullptr; +#else +extern sp<::android::hardware::health::V2_0::IHealth> gHealth; +#endif -struct healthd_mode_ops *healthd_mode_ops; +struct healthd_mode_ops *healthd_mode_ops = nullptr; int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) { struct epoll_event ev; @@ -127,17 +135,87 @@ static void wakealarm_set_interval(int interval) { KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n"); } +#ifdef HEALTHD_USE_HEALTH_2_0 +status_t convertStatus(android::hardware::health::V2_0::Result r) { + using android::hardware::health::V2_0::Result; + switch(r) { + case Result::SUCCESS: return OK; + case Result::NOT_SUPPORTED: return BAD_VALUE; + case Result::NOT_FOUND: return NAME_NOT_FOUND; + case Result::CALLBACK_DIED: return DEAD_OBJECT; + case Result::UNKNOWN: // fallthrough + default: + return UNKNOWN_ERROR; + } +} +#endif + status_t healthd_get_property(int id, struct BatteryProperty *val) { +#ifndef HEALTHD_USE_HEALTH_2_0 return gBatteryMonitor->getProperty(id, val); +#else + using android::hardware::health::V1_0::BatteryStatus; + using android::hardware::health::V2_0::Result; + val->valueInt64 = INT64_MIN; + status_t err = UNKNOWN_ERROR; + switch (id) { + case BATTERY_PROP_CHARGE_COUNTER: { + gHealth->getChargeCounter([&](Result r, int32_t v) { + err = convertStatus(r); + val->valueInt64 = v; + }); + break; + } + case BATTERY_PROP_CURRENT_NOW: { + gHealth->getCurrentNow([&](Result r, int32_t v) { + err = convertStatus(r); + val->valueInt64 = v; + }); + break; + } + case BATTERY_PROP_CURRENT_AVG: { + gHealth->getCurrentAverage([&](Result r, int32_t v) { + err = convertStatus(r); + val->valueInt64 = v; + }); + break; + } + case BATTERY_PROP_CAPACITY: { + gHealth->getCapacity([&](Result r, int32_t v) { + err = convertStatus(r); + val->valueInt64 = v; + }); + break; + } + case BATTERY_PROP_ENERGY_COUNTER: { + gHealth->getEnergyCounter([&](Result r, int64_t v) { + err = convertStatus(r); + val->valueInt64 = v; + }); + break; + } + case BATTERY_PROP_BATTERY_STATUS: { + gHealth->getChargeStatus([&](Result r, BatteryStatus v) { + err = convertStatus(r); + val->valueInt64 = static_cast<int64_t>(v); + }); + break; + } + default: { + err = BAD_VALUE; + break; + } + } + return err; +#endif } -void healthd_battery_update(void) { +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 = gBatteryMonitor->update() ? - healthd_config.periodic_chores_interval_fast : - healthd_config.periodic_chores_interval_slow; + 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); @@ -155,8 +233,25 @@ void healthd_battery_update(void) { -1 : healthd_config.periodic_chores_interval_fast * 1000; } +void healthd_battery_update(void) { +#ifndef HEALTHD_USE_HEALTH_2_0 + healthd_battery_update_internal(gBatteryMonitor->update()); +#else + gHealth->update(); +#endif +} + void healthd_dump_battery_state(int fd) { +#ifndef HEALTHD_USE_HEALTH_2_0 gBatteryMonitor->dumpState(fd); +#else + native_handle_t* nativeHandle = native_handle_create(1, 0); + nativeHandle->data[0] = fd; + ::android::hardware::hidl_handle handle; + handle.setTo(nativeHandle, true /* shouldOwn */); + gHealth->debug(handle, {} /* options */); +#endif + fsync(fd); } @@ -273,12 +368,21 @@ static int healthd_init() { return -1; } +#ifndef HEALTHD_USE_HEALTH_2_0 healthd_board_init(&healthd_config); +#else + // healthd_board_* functions are removed in health@2.0 +#endif + healthd_mode_ops->init(&healthd_config); wakealarm_init(); uevent_init(); + +#ifndef HEALTHD_USE_HEALTH_2_0 gBatteryMonitor = new BatteryMonitor(); gBatteryMonitor->init(&healthd_config); +#endif + return 0; } diff --git a/healthd/include/health2/Health.h b/healthd/include/health2/Health.h new file mode 100644 index 000000000..4e78380e7 --- /dev/null +++ b/healthd/include/health2/Health.h @@ -0,0 +1,61 @@ +#ifndef ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H +#define ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H + +#include <memory> +#include <vector> + +#include <android/hardware/health/1.0/types.h> +#include <android/hardware/health/2.0/IHealth.h> +#include <healthd/BatteryMonitor.h> +#include <hidl/Status.h> + +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: + Health(struct healthd_config* c); + + // TODO(b/62229583): clean up and hide these functions. + void notifyListeners(const HealthInfo& info); + + // Methods from IHealth follow. + Return<Result> registerCallback(const sp<IHealthInfoCallback>& callback) override; + Return<Result> unregisterCallback(const sp<IHealthInfoCallback>& callback) override; + Return<Result> update() override; + Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override; + Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override; + Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override; + Return<void> getCapacity(getCapacity_cb _hidl_cb) override; + Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override; + Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override; + + // Methods from ::android::hidl::base::V1_0::IBase follow. + Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override; + + void serviceDied(uint64_t cookie, const wp<IBase>& /* who */) override; + + private: + std::mutex callbacks_lock_; + std::vector<sp<IHealthInfoCallback>> callbacks_; + std::unique_ptr<BatteryMonitor> battery_monitor_; + + bool unregisterCallbackInternal(const sp<IBase>& cb); + +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace health +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H |