From e438211a0f8b7fac1ce98355aa84db213cf95ce4 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 2 Oct 2019 19:09:37 -0700 Subject: [PATCH] health 2.1 implementation. Test: VTS health 2.1 test Bug: 137670450 Change-Id: I04ea74facbaa534eee00a51474a013db3b6eb212 --- health/2.0/README.md | 5 + health/2.1/README.md | 245 ++++++++++++++++ health/2.1/default/Android.bp | 81 ++++++ .../android.hardware.health@2.1-service.rc | 6 + .../default/android.hardware.health@2.1.xml | 7 + health/2.1/default/impl.cpp | 53 ++++ health/2.1/default/service.cpp | 37 +++ health/utils/libhealth2impl/Android.bp | 51 ++++ health/utils/libhealth2impl/BinderHealth.cpp | 190 +++++++++++++ health/utils/libhealth2impl/HalHealthLoop.cpp | 94 ++++++ health/utils/libhealth2impl/Health.cpp | 267 ++++++++++++++++++ .../include/health2impl/BinderHealth.h | 111 ++++++++ .../include/health2impl/Callback.h | 73 +++++ .../include/health2impl/HalHealthLoop.h | 67 +++++ .../include/health2impl/Health.h | 90 ++++++ 15 files changed, 1377 insertions(+) create mode 100644 health/2.1/README.md create mode 100644 health/2.1/default/Android.bp create mode 100644 health/2.1/default/android.hardware.health@2.1-service.rc create mode 100644 health/2.1/default/android.hardware.health@2.1.xml create mode 100644 health/2.1/default/impl.cpp create mode 100644 health/2.1/default/service.cpp create mode 100644 health/utils/libhealth2impl/Android.bp create mode 100644 health/utils/libhealth2impl/BinderHealth.cpp create mode 100644 health/utils/libhealth2impl/HalHealthLoop.cpp create mode 100644 health/utils/libhealth2impl/Health.cpp create mode 100644 health/utils/libhealth2impl/include/health2impl/BinderHealth.h create mode 100644 health/utils/libhealth2impl/include/health2impl/Callback.h create mode 100644 health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h create mode 100644 health/utils/libhealth2impl/include/health2impl/Health.h diff --git a/health/2.0/README.md b/health/2.0/README.md index 4ecfb9a826..8a7c922e14 100644 --- a/health/2.0/README.md +++ b/health/2.0/README.md @@ -1,3 +1,8 @@ +# Implement the 2.1 HAL instead! + +It is strongly recommended that you implement the 2.1 HAL directly. See +`hardware/interfaces/health/2.1/README.md` for more details. + # Upgrading from Health 1.0 HAL 1. Remove `android.hardware.health@1.0*` from `PRODUCT_PACKAGES` diff --git a/health/2.1/README.md b/health/2.1/README.md new file mode 100644 index 0000000000..5a19d7b491 --- /dev/null +++ b/health/2.1/README.md @@ -0,0 +1,245 @@ +# Implementing Health 2.1 HAL + +1. Install common binderized service. The binderized service `dlopen()`s + passthrough implementations on the device, so there is no need to write + your own. + + ```mk + # Install default binderized implementation to vendor. + PRODUCT_PACKAGES += android.hardware.health@2.1-service + ``` + +1. Delete existing VINTF manifest entry. Search for `android.hardware.health` in + your device manifest, and delete the whole `` entry for older versions + of the HAL. Instead, when `android.hardware.health@2.1-service` is installed, + a VINTF manifest fragment is installed to `/vendor/etc/vintf`, so there is + no need to manually specify it in your device manifest. See + [Manifest fragments](https://source.android.com/devices/architecture/vintf/objects#manifest-fragments) + for details. + +1. Install the proper passthrough implemetation. + + 1. If you want to use default implementation: + + ```mk + # Install default passthrough implementation to vendor. + PRODUCT_PACKAGES += android.hardware.health@2.1-impl + ``` + + You are done. Otherwise, go to the next step. + + 1. If you want to write your own implementation, + + 1. Copy skeleton implementation from the [appendix](#impl). + + 1. Modify the implementation to suit your needs. + + * If you have a board or device specific `libhealthd`, see + [Upgrading with a customized libhealthd](#update-from-1-0). + * If you are upgrading from 1.0 health HAL, see + [Upgrading from Health HAL 1.0](#update-from-1-0). + * If you are upgrading from a customized 2.0 health HAL + implementation, See + [Upgrading from Health HAL 2.0](#update-from-2-0). + + 1. [Update necessary SELinux permissions](#selinux). + + 1. [Fix `/charger` symlink](#charger-symlink). + +# Upgrading with a customized libhealthd or from Health HAL 1.0 {#update-from-1-0} + +`libhealthd` contains two functions: `healthd_board_init()` and +`healthd_board_battery_update()`. Similarly, Health HAL 1.0 contains `init()` +and `update()`, with an additional `energyCounter()` function. + +* `healthd_board_init()` / `@1.0::IHealth.init()` should be called before + passing the `healthd_config` struct to your `HealthImpl` class. See + `HIDL_FETCH_IHealth` in [`HealthImpl.cpp`](#health_impl_cpp). + +* `healthd_board_battery_update()` / `@1.0::IHealth.update()` should be called + in `HealthImpl::UpdateHealthInfo()`. Example: + + ```c++ + void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) { + struct BatteryProperties props; + convertFromHealthInfo(health_info->legacy.legacy, &props); + healthd_board_battery_update(&props); + convertToHealthInfo(&props, health_info->legacy.legacy); + } + ``` + For efficiency, you should move code in `healthd_board_battery_update` to + `HealthImpl::UpdateHealthInfo` and modify `health_info` directly to avoid + conversion to `BatteryProperties`. + +* Code for `@1.0::IHealth.energyCounter()` should be moved to + `HealthImpl::getEnergyCounter()`. Example: + + ```c++ + Return Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) { + int64_t energy = /* ... */; + _hidl_cb(Result::SUCCESS, energy); + return Void(); + } + ``` + +# Upgrading from Health HAL 2.0 {#update-from-2-0} + +* If you have implemented `healthd_board_init()` and/or + `healthd_board_battery_update()` (instead of using `libhealthd.default`), + see [the section above](#update-from-1-0) + for instructions to convert them. + +* If you have implemented `get_storage_info()` and/or `get_disk_stats()` + (instead of using libhealthstoragedefault), implement `HealthImpl::getDiskStats` + and/or `HealthImpl::getStorageInfo` directly. There is no need to override + `HealthImpl::getHealthInfo` or `HealthImpl::getHealthInfo_2_1` because they call + `getDiskStats` and `getStorageInfo` to retrieve storage information. + +# Update necessary SELinux permissions {#selinux} + +For example (replace `` with the device name): +``` +# device///sepolicy/vendor/hal_health_default.te +# Add device specific permissions to hal_health_default domain, especially +# if a device-specific libhealthd is used and/or device-specific storage related +# APIs are implemented. +``` + +# Fix `/charger` symlink {#charger-symlink} +If you are using `/charger` in your `init.rc` scripts, it is recommended +(required for devices running in Android R) that the path is changed to +`/system/bin/charger` instead. + +Search for `service charger` in your device configuration directory to see if +this change applies to your device. Below is an example of how the script should +look like: + +``` +service charger /system/bin/charger + class charger + user system + group system wakelock input + capabilities SYS_BOOT + file /dev/kmsg w + file /sys/fs/pstore/console-ramoops-0 r + file /sys/fs/pstore/console-ramoops r + file /proc/last_kmsg r +``` + +# Appendix: sample code for the implementation {#impl} + +## `device///health/Android.bp` {#android_bp} + +```bp +cc_library_shared { + name: "android.hardware.health@2.1-impl-", + stem: "android.hardware.health@2.0-impl-2.1-", + + // Install to vendor and recovery. + proprietary: true, + recovery_available: true, + + relative_install_path: "hw", + + shared_libs: [ + "libbase", + "libcutils", + "libhidlbase", + "liblog", + "libutils", + "android.hardware.health@2.1", + "android.hardware.health@2.0", + ], + + static_libs: [ + "android.hardware.health@1.0-convert", + "libbatterymonitor", + "libhealthloop", + "libhealth2impl", + // "libhealthd." + ], + + srcs: [ + "HealthImpl.cpp", + ], + + // No vintf_fragments because both -impl and -service should have been + // installed. +} +``` + +## `device///health/HealthImpl.cpp` {#health_impl_cpp} + +```c++ +#include +#include + +#include +#include +#include + +using ::android::sp; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::health::InitHealthdConfig; +using ::android::hardware::health::V2_1::IHealth; +using ::android::hidl::base::V1_0::IBase; + +using namespace std::literals; + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +// android::hardware::health::V2_1::implementation::Health implements most +// defaults. Uncomment functions that you need to override. +class HealthImpl : public Health { + public: + HealthImpl(std::unique_ptr&& config) + : Health(std::move(config)) {} + + // A subclass can override this if these information should be retrieved + // differently. + // 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; + // Return getStorageInfo(getStorageInfo_cb _hidl_cb) override; + // Return getDiskStats(getDiskStats_cb _hidl_cb) override; + // Return getHealthInfo(getHealthInfo_cb _hidl_cb) override; + + // Functions introduced in Health HAL 2.1. + // Return getHealthConfig(getHealthConfig_cb _hidl_cb) override; + // Return getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override; + // Return shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override; + + protected: + // A subclass can override this to modify any health info object before + // returning to clients. This is similar to healthd_board_battery_update(). + // By default, it does nothing. + // void UpdateHealthInfo(HealthInfo* health_info) override; +}; + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android + +extern "C" IHealth* HIDL_FETCH_IHealth(const char* instance) { + using ::android::hardware::health::V2_1::implementation::HealthImpl; + if (instance != "default"sv) { + return nullptr; + } + auto config = std::make_unique(); + InitHealthdConfig(config.get()); + + // healthd_board_init(config.get()); + + return new HealthImpl(std::move(config)); +} +``` diff --git a/health/2.1/default/Android.bp b/health/2.1/default/Android.bp new file mode 100644 index 0000000000..3649853a3d --- /dev/null +++ b/health/2.1/default/Android.bp @@ -0,0 +1,81 @@ +// Copyright (C) 2019 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. + +cc_defaults { + name: "android.hardware.health@2.1-impl-defaults", + relative_install_path: "hw", + + shared_libs: [ + "libbase", + "libcutils", + "libhidlbase", + "liblog", + "libutils", + "android.hardware.health@2.1", + "android.hardware.health@2.0", + ], + + static_libs: [ + "android.hardware.health@1.0-convert", + "libbatterymonitor", + "libhealthloop", + "libhealth2impl", + ], +} + +// Default passthrough implementation of the health@2.1 HAL. +// Passhtrough implementations of the health@2.1 HAL must be installed in +// vendor in order to support charger. +// Passhtrough implementations of the health@2.1 HAL must be installed in +// recovery in order to allow recovery to check battery status. +// See README.md for details. +cc_library_shared { + name: "android.hardware.health@2.1-impl", + stem: "android.hardware.health@2.0-impl-2.1", + + // Only vendor and recovery variants are allowed, not core. + vendor: true, + recovery_available: true, + + defaults: ["android.hardware.health@2.1-impl-defaults"], + + srcs: [ + "impl.cpp", + ], + + // No vintf_fragments because both -impl and -service should have been + // installed. +} + +// Default binderized service of the health@2.1 HAL. +// This binderized implementation dlopen()s the passthrough implementation, +// so there is no need to implement your own. +cc_binary { + name: "android.hardware.health@2.1-service", + vendor: true, + defaults: ["android.hardware.health@2.1-impl-defaults"], + init_rc: ["android.hardware.health@2.1-service.rc"], + + srcs: [ + "service.cpp", + ], + + vintf_fragments: [ + "android.hardware.health@2.1.xml" + ], + + overrides: [ + "healthd", + ], +} diff --git a/health/2.1/default/android.hardware.health@2.1-service.rc b/health/2.1/default/android.hardware.health@2.1-service.rc new file mode 100644 index 0000000000..917f1c2f19 --- /dev/null +++ b/health/2.1/default/android.hardware.health@2.1-service.rc @@ -0,0 +1,6 @@ +service health-hal-2-1 /vendor/bin/hw/android.hardware.health@2.1-service + class hal + user system + group system + capabilities WAKE_ALARM + file /dev/kmsg w diff --git a/health/2.1/default/android.hardware.health@2.1.xml b/health/2.1/default/android.hardware.health@2.1.xml new file mode 100644 index 0000000000..34fdca6b91 --- /dev/null +++ b/health/2.1/default/android.hardware.health@2.1.xml @@ -0,0 +1,7 @@ + + + android.hardware.health + hwbinder + @2.1::IHealth/default + + diff --git a/health/2.1/default/impl.cpp b/health/2.1/default/impl.cpp new file mode 100644 index 0000000000..7389a21f12 --- /dev/null +++ b/health/2.1/default/impl.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 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 +#include + +#include +#include + +using ::android::sp; +using ::android::hardware::health::InitHealthdConfig; +using ::android::hardware::health::V2_1::IHealth; +using ::android::hardware::health::V2_1::implementation::Health; + +using namespace std::literals; + +// Passthrough implementation of the health service. Use default configuration. +// It does not invoke callbacks unless update() is called explicitly. No +// background thread is spawned to handle callbacks. +// +// The passthrough implementation is only allowed in recovery mode, charger, and +// opened by the hwbinder service. +// If Android is booted normally, the hwbinder service is used instead. +// +// This implementation only implements the "default" instance. It rejects +// other instance names. +// Note that the Android framework only reads values from the "default" +// health HAL 2.1 instance. +extern "C" IHealth* HIDL_FETCH_IHealth(const char* instance) { + if (instance != "default"sv) { + return nullptr; + } + auto config = std::make_unique(); + InitHealthdConfig(config.get()); + + // This implementation uses default config. If you want to customize it + // (e.g. with healthd_board_init), do it here. + + return new Health(std::move(config)); +} diff --git a/health/2.1/default/service.cpp b/health/2.1/default/service.cpp new file mode 100644 index 0000000000..f8334c57bc --- /dev/null +++ b/health/2.1/default/service.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 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.1-service" + +#include +#include +#include + +using ::android::sp; +using ::android::hardware::health::V2_1::IHealth; +using ::android::hardware::health::V2_1::implementation::BinderHealth; +using IHealth_2_0 = ::android::hardware::health::V2_0::IHealth; + +static constexpr const char* gInstanceName = "default"; + +int main(int /* argc */, char* /* argv */[]) { + sp passthrough = + IHealth::castFrom(IHealth_2_0::getService(gInstanceName, true /* getStub */)); + CHECK(passthrough != nullptr) + << "Cannot find passthrough implementation of health 2.1 HAL for instance " + << gInstanceName; + sp binder = new BinderHealth(gInstanceName, passthrough); + return binder->StartLoop(); +} diff --git a/health/utils/libhealth2impl/Android.bp b/health/utils/libhealth2impl/Android.bp new file mode 100644 index 0000000000..14374a29ee --- /dev/null +++ b/health/utils/libhealth2impl/Android.bp @@ -0,0 +1,51 @@ +// Copyright (C) 2019 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. + +// A helper library for health@2.x HAL implementation. +// HAL implementations can link to this library and extend the Health class. +cc_library_static { + name: "libhealth2impl", + vendor_available: true, + recovery_available: true, + srcs: [ + "BinderHealth.cpp", + "HalHealthLoop.cpp", + "Health.cpp", + ], + shared_libs: [ + "libbase", + "libcutils", + "libhidlbase", + "liblog", + "libutils", + "android.hardware.health@2.1", + "android.hardware.health@2.0", + ], + static_libs: [ + "libbatterymonitor", + "libhealthloop", + "android.hardware.health@1.0-convert", + ], + cflags: [ + "-Wall", + "-Werror", + ], + export_static_lib_headers: [ + "libbatterymonitor", + "libhealthloop", + ], + export_include_dirs: [ + "include", + ], +} diff --git a/health/utils/libhealth2impl/BinderHealth.cpp b/health/utils/libhealth2impl/BinderHealth.cpp new file mode 100644 index 0000000000..625d0e0c40 --- /dev/null +++ b/health/utils/libhealth2impl/BinderHealth.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2019 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 + +#include +#include +#include + +#include +#include + +using android::hardware::handleTransportPoll; +using android::hardware::IPCThreadState; +using android::hardware::setupTransportPolling; + +using android::hardware::health::V2_0::Result; + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +bool IsDeadObjectLogged(const Return& ret) { + if (ret.isOk()) return false; + if (ret.isDeadObject()) return true; + LOG(ERROR) << "Cannot call healthInfoChanged* on callback: " << ret.description(); + return false; +} + +BinderHealth::BinderHealth(const std::string& name, const sp& impl) + : HalHealthLoop(name, impl) { + CHECK_NE(this, impl.get()); + CHECK(!impl->isRemote()); +} + +// +// Methods that handle callbacks. +// + +Return BinderHealth::registerCallback(const sp& callback) { + if (callback == nullptr) { + return Result::SUCCESS; + } + + Callback* wrapped = nullptr; + { + std::lock_guard lock(callbacks_lock_); + wrapped = callbacks_.emplace_back(Wrap(callback)).get(); + // 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 + } + + getHealthInfo_2_1([&](auto res, const auto& health_info) { + if (res != Result::SUCCESS) { + LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res); + return; + } + auto ret = wrapped->Notify(health_info); + if (IsDeadObjectLogged(ret)) { + // Remove callback reference. + std::lock_guard lock(callbacks_lock_); + auto it = std::find_if(callbacks_.begin(), callbacks_.end(), + [wrapped](const auto& cb) { return cb.get() == wrapped; }); + if (it != callbacks_.end()) { + callbacks_.erase(it); + } + // unlock + } + }); + + return Result::SUCCESS; +} + +bool BinderHealth::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)->Get(), callback)) { + it = callbacks_.erase(it); + removed = true; + } else { + ++it; + } + } + (void)callback->unlinkToDeath(this).isOk(); // ignore errors + return removed; +} + +Return BinderHealth::update() { + Result result = service()->update(); + if (result != Result::SUCCESS) return result; + getHealthInfo_2_1([&](auto res, const auto& health_info) { + if (res != Result::SUCCESS) { + result = res; + return; + } + OnHealthInfoChanged(health_info); + }); + return result; +} + +Return BinderHealth::unregisterCallback(const sp& callback) { + return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND; +} + +void BinderHealth::OnHealthInfoChanged(const HealthInfo& health_info) { + // Notify all callbacks + std::unique_lock lock(callbacks_lock_); + for (auto it = callbacks_.begin(); it != callbacks_.end();) { + auto ret = (*it)->Notify(health_info); + if (IsDeadObjectLogged(ret)) { + it = callbacks_.erase(it); + } else { + ++it; + } + } + lock.unlock(); + + // adjusts uevent / wakealarm periods + HalHealthLoop::OnHealthInfoChanged(health_info); +} + +void BinderHealth::serviceDied(uint64_t /* cookie */, const wp& who) { + (void)unregisterCallbackInternal(who.promote()); +} + +void BinderHealth::BinderEvent(uint32_t /*epevents*/) { + if (binder_fd_ >= 0) { + handleTransportPoll(binder_fd_); + } +} + +void BinderHealth::Init(struct healthd_config* config) { + // Set up epoll and get uevent / wake alarm periods + HalHealthLoop::Init(config); + + LOG(INFO) << instance_name() << " instance initializing with healthd_config..."; + + binder_fd_ = setupTransportPolling(); + + if (binder_fd_ >= 0) { + auto binder_event = [](auto* health_loop, uint32_t epevents) { + static_cast(health_loop)->BinderEvent(epevents); + }; + if (RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) { + PLOG(ERROR) << instance_name() << " instance: Register for binder events failed"; + } + } + + CHECK_EQ(registerAsService(instance_name()), android::OK) + << instance_name() << ": Failed to register HAL"; + + LOG(INFO) << instance_name() << ": Hal init done"; +} + +int BinderHealth::PrepareToWait(void) { + IPCThreadState::self()->flushCommands(); + return HalHealthLoop::PrepareToWait(); +} + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/health/utils/libhealth2impl/HalHealthLoop.cpp b/health/utils/libhealth2impl/HalHealthLoop.cpp new file mode 100644 index 0000000000..3901a763c3 --- /dev/null +++ b/health/utils/libhealth2impl/HalHealthLoop.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 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 + +#include +#include +#include +#include + +#include + +using android::hardware::configureRpcThreadpool; +using android::hardware::handleTransportPoll; +using android::hardware::IPCThreadState; +using android::hardware::setupTransportPolling; + +using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig; +using android::hardware::health::V2_0::Result; + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +void HalHealthLoop::Init(struct healthd_config* config) { + // Retrieve healthd_config from the HAL. + service_->getHealthConfig([config](auto res, const auto& health_config) { + CHECK(res == Result::SUCCESS); + + convertFromHealthConfig(health_config.battery, config); + config->boot_min_cap = health_config.bootMinCap; + + // Leave screen_on empty because it is handled in GetScreenOn below. + + // Leave ignorePowerSupplyNames empty because it isn't + // used by clients of health HAL. + }); +} + +void HalHealthLoop::Heartbeat(void) { + // noop +} + +void HalHealthLoop::ScheduleBatteryUpdate() { + // ignore errors. impl may not be able to handle any callbacks, so + // update() may return errors. + Result res = service_->update(); + if (res != Result::SUCCESS) { + LOG(WARNING) << "update() on the health HAL implementation failed with " << toString(res); + } + + service_->getHealthInfo_2_1([this](auto res, const auto& health_info) { + CHECK(res == Result::SUCCESS) + << "getHealthInfo_2_1() on the health HAL implementation failed with " + << toString(res); + this->OnHealthInfoChanged(health_info); + }); +} + +int HalHealthLoop::PrepareToWait() { + return -1; +} + +void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) { + set_charger_online(health_info); + AdjustWakealarmPeriods(charger_online()); +} + +void HalHealthLoop::set_charger_online(const HealthInfo& health_info) { + const auto& props = health_info.legacy.legacy; + charger_online_ = + props.chargerAcOnline || props.chargerUsbOnline || props.chargerWirelessOnline; +} + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/health/utils/libhealth2impl/Health.cpp b/health/utils/libhealth2impl/Health.cpp new file mode 100644 index 0000000000..f4684ae6f6 --- /dev/null +++ b/health/utils/libhealth2impl/Health.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2019 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using android::hardware::health::V1_0::BatteryStatus; +using android::hardware::health::V1_0::toString; +using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo; +using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig; +using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo; +using android::hardware::health::V2_0::Result; +using android::hardware::health::V2_1::IHealth; + +using ScreenOn = decltype(healthd_config::screen_on); + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +/* +// If you need to call healthd_board_init, construct the Health instance with +// the healthd_config after calling healthd_board_init: +struct healthd_config* init_config(struct healthd_config* config) { + healthd_board_init(config); + return config; +} +class MyHealth : public Health { + MyHealth(struct healthd_config* config) : + Health(init_config(config)) {} +}; +*/ + +Health::Health(std::unique_ptr&& config) : healthd_config_(std::move(config)) { + battery_monitor_.init(healthd_config_.get()); +} + +// +// Callbacks are not supported by the passthrough implementation. +// + +Return Health::registerCallback(const sp&) { + return Result::NOT_SUPPORTED; +} + +Return Health::unregisterCallback(const sp&) { + return Result::NOT_SUPPORTED; +} + +Return Health::update() { + Result result = Result::UNKNOWN; + getHealthInfo_2_1([&](auto res, const auto& /* health_info */) { + result = res; + if (res != Result::SUCCESS) { + LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res); + return; + } + + battery_monitor_.logValues(); + }); + return result; +} + +// +// Getters. +// + +template +static Return GetProperty(BatteryMonitor* 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 Void(); +} + +Return Health::getChargeCounter(getChargeCounter_cb _hidl_cb) { + return GetProperty(&battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, 0, _hidl_cb); +} + +Return Health::getCurrentNow(getCurrentNow_cb _hidl_cb) { + return GetProperty(&battery_monitor_, BATTERY_PROP_CURRENT_NOW, 0, _hidl_cb); +} + +Return Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) { + return GetProperty(&battery_monitor_, BATTERY_PROP_CURRENT_AVG, 0, _hidl_cb); +} + +Return Health::getCapacity(getCapacity_cb _hidl_cb) { + return GetProperty(&battery_monitor_, BATTERY_PROP_CAPACITY, 0, _hidl_cb); +} + +Return Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) { + return GetProperty(&battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, 0, _hidl_cb); +} + +Return Health::getChargeStatus(getChargeStatus_cb _hidl_cb) { + return GetProperty(&battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, + _hidl_cb); +} + +Return Health::getStorageInfo(getStorageInfo_cb _hidl_cb) { + // This implementation does not support StorageInfo. An implementation may extend this + // class and override this function to support storage info. + _hidl_cb(Result::NOT_SUPPORTED, {}); + return Void(); +} + +Return Health::getDiskStats(getDiskStats_cb _hidl_cb) { + // This implementation does not support DiskStats. An implementation may extend this + // class and override this function to support disk stats. + _hidl_cb(Result::NOT_SUPPORTED, {}); + return Void(); +} + +template +static inline void GetHealthInfoField(Health* service, Method func, T* out) { + *out = T{}; + std::invoke(func, service, [out](Result result, const T& value) { + if (result == Result::SUCCESS) *out = value; + }); +} + +Return Health::getHealthInfo(getHealthInfo_cb _hidl_cb) { + return getHealthInfo_2_1( + [&](auto res, const auto& health_info) { _hidl_cb(res, health_info.legacy); }); +} + +Return Health::getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) { + battery_monitor_.updateValues(); + + HealthInfo health_info = battery_monitor_.getHealthInfo_2_1(); + + // Fill in storage infos; these aren't retrieved by BatteryMonitor. + GetHealthInfoField(this, &Health::getStorageInfo, &health_info.legacy.storageInfos); + GetHealthInfoField(this, &Health::getDiskStats, &health_info.legacy.diskStats); + + // A subclass may want to update health info struct before returning it. + UpdateHealthInfo(&health_info); + + _hidl_cb(Result::SUCCESS, health_info); + return Void(); +} + +Return Health::debug(const hidl_handle& handle, const hidl_vec&) { + if (handle == nullptr || handle->numFds == 0) { + return Void(); + } + + int fd = handle->data[0]; + battery_monitor_.dumpState(fd); + getHealthInfo_2_1([fd](auto res, const auto& info) { + android::base::WriteStringToFd("\ngetHealthInfo -> ", fd); + if (res == Result::SUCCESS) { + android::base::WriteStringToFd(toString(info), fd); + } else { + android::base::WriteStringToFd(toString(res), fd); + } + android::base::WriteStringToFd("\n", fd); + }); + + fsync(fd); + return Void(); +} + +Return Health::getHealthConfig(getHealthConfig_cb _hidl_cb) { + HealthConfig config = {}; + convertToHealthConfig(healthd_config_.get(), config.battery); + config.bootMinCap = static_cast(healthd_config_->boot_min_cap); + + _hidl_cb(Result::SUCCESS, config); + return Void(); +} + +Return Health::shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) { + if (!healthd_config_->screen_on) { + _hidl_cb(Result::NOT_SUPPORTED, true); + return Void(); + } + + Result returned_result = Result::UNKNOWN; + bool screen_on = true; + getHealthInfo_2_1([&](auto res, const auto& health_info) { + returned_result = res; + if (returned_result != Result::SUCCESS) return; + + struct BatteryProperties props = {}; + V1_0::hal_conversion::convertFromHealthInfo(health_info.legacy.legacy, &props); + screen_on = healthd_config_->screen_on(&props); + }); + _hidl_cb(returned_result, screen_on); + return Void(); +} + +// +// Subclass helpers / overrides +// + +void Health::UpdateHealthInfo(HealthInfo* /* health_info */) { + /* + // Sample code for a subclass to implement this: + // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here. + health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds(); + + // If you need to call healthd_board_battery_update: + struct BatteryProperties props; + convertFromHealthInfo(health_info.legacy.legacy, &props); + healthd_board_battery_update(&props); + convertToHealthInfo(&props, health_info.legacy.legacy); + */ +} + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/health/utils/libhealth2impl/include/health2impl/BinderHealth.h b/health/utils/libhealth2impl/include/health2impl/BinderHealth.h new file mode 100644 index 0000000000..1da5bd1182 --- /dev/null +++ b/health/utils/libhealth2impl/include/health2impl/BinderHealth.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +// binderized health HAL implementation. +class BinderHealth : public HalHealthLoop, public IHealth, public hidl_death_recipient { + public: + // |impl| should be the passthrough implementation. + BinderHealth(const std::string& name, const sp& impl); + + // Methods from ::android::hardware::health::V2_0::IHealth follow. + Return<::android::hardware::health::V2_0::Result> registerCallback( + const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override; + Return<::android::hardware::health::V2_0::Result> unregisterCallback( + const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override; + Return<::android::hardware::health::V2_0::Result> update() override; + Return getChargeCounter(getChargeCounter_cb _hidl_cb) override { + return service()->getChargeCounter(_hidl_cb); + } + Return getCurrentNow(getCurrentNow_cb _hidl_cb) override { + return service()->getCurrentNow(_hidl_cb); + } + Return getCurrentAverage(getCurrentAverage_cb _hidl_cb) override { + return service()->getCurrentAverage(_hidl_cb); + } + Return getCapacity(getCapacity_cb _hidl_cb) override { + return service()->getCapacity(_hidl_cb); + } + Return getEnergyCounter(getEnergyCounter_cb _hidl_cb) override { + return service()->getEnergyCounter(_hidl_cb); + } + Return getChargeStatus(getChargeStatus_cb _hidl_cb) override { + return service()->getChargeStatus(_hidl_cb); + } + Return getStorageInfo(getStorageInfo_cb _hidl_cb) override { + return service()->getStorageInfo(_hidl_cb); + } + Return getDiskStats(getDiskStats_cb _hidl_cb) override { + return service()->getDiskStats(_hidl_cb); + } + Return getHealthInfo(getHealthInfo_cb _hidl_cb) override { + return service()->getHealthInfo(_hidl_cb); + } + + // Methods from ::android::hardware::health::V2_1::IHealth follow. + Return getHealthConfig(getHealthConfig_cb _hidl_cb) override { + return service()->getHealthConfig(_hidl_cb); + } + Return getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override { + return service()->getHealthInfo_2_1(_hidl_cb); + } + Return shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override { + return service()->shouldKeepScreenOn(_hidl_cb); + } + + // Methods from ::android::hidl::base::V1_0::IBase follow. + Return debug(const hidl_handle& fd, const hidl_vec& args) override { + return service()->debug(fd, args); + } + + // hidl_death_recipient implementation. + void serviceDied(uint64_t cookie, const wp& who) override; + + // Called by BinderHealthCallback. + void OnHealthInfoChanged(const HealthInfo& health_info) override; + + protected: + void Init(struct healthd_config* config) override; + int PrepareToWait() override; + // A subclass may override this if it wants to handle binder events differently. + virtual void BinderEvent(uint32_t epevents); + + private: + bool unregisterCallbackInternal(const sp& callback); + int binder_fd_ = -1; + std::mutex callbacks_lock_; + std::vector> callbacks_; +}; + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/health/utils/libhealth2impl/include/health2impl/Callback.h b/health/utils/libhealth2impl/include/health2impl/Callback.h new file mode 100644 index 0000000000..a30480b490 --- /dev/null +++ b/health/utils/libhealth2impl/include/health2impl/Callback.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include +#include + +using ::android::sp; +using ::android::hardware::Return; +using ::android::hidl::base::V1_0::IBase; + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +// Wraps an IHealthInfoCallback. +class Callback { + public: + virtual ~Callback() {} + virtual Return Notify(const HealthInfo&) = 0; + virtual sp Get() = 0; +}; + +class Callback_2_0 : public Callback { + public: + Callback_2_0(const sp& callback) : callback_(callback) {} + Return Notify(const HealthInfo& info) override { + return callback_->healthInfoChanged(info.legacy); + } + sp Get() override { return callback_; } + + private: + sp callback_; +}; + +class Callback_2_1 : public Callback { + public: + Callback_2_1(const sp& callback) : callback_(callback) {} + Return Notify(const HealthInfo& info) override { + return callback_->healthInfoChanged_2_1(info); + } + sp Get() override { return callback_; } + + private: + sp callback_; +}; + +inline std::unique_ptr Wrap(const sp& callback_2_0) { + auto callback_2_1 = IHealthInfoCallback::castFrom(callback_2_0).withDefault(nullptr); + if (callback_2_1) return std::make_unique(callback_2_1); + return std::make_unique(callback_2_0); +} + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h b/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h new file mode 100644 index 0000000000..d9b5580ee0 --- /dev/null +++ b/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +#include +#include + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +// An implementation of HealthLoop for using a given health HAL. This is useful +// for services that opens the passthrough implementation and starts the HealthLoop +// to periodically poll data from the implementation. +class HalHealthLoop : public HealthLoop { + public: + HalHealthLoop(const std::string& name, const sp& service) + : instance_name_(name), service_(service) {} + + protected: + virtual void Init(struct healthd_config* config) override; + virtual void Heartbeat() override; + virtual int PrepareToWait() override; + virtual void ScheduleBatteryUpdate() override; + + // HealthLoop periodically calls ScheduleBatteryUpdate, which calls + // OnHealthInfoChanged callback. A client can override this function to + // broadcast the health_info to interested listeners. By default, this + // adjust uevents / wakealarm periods. + virtual void OnHealthInfoChanged(const HealthInfo& health_info); + + const std::string& instance_name() const { return instance_name_; } + const sp& service() const { return service_; } + bool charger_online() const { return charger_online_; } + + // Helpers for subclasses to implement OnHealthInfoChanged. + void set_charger_online(const HealthInfo& health_info); + + private: + const std::string& instance_name_; + sp service_; + bool charger_online_ = false; +}; + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android diff --git a/health/utils/libhealth2impl/include/health2impl/Health.h b/health/utils/libhealth2impl/include/health2impl/Health.h new file mode 100644 index 0000000000..853b0cd7e5 --- /dev/null +++ b/health/utils/libhealth2impl/include/health2impl/Health.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include + +using ::android::sp; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hidl::base::V1_0::IBase; + +namespace android { +namespace hardware { +namespace health { +namespace V2_1 { +namespace implementation { + +class Health : public IHealth { + public: + Health(std::unique_ptr&& config); + + // Methods from ::android::hardware::health::V2_0::IHealth follow. + Return<::android::hardware::health::V2_0::Result> registerCallback( + const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override; + Return<::android::hardware::health::V2_0::Result> unregisterCallback( + const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override; + Return<::android::hardware::health::V2_0::Result> 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; + Return getStorageInfo(getStorageInfo_cb _hidl_cb) override; + Return getDiskStats(getDiskStats_cb _hidl_cb) override; + Return getHealthInfo(getHealthInfo_cb _hidl_cb) override; + + // Methods from ::android::hardware::health::V2_1::IHealth follow. + Return getHealthConfig(getHealthConfig_cb _hidl_cb) override; + Return getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override; + Return shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override; + + // Methods from ::android::hidl::base::V1_0::IBase follow. + Return debug(const hidl_handle& fd, const hidl_vec& args) override; + + protected: + // A subclass can override this to modify any health info object before + // returning to clients. This is similar to healthd_board_battery_update(). + // By default, it does nothing. + virtual void UpdateHealthInfo(HealthInfo* health_info); + + private: + bool unregisterCallbackInternal(const sp& callback); + + BatteryMonitor battery_monitor_; + std::unique_ptr healthd_config_; + + std::mutex callbacks_lock_; + std::vector> callbacks_; +}; + +} // namespace implementation +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android