diff options
| author | 2020-12-02 21:45:36 +0000 | |
|---|---|---|
| committer | 2020-12-02 21:45:36 +0000 | |
| commit | a859b8bea88a688432dd448b8a444526a142ab67 (patch) | |
| tree | f5905f255b732c3624f73dc19dbd93c752d9553b | |
| parent | 58a0f5f1203ef505d3bcc68a42574bc961f1ba3c (diff) | |
| parent | f20b1449d7f7d6171c54212763fba6e31543fe0a (diff) | |
Merge "Introduce controller for VibratorManager HAL"
8 files changed, 486 insertions, 80 deletions
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp index e8606caffd..bcd99575b4 100644 --- a/services/vibratorservice/VibratorHalController.cpp +++ b/services/vibratorservice/VibratorHalController.cpp @@ -46,7 +46,7 @@ namespace vibrator { // ------------------------------------------------------------------------------------------------- -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 @@ HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* f 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 @@ HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* f 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 @@ HalResult<void> HalController::ping() { 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 0000000000..b24e5c49b9 --- /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 9c4166c63c..8a08e5b678 100644 --- a/services/vibratorservice/VibratorManagerHalWrapper.cpp +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -72,11 +72,11 @@ HalResult<void> LegacyManagerHalWrapper::cancelSynced() { // ------------------------------------------------------------------------------------------------- -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 @@ HalResult<std::vector<int32_t>> AidlManagerHalWrapper::getVibratorIds() { // 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 d1028a4519..c405545aab 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h @@ -27,27 +27,20 @@ namespace android { 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 @@ public: 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 0000000000..cf825625ef --- /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 309d681c3a..563f55e9f3 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h @@ -134,20 +134,8 @@ private: 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 cda5e9a30c..2d9d0d6138 100644 --- a/services/vibratorservice/test/VibratorHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp @@ -79,38 +79,6 @@ public: 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 @@ public: 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, TestMultiThreadConnectsOnlyOnce) { } 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 0000000000..3b036eeeab --- /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); +} |