Merge "health: Add default impl of health AIDL HAL"
diff --git a/health/aidl/default/Android.bp b/health/aidl/default/Android.bp
new file mode 100644
index 0000000..cb78e77
--- /dev/null
+++ b/health/aidl/default/Android.bp
@@ -0,0 +1,76 @@
+// Copyright (C) 2021 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: "libhealth_aidl_common_defaults",
+ vendor: true,
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "android.hardware.health-V1-ndk",
+
+ // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
+ "libhidlbase",
+ ],
+ static_libs: [
+ "libbatterymonitor",
+ "libhealthloop",
+
+ // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
+ "android.hardware.health-translate-ndk",
+ ],
+}
+
+// AIDL version of libhealth2impl.
+// A helper library for health HAL implementation.
+// HAL implementations can link to this library and extend the Health class.
+cc_library_static {
+ name: "libhealth_aidl_impl",
+ defaults: [
+ "libhealth_aidl_common_defaults",
+ ],
+ export_include_dirs: ["include"],
+ export_static_lib_headers: [
+ "libbatterymonitor",
+ ],
+ srcs: [
+ "health-convert.cpp",
+ "HalHealthLoop.cpp",
+ "Health.cpp",
+ "LinkedCallback.cpp",
+ ],
+ visibility: [
+ ":__subpackages__",
+ "//hardware/interfaces/tests/extension/health:__subpackages__",
+ ],
+}
+
+// AIDL version of android.hardware.health@2.1-service.
+// Default binder service of the health HAL.
+cc_binary {
+ name: "android.hardware.health-service.example",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.health-service.example.rc"],
+ vintf_fragments: ["android.hardware.health-service.example.xml"],
+ defaults: [
+ "libhealth_aidl_common_defaults",
+ ],
+ static_libs: [
+ "libhealth_aidl_impl",
+ ],
+ srcs: ["main.cpp"],
+}
diff --git a/health/aidl/default/HalHealthLoop.cpp b/health/aidl/default/HalHealthLoop.cpp
new file mode 100644
index 0000000..c9a081e
--- /dev/null
+++ b/health/aidl/default/HalHealthLoop.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 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 <health-impl/HalHealthLoop.h>
+
+#include <android-base/logging.h>
+
+#include <health-impl/Health.h>
+#include "health-convert.h"
+
+namespace aidl::android::hardware::health {
+
+// Unlike the HIDL version android::hardware::health::V2_1::implementation::HalHealthLoop,
+// do not define HalHealthLoop::Init because we no longer have Health::getHealthConfig.
+// Let the Health class handle Init.
+void HalHealthLoop::Init(struct healthd_config* config) {
+ callback_->OnInit(this, config);
+}
+
+void HalHealthLoop::Heartbeat() {
+ callback_->OnHeartbeat();
+}
+
+void HalHealthLoop::ScheduleBatteryUpdate() {
+ // ignore errors. impl may not be able to handle any callbacks, so
+ // update() may return errors.
+ if (auto res = service_->update(); !res.isOk()) {
+ LOG(WARNING) << "update() on the health HAL implementation failed with "
+ << res.getDescription();
+ }
+
+ HealthInfo health_info;
+ auto res = service_->getHealthInfo(&health_info);
+ CHECK(res.isOk()) << "getHealthInfo() on the health HAL implementation failed with "
+ << res.getDescription();
+ OnHealthInfoChanged(health_info);
+}
+
+int HalHealthLoop::PrepareToWait() {
+ return callback_->OnPrepareToWait();
+}
+
+void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) {
+ callback_->OnHealthInfoChanged(health_info);
+ set_charger_online(health_info);
+ AdjustWakealarmPeriods(charger_online());
+}
+
+void HalHealthLoop::set_charger_online(const HealthInfo& health_info) {
+ charger_online_ = health_info.chargerAcOnline || health_info.chargerUsbOnline ||
+ health_info.chargerWirelessOnline;
+}
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/Health.cpp b/health/aidl/default/Health.cpp
new file mode 100644
index 0000000..2d91ce0
--- /dev/null
+++ b/health/aidl/default/Health.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2021 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 "health-impl/Health.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/hardware/health/translate-ndk.h>
+#include <health/utils.h>
+
+#include "LinkedCallback.h"
+#include "health-convert.h"
+
+using std::string_literals::operator""s;
+
+namespace aidl::android::hardware::health {
+
+namespace {
+// Wrap LinkedCallback::OnCallbackDied() into a void(void*).
+void OnCallbackDiedWrapped(void* cookie) {
+ LinkedCallback* linked = reinterpret_cast<LinkedCallback*>(cookie);
+ linked->OnCallbackDied();
+}
+} // namespace
+
+/*
+// If you need to call healthd_board_init, construct the Health instance with
+// the healthd_config after calling healthd_board_init:
+class MyHealth : public Health {
+ protected:
+ MyHealth() : Health(CreateConfig()) {}
+ private:
+ static std::unique_ptr<healthd_config> CreateConfig() {
+ auto config = std::make_unique<healthd_config>();
+ ::android::hardware::health::InitHealthdConfig(config.get());
+ healthd_board_init(config.get());
+ return std::move(config);
+ }
+};
+*/
+Health::Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config)
+ : instance_name_(instance_name),
+ healthd_config_(std::move(config)),
+ death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) {
+ battery_monitor_.init(healthd_config_.get());
+}
+
+//
+// Getters.
+//
+
+template <typename T>
+static ndk::ScopedAStatus GetProperty(::android::BatteryMonitor* monitor, int id, T defaultValue,
+ T* out) {
+ *out = defaultValue;
+ struct ::android::BatteryProperty prop;
+ ::android::status_t err = monitor->getProperty(static_cast<int>(id), &prop);
+ if (err == ::android::OK) {
+ *out = static_cast<T>(prop.valueInt64);
+ } else {
+ LOG(DEBUG) << "getProperty(" << id << ")"
+ << " fails: (" << err << ") " << ::android::statusToString(err);
+ }
+
+ switch (err) {
+ case ::android::OK:
+ return ndk::ScopedAStatus::ok();
+ case ::android::NAME_NOT_FOUND:
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ default:
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
+ }
+}
+
+ndk::ScopedAStatus Health::getChargeCounterUah(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCapacity(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out);
+}
+
+ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) {
+ return GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_ENERGY_COUNTER, 0, out);
+}
+
+ndk::ScopedAStatus Health::getChargeStatus(BatteryStatus* out) {
+ return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_BATTERY_STATUS,
+ BatteryStatus::UNKNOWN, out);
+}
+
+ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
+ // This implementation does not support DiskStats. An implementation may extend this
+ // class and override this function to support disk stats.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
+ // This implementation does not support StorageInfo. An implementation may extend this
+ // class and override this function to support storage info.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
+ battery_monitor_.updateValues();
+
+ // TODO(b/177269435): BatteryMonitor should store AIDL HealthInfo instead.
+ auto health_info_2_1 = battery_monitor_.getHealthInfo_2_1();
+ if (!::android::h2a::translate(health_info_2_1, out)) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN, "Cannot translate HIDL HealthInfo to AIDL");
+ }
+
+ // Fill in storage infos; these aren't retrieved by BatteryMonitor.
+ if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
+ if (res.getServiceSpecificError() == 0 &&
+ res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN,
+ ("getStorageInfo fails: " + res.getDescription()).c_str());
+ }
+ LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
+ << res.getDescription();
+ out->storageInfos = {};
+ }
+ if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
+ if (res.getServiceSpecificError() == 0 &&
+ res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN,
+ ("getDiskStats fails: " + res.getDescription()).c_str());
+ }
+ LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
+ << res.getDescription();
+ out->diskStats = {};
+ }
+
+ // A subclass may want to update health info struct before returning it.
+ UpdateHealthInfo(out);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+binder_status_t Health::dump(int fd, const char**, uint32_t) {
+ battery_monitor_.dumpState(fd);
+
+ ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
+ HealthInfo health_info;
+ auto res = getHealthInfo(&health_info);
+ if (res.isOk()) {
+ ::android::base::WriteStringToFd(health_info.toString(), fd);
+ } else {
+ ::android::base::WriteStringToFd(res.getDescription(), fd);
+ }
+
+ fsync(fd);
+ return STATUS_OK;
+}
+
+std::optional<bool> Health::ShouldKeepScreenOn() {
+ if (!healthd_config_->screen_on) {
+ return std::nullopt;
+ }
+
+ HealthInfo health_info;
+ auto res = getHealthInfo(&health_info);
+ if (!res.isOk()) {
+ return std::nullopt;
+ }
+
+ ::android::BatteryProperties props = {};
+ convert(health_info, &props);
+ return healthd_config_->screen_on(&props);
+}
+
+namespace {
+bool IsDeadObjectLogged(const ndk::ScopedAStatus& ret) {
+ if (ret.isOk()) return false;
+ if (ret.getStatus() == ::STATUS_DEAD_OBJECT) return true;
+ LOG(ERROR) << "Cannot call healthInfoChanged on callback: " << ret.getDescription();
+ return false;
+}
+} // namespace
+
+//
+// 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, modify its signature
+ // and implementation to operate on HealthInfo directly, then call:
+ healthd_board_battery_update(health_info);
+ */
+}
+
+//
+// Methods that handle callbacks.
+//
+
+ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
+ if (callback == nullptr) {
+ // For now, this shouldn't happen because argument is not nullable.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
+
+ {
+ std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ callbacks_.emplace_back(LinkedCallback::Make(ref<Health>(), callback));
+ // unlock
+ }
+
+ HealthInfo health_info;
+ if (auto res = getHealthInfo(&health_info); !res.isOk()) {
+ LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
+ // No health info to send, so return early.
+ return ndk::ScopedAStatus::ok();
+ }
+
+ if (auto res = callback->healthInfoChanged(health_info); IsDeadObjectLogged(res)) {
+ (void)unregisterCallback(callback);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Health::unregisterCallback(
+ const std::shared_ptr<IHealthInfoCallback>& callback) {
+ if (callback == nullptr) {
+ // For now, this shouldn't happen because argument is not nullable.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
+
+ std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+
+ auto matches = [callback](const auto& linked) {
+ return linked->callback() == callback; // compares shared_ptr
+ };
+ auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), matches);
+ bool removed = (it != callbacks_.end());
+ callbacks_.erase(it, callbacks_.end()); // calls unlinkToDeath on deleted callbacks.
+ return removed ? ndk::ScopedAStatus::ok()
+ : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+}
+
+// A combination of the HIDL version
+// android::hardware::health::V2_1::implementation::Health::update() and
+// android::hardware::health::V2_1::implementation::BinderHealth::update()
+ndk::ScopedAStatus Health::update() {
+ HealthInfo health_info;
+ if (auto res = getHealthInfo(&health_info); !res.isOk()) {
+ LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
+ // Propagate service specific errors. If there's none, report unknown error.
+ if (res.getServiceSpecificError() != 0 ||
+ res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return res;
+ }
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
+ }
+ battery_monitor_.logValues();
+ OnHealthInfoChanged(health_info);
+ return ndk::ScopedAStatus::ok();
+}
+
+void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
+ // Notify all callbacks
+ std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ // is_dead notifies a callback and return true if it is dead.
+ auto is_dead = [&](const auto& linked) {
+ auto res = linked->callback()->healthInfoChanged(health_info);
+ return IsDeadObjectLogged(res);
+ };
+ auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead);
+ callbacks_.erase(it, callbacks_.end()); // calls unlinkToDeath on deleted callbacks.
+ lock.unlock();
+
+ // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
+}
+
+void Health::BinderEvent(uint32_t /*epevents*/) {
+ if (binder_fd_ >= 0) {
+ ABinderProcess_handlePolledCommands();
+ }
+}
+
+void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
+ LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
+
+ // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
+ // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
+ *config = *healthd_config_.get();
+
+ binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
+
+ if (status == ::STATUS_OK && binder_fd_ >= 0) {
+ std::shared_ptr<Health> thiz = ref<Health>();
+ auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
+ if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
+ PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
+ }
+ }
+
+ std::string health_name = IHealth::descriptor + "/"s + instance_name_;
+ CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
+ << instance_name_ << ": Failed to register HAL";
+
+ LOG(INFO) << instance_name_ << ": Hal init done";
+}
+
+// Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
+// in PrepareToWait(). See b/139697085.
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/LinkedCallback.cpp b/health/aidl/default/LinkedCallback.cpp
new file mode 100644
index 0000000..2985ffe
--- /dev/null
+++ b/health/aidl/default/LinkedCallback.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 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 <android-base/logging.h>
+#include <android/binder_ibinder.h>
+
+#include <health-impl/Health.h>
+#include <utils/Errors.h>
+
+#include "LinkedCallback.h"
+
+namespace aidl::android::hardware::health {
+
+std::unique_ptr<LinkedCallback> LinkedCallback::Make(
+ std::shared_ptr<Health> service, std::shared_ptr<IHealthInfoCallback> callback) {
+ std::unique_ptr<LinkedCallback> ret(new LinkedCallback());
+ binder_status_t linkRet =
+ AIBinder_linkToDeath(callback->asBinder().get(), service->death_recipient_.get(),
+ reinterpret_cast<void*>(ret.get()));
+ if (linkRet != ::STATUS_OK) {
+ LOG(WARNING) << __func__ << "Cannot link to death: " << linkRet;
+ return nullptr;
+ }
+ ret->service_ = service;
+ ret->callback_ = std::move(callback);
+ return ret;
+}
+
+LinkedCallback::LinkedCallback() = default;
+
+LinkedCallback::~LinkedCallback() {
+ if (callback_ == nullptr) {
+ return;
+ }
+ auto status =
+ AIBinder_unlinkToDeath(callback_->asBinder().get(), service()->death_recipient_.get(),
+ reinterpret_cast<void*>(this));
+ if (status != STATUS_OK && status != STATUS_DEAD_OBJECT) {
+ LOG(WARNING) << __func__ << "Cannot unlink to death: " << ::android::statusToString(status);
+ }
+}
+
+std::shared_ptr<Health> LinkedCallback::service() {
+ auto service_sp = service_.lock();
+ CHECK_NE(nullptr, service_sp);
+ return service_sp;
+}
+
+void LinkedCallback::OnCallbackDied() {
+ service()->unregisterCallback(callback_);
+}
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/LinkedCallback.h b/health/aidl/default/LinkedCallback.h
new file mode 100644
index 0000000..82490a7
--- /dev/null
+++ b/health/aidl/default/LinkedCallback.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 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 <memory>
+
+#include <aidl/android/hardware/health/IHealthInfoCallback.h>
+#include <android-base/macros.h>
+#include <android/binder_auto_utils.h>
+
+#include <health-impl/Health.h>
+
+namespace aidl::android::hardware::health {
+
+// Type of the cookie pointer in linkToDeath.
+// A (Health, IHealthInfoCallback) tuple.
+class LinkedCallback {
+ public:
+ // Automatically linkToDeath upon construction with the returned object as the cookie.
+ // service->death_reciepient() should be from CreateDeathRecipient().
+ // Not using a strong reference to |service| to avoid circular reference. The lifetime
+ // of |service| must be longer than this LinkedCallback object.
+ static std::unique_ptr<LinkedCallback> Make(std::shared_ptr<Health> service,
+ std::shared_ptr<IHealthInfoCallback> callback);
+
+ // Automatically unlinkToDeath upon destruction. So, it is always safe to reinterpret_cast
+ // the cookie back to the LinkedCallback object.
+ ~LinkedCallback();
+
+ // The wrapped IHealthInfoCallback object.
+ const std::shared_ptr<IHealthInfoCallback>& callback() const { return callback_; }
+
+ // On callback died, unreigster it from the service.
+ void OnCallbackDied();
+
+ private:
+ LinkedCallback();
+ DISALLOW_COPY_AND_ASSIGN(LinkedCallback);
+
+ std::shared_ptr<Health> service();
+
+ std::weak_ptr<Health> service_;
+ std::shared_ptr<IHealthInfoCallback> callback_;
+};
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/android.hardware.health-service.example.rc b/health/aidl/default/android.hardware.health-service.example.rc
new file mode 100644
index 0000000..b393c58
--- /dev/null
+++ b/health/aidl/default/android.hardware.health-service.example.rc
@@ -0,0 +1,6 @@
+service vendor.health-default /vendor/bin/hw/android.hardware.health-service.example
+ class hal
+ user system
+ group system
+ capabilities WAKE_ALARM BLOCK_SUSPEND
+ file /dev/kmsg w
diff --git a/health/aidl/default/android.hardware.health-service.example.xml b/health/aidl/default/android.hardware.health-service.example.xml
new file mode 100644
index 0000000..98026cb
--- /dev/null
+++ b/health/aidl/default/android.hardware.health-service.example.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.health</name>
+ <version>1</version>
+ <fqname>IHealth/default</fqname>
+ </hal>
+</manifest>
diff --git a/health/aidl/default/health-convert.cpp b/health/aidl/default/health-convert.cpp
new file mode 100644
index 0000000..b5251f4
--- /dev/null
+++ b/health/aidl/default/health-convert.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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 "health-convert.h"
+
+namespace aidl::android::hardware::health {
+
+void convert(const HealthInfo& info, struct ::android::BatteryProperties* p) {
+ p->chargerAcOnline = info.chargerAcOnline;
+ p->chargerUsbOnline = info.chargerUsbOnline;
+ p->chargerWirelessOnline = info.chargerWirelessOnline;
+ p->maxChargingCurrent = info.maxChargingCurrentMicroamps;
+ p->maxChargingVoltage = info.maxChargingVoltageMicrovolts;
+ p->batteryStatus = static_cast<int>(info.batteryStatus);
+ p->batteryHealth = static_cast<int>(info.batteryHealth);
+ p->batteryPresent = info.batteryPresent;
+ p->batteryLevel = info.batteryLevel;
+ p->batteryVoltage = info.batteryVoltageMillivolts;
+ p->batteryTemperature = info.batteryTemperatureTenthsCelsius;
+ p->batteryCurrent = info.batteryCurrentMicroamps;
+ p->batteryCycleCount = info.batteryCycleCount;
+ p->batteryFullCharge = info.batteryFullChargeUah;
+ p->batteryChargeCounter = info.batteryChargeCounterUah;
+ p->batteryTechnology = ::android::String8(info.batteryTechnology.c_str());
+}
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/health-convert.h b/health/aidl/default/health-convert.h
new file mode 100644
index 0000000..179ffc4
--- /dev/null
+++ b/health/aidl/default/health-convert.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/hardware/health/HealthInfo.h>
+#include <batteryservice/BatteryService.h>
+#include <healthd/healthd.h>
+
+// Conversion between healthd types and AIDL health HAL types. Note that most
+// of the conversion loses information, because these types have a different
+// set of information.
+
+namespace aidl::android::hardware::health {
+
+void convert(const HealthInfo& info, struct ::android::BatteryProperties* out);
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/include/health-impl/HalHealthLoop.h b/health/aidl/default/include/health-impl/HalHealthLoop.h
new file mode 100644
index 0000000..c46aaa4
--- /dev/null
+++ b/health/aidl/default/include/health-impl/HalHealthLoop.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 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 <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/IHealth.h>
+#include <health/HealthLoop.h>
+
+namespace aidl::android::hardware::health {
+
+class HalHealthLoop;
+
+class HalHealthLoopCallback {
+ public:
+ virtual ~HalHealthLoopCallback() = default;
+
+ // Called by HalHealthLoop::Init
+ virtual void OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) = 0;
+ // Called by HalHealthLoop::Heartbeat
+ virtual void OnHeartbeat(){};
+ // Called by HalHealthLoop::PrepareToWait
+ virtual int OnPrepareToWait() { return -1; }
+ // Called by HalHealthLoop::ScheduleBatteryUpdate
+ virtual void OnHealthInfoChanged(const HealthInfo&) {}
+};
+
+// AIDL version of android::hardware::health::V2_1::implementation::HalHealthLoop.
+// An implementation of HealthLoop for using a given health HAL.
+class HalHealthLoop final : public ::android::hardware::health::HealthLoop {
+ public:
+ // Caller must ensure that the lifetime of service_ is not shorter than this object.
+ HalHealthLoop(std::shared_ptr<IHealth> service, std::shared_ptr<HalHealthLoopCallback> callback)
+ : service_(std::move(service)), callback_(std::move(callback)) {}
+
+ using HealthLoop::RegisterEvent;
+
+ bool charger_online() const { return charger_online_; }
+
+ protected:
+ virtual void Init(struct healthd_config* config) override;
+ virtual void Heartbeat() override;
+ virtual int PrepareToWait() override;
+ virtual void ScheduleBatteryUpdate() override;
+
+ private:
+ std::shared_ptr<IHealth> service_;
+ std::shared_ptr<HalHealthLoopCallback> callback_;
+ bool charger_online_ = false;
+
+ // Helpers of OnHealthInfoChanged.
+ void set_charger_online(const HealthInfo& health_info);
+
+ // HealthLoop periodically calls ScheduleBatteryUpdate, which calls
+ // OnHealthInfoChanged callback. A subclass of the callback can override
+ // HalHealthLoopCallback::OnHealthInfoChanged to
+ // broadcast the health_info to interested listeners.
+ // This adjust uevents / wakealarm periods.
+ void OnHealthInfoChanged(const HealthInfo& health_info);
+};
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/include/health-impl/Health.h b/health/aidl/default/include/health-impl/Health.h
new file mode 100644
index 0000000..e49f44c
--- /dev/null
+++ b/health/aidl/default/include/health-impl/Health.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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 <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/BnHealth.h>
+#include <aidl/android/hardware/health/IHealthInfoCallback.h>
+#include <android/binder_auto_utils.h>
+#include <health-impl/HalHealthLoop.h>
+#include <healthd/BatteryMonitor.h>
+#include <healthd/healthd.h>
+
+namespace aidl::android::hardware::health {
+
+class LinkedCallback;
+
+// AIDL version of android::hardware::health::V2_1::implementation::Health and BinderHealth.
+// There's no need to separate the two in AIDL because AIDL does not support passthrough transport.
+//
+// Instead of inheriting from HalHealthLoop directly, implements the callback interface to
+// HalHealthLoop instead.
+//
+// Sample implementation of health HAL.
+class Health : public BnHealth, public HalHealthLoopCallback {
+ public:
+ // Initialize with |config|.
+ // A subclass may modify |config| before passing it to the parent constructor.
+ // See implementation of Health for code samples.
+ Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config);
+
+ ndk::ScopedAStatus registerCallback(
+ const std::shared_ptr<IHealthInfoCallback>& callback) override;
+ ndk::ScopedAStatus unregisterCallback(
+ const std::shared_ptr<IHealthInfoCallback>& callback) override;
+ ndk::ScopedAStatus update() override;
+
+ // A subclass should not override this. Override UpdateHealthInfo instead.
+ ndk::ScopedAStatus getHealthInfo(HealthInfo* out) override final;
+
+ // A subclass is recommended to override the path in healthd_config in the constructor.
+ // Only override these if there are no suitable kernel interfaces to read these values.
+ ndk::ScopedAStatus getChargeCounterUah(int32_t* out) override;
+ ndk::ScopedAStatus getCurrentNowMicroamps(int32_t* out) override;
+ ndk::ScopedAStatus getCurrentAverageMicroamps(int32_t* out) override;
+ ndk::ScopedAStatus getCapacity(int32_t* out) override;
+ ndk::ScopedAStatus getChargeStatus(BatteryStatus* out) override;
+
+ // A subclass may either override these or provide function pointers in
+ // in healthd_config in the constructor.
+ // Prefer overriding these for efficiency.
+ ndk::ScopedAStatus getEnergyCounterNwh(int64_t* out) override;
+
+ // A subclass may override these for a specific device.
+ // The default implementations return nothing in |out|.
+ ndk::ScopedAStatus getDiskStats(std::vector<DiskStats>* out) override;
+ ndk::ScopedAStatus getStorageInfo(std::vector<StorageInfo>* out) override;
+
+ // A subclass may override these to provide a different implementation.
+ binder_status_t dump(int fd, const char** args, uint32_t num_args) override;
+
+ // HalHealthLoopCallback implementation.
+ void OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) override;
+ void OnHealthInfoChanged(const HealthInfo& health_info) override;
+
+ // A subclass may override this if it wants to handle binder events differently.
+ virtual void BinderEvent(uint32_t epevents);
+
+ // A subclass may override this to determine whether screen should be kept on in charger mode.
+ // Default is to invoke healthd_config->screen_on() on the BatteryProperties converted
+ // from getHealthInfo.
+ // Prefer overriding these to providing screen_on in healthd_config in the constructor
+ // for efficiency.
+ virtual std::optional<bool> ShouldKeepScreenOn();
+
+ 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.
+ // See implementation of Health for code samples.
+ virtual void UpdateHealthInfo(HealthInfo* health_info);
+
+ private:
+ friend LinkedCallback; // for exposing death_recipient_
+
+ bool unregisterCallbackInternal(std::shared_ptr<IHealthInfoCallback> callback);
+
+ std::string instance_name_;
+ ::android::BatteryMonitor battery_monitor_;
+ std::unique_ptr<struct healthd_config> healthd_config_;
+
+ ndk::ScopedAIBinder_DeathRecipient death_recipient_;
+ int binder_fd_ = -1;
+ std::mutex callbacks_lock_;
+ std::vector<std::unique_ptr<LinkedCallback>> callbacks_;
+};
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/main.cpp b/health/aidl/default/main.cpp
new file mode 100644
index 0000000..014ae34
--- /dev/null
+++ b/health/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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 <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <health-impl/Health.h>
+#include <health/utils.h>
+
+using aidl::android::hardware::health::HalHealthLoop;
+using aidl::android::hardware::health::Health;
+
+static constexpr const char* gInstanceName = "default";
+
+int main() {
+ // TODO(b/203246116): handle charger
+ // make a default health service
+ auto config = std::make_unique<healthd_config>();
+ ::android::hardware::health::InitHealthdConfig(config.get());
+ auto binder = ndk::SharedRefBase::make<Health>(gInstanceName, std::move(config));
+ auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
+ return hal_health_loop->StartLoop();
+}
diff --git a/health/utils/libhealthloop/include/health/HealthLoop.h b/health/utils/libhealthloop/include/health/HealthLoop.h
index 693e6cb..54b2740 100644
--- a/health/utils/libhealthloop/include/health/HealthLoop.h
+++ b/health/utils/libhealthloop/include/health/HealthLoop.h
@@ -46,6 +46,12 @@
// Init is called right after epollfd_ is initialized (so RegisterEvent
// is allowed) but before other things are initialized (so SetChargerOnline
// is not allowed.)
+ // The implementation of Init() should pull configuration from the
+ // underlying health HAL (via getHealthConfig()), and store it into
+ // |config|. The implementation may not initialize:
+ // - screen_on, because charger calls getScreenOn() from the HAL directly
+ // - ignorePowerSupplyNames, because it isn't used by any clients of the
+ // health HAL.
virtual void Init(healthd_config* config) = 0;
virtual void Heartbeat() = 0;
virtual int PrepareToWait() = 0;