Merge "Introduce controller for VibratorManager HAL"
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);
+}