Introduce controller for VibratorManager HAL
This controller will automatically reconnect to IVibratorManager when
HAL service fails, and also falls back to legacy VibratorManager when
the HAL service is not available on the device.
This change also simplifies the HalConnector logic in both vibrator
controllers.
Bug: 167946816
Test: atest libvibratorservice_test
Change-Id: I709227cbb2fb2e1a0fcac32676182d6c9baded88
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index e8606ca..bcd9957 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -46,7 +46,7 @@
// -------------------------------------------------------------------------------------------------
-std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
+std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler) {
static bool gHalExists = true;
if (!gHalExists) {
// We already tried to connect to all of the vibrator HAL versions and none was available.
@@ -106,7 +106,7 @@
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
if (mConnectedHal == nullptr) {
// Init was never called, so connect to HAL for the first time during this call.
- mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ mConnectedHal = mConnector(mCallbackScheduler);
if (mConnectedHal == nullptr) {
ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
@@ -129,7 +129,7 @@
bool HalController::init() {
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
if (mConnectedHal == nullptr) {
- mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ mConnectedHal = mConnector(mCallbackScheduler);
}
return mConnectedHal != nullptr;
}
@@ -142,7 +142,7 @@
void HalController::tryReconnect() {
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
if (mConnectedHal == nullptr) {
- mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ mConnectedHal = mConnector(mCallbackScheduler);
} else {
mConnectedHal->tryReconnect();
}
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
new file mode 100644
index 0000000..b24e5c4
--- /dev/null
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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 "VibratorManagerHalController"
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalController.h>
+
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+std::shared_ptr<ManagerHalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler) {
+ static bool gHalExists = true;
+ if (gHalExists) {
+ sp<Aidl::IVibratorManager> hal = waitForVintfService<Aidl::IVibratorManager>();
+ if (hal) {
+ ALOGV("Successfully connected to VibratorManager HAL AIDL service.");
+ return std::make_shared<AidlManagerHalWrapper>(std::move(scheduler), aidlHal);
+ }
+ }
+
+ gHalExists = false;
+ return std::make_shared<LegacyManagerHalWrapper>();
+}
+
+static constexpr int MAX_RETRIES = 1;
+
+template <typename T>
+HalResult<T> ManagerHalController::processHalResult(HalResult<T> result, const char* functionName) {
+ if (result.isFailed()) {
+ ALOGE("%s failed: %s", functionName, result.errorMessage());
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ mConnectedHal->tryReconnect();
+ }
+ return result;
+}
+
+template <typename T>
+HalResult<T> ManagerHalController::apply(ManagerHalController::hal_fn<T>& halFn,
+ const char* functionName) {
+ std::shared_ptr<ManagerHalWrapper> hal = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ // Init was never called, so connect to HAL for the first time during this call.
+ mConnectedHal = mConnector(mCallbackScheduler);
+
+ if (mConnectedHal == nullptr) {
+ ALOGV("Skipped %s because VibratorManager HAL is not available", functionName);
+ return HalResult<T>::unsupported();
+ }
+ }
+ hal = mConnectedHal;
+ }
+
+ HalResult<T> ret = processHalResult(halFn(hal), functionName);
+ for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
+ ret = processHalResult(halFn(hal), functionName);
+ }
+
+ return ret;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+bool ManagerHalController::init() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mConnector(mCallbackScheduler);
+ }
+ return mConnectedHal != nullptr;
+}
+
+HalResult<void> ManagerHalController::ping() {
+ hal_fn<void> pingFn = [](std::shared_ptr<ManagerHalWrapper> hal) { return hal->ping(); };
+ return apply(pingFn, "ping");
+}
+
+void ManagerHalController::tryReconnect() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mConnector(mCallbackScheduler);
+ } else {
+ mConnectedHal->tryReconnect();
+ }
+}
+
+HalResult<ManagerCapabilities> ManagerHalController::getCapabilities() {
+ hal_fn<ManagerCapabilities> getCapabilitiesFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->getCapabilities();
+ };
+ return apply(getCapabilitiesFn, "getCapabilities");
+}
+
+HalResult<std::vector<int32_t>> ManagerHalController::getVibratorIds() {
+ hal_fn<std::vector<int32_t>> getVibratorIdsFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->getVibratorIds();
+ };
+ return apply(getVibratorIdsFn, "getVibratorIds");
+}
+
+HalResult<std::shared_ptr<HalController>> ManagerHalController::getVibrator(int32_t id) {
+ hal_fn<std::shared_ptr<HalController>> getVibratorFn =
+ [&](std::shared_ptr<ManagerHalWrapper> hal) { return hal->getVibrator(id); };
+ return apply(getVibratorFn, "getVibrator");
+}
+
+HalResult<void> ManagerHalController::prepareSynced(const std::vector<int32_t>& ids) {
+ hal_fn<void> prepareSyncedFn = [&](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->prepareSynced(ids);
+ };
+ return apply(prepareSyncedFn, "prepareSynced");
+}
+
+HalResult<void> ManagerHalController::triggerSynced(
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> triggerSyncedFn = [&](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->triggerSynced(completionCallback);
+ };
+ return apply(triggerSyncedFn, "triggerSynced");
+}
+
+HalResult<void> ManagerHalController::cancelSynced() {
+ hal_fn<void> cancelSyncedFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->cancelSynced();
+ };
+ return apply(cancelSyncedFn, "cancelSynced");
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 9c4166c..8a08e5b 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -72,11 +72,11 @@
// -------------------------------------------------------------------------------------------------
-std::shared_ptr<HalWrapper> AidlManagerHalWrapper::ManagedHalConnector::connect(
- std::shared_ptr<CallbackScheduler> callbackScheduler) {
- std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [&]() {
+std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
+ int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
+ std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=]() {
sp<Aidl::IVibrator> vibrator;
- auto result = this->mManager->getHal()->getVibrator(this->mVibratorId, &vibrator);
+ auto result = this->getHal()->getVibrator(vibratorId, &vibrator);
return HalResult<sp<Aidl::IVibrator>>::fromStatus(result, vibrator);
};
auto result = reconnectFn();
@@ -133,9 +133,10 @@
// Cache copy of returned value and the individual controllers.
mVibratorIds.emplace(ret.value());
for (auto& id : ids) {
- auto connector = std::make_unique<ManagedHalConnector>(this, id);
- auto controller =
- std::make_unique<HalController>(std::move(connector), mCallbackScheduler);
+ HalController::Connector connector = [&, id](auto scheduler) {
+ return this->connectToVibrator(id, scheduler);
+ };
+ auto controller = std::make_unique<HalController>(mCallbackScheduler, connector);
mVibrators[id] = std::move(controller);
}
}
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index d1028a4..c405545 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -27,27 +27,20 @@
namespace vibrator {
-// Handles the connection to he underlying HAL implementation available.
-class HalConnector {
-public:
- HalConnector() = default;
- virtual ~HalConnector() = default;
-
- virtual std::shared_ptr<HalWrapper> connect(std::shared_ptr<CallbackScheduler> scheduler);
-};
+std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler);
// Controller for Vibrator HAL handle.
-// This relies on HalConnector to connect to the underlying Vibrator HAL service and reconnects to
-// it after each failed api call. This also ensures connecting to the service is thread-safe.
+// This relies on a given Connector to connect to the underlying Vibrator HAL service and reconnects
+// after each failed api call. This also ensures connecting to the service is thread-safe.
class HalController : public HalWrapper {
public:
- HalController()
- : HalController(std::make_unique<HalConnector>(), std::make_shared<CallbackScheduler>()) {
- }
- HalController(std::unique_ptr<HalConnector> halConnector,
- std::shared_ptr<CallbackScheduler> callbackScheduler)
+ using Connector =
+ std::function<std::shared_ptr<HalWrapper>(std::shared_ptr<CallbackScheduler>)>;
+
+ HalController() : HalController(std::make_shared<CallbackScheduler>(), &connectHal) {}
+ HalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector)
: HalWrapper(std::move(callbackScheduler)),
- mHalConnector(std::move(halConnector)),
+ mConnector(connector),
mConnectedHal(nullptr) {}
virtual ~HalController() = default;
@@ -89,7 +82,7 @@
const std::function<void()>& completionCallback) final override;
private:
- std::unique_ptr<HalConnector> mHalConnector;
+ Connector mConnector;
std::mutex mConnectedHalMutex;
// Shared pointer to allow local copies to be used by different threads.
std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
new file mode 100644
index 0000000..cf82562
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
@@ -0,0 +1,85 @@
+/*
+ * 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_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
+#define ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
+
+#include <android/hardware/vibrator/IVibratorManager.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <unordered_map>
+
+namespace android {
+
+namespace vibrator {
+
+std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler);
+
+// Controller for VibratorManager HAL handle.
+class ManagerHalController : public ManagerHalWrapper {
+public:
+ using Connector =
+ std::function<std::shared_ptr<ManagerHalWrapper>(std::shared_ptr<CallbackScheduler>)>;
+
+ ManagerHalController()
+ : ManagerHalController(std::make_shared<CallbackScheduler>(), &connectManagerHal) {}
+ ManagerHalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector)
+ : mConnector(connector), mConnectedHal(nullptr) {}
+ virtual ~ManagerHalController() = default;
+
+ /* Connects to the HAL service, possibly waiting for the registered service to
+ * become available. This will automatically be called at the first API usage if it was not
+ * manually called beforehand. Calling this manually during the setup phase can avoid slowing
+ * the first API call later on. This will fallback to a legacy manager implementation if the
+ * service is not available.
+ */
+ virtual void init();
+
+ /* reloads HAL service instance without waiting. This relies on the HAL found by init()
+ * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
+ */
+ void tryReconnect() override final;
+
+ HalResult<void> ping() override final;
+
+ HalResult<ManagerCapabilities> getCapabilities() override final;
+ HalResult<std::vector<int32_t>> getVibratorIds() override final;
+ HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
+
+ HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
+ HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
+ HalResult<void> cancelSynced() override final;
+
+private:
+ Connector mConnector;
+ std::mutex mConnectedHalMutex;
+ // Shared pointer to allow local copies to be used by different threads.
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+
+ template <typename T>
+ HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+
+ template <typename T>
+ using hal_fn = std::function<HalResult<T>(std::shared_ptr<ManagerHalWrapper>)>;
+
+ template <typename T>
+ HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
index 309d681..563f55e 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -134,20 +134,8 @@
std::shared_ptr<CallbackScheduler> mCallbackScheduler;
sp<hardware::vibrator::IVibratorManager> getHal();
-
- // Connector that creates a HalWrapper from an IVibrator loaded from IVibratorManager.
- class ManagedHalConnector : public HalConnector {
- public:
- ManagedHalConnector(AidlManagerHalWrapper* manager, int32_t vibratorId)
- : mManager(manager), mVibratorId(vibratorId) {}
- ~ManagedHalConnector() = default;
-
- std::shared_ptr<HalWrapper> connect(std::shared_ptr<CallbackScheduler>) override final;
-
- private:
- AidlManagerHalWrapper* mManager;
- const int32_t mVibratorId;
- };
+ std::shared_ptr<HalWrapper> connectToVibrator(int32_t vibratorId,
+ std::shared_ptr<CallbackScheduler> scheduler);
};
}; // namespace vibrator
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index cda5e9a..2d9d0d6 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -79,38 +79,6 @@
vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
};
-class TestHalConnector : public vibrator::HalConnector {
-public:
- TestHalConnector(int32_t* connectCounter, std::shared_ptr<MockHalWrapper> mockHal)
- : mConnectCounter(connectCounter), mMockHal(std::move(mockHal)) {}
- ~TestHalConnector() = default;
-
- std::shared_ptr<vibrator::HalWrapper> connect(
- std::shared_ptr<vibrator::CallbackScheduler>) override final {
- android_atomic_inc(mConnectCounter);
- return mMockHal;
- }
-
-private:
- int32_t* mConnectCounter;
- std::shared_ptr<MockHalWrapper> mMockHal;
-};
-
-class FailingHalConnector : public vibrator::HalConnector {
-public:
- FailingHalConnector(int32_t* connectCounter) : mConnectCounter(connectCounter) {}
- ~FailingHalConnector() = default;
-
- std::shared_ptr<vibrator::HalWrapper> connect(
- std::shared_ptr<vibrator::CallbackScheduler>) override final {
- android_atomic_inc(mConnectCounter);
- return nullptr;
- }
-
-private:
- int32_t* mConnectCounter;
-};
-
// -------------------------------------------------------------------------------------------------
class VibratorHalControllerTest : public Test {
@@ -119,9 +87,12 @@
mConnectCounter = 0;
auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler);
- auto halConnector = std::make_unique<TestHalConnector>(&mConnectCounter, mMockHal);
- mController = std::make_unique<vibrator::HalController>(std::move(halConnector),
- std::move(callbackScheduler));
+ mController = std::make_unique<
+ vibrator::HalController>(std::move(callbackScheduler),
+ [&](std::shared_ptr<vibrator::CallbackScheduler>) {
+ android_atomic_inc(&(this->mConnectCounter));
+ return this->mMockHal;
+ });
ASSERT_NE(mController, nullptr);
}
@@ -334,9 +305,11 @@
}
TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToReconnect) {
- auto failingHalConnector = std::make_unique<FailingHalConnector>(&mConnectCounter);
- mController =
- std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr);
+ mController = std::make_unique<
+ vibrator::HalController>(nullptr, [&](std::shared_ptr<vibrator::CallbackScheduler>) {
+ android_atomic_inc(&(this->mConnectCounter));
+ return nullptr;
+ });
ASSERT_EQ(0, mConnectCounter);
ASSERT_FALSE(mController->init());
diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
new file mode 100644
index 0000000..3b036ee
--- /dev/null
+++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 "VibratorManagerHalControllerTest"
+
+#include <cutils/atomic.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+#include "test_utils.h"
+
+using namespace android;
+using namespace testing;
+
+static constexpr int MAX_ATTEMPTS = 2;
+
+class MockManagerHalWrapper : public vibrator::ManagerHalWrapper {
+public:
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(vibrator::HalResult<int32_t>, getCapabilities, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::vector<int32_t>>, getVibratorIds, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::shared_ptr<vibrator::HalController>>, getVibrator,
+ (int32_t id), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, prepareSynced, (const std::vector<int32_t>& ids),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, triggerSynced,
+ (const std::function<void()>& completionCallback), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override));
+};
+
+class VibratorManagerHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mConnectCounter = 0;
+ auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
+ mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler);
+ mController = std::make_unique<
+ vibrator::HalController>(std::move(callbackScheduler),
+ [&](std::shared_ptr<vibrator::CallbackScheduler>) {
+ android_atomic_inc(&(this->mConnectCounter));
+ return this->mMockHal;
+ });
+ ASSERT_NE(mController, nullptr);
+ }
+
+protected:
+ int32_t mConnectCounter;
+ std::shared_ptr<MockManagerHalWrapper> mMockHal;
+ std::unique_ptr<vibrator::ManagerHalController> mController;
+
+ void setHalExpectations(int32_t cardinality, std::vector<int32_t> ids,
+ vibrator::HalResult<void> voidResult,
+ vibrator::HalResult<vibrator::ManagerCapabilities> capabilitiesResult,
+ vibrator::HalResult<std::vector<int32_t>> idsResult,
+ vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), getCapabilities())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(capabilitiesResult));
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(idsResult));
+ EXPECT_CALL(*mMockHal.get(), getVibrator(_))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(vibratorResult));
+ EXPECT_CALL(*mMockHal.get(), prepareSynced(_))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), triggerSynced(_))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), cancelSynced())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+
+ if (cardinality > 1) {
+ // One reconnection call after each failure.
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * cardinality));
+ }
+ }
+};
+
+TEST_F(VibratorManagerHalControllerTest, TestInit) {
+ ASSERT_TRUE(mController->init());
+ ASSERT_EQ(1, mConnectCounter);
+
+ // Noop when wrapper was already initialized.
+ ASSERT_TRUE(mController->init());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestApiCallsAreForwardedToHal) {
+ std::vector<int32_t> ids;
+ ids.push_back(1);
+ ids.push_back(2);
+
+ setHalExpectations(/* cardinality= */ 1, ids, vibrator::HalResult<void>::ok(),
+ vibrator::HalResult<vibrator::ManagerCapabilities>::ok(
+ vibrator::ManagerCapabilities::SYNC),
+ vibrator::HalResult<std::vector<int32_t>>::ok(ids),
+ vibrator::HalResult<std::shared_ptr<vibrator::HalController>>::ok(nullptr));
+
+ ASSERT_TRUE(mController->ping().isOk());
+
+ auto getCapabilitiesResult = mController->getCapabilities();
+ ASSERT_TRUE(getCapabilitiesResult.isOk());
+ ASSERT_EQ(vibrator::ManagerCapabilities::SYNC, getCapabilitiesResult.value());
+
+ auto getVibratorIdsResult = mController->getVibratorIds();
+ ASSERT_TRUE(getVibratorIdsResult.isOk());
+ ASSERT_EQ(ids, getVibratorIdsResult.value());
+
+ auto getVibratorResult = mController->getVibrator(1);
+ ASSERT_TRUE(getVibratorResult.isOk());
+ ASSERT_EQ(nullptr, getVibratorResult.value());
+
+ ASSERT_TRUE(mController->prepareSynced(ids).isOk());
+ ASSERT_TRUE(mController->triggerSynced([]() {}).isOk());
+ ASSERT_TRUE(mController->cancelSynced().isOk());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+ std::vector<int32_t> ids;
+ setHalExpectations(/* cardinality= */ 1, ids, vibrator::HalResult<void>::unsupported(),
+ vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(),
+ vibrator::HalResult<std::vector<int32_t>>::unsupported(),
+ vibrator::HalResult<
+ std::shared_ptr<vibrator::HalController>>::unsupported());
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getVibratorIds().isUnsupported());
+ ASSERT_TRUE(mController->getVibrator(1).isUnsupported());
+ ASSERT_TRUE(mController->prepareSynced(ids).isUnsupported());
+ ASSERT_TRUE(mController->triggerSynced([]() {}).isUnsupported());
+ ASSERT_TRUE(mController->cancelSynced().isUnsupported());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestFailedApiResultResetsHalConnection) {
+ std::vector<int32_t> ids;
+ setHalExpectations(/* cardinality= */ 1, ids, vibrator::HalResult<void>::failed("message"),
+ vibrator::HalResult<vibrator::ManagerCapabilities>::failed("message"),
+ vibrator::HalResult<std::vector<int32_t>>::failed("message"),
+ vibrator::HalResult<std::shared_ptr<vibrator::HalController>>::failed(
+ "message"));
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isFailed());
+ ASSERT_TRUE(mController->getCapabilities().isFailed());
+ ASSERT_TRUE(mController->getVibratorIds().isFailed());
+ ASSERT_TRUE(mController->getVibrator(1).isFailed());
+ ASSERT_TRUE(mController->prepareSynced(ids).isFailed());
+ ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed());
+ ASSERT_TRUE(mController->cancelSynced().isFailed());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+ }
+
+ ASSERT_EQ(0, mConnectCounter);
+ ASSERT_TRUE(mController->ping().isOk());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ ASSERT_EQ(0, mConnectCounter);
+
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(10))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // Connector was called only by the first thread to use the api.
+ ASSERT_EQ(1, mConnectCounter);
+}