From 7a6956ee0d297fa5fc2b34f94e42997d0c6494fc Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Mon, 8 Nov 2021 23:52:44 -0800 Subject: [PATCH] health: add libhealthshim This is a shim library that wraps a HIDL IHealth to an AIDL BnHealth. Also add tests for the library. Bug: 177269435 Test: libhealthshim_test Change-Id: Ia5f32c1ae0693c70cf698b46b48d5f77006cf6c7 --- health/aidl/Android.bp | 5 + .../android/hardware/health/translate-ndk.cpp | 61 ++--- .../android/hardware/health/translate-ndk.h | 3 + health/utils/libhealthshim/Android.bp | 78 +++++++ .../libhealthshim/include/health-shim/shim.h | 55 +++++ health/utils/libhealthshim/shim.cpp | 220 ++++++++++++++++++ health/utils/libhealthshim/test.cpp | 163 +++++++++++++ 7 files changed, 557 insertions(+), 28 deletions(-) create mode 100644 health/utils/libhealthshim/Android.bp create mode 100644 health/utils/libhealthshim/include/health-shim/shim.h create mode 100644 health/utils/libhealthshim/shim.cpp create mode 100644 health/utils/libhealthshim/test.cpp diff --git a/health/aidl/Android.bp b/health/aidl/Android.bp index fae7592860..6e2f1d4f6b 100644 --- a/health/aidl/Android.bp +++ b/health/aidl/Android.bp @@ -25,6 +25,7 @@ aidl_interface { name: "android.hardware.health", vendor_available: true, recovery_available: true, + host_supported: true, srcs: ["android/hardware/health/*.aidl"], stability: "vintf", backend: { @@ -48,6 +49,7 @@ cc_library { name: "android.hardware.health-translate-ndk", vendor_available: true, recovery_available: true, + host_supported: true, srcs: ["android/hardware/health/translate-ndk.cpp"], shared_libs: [ "libbinder_ndk", @@ -61,6 +63,9 @@ cc_library { "android.hardware.health@2.0", "android.hardware.health@2.1", ], + defaults: [ + "libbinder_ndk_host_user", + ], } java_library { diff --git a/health/aidl/android/hardware/health/translate-ndk.cpp b/health/aidl/android/hardware/health/translate-ndk.cpp index 7fe6ced444..78880cc82f 100644 --- a/health/aidl/android/hardware/health/translate-ndk.cpp +++ b/health/aidl/android/hardware/health/translate-ndk.cpp @@ -105,37 +105,42 @@ __attribute__((warn_unused_result)) bool translate( return true; } +__attribute__((warn_unused_result)) bool translate( + const ::android::hardware::health::V2_0::HealthInfo& in, + aidl::android::hardware::health::HealthInfo* out) { + out->chargerAcOnline = static_cast(in.legacy.chargerAcOnline); + out->chargerUsbOnline = static_cast(in.legacy.chargerUsbOnline); + out->chargerWirelessOnline = static_cast(in.legacy.chargerWirelessOnline); + out->maxChargingCurrentMicroamps = static_cast(in.legacy.maxChargingCurrent); + out->maxChargingVoltageMicrovolts = static_cast(in.legacy.maxChargingVoltage); + out->batteryStatus = + static_cast(in.legacy.batteryStatus); + out->batteryHealth = + static_cast(in.legacy.batteryHealth); + out->batteryPresent = static_cast(in.legacy.batteryPresent); + out->batteryLevel = static_cast(in.legacy.batteryLevel); + out->batteryVoltageMillivolts = static_cast(in.legacy.batteryVoltage); + out->batteryTemperatureTenthsCelsius = static_cast(in.legacy.batteryTemperature); + out->batteryCurrentMicroamps = static_cast(in.legacy.batteryCurrent); + out->batteryCycleCount = static_cast(in.legacy.batteryCycleCount); + out->batteryFullChargeUah = static_cast(in.legacy.batteryFullCharge); + out->batteryChargeCounterUah = static_cast(in.legacy.batteryChargeCounter); + out->batteryTechnology = in.legacy.batteryTechnology; + out->batteryCurrentAverageMicroamps = static_cast(in.batteryCurrentAverage); + out->diskStats.clear(); + out->diskStats.resize(in.diskStats.size()); + for (size_t i = 0; i < in.diskStats.size(); ++i) + if (!translate(in.diskStats[i], &out->diskStats[i])) return false; + out->storageInfos.clear(); + out->storageInfos.resize(in.storageInfos.size()); + for (size_t i = 0; i < in.storageInfos.size(); ++i) + if (!translate(in.storageInfos[i], &out->storageInfos[i])) return false; + return true; +} __attribute__((warn_unused_result)) bool translate( const ::android::hardware::health::V2_1::HealthInfo& in, aidl::android::hardware::health::HealthInfo* out) { - out->chargerAcOnline = static_cast(in.legacy.legacy.chargerAcOnline); - out->chargerUsbOnline = static_cast(in.legacy.legacy.chargerUsbOnline); - out->chargerWirelessOnline = static_cast(in.legacy.legacy.chargerWirelessOnline); - out->maxChargingCurrentMicroamps = static_cast(in.legacy.legacy.maxChargingCurrent); - out->maxChargingVoltageMicrovolts = static_cast(in.legacy.legacy.maxChargingVoltage); - out->batteryStatus = static_cast( - in.legacy.legacy.batteryStatus); - out->batteryHealth = static_cast( - in.legacy.legacy.batteryHealth); - out->batteryPresent = static_cast(in.legacy.legacy.batteryPresent); - out->batteryLevel = static_cast(in.legacy.legacy.batteryLevel); - out->batteryVoltageMillivolts = static_cast(in.legacy.legacy.batteryVoltage); - out->batteryTemperatureTenthsCelsius = - static_cast(in.legacy.legacy.batteryTemperature); - out->batteryCurrentMicroamps = static_cast(in.legacy.legacy.batteryCurrent); - out->batteryCycleCount = static_cast(in.legacy.legacy.batteryCycleCount); - out->batteryFullChargeUah = static_cast(in.legacy.legacy.batteryFullCharge); - out->batteryChargeCounterUah = static_cast(in.legacy.legacy.batteryChargeCounter); - out->batteryTechnology = in.legacy.legacy.batteryTechnology; - out->batteryCurrentAverageMicroamps = static_cast(in.legacy.batteryCurrentAverage); - out->diskStats.clear(); - out->diskStats.resize(in.legacy.diskStats.size()); - for (size_t i = 0; i < in.legacy.diskStats.size(); ++i) - if (!translate(in.legacy.diskStats[i], &out->diskStats[i])) return false; - out->storageInfos.clear(); - out->storageInfos.resize(in.legacy.storageInfos.size()); - for (size_t i = 0; i < in.legacy.storageInfos.size(); ++i) - if (!translate(in.legacy.storageInfos[i], &out->storageInfos[i])) return false; + if (!translate(in.legacy, out)) return false; out->batteryCapacityLevel = static_cast( in.batteryCapacityLevel); out->batteryChargeTimeToFullNowSeconds = diff --git a/health/aidl/include/android/hardware/health/translate-ndk.h b/health/aidl/include/android/hardware/health/translate-ndk.h index 2f8fe04161..91add4223f 100644 --- a/health/aidl/include/android/hardware/health/translate-ndk.h +++ b/health/aidl/include/android/hardware/health/translate-ndk.h @@ -32,6 +32,9 @@ __attribute__((warn_unused_result)) bool translate( __attribute__((warn_unused_result)) bool translate( const ::android::hardware::health::V2_0::DiskStats& in, aidl::android::hardware::health::DiskStats* out); +__attribute__((warn_unused_result)) bool translate( + const ::android::hardware::health::V2_0::HealthInfo& in, + aidl::android::hardware::health::HealthInfo* out); __attribute__((warn_unused_result)) bool translate( const ::android::hardware::health::V2_1::HealthInfo& in, aidl::android::hardware::health::HealthInfo* out); diff --git a/health/utils/libhealthshim/Android.bp b/health/utils/libhealthshim/Android.bp new file mode 100644 index 0000000000..311e951b7f --- /dev/null +++ b/health/utils/libhealthshim/Android.bp @@ -0,0 +1,78 @@ +// 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. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_defaults { + name: "libhealthshim_defaults", + host_supported: true, // for testing + defaults: [ + "libbinder_ndk_host_user", + ], + cflags: [ + "-Wall", + "-Werror", + ], + static_libs: [ + "android.hardware.health-V1-ndk", + "android.hardware.health-translate-ndk", + "android.hardware.health@1.0", + "android.hardware.health@2.0", + ], + shared_libs: [ + // These can be expected from the device or from host. + "libbase", + "libbinder_ndk", + "libcutils", + "libhidlbase", + "liblog", + "libutils", + ], +} + +// Shim library that wraps a HIDL IHealth object into an AIDL IHealth object. +cc_library_static { + name: "libhealthshim", + defaults: ["libhealthshim_defaults"], + recovery_available: true, + srcs: [ + "shim.cpp", + ], + export_include_dirs: [ + "include", + ], +} + +cc_test { + name: "libhealthshim_test", + defaults: ["libhealthshim_defaults"], + static_libs: [ + "libhealthshim", + "libgmock", + ], + srcs: [ + "test.cpp", + ], + test_suites: ["general-tests"], + test_options: { + unit_test: true, + }, +} diff --git a/health/utils/libhealthshim/include/health-shim/shim.h b/health/utils/libhealthshim/include/health-shim/shim.h new file mode 100644 index 0000000000..f36fa5d307 --- /dev/null +++ b/health/utils/libhealthshim/include/health-shim/shim.h @@ -0,0 +1,55 @@ +/* + * 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 + +namespace aidl::android::hardware::health { + +// Shim that wraps HIDL IHealth with an AIDL BnHealth. +// The wrapper always have isRemote() == false because it is BnHealth. +class HealthShim : public BnHealth { + using HidlHealth = ::android::hardware::health::V2_0::IHealth; + using HidlHealthInfoCallback = ::android::hardware::health::V2_0::IHealthInfoCallback; + + public: + explicit HealthShim(const ::android::sp& service); + + ndk::ScopedAStatus registerCallback( + const std::shared_ptr& in_callback) override; + ndk::ScopedAStatus unregisterCallback( + const std::shared_ptr& in_callback) override; + ndk::ScopedAStatus update() override; + ndk::ScopedAStatus getChargeCounterUah(int32_t* _aidl_return) override; + ndk::ScopedAStatus getCurrentNowMicroamps(int32_t* _aidl_return) override; + ndk::ScopedAStatus getCurrentAverageMicroamps(int32_t* _aidl_return) override; + ndk::ScopedAStatus getCapacity(int32_t* _aidl_return) override; + ndk::ScopedAStatus getEnergyCounterNwh(int64_t* _aidl_return) override; + ndk::ScopedAStatus getChargeStatus(BatteryStatus* _aidl_return) override; + ndk::ScopedAStatus getStorageInfo(std::vector* _aidl_return) override; + ndk::ScopedAStatus getDiskStats(std::vector* _aidl_return) override; + ndk::ScopedAStatus getHealthInfo(HealthInfo* _aidl_return) override; + + private: + ::android::sp service_; + std::map, ::android::sp> + callback_map_; +}; + +} // namespace aidl::android::hardware::health diff --git a/health/utils/libhealthshim/shim.cpp b/health/utils/libhealthshim/shim.cpp new file mode 100644 index 0000000000..13296791e6 --- /dev/null +++ b/health/utils/libhealthshim/shim.cpp @@ -0,0 +1,220 @@ +/* + * 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 + +using ::android::sp; +using ::android::h2a::translate; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::health::V2_0::Result; +using ::android::hardware::health::V2_0::toString; +using ::ndk::ScopedAStatus; +using HidlHealth = ::android::hardware::health::V2_0::IHealth; +using HidlHealthInfoCallback = ::android::hardware::health::V2_0::IHealthInfoCallback; +using HidlHealthInfo = ::android::hardware::health::V2_0::HealthInfo; + +namespace aidl::android::hardware::health { + +namespace { + +class HealthInfoCallbackShim : public HidlHealthInfoCallback { + using AidlHealthInfoCallback = ::aidl::android::hardware::health::IHealthInfoCallback; + using AidlHealthInfo = ::aidl::android::hardware::health::HealthInfo; + + public: + explicit HealthInfoCallbackShim(const std::shared_ptr& impl) + : impl_(impl) {} + Return healthInfoChanged(const HidlHealthInfo& info) override { + AidlHealthInfo aidl_info; + // translate() should always return true. + CHECK(translate(info, &aidl_info)); + // This is a oneway function, so we can't (and shouldn't) check for errors. + (void)impl_->healthInfoChanged(aidl_info); + return Void(); + } + + private: + std::shared_ptr impl_; +}; + +ScopedAStatus ResultToStatus(Result result) { + switch (result) { + case Result::SUCCESS: + return ScopedAStatus::ok(); + case Result::NOT_SUPPORTED: + return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + case Result::UNKNOWN: + return ScopedAStatus::fromServiceSpecificError(IHealth::STATUS_UNKNOWN); + case Result::NOT_FOUND: + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + case Result::CALLBACK_DIED: + return ScopedAStatus::fromServiceSpecificError(IHealth::STATUS_CALLBACK_DIED); + } + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + IHealth::STATUS_UNKNOWN, ("Unrecognized result value " + toString(result)).c_str()); +} + +template +ScopedAStatus ReturnAndResultToStatus(const Return& ret, Result result) { + if (ret.isOk()) { + return ResultToStatus(result); + } + if (ret.isDeadObject()) { + return ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT); + } + return ScopedAStatus::fromServiceSpecificErrorWithMessage(IHealth::STATUS_UNKNOWN, + ret.description().c_str()); +} + +ScopedAStatus ReturnResultToStatus(const Return& return_result) { + return ReturnAndResultToStatus(return_result, return_result.isOk() + ? static_cast(return_result) + : Result::UNKNOWN); +} + +} // namespace + +HealthShim::HealthShim(const sp& service) : service_(service) {} + +ScopedAStatus HealthShim::registerCallback( + const std::shared_ptr& in_callback) { + sp shim(new HealthInfoCallbackShim(in_callback)); + callback_map_.emplace(in_callback, shim); + return ReturnResultToStatus(service_->registerCallback(shim)); +} + +ScopedAStatus HealthShim::unregisterCallback( + const std::shared_ptr& in_callback) { + auto it = callback_map_.find(in_callback); + if (it == callback_map_.end()) { + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + sp shim = it->second; + callback_map_.erase(it); + return ReturnResultToStatus(service_->unregisterCallback(shim)); +} + +ScopedAStatus HealthShim::update() { + return ReturnResultToStatus(service_->update()); +} + +ScopedAStatus HealthShim::getChargeCounterUah(int32_t* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getChargeCounter([out, &out_result](auto result, auto value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + *out = value; + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getCurrentNowMicroamps(int32_t* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getCurrentNow([out, &out_result](auto result, auto value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + *out = value; + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getCurrentAverageMicroamps(int32_t* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getCurrentAverage([out, &out_result](auto result, auto value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + *out = value; + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getCapacity(int32_t* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getCapacity([out, &out_result](auto result, auto value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + *out = value; + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getEnergyCounterNwh(int64_t* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getEnergyCounter([out, &out_result](auto result, auto value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + *out = value; + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getChargeStatus(BatteryStatus* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getChargeStatus([out, &out_result](auto result, auto value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + *out = static_cast(value); + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getStorageInfo(std::vector* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getStorageInfo([out, &out_result](auto result, const auto& value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + out->clear(); + out->reserve(value.size()); + for (const auto& hidl_info : value) { + auto& aidl_info = out->emplace_back(); + // translate() should always return true. + CHECK(translate(hidl_info, &aidl_info)); + } + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getDiskStats(std::vector* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getDiskStats([out, &out_result](auto result, const auto& value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + out->clear(); + out->reserve(value.size()); + for (const auto& hidl_info : value) { + auto& aidl_info = out->emplace_back(); + // translate() should always return true. + CHECK(translate(hidl_info, &aidl_info)); + } + }); + return ReturnAndResultToStatus(ret, out_result); +} + +ScopedAStatus HealthShim::getHealthInfo(HealthInfo* out) { + Result out_result = Result::UNKNOWN; + auto ret = service_->getHealthInfo([out, &out_result](auto result, const auto& value) { + out_result = result; + if (out_result != Result::SUCCESS) return; + // translate() should always return true. + CHECK(translate(value, out)); + }); + return ReturnAndResultToStatus(ret, out_result); +} + +} // namespace aidl::android::hardware::health diff --git a/health/utils/libhealthshim/test.cpp b/health/utils/libhealthshim/test.cpp new file mode 100644 index 0000000000..d1dfb8bf63 --- /dev/null +++ b/health/utils/libhealthshim/test.cpp @@ -0,0 +1,163 @@ +/* + * 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 HidlHealth = android::hardware::health::V2_0::IHealth; +using HidlHealthInfoCallback = android::hardware::health::V2_0::IHealthInfoCallback; +using HidlStorageInfo = android::hardware::health::V2_0::StorageInfo; +using HidlDiskStats = android::hardware::health::V2_0::DiskStats; +using HidlHealthInfo = android::hardware::health::V2_0::HealthInfo; +using HidlBatteryStatus = android::hardware::health::V1_0::BatteryStatus; +using android::sp; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::health::V2_0::Result; +using ndk::SharedRefBase; +using testing::Invoke; +using testing::NiceMock; + +namespace aidl::android::hardware::health { +MATCHER(IsOk, "") { + *result_listener << "status is " << arg.getDescription(); + return arg.isOk(); +} + +MATCHER_P(ExceptionIs, exception_code, "") { + *result_listener << "status is " << arg.getDescription(); + return arg.getExceptionCode() == exception_code; +} + +class MockHidlHealth : public HidlHealth { + public: + MOCK_METHOD(Return, registerCallback, (const sp& callback), + (override)); + MOCK_METHOD(Return, unregisterCallback, (const sp& callback), + (override)); + MOCK_METHOD(Return, update, (), (override)); + MOCK_METHOD(Return, getChargeCounter, (getChargeCounter_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getCurrentNow, (getCurrentNow_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getCurrentAverage, (getCurrentAverage_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getCapacity, (getCapacity_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getEnergyCounter, (getEnergyCounter_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getChargeStatus, (getChargeStatus_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getStorageInfo, (getStorageInfo_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getDiskStats, (getDiskStats_cb _hidl_cb), (override)); + MOCK_METHOD(Return, getHealthInfo, (getHealthInfo_cb _hidl_cb), (override)); +}; + +class HealthShimTest : public ::testing::Test { + public: + void SetUp() override { + hidl = new NiceMock(); + shim = SharedRefBase::make(hidl); + } + sp hidl; + std::shared_ptr shim; +}; + +#define ADD_TEST(name, aidl_name, AidlValueType, hidl_value, not_supported_hidl_value) \ + TEST_F(HealthShimTest, name) { \ + ON_CALL(*hidl, name).WillByDefault(Invoke([](auto cb) { \ + cb(Result::SUCCESS, hidl_value); \ + return Void(); \ + })); \ + AidlValueType value; \ + ASSERT_THAT(shim->aidl_name(&value), IsOk()); \ + ASSERT_EQ(value, static_cast(hidl_value)); \ + } \ + \ + TEST_F(HealthShimTest, name##Unsupported) { \ + ON_CALL(*hidl, name).WillByDefault(Invoke([](auto cb) { \ + cb(Result::NOT_SUPPORTED, not_supported_hidl_value); \ + return Void(); \ + })); \ + AidlValueType value; \ + ASSERT_THAT(shim->aidl_name(&value), ExceptionIs(EX_UNSUPPORTED_OPERATION)); \ + } + +ADD_TEST(getChargeCounter, getChargeCounterUah, int32_t, 0xFEEDBEEF, 0) +ADD_TEST(getCurrentNow, getCurrentNowMicroamps, int32_t, 0xC0FFEE, 0) +ADD_TEST(getCurrentAverage, getCurrentAverageMicroamps, int32_t, 0xA2D401D, 0) +ADD_TEST(getCapacity, getCapacity, int32_t, 77, 0) +ADD_TEST(getEnergyCounter, getEnergyCounterNwh, int64_t, 0x1234567887654321, 0) +ADD_TEST(getChargeStatus, getChargeStatus, BatteryStatus, HidlBatteryStatus::CHARGING, + HidlBatteryStatus::UNKNOWN) + +#undef ADD_TEST + +template +bool Translate(const HidlValueType& hidl_value, AidlValueType* aidl_value) { + return ::android::h2a::translate(hidl_value, aidl_value); +} + +template +bool Translate(const std::vector& hidl_vec, std::vector* aidl_vec) { + aidl_vec->clear(); + aidl_vec->reserve(hidl_vec.size()); + for (const auto& hidl_value : hidl_vec) { + auto& aidl_value = aidl_vec->emplace_back(); + if (!Translate(hidl_value, &aidl_value)) return false; + } + return true; +} + +#define ADD_INFO_TEST(name, AidlValueType, hidl_value) \ + TEST_F(HealthShimTest, name) { \ + AidlValueType expected_aidl_value; \ + ASSERT_TRUE(Translate(hidl_value, &expected_aidl_value)); \ + ON_CALL(*hidl, name).WillByDefault(Invoke([&](auto cb) { \ + cb(Result::SUCCESS, hidl_value); \ + return Void(); \ + })); \ + AidlValueType aidl_value; \ + ASSERT_THAT(shim->name(&aidl_value), IsOk()); \ + ASSERT_EQ(aidl_value, expected_aidl_value); \ + } \ + \ + TEST_F(HealthShimTest, name##Unsupported) { \ + ON_CALL(*hidl, name).WillByDefault(Invoke([](auto cb) { \ + cb(Result::NOT_SUPPORTED, {}); \ + return Void(); \ + })); \ + AidlValueType aidl_value; \ + ASSERT_THAT(shim->name(&aidl_value), ExceptionIs(EX_UNSUPPORTED_OPERATION)); \ + } + +ADD_INFO_TEST(getStorageInfo, std::vector, + (std::vector{{ + .lifetimeA = 15, + .lifetimeB = 18, + }})) + +ADD_INFO_TEST(getDiskStats, std::vector, + (std::vector{{ + .reads = 100, + .writes = 200, + }})) + +ADD_INFO_TEST(getHealthInfo, HealthInfo, + (HidlHealthInfo{ + .batteryCurrentAverage = 999, + })) + +#undef ADD_INFO_TEST + +} // namespace aidl::android::hardware::health