From ed0fead8e42f11a6856178926548a03c61659753 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 2 Oct 2019 18:22:12 -0700 Subject: [PATCH] health 2.1 Test: builds Bug: 137670450 Change-Id: Ie8ec2733ee5338fef3639ab4deda47c9e5ce2179 --- health/2.1/Android.bp | 20 ++ health/2.1/IHealth.hal | 80 ++++++ health/2.1/IHealthInfoCallback.hal | 36 +++ health/2.1/types.hal | 109 +++++++ health/2.1/vts/OWNERS | 3 + health/2.1/vts/functional/Android.bp | 29 ++ .../functional/VtsHalHealthV2_1TargetTest.cpp | 269 ++++++++++++++++++ 7 files changed, 546 insertions(+) create mode 100644 health/2.1/Android.bp create mode 100644 health/2.1/IHealth.hal create mode 100644 health/2.1/IHealthInfoCallback.hal create mode 100644 health/2.1/types.hal create mode 100644 health/2.1/vts/OWNERS create mode 100644 health/2.1/vts/functional/Android.bp create mode 100644 health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp diff --git a/health/2.1/Android.bp b/health/2.1/Android.bp new file mode 100644 index 0000000000..254bfc0d30 --- /dev/null +++ b/health/2.1/Android.bp @@ -0,0 +1,20 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.health@2.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IHealth.hal", + "IHealthInfoCallback.hal", + ], + interfaces: [ + "android.hardware.health@1.0", + "android.hardware.health@2.0", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/health/2.1/IHealth.hal b/health/2.1/IHealth.hal new file mode 100644 index 0000000000..8a5152a6ff --- /dev/null +++ b/health/2.1/IHealth.hal @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.health@2.1; + +import @2.0::IHealth; +import @2.0::Result; +import HealthConfig; +import HealthInfo; +import IHealthInfoCallback; + +/** + * IHealth manages health info and posts events on registered callbacks. + * + * An implementation of @2.1::IHealth must be able to handle both + * @2.0::IHealthInfoCallback and @2.1::IHealthInfoCallback. + * - When registerCallback() is called, an implementation must cast the callback + * to @2.1::IHealthInfoCallback. + * - If the cast is successful, when a health info broadcast is sent, the + * implementation must call + * @2.1::IHealthInfoCallback.healthInfoChanged_2_1(). All fields introduced + * in 2.1 must be set appropriately. The implementation must not call + * @2.0::IHealthInfoCallback.healthInfoChanged(). + * - If the cast is unsuccessful, the implementation must call + * @2.0::IHealthInfoCallback.healthInfoChanged(). + * - When unregisterCallback() is called, from then on, updates must not be sent + * through either healthInfoChanged_2_1() or healthInfoChanged(). + * + * Passthrough implementations are not required to send health info to all + * callbacks periodically, but they must do so when update() is called. + * Binderized implementations must send health info to all callbacks + * periodically. The intervals between two notifications must be retrieved from + * the passthrough implementation through the getHealthConfig() function. + */ +interface IHealth extends @2.0::IHealth { + /** + * Get configuration of this HAL. + * + * @return result SUCCESS if successful, + * NOT_SUPPORTED if this API is not supported, + * UNKNOWN for other errors. + * @return config HAL configuration, to be ignored if result is not + * SUCCESS. + */ + getHealthConfig() generates (Result result, HealthConfig config); + + /** + * Get Health Information. + * + * @return result SUCCESS if successful, + * NOT_SUPPORTED if this API is not supported, + * UNKNOWN for other errors. + * @return value Health information, to be ignored if result is not + * SUCCESS. + */ + getHealthInfo_2_1() generates (Result result, @2.1::HealthInfo value); + + /** + * Return whether the screen should be kept on in charger mode. + * + * @return result SUCCESS if successful, + * NOT_SUPPORTED if this API is not supported, + * UNKNOWN for other errors. + * @return value whether screen should be kept on. + */ + shouldKeepScreenOn() generates (Result result, bool value); +}; diff --git a/health/2.1/IHealthInfoCallback.hal b/health/2.1/IHealthInfoCallback.hal new file mode 100644 index 0000000000..275f0183b3 --- /dev/null +++ b/health/2.1/IHealthInfoCallback.hal @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.health@2.1; + +import @2.0::IHealthInfoCallback; + +/** + * IHealthInfoCallback is the updated callback interface to + * {@link IHealth.registerCallback}. + * + * A @2.1::IHealthInfoCallback must implement healthInfoChanged_2_1(). The + * inherited healthInfoChanged() function is never called when the HAL + * implementation post events. See documentation on @2.1::IHealth for details. + */ +interface IHealthInfoCallback extends @2.0::IHealthInfoCallback { + /** + * An implementation of IHealth must call healthInfoChanged on all + * registered callbacks after health info changes. + * @param info the updated HealthInfo + */ + oneway healthInfoChanged_2_1(HealthInfo info); +}; diff --git a/health/2.1/types.hal b/health/2.1/types.hal new file mode 100644 index 0000000000..efd8d6f675 --- /dev/null +++ b/health/2.1/types.hal @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.health@2.1; + +import @1.0::HealthConfig; +import @2.0::HealthInfo; + +/** + * Battery capacity level. This enum provides additional information along side + * with the battery capacity. + * Clients of this HAL must use this value before inferring it from the + * battery capacity. + */ +enum BatteryCapacityLevel : int32_t { + /** + * Battery capacity level is unknown. + * Battery capacity level must be set to this value if and only if battery + * is not present. + */ + UNKNOWN = 0, + /** + * Battery is at critical level. The Android framework must schedule a + * shutdown when it sees this value from the HAL. + */ + CRITICAL, + /** + * Battery is low. The Android framework may limit the performance of + * the device when it sees this value from the HAL. + */ + LOW, + /** + * Battery level is normal. + */ + NORMAL, + /** + * Battery level is high. + */ + HIGH, + /** + * Battery is full. It must be set to FULL if and only if battery level is + * 100. + */ + FULL, +}; + +/** + * Combined Health Information. + */ +struct HealthInfo { + /** + * V2.0 HealthInfo. + * If a member is unsupported, it is filled with: + * - 0 (for integers); + * - false (for booleans); + * - empty string (for strings); + * - UNKNOWN (for BatteryStatus and BatteryHealth). + */ + @2.0::HealthInfo legacy; + + /** + * Battery capacity level. See BatteryCapacityLevel for more details. + */ + BatteryCapacityLevel batteryCapacityLevel; + + /** + * Estimated time to fully charge the device (in seconds). + * Value must be 0 if and only if batteryCapacityLevel is FULL or UNKNOWN. + * Otherwise, value must be positive. + */ + int64_t batteryChargeTimeToFullNowSeconds; + + /** + * Estimated battery full capacity (in microamp hours, uAh). + * Value must be 0 if unknown. + * Value must be positive if known, and must be between [50%, 120%] of + * batteryFullCharge (the designed capacity). + */ + int32_t batteryFullCapacityUah; +}; + +/** + * Combined configuration of a health HAL implementation. + */ +struct HealthConfig { + /** + * 1.0 version of health config. + */ + @1.0::HealthConfig battery; + + /** + * Minimum battery level for charger to reboot into Android (in percent). + * Value should be in range [0, 100]. + */ + int32_t bootMinCap; +}; diff --git a/health/2.1/vts/OWNERS b/health/2.1/vts/OWNERS new file mode 100644 index 0000000000..20450ba407 --- /dev/null +++ b/health/2.1/vts/OWNERS @@ -0,0 +1,3 @@ +elsk@google.com +hridya@google.com +sspatil@google.com diff --git a/health/2.1/vts/functional/Android.bp b/health/2.1/vts/functional/Android.bp new file mode 100644 index 0000000000..5aa873ab98 --- /dev/null +++ b/health/2.1/vts/functional/Android.bp @@ -0,0 +1,29 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "VtsHalHealthV2_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalHealthV2_1TargetTest.cpp"], + static_libs: [ + "libgflags", + "libgmock", + "android.hardware.health@1.0", + "android.hardware.health@2.0", + "android.hardware.health@2.1", + ], + test_suites: ["general-tests"], +} diff --git a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp new file mode 100644 index 0000000000..7df4926ecf --- /dev/null +++ b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "health_hidl_hal_test" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::android::hardware::health::V1_0::BatteryStatus; +using ::android::hardware::health::V2_0::Result; +using ::testing::AnyOf; +using ::testing::AssertionFailure; +using ::testing::AssertionResult; +using ::testing::AssertionSuccess; +using namespace std::chrono_literals; + +using ::android::hardware::health::V1_0::toString; +using ::android::hardware::health::V2_0::toString; +using ::android::hardware::health::V2_1::toString; + +// Return expr if it is evaluated to false. +#define TEST_AND_RETURN(expr) \ + do { \ + auto res = (expr); \ + if (!res) return res; \ + } while (0) + +// Return a descriptive AssertionFailure() if expr is evaluated to false. +#define TEST_AND_RETURN_FAILURE(expr) \ + do { \ + auto res = (expr); \ + if (!res) { \ + return AssertionFailure() << #expr " is false"; \ + } \ + } while (0) + +namespace android { +namespace hardware { +namespace health { + +namespace V2_0 { +std::ostream& operator<<(std::ostream& os, const Result& res) { + return os << toString(res); +} +} // namespace V2_0 + +namespace V2_1 { + +class HealthHidlTest : public testing::TestWithParam { + public: + virtual void SetUp() override { + service_ = IHealth::getService(GetParam()); + ASSERT_NE(nullptr, service_.get()) << "Instance '" << GetParam() << "'' is not available."; + } + + sp service_; +}; + +class CallbackBase { + public: + Return healthInfoChangedInternal() { + std::lock_guard lock(mutex_); + invoked_ = true; + invoked_notify_.notify_all(); + return Void(); + } + template + bool waitInvoke(std::chrono::duration duration) { + std::unique_lock lock(mutex_); + bool r = invoked_notify_.wait_for(lock, duration, [this] { return this->invoked_; }); + invoked_ = false; + return r; + } + + private: + std::mutex mutex_; + std::condition_variable invoked_notify_; + bool invoked_ = false; +}; + +class Callback_2_0 : public android::hardware::health::V2_0::IHealthInfoCallback, + public CallbackBase { + Return healthInfoChanged(const android::hardware::health::V2_0::HealthInfo&) override { + return healthInfoChangedInternal(); + } +}; + +class Callback_2_1 : public android::hardware::health::V2_1::IHealthInfoCallback, + public CallbackBase { + Return healthInfoChanged(const android::hardware::health::V2_0::HealthInfo&) override { + ADD_FAILURE() << "android::hardware::health::V2_1::IHealthInfoCallback::healthInfoChanged " + << "is called, but it shouldn't be"; + return Void(); + } + Return healthInfoChanged_2_1(const HealthInfo&) override { + return healthInfoChangedInternal(); + } +}; + +template +AssertionResult IsOk(const Return& r) { + return r.isOk() ? AssertionSuccess() : (AssertionFailure() << r.description()); +} + +// Both IsOk() and Result::SUCCESS +AssertionResult ResultIsSuccess(const Return& r) { + if (!r.isOk()) { + return AssertionFailure() << r.description(); + } + if (static_cast(r) != Result::SUCCESS) { + return AssertionFailure() << toString(static_cast(r)); + } + return AssertionSuccess(); +} + +/** + * Test whether callbacks work. Tested functions are IHealth::registerCallback, + * unregisterCallback, and update. + */ +template +AssertionResult TestCallbacks(sp service) { + sp first = new Callback(); + sp second = new Callback(); + + TEST_AND_RETURN(ResultIsSuccess(service->registerCallback(first))); + TEST_AND_RETURN(ResultIsSuccess(service->registerCallback(second))); + + // registerCallback may or may not invoke the callback immediately, so the test needs + // to wait for the invocation. If the implementation chooses not to invoke the callback + // immediately, just wait for some time. + first->waitInvoke(200ms); + second->waitInvoke(200ms); + + // assert that the first callback is invoked when update is called. + TEST_AND_RETURN(ResultIsSuccess(service->update())); + + TEST_AND_RETURN_FAILURE(first->waitInvoke(1s)); + TEST_AND_RETURN_FAILURE(second->waitInvoke(1s)); + + TEST_AND_RETURN(ResultIsSuccess(service->unregisterCallback(first))); + + // clear any potentially pending callbacks result from wakealarm / kernel events + // If there is none, just wait for some time. + first->waitInvoke(200ms); + second->waitInvoke(200ms); + + // assert that the second callback is still invoked even though the first is unregistered. + TEST_AND_RETURN(ResultIsSuccess(service->update())); + + TEST_AND_RETURN_FAILURE(!first->waitInvoke(200ms)); + TEST_AND_RETURN_FAILURE(second->waitInvoke(1s)); + + TEST_AND_RETURN(ResultIsSuccess(service->unregisterCallback(second))); + return AssertionSuccess(); +} + +TEST_P(HealthHidlTest, Callbacks_2_0) { + EXPECT_TRUE(TestCallbacks(service_)); +} + +TEST_P(HealthHidlTest, Callbacks_2_1) { + EXPECT_TRUE(TestCallbacks(service_)); +} + +template +AssertionResult TestUnregisterNonExistentCallback(sp service) { + sp callback = new Callback(); + auto ret = service->unregisterCallback(callback); + TEST_AND_RETURN(IsOk(ret)); + if (static_cast(ret) != Result::NOT_FOUND) { + return AssertionFailure() + << "Unregistering non-existent callback should return NOT_FOUND, but returned " + << static_cast(ret); + } + return AssertionSuccess(); +} + +TEST_P(HealthHidlTest, UnregisterNonExistentCallback_2_0) { + EXPECT_TRUE(TestUnregisterNonExistentCallback(service_)); +} + +TEST_P(HealthHidlTest, UnregisterNonExistentCallback_2_1) { + EXPECT_TRUE(TestUnregisterNonExistentCallback(service_)); +} + +template +AssertionResult IsEnum(T value) { + for (auto it : hidl_enum_range()) { + if (it == value) { + return AssertionSuccess(); + } + } + + return AssertionFailure() << static_cast>(value) << " is not valid"; +} + +/* + * Tests the values returned by getHealthInfo() from interface IHealth. + */ +TEST_P(HealthHidlTest, getHealthInfo_2_1) { + EXPECT_TRUE(IsOk(service_->getHealthInfo_2_1([](auto result, const auto& value) { + if (result == Result::NOT_SUPPORTED) { + return; + } + ASSERT_EQ(Result::SUCCESS, result); + const auto& legacy = value.legacy.legacy; + + EXPECT_TRUE(IsEnum(value.batteryCapacityLevel)) << " BatteryCapacityLevel"; + EXPECT_GE(value.batteryChargeTimeToFullNowSeconds, 0); + + EXPECT_GE(value.batteryFullCapacityUah, 0) << "batteryFullCapacityUah is unknown"; + EXPECT_GE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 0.50); + EXPECT_LE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 1.20); + }))); +} + +TEST_P(HealthHidlTest, getHealthConfig) { + EXPECT_TRUE(IsOk(service_->getHealthConfig([](auto result, const auto&) { + EXPECT_THAT(result, AnyOf(Result::SUCCESS, Result::NOT_SUPPORTED)); + }))); +} + +TEST_P(HealthHidlTest, shouldKeepScreenOn) { + EXPECT_TRUE(IsOk(service_->shouldKeepScreenOn([](auto result, const auto&) { + EXPECT_THAT(result, AnyOf(Result::SUCCESS, Result::NOT_SUPPORTED)); + }))); +} + +INSTANTIATE_TEST_SUITE_P( + , HealthHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHealth::descriptor)), + android::hardware::PrintInstanceNameToString); + +} // namespace V2_1 +} // namespace health +} // namespace hardware +} // namespace android + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +}