diff --git a/health/aidl/default/Android.bp b/health/aidl/default/Android.bp new file mode 100644 index 0000000000..cb78e772c1 --- /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 0000000000..c9a081e0f2 --- /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 + +#include + +#include +#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 0000000000..2d91ce0fcd --- /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 +#include +#include +#include +#include +#include + +#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(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 CreateConfig() { + auto config = std::make_unique(); + ::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&& config) + : instance_name_(instance_name), + healthd_config_(std::move(config)), + death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) { + battery_monitor_.init(healthd_config_.get()); +} + +// +// Getters. +// + +template +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(id), &prop); + if (err == ::android::OK) { + *out = static_cast(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(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out); +} + +ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) { + return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out); +} + +ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) { + return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out); +} + +ndk::ScopedAStatus Health::getCapacity(int32_t* out) { + return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out); +} + +ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) { + return GetProperty(&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*) { + // 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*) { + // 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 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& 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 lock(callbacks_lock_); + callbacks_.emplace_back(LinkedCallback::Make(ref(), 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& 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 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 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 thiz = ref(); + 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 0000000000..2985ffe959 --- /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 +#include + +#include +#include + +#include "LinkedCallback.h" + +namespace aidl::android::hardware::health { + +std::unique_ptr LinkedCallback::Make( + std::shared_ptr service, std::shared_ptr callback) { + std::unique_ptr ret(new LinkedCallback()); + binder_status_t linkRet = + AIBinder_linkToDeath(callback->asBinder().get(), service->death_recipient_.get(), + reinterpret_cast(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(this)); + if (status != STATUS_OK && status != STATUS_DEAD_OBJECT) { + LOG(WARNING) << __func__ << "Cannot unlink to death: " << ::android::statusToString(status); + } +} + +std::shared_ptr 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 0000000000..82490a7015 --- /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 + +#include +#include +#include + +#include + +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 Make(std::shared_ptr service, + std::shared_ptr 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& callback() const { return callback_; } + + // On callback died, unreigster it from the service. + void OnCallbackDied(); + + private: + LinkedCallback(); + DISALLOW_COPY_AND_ASSIGN(LinkedCallback); + + std::shared_ptr service(); + + std::weak_ptr service_; + std::shared_ptr 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 0000000000..b393c58fa2 --- /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 0000000000..98026cbdc0 --- /dev/null +++ b/health/aidl/default/android.hardware.health-service.example.xml @@ -0,0 +1,7 @@ + + + android.hardware.health + 1 + IHealth/default + + diff --git a/health/aidl/default/health-convert.cpp b/health/aidl/default/health-convert.cpp new file mode 100644 index 0000000000..b5251f4bb5 --- /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(info.batteryStatus); + p->batteryHealth = static_cast(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 0000000000..179ffc497d --- /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 +#include +#include + +// 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 0000000000..c46aaa4cb6 --- /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 +#include + +#include +#include + +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 service, std::shared_ptr 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 service_; + std::shared_ptr 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 0000000000..e49f44c7e4 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +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&& config); + + ndk::ScopedAStatus registerCallback( + const std::shared_ptr& callback) override; + ndk::ScopedAStatus unregisterCallback( + const std::shared_ptr& 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* out) override; + ndk::ScopedAStatus getStorageInfo(std::vector* 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 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 callback); + + std::string instance_name_; + ::android::BatteryMonitor battery_monitor_; + std::unique_ptr healthd_config_; + + ndk::ScopedAIBinder_DeathRecipient death_recipient_; + int binder_fd_ = -1; + std::mutex callbacks_lock_; + std::vector> 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 0000000000..014ae34f69 --- /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 +#include +#include +#include + +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(); + ::android::hardware::health::InitHealthdConfig(config.get()); + auto binder = ndk::SharedRefBase::make(gInstanceName, std::move(config)); + auto hal_health_loop = std::make_shared(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 693e6cbb9a..54b2740c1b 100644 --- a/health/utils/libhealthloop/include/health/HealthLoop.h +++ b/health/utils/libhealthloop/include/health/HealthLoop.h @@ -46,6 +46,12 @@ class HealthLoop { // 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;