Merge "Create controller for Power HAL handles."
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
new file mode 100644
index 0000000..44b8915
--- /dev/null
+++ b/include/powermanager/PowerHalController.h
@@ -0,0 +1,78 @@
+/*
+ * 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
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index eaf7fa8..ec3dfc8 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -4,6 +4,7 @@
srcs: [
"BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
+ "PowerHalController.cpp",
"PowerHalLoader.cpp",
"PowerHalWrapper.cpp",
"PowerSaveState.cpp",
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
new file mode 100644
index 0000000..d26d582
--- /dev/null
+++ b/services/powermanager/PowerHalController.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 1cf170e..70837cf 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -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",
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
new file mode 100644
index 0000000..ac1e19a
--- /dev/null
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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));
+}