Loading include/powermanager/PowerHalController.h 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ #ifndef ANDROID_POWERHALCONTROLLER_H #define ANDROID_POWERHALCONTROLLER_H #include <android-base/thread_annotations.h> #include <android/hardware/power/Boost.h> #include <android/hardware/power/IPower.h> #include <android/hardware/power/Mode.h> #include <powermanager/PowerHalWrapper.h> using android::hardware::power::Boost; using android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::PowerHint; namespace android { // ------------------------------------------------------------------------------------------------- // Connects to underlying Power HAL handles. class PowerHalConnector { public: PowerHalConnector() = default; virtual ~PowerHalConnector() = default; virtual std::unique_ptr<PowerHalWrapper> connect(); virtual void reset(); }; // ------------------------------------------------------------------------------------------------- // Controller for Power HAL handle. // This relies on PowerHalConnector to connect to the underlying Power HAL service and reconnects to // it after each failed api call. This also ensures connecting to the service is thread-safe. class PowerHalController : public PowerHalWrapper { public: PowerHalController() : PowerHalController(std::make_unique<PowerHalConnector>()) {} explicit PowerHalController(std::unique_ptr<PowerHalConnector> connector) : mHalConnector(std::move(connector)) {} void init(); PowerHalResult setBoost(Boost boost, int32_t durationMs) override; PowerHalResult setMode(Mode mode, bool enabled) override; private: std::mutex mConnectedHalMutex; std::unique_ptr<PowerHalConnector> mHalConnector; // Shared pointers to keep global pointer and allow local copies to be used in different threads std::shared_ptr<PowerHalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr; const std::shared_ptr<PowerHalWrapper> mDefaultHal = std::make_shared<EmptyPowerHalWrapper>(); std::shared_ptr<PowerHalWrapper> initHal(); PowerHalResult processHalResult(PowerHalResult result, const char* functionName); }; // ------------------------------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_POWERHALCONTROLLER_H services/powermanager/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ cc_library_shared { srcs: [ "BatterySaverPolicyConfig.cpp", "CoolingDevice.cpp", "PowerHalController.cpp", "PowerHalLoader.cpp", "PowerHalWrapper.cpp", "PowerSaveState.cpp", Loading services/powermanager/PowerHalController.cpp 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 "PowerHalController" #include <utils/Log.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/Boost.h> #include <android/hardware/power/IPower.h> #include <android/hardware/power/Mode.h> #include <powermanager/PowerHalController.h> #include <powermanager/PowerHalLoader.h> using android::hardware::power::Boost; using android::hardware::power::Mode; namespace android { // ------------------------------------------------------------------------------------------------- std::unique_ptr<PowerHalWrapper> PowerHalConnector::connect() { sp<IPowerAidl> halAidl = PowerHalLoader::loadAidl(); if (halAidl) { return std::make_unique<AidlPowerHalWrapper>(halAidl); } sp<IPowerV1_0> halHidlV1_0 = PowerHalLoader::loadHidlV1_0(); sp<IPowerV1_1> halHidlV1_1 = PowerHalLoader::loadHidlV1_1(); if (halHidlV1_1) { return std::make_unique<HidlPowerHalWrapperV1_1>(halHidlV1_0, halHidlV1_1); } if (halHidlV1_0) { return std::make_unique<HidlPowerHalWrapperV1_0>(halHidlV1_0); } return nullptr; } void PowerHalConnector::reset() { PowerHalLoader::unloadAll(); } // ------------------------------------------------------------------------------------------------- void PowerHalController::init() { initHal(); } // Check validity of current handle to the power HAL service, and create a new one if necessary. std::shared_ptr<PowerHalWrapper> PowerHalController::initHal() { std::lock_guard<std::mutex> lock(mConnectedHalMutex); if (mConnectedHal == nullptr) { mConnectedHal = mHalConnector->connect(); if (mConnectedHal == nullptr) { // Unable to connect to Power HAL service. Fallback to default. return mDefaultHal; } } return mConnectedHal; } // Check if a call to Power HAL function failed; if so, log the failure and invalidate the // current Power HAL handle. PowerHalResult PowerHalController::processHalResult(PowerHalResult result, const char* fnName) { if (result == PowerHalResult::FAILED) { ALOGE("%s() failed: power HAL service not available.", fnName); std::lock_guard<std::mutex> lock(mConnectedHalMutex); // Drop Power HAL handle. This will force future api calls to reconnect. mConnectedHal = nullptr; mHalConnector->reset(); } return result; } PowerHalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) { std::shared_ptr<PowerHalWrapper> handle = initHal(); auto result = handle->setBoost(boost, durationMs); return processHalResult(result, "setBoost"); } PowerHalResult PowerHalController::setMode(Mode mode, bool enabled) { std::shared_ptr<PowerHalWrapper> handle = initHal(); auto result = handle->setMode(mode, enabled); return processHalResult(result, "setMode"); } }; // namespace android services/powermanager/tests/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. cc_test { name: "powermanager_test", test_suites: ["device-tests"], srcs: [ "PowerHalControllerTest.cpp", "PowerHalLoaderTest.cpp", "PowerHalWrapperAidlTest.cpp", "PowerHalWrapperHidlV1_0Test.cpp", Loading services/powermanager/tests/PowerHalControllerTest.cpp 0 → 100644 +287 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 "PowerHalControllerTest" #include <android/hardware/power/Boost.h> #include <android/hardware/power/IPower.h> #include <android/hardware/power/Mode.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalWrapper.h> #include <powermanager/PowerHalController.h> #include <thread> #include <utils/Log.h> using android::hardware::power::Boost; using android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::IPower; using android::hardware::power::V1_0::PowerHint; using namespace android; using namespace std::chrono_literals; using namespace testing; // ------------------------------------------------------------------------------------------------- class MockIPowerV1_0 : public IPower { public: MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); MOCK_METHOD( hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); MOCK_METHOD( hardware::Return<void>, getPlatformLowPowerStats, (getPlatformLowPowerStats_cb _hidl_cb), (override)); }; class TestPowerHalConnector : public PowerHalConnector { public: TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {} virtual ~TestPowerHalConnector() = default; virtual std::unique_ptr<PowerHalWrapper> connect() override { mCountMutex.lock(); ++mConnectedCount; mCountMutex.unlock(); return std::make_unique<HidlPowerHalWrapperV1_0>(mHal); } void reset() override { mCountMutex.lock(); ++mResetCount; mCountMutex.unlock(); } int getConnectCount() { return mConnectedCount; } int getResetCount() { return mResetCount; } private: sp<IPower> mHal = nullptr; std::mutex mCountMutex; int mConnectedCount = 0; int mResetCount = 0; }; class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector { public: AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {} std::unique_ptr<PowerHalWrapper> connect() override { // Call parent to update counter, but ignore connected PowerHalWrapper. TestPowerHalConnector::connect(); return nullptr; } }; // ------------------------------------------------------------------------------------------------- class PowerHalControllerTest : public Test { public: void SetUp() override { mMockHal = new StrictMock<MockIPowerV1_0>(); std::unique_ptr<TestPowerHalConnector> halConnector = std::make_unique<TestPowerHalConnector>(mMockHal); mHalConnector = halConnector.get(); mHalController = std::make_unique<PowerHalController>(std::move(halConnector)); } protected: sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr; TestPowerHalConnector* mHalConnector = nullptr; std::unique_ptr<PowerHalController> mHalController = nullptr; }; // ------------------------------------------------------------------------------------------------- TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); mHalController->init(); mHalController->init(); // PowerHalConnector was called only once and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) { std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector = std::make_unique<AlwaysFailingTestPowerHalConnector>(); AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get(); PowerHalController halController(std::move(halConnector)); int powerHalConnectCount = failingHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); // Still works with EmptyPowerHalWrapper as fallback ignoring every api call and logging. auto result = halController.setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); result = halController.setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); // PowerHalConnector was called every time to attempt to reconnect with underlying service. powerHalConnectCount = failingHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 2); // PowerHalConnector was never reset. int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); { InSequence seg; EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100))) .Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))) .Times(Exactly(1)); } auto result = mHalController->setBoost(Boost::INTERACTION, 100); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); result = mHalController->setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); // PowerHalConnector was called only once and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) .WillByDefault([](PowerHint, int32_t) { return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); }); EXPECT_CALL(*mMockHal.get(), powerHint(_, _)) .Times(Exactly(4)); auto result = mHalController->setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); result = mHalController->setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::FAILED, result); result = mHalController->setMode(Mode::VR, false); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); result = mHalController->setMode(Mode::LOW_POWER, true); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); // PowerHalConnector was called only twice: on first api call and after failed call. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 2); // PowerHalConnector was reset once after failed call. int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 1); } TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); // PowerHalConnector was called only once and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); EXPECT_CALL(*mMockHal.get(), powerHint(_, _)) .Times(Exactly(10)); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { threads.push_back(std::thread([&]() { auto result = mHalController->setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); } std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); // PowerHalConnector was called only by the first thread to use the api and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) .WillByDefault([](PowerHint, int32_t) { return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); }); EXPECT_CALL(*mMockHal.get(), powerHint(_, _)) .Times(Exactly(40)); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { threads.push_back(std::thread([&]() { auto result = mHalController->setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); threads.push_back(std::thread([&]() { auto result = mHalController->setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::FAILED, result); })); threads.push_back(std::thread([&]() { auto result = mHalController->setMode(Mode::LOW_POWER, false); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); threads.push_back(std::thread([&]() { auto result = mHalController->setMode(Mode::VR, true); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); } std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); // PowerHalConnector was called at least once by the first thread. // Reset and reconnect calls were made at most 10 times, once after each failure. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11))); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_THAT(powerHalResetCount, Le(10)); } Loading
include/powermanager/PowerHalController.h 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ #ifndef ANDROID_POWERHALCONTROLLER_H #define ANDROID_POWERHALCONTROLLER_H #include <android-base/thread_annotations.h> #include <android/hardware/power/Boost.h> #include <android/hardware/power/IPower.h> #include <android/hardware/power/Mode.h> #include <powermanager/PowerHalWrapper.h> using android::hardware::power::Boost; using android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::PowerHint; namespace android { // ------------------------------------------------------------------------------------------------- // Connects to underlying Power HAL handles. class PowerHalConnector { public: PowerHalConnector() = default; virtual ~PowerHalConnector() = default; virtual std::unique_ptr<PowerHalWrapper> connect(); virtual void reset(); }; // ------------------------------------------------------------------------------------------------- // Controller for Power HAL handle. // This relies on PowerHalConnector to connect to the underlying Power HAL service and reconnects to // it after each failed api call. This also ensures connecting to the service is thread-safe. class PowerHalController : public PowerHalWrapper { public: PowerHalController() : PowerHalController(std::make_unique<PowerHalConnector>()) {} explicit PowerHalController(std::unique_ptr<PowerHalConnector> connector) : mHalConnector(std::move(connector)) {} void init(); PowerHalResult setBoost(Boost boost, int32_t durationMs) override; PowerHalResult setMode(Mode mode, bool enabled) override; private: std::mutex mConnectedHalMutex; std::unique_ptr<PowerHalConnector> mHalConnector; // Shared pointers to keep global pointer and allow local copies to be used in different threads std::shared_ptr<PowerHalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr; const std::shared_ptr<PowerHalWrapper> mDefaultHal = std::make_shared<EmptyPowerHalWrapper>(); std::shared_ptr<PowerHalWrapper> initHal(); PowerHalResult processHalResult(PowerHalResult result, const char* functionName); }; // ------------------------------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_POWERHALCONTROLLER_H
services/powermanager/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ cc_library_shared { srcs: [ "BatterySaverPolicyConfig.cpp", "CoolingDevice.cpp", "PowerHalController.cpp", "PowerHalLoader.cpp", "PowerHalWrapper.cpp", "PowerSaveState.cpp", Loading
services/powermanager/PowerHalController.cpp 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 "PowerHalController" #include <utils/Log.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/Boost.h> #include <android/hardware/power/IPower.h> #include <android/hardware/power/Mode.h> #include <powermanager/PowerHalController.h> #include <powermanager/PowerHalLoader.h> using android::hardware::power::Boost; using android::hardware::power::Mode; namespace android { // ------------------------------------------------------------------------------------------------- std::unique_ptr<PowerHalWrapper> PowerHalConnector::connect() { sp<IPowerAidl> halAidl = PowerHalLoader::loadAidl(); if (halAidl) { return std::make_unique<AidlPowerHalWrapper>(halAidl); } sp<IPowerV1_0> halHidlV1_0 = PowerHalLoader::loadHidlV1_0(); sp<IPowerV1_1> halHidlV1_1 = PowerHalLoader::loadHidlV1_1(); if (halHidlV1_1) { return std::make_unique<HidlPowerHalWrapperV1_1>(halHidlV1_0, halHidlV1_1); } if (halHidlV1_0) { return std::make_unique<HidlPowerHalWrapperV1_0>(halHidlV1_0); } return nullptr; } void PowerHalConnector::reset() { PowerHalLoader::unloadAll(); } // ------------------------------------------------------------------------------------------------- void PowerHalController::init() { initHal(); } // Check validity of current handle to the power HAL service, and create a new one if necessary. std::shared_ptr<PowerHalWrapper> PowerHalController::initHal() { std::lock_guard<std::mutex> lock(mConnectedHalMutex); if (mConnectedHal == nullptr) { mConnectedHal = mHalConnector->connect(); if (mConnectedHal == nullptr) { // Unable to connect to Power HAL service. Fallback to default. return mDefaultHal; } } return mConnectedHal; } // Check if a call to Power HAL function failed; if so, log the failure and invalidate the // current Power HAL handle. PowerHalResult PowerHalController::processHalResult(PowerHalResult result, const char* fnName) { if (result == PowerHalResult::FAILED) { ALOGE("%s() failed: power HAL service not available.", fnName); std::lock_guard<std::mutex> lock(mConnectedHalMutex); // Drop Power HAL handle. This will force future api calls to reconnect. mConnectedHal = nullptr; mHalConnector->reset(); } return result; } PowerHalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) { std::shared_ptr<PowerHalWrapper> handle = initHal(); auto result = handle->setBoost(boost, durationMs); return processHalResult(result, "setBoost"); } PowerHalResult PowerHalController::setMode(Mode mode, bool enabled) { std::shared_ptr<PowerHalWrapper> handle = initHal(); auto result = handle->setMode(mode, enabled); return processHalResult(result, "setMode"); } }; // namespace android
services/powermanager/tests/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. cc_test { name: "powermanager_test", test_suites: ["device-tests"], srcs: [ "PowerHalControllerTest.cpp", "PowerHalLoaderTest.cpp", "PowerHalWrapperAidlTest.cpp", "PowerHalWrapperHidlV1_0Test.cpp", Loading
services/powermanager/tests/PowerHalControllerTest.cpp 0 → 100644 +287 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 "PowerHalControllerTest" #include <android/hardware/power/Boost.h> #include <android/hardware/power/IPower.h> #include <android/hardware/power/Mode.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalWrapper.h> #include <powermanager/PowerHalController.h> #include <thread> #include <utils/Log.h> using android::hardware::power::Boost; using android::hardware::power::Mode; using android::hardware::power::V1_0::Feature; using android::hardware::power::V1_0::IPower; using android::hardware::power::V1_0::PowerHint; using namespace android; using namespace std::chrono_literals; using namespace testing; // ------------------------------------------------------------------------------------------------- class MockIPowerV1_0 : public IPower { public: MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); MOCK_METHOD( hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); MOCK_METHOD( hardware::Return<void>, getPlatformLowPowerStats, (getPlatformLowPowerStats_cb _hidl_cb), (override)); }; class TestPowerHalConnector : public PowerHalConnector { public: TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {} virtual ~TestPowerHalConnector() = default; virtual std::unique_ptr<PowerHalWrapper> connect() override { mCountMutex.lock(); ++mConnectedCount; mCountMutex.unlock(); return std::make_unique<HidlPowerHalWrapperV1_0>(mHal); } void reset() override { mCountMutex.lock(); ++mResetCount; mCountMutex.unlock(); } int getConnectCount() { return mConnectedCount; } int getResetCount() { return mResetCount; } private: sp<IPower> mHal = nullptr; std::mutex mCountMutex; int mConnectedCount = 0; int mResetCount = 0; }; class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector { public: AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {} std::unique_ptr<PowerHalWrapper> connect() override { // Call parent to update counter, but ignore connected PowerHalWrapper. TestPowerHalConnector::connect(); return nullptr; } }; // ------------------------------------------------------------------------------------------------- class PowerHalControllerTest : public Test { public: void SetUp() override { mMockHal = new StrictMock<MockIPowerV1_0>(); std::unique_ptr<TestPowerHalConnector> halConnector = std::make_unique<TestPowerHalConnector>(mMockHal); mHalConnector = halConnector.get(); mHalController = std::make_unique<PowerHalController>(std::move(halConnector)); } protected: sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr; TestPowerHalConnector* mHalConnector = nullptr; std::unique_ptr<PowerHalController> mHalController = nullptr; }; // ------------------------------------------------------------------------------------------------- TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); mHalController->init(); mHalController->init(); // PowerHalConnector was called only once and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) { std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector = std::make_unique<AlwaysFailingTestPowerHalConnector>(); AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get(); PowerHalController halController(std::move(halConnector)); int powerHalConnectCount = failingHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); // Still works with EmptyPowerHalWrapper as fallback ignoring every api call and logging. auto result = halController.setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); result = halController.setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); // PowerHalConnector was called every time to attempt to reconnect with underlying service. powerHalConnectCount = failingHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 2); // PowerHalConnector was never reset. int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); { InSequence seg; EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100))) .Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))) .Times(Exactly(1)); } auto result = mHalController->setBoost(Boost::INTERACTION, 100); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); result = mHalController->setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); // PowerHalConnector was called only once and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) .WillByDefault([](PowerHint, int32_t) { return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); }); EXPECT_CALL(*mMockHal.get(), powerHint(_, _)) .Times(Exactly(4)); auto result = mHalController->setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); result = mHalController->setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::FAILED, result); result = mHalController->setMode(Mode::VR, false); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); result = mHalController->setMode(Mode::LOW_POWER, true); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); // PowerHalConnector was called only twice: on first api call and after failed call. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 2); // PowerHalConnector was reset once after failed call. int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 1); } TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true); ASSERT_EQ(PowerHalResult::UNSUPPORTED, result); // PowerHalConnector was called only once and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); EXPECT_CALL(*mMockHal.get(), powerHint(_, _)) .Times(Exactly(10)); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { threads.push_back(std::thread([&]() { auto result = mHalController->setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); } std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); // PowerHalConnector was called only by the first thread to use the api and never reset. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 1); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_EQ(powerHalResetCount, 0); } TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) { int powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_EQ(powerHalConnectCount, 0); ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) .WillByDefault([](PowerHint, int32_t) { return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); }); EXPECT_CALL(*mMockHal.get(), powerHint(_, _)) .Times(Exactly(40)); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { threads.push_back(std::thread([&]() { auto result = mHalController->setBoost(Boost::INTERACTION, 1000); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); threads.push_back(std::thread([&]() { auto result = mHalController->setMode(Mode::LAUNCH, true); ASSERT_EQ(PowerHalResult::FAILED, result); })); threads.push_back(std::thread([&]() { auto result = mHalController->setMode(Mode::LOW_POWER, false); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); threads.push_back(std::thread([&]() { auto result = mHalController->setMode(Mode::VR, true); ASSERT_EQ(PowerHalResult::SUCCESSFUL, result); })); } std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); // PowerHalConnector was called at least once by the first thread. // Reset and reconnect calls were made at most 10 times, once after each failure. powerHalConnectCount = mHalConnector->getConnectCount(); EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11))); int powerHalResetCount = mHalConnector->getResetCount(); EXPECT_THAT(powerHalResetCount, Le(10)); }