Create loader for Power HAL services
Move logic to load underlying Power HAL services to a static class in
services/powermanager, with tests. This can now be reused by
BatteryStatsService and PowerManagerService, removing the dependency
between them.
Bug: 150878220
Test: atest powermanager_test
Change-Id: I52eec130811d49111526f3e152faf4302518c5a8
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
new file mode 100644
index 0000000..487b95b
--- /dev/null
+++ b/include/powermanager/PowerHalLoader.h
@@ -0,0 +1,52 @@
+/*
+ * 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_POWERHALLOADER_H
+#define ANDROID_POWERHALLOADER_H
+
+#include <android-base/thread_annotations.h>
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerAidl = android::hardware::power::IPower;
+
+namespace android {
+
+// Loads available Power HAL services.
+class PowerHalLoader {
+public:
+ static void unloadAll();
+ static sp<IPowerAidl> loadAidl();
+ static sp<IPowerV1_0> loadHidlV1_0();
+ static sp<IPowerV1_1> loadHidlV1_1();
+
+private:
+ static std::mutex gHalMutex;
+ static sp<IPowerAidl> gHalAidl GUARDED_BY(gHalMutex);
+ static sp<IPowerV1_0> gHalHidlV1_0 GUARDED_BY(gHalMutex);
+ static sp<IPowerV1_1> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+
+ static sp<IPowerV1_0> loadHidlV1_0Locked() EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+
+ PowerHalLoader() = default;
+};
+
+} // namespace android
+
+#endif // ANDROID_POWERHALLOADER_H
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index cff4a02..eaf7fa8 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -4,6 +4,7 @@
srcs: [
"BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
+ "PowerHalLoader.cpp",
"PowerHalWrapper.cpp",
"PowerSaveState.cpp",
"Temperature.cpp",
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
new file mode 100644
index 0000000..3ae5384
--- /dev/null
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "PowerHalLoader"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+
+#include <powermanager/PowerHalLoader.h>
+
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+using IPowerAidl = android::hardware::power::IPower;
+
+namespace android {
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T, typename F>
+sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) {
+ if (!exists) {
+ return nullptr;
+ }
+ if (hal) {
+ return hal;
+ }
+ hal = loadFn();
+ if (hal) {
+ ALOGV("Successfully connected to Power HAL %s service.", halName);
+ } else {
+ ALOGV("Power HAL %s service not available.", halName);
+ exists = false;
+ }
+ return hal;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::mutex PowerHalLoader::gHalMutex;
+sp<IPowerAidl> PowerHalLoader::gHalAidl = nullptr;
+sp<IPowerV1_0> PowerHalLoader::gHalHidlV1_0 = nullptr;
+sp<IPowerV1_1> PowerHalLoader::gHalHidlV1_1 = nullptr;
+
+void PowerHalLoader::unloadAll() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ gHalAidl = nullptr;
+ gHalHidlV1_0 = nullptr;
+ gHalHidlV1_1 = nullptr;
+}
+
+sp<IPowerAidl> PowerHalLoader::loadAidl() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return waitForVintfService<IPowerAidl>(); };
+ return loadHal<IPowerAidl>(gHalExists, gHalAidl, loadFn, "AIDL");
+}
+
+sp<IPowerV1_0> PowerHalLoader::loadHidlV1_0() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ return loadHidlV1_0Locked();
+}
+
+sp<IPowerV1_1> PowerHalLoader::loadHidlV1_1() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return IPowerV1_1::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<IPowerV1_1>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
+}
+
+sp<IPowerV1_0> PowerHalLoader::loadHidlV1_0Locked() {
+ static bool gHalExists = true;
+ static auto loadFn = []() { return IPowerV1_0::getService(); };
+ return loadHal<IPowerV1_0>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace android
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 65cde030..1cf170e 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+
cc_test {
name: "powermanager_test",
test_suites: ["device-tests"],
srcs: [
+ "PowerHalLoaderTest.cpp",
"PowerHalWrapperAidlTest.cpp",
"PowerHalWrapperHidlV1_0Test.cpp",
"PowerHalWrapperHidlV1_1Test.cpp",
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
new file mode 100644
index 0000000..2310a72
--- /dev/null
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "PowerHalLoaderTest"
+
+#include <android-base/logging.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+
+#include <future>
+#include <gtest/gtest.h>
+
+#include <powermanager/PowerHalLoader.h>
+
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerAidl = android::hardware::power::IPower;
+
+using namespace android;
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+sp<T> loadHal();
+
+template <>
+sp<IPowerAidl> loadHal<IPowerAidl>() {
+ return PowerHalLoader::loadAidl();
+}
+
+template <>
+sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+ return PowerHalLoader::loadHidlV1_0();
+}
+
+template <>
+sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+ return PowerHalLoader::loadHidlV1_1();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+class PowerHalLoaderTest : public testing::Test {
+public:
+ sp<T> load() {
+ return ::loadHal<T>();
+ }
+ void unload() {
+ PowerHalLoader::unloadAll();
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_EQ(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestUnload) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ this->unload();
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_NE(secondHal, nullptr);
+ ASSERT_NE(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
+ std::vector<std::future<sp<TypeParam>>> futures;
+ for (int i = 0; i < 10; i++) {
+ futures.push_back(
+ std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
+ }
+
+ futures[0].wait();
+ sp<TypeParam> firstHal = futures[0].get();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+
+ for (int i = 1; i < 10; i++) {
+ futures[i].wait();
+ sp<TypeParam> currentHal = futures[i].get();
+ ASSERT_EQ(firstHal, currentHal);
+ }
+}