Merge "Create vibrator controller for Vibrator HALs"
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index e3c254d..c45a1a1 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -17,6 +17,7 @@
srcs: [
"VibratorCallbackScheduler.cpp",
+ "VibratorHalController.cpp",
"VibratorHalWrapper.cpp",
],
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
new file mode 100644
index 0000000..ef1d061
--- /dev/null
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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 "VibratorHalController"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+using hal_connect_fn = std::function<sp<T>()>;
+
+template <typename T>
+sp<T> connectToHal(bool* halExists, const hal_connect_fn<T>& connectFn, const char* halName) {
+ if (!*halExists) {
+ return nullptr;
+ }
+ sp<T> hal = connectFn();
+ if (hal) {
+ ALOGV("Successfully connected to Vibrator HAL %s service.", halName);
+ } else {
+ ALOGV("Vibrator HAL %s service not available.", halName);
+ *halExists = false;
+ }
+ return hal;
+}
+
+sp<Aidl::IVibrator> connectToAidl() {
+ static bool gHalExists = true;
+ static hal_connect_fn<Aidl::IVibrator> connectFn = []() {
+ return waitForVintfService<Aidl::IVibrator>();
+ };
+ return connectToHal(&gHalExists, connectFn, "AIDL");
+}
+
+sp<V1_0::IVibrator> connectToHidl() {
+ static bool gHalExists = true;
+ static hal_connect_fn<V1_0::IVibrator> connectFn = []() {
+ return V1_0::IVibrator::getService();
+ };
+ return connectToHal(&gHalExists, connectFn, "v1.0");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
+ sp<Aidl::IVibrator> aidlHal = connectToAidl();
+ if (aidlHal) {
+ return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal);
+ }
+ sp<V1_0::IVibrator> halV1_0 = connectToHidl();
+ if (halV1_0 == nullptr) {
+ // No Vibrator HAL service available.
+ return nullptr;
+ }
+ sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
+ if (halV1_3) {
+ ALOGV("Successfully converted to Vibrator HAL v1.3 service.");
+ return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
+ }
+ sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
+ if (halV1_2) {
+ ALOGV("Successfully converted to Vibrator HAL v1.2 service.");
+ return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
+ }
+ sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
+ if (halV1_1) {
+ ALOGV("Successfully converted to Vibrator HAL v1.1 service.");
+ return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
+ }
+ return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
+ if (result.isFailed()) {
+ ALOGE("%s failed: Vibrator HAL not available", functionName);
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ // Drop HAL handle. This will force future api calls to reconnect.
+ mConnectedHal = nullptr;
+ }
+ return result;
+}
+
+template <typename T>
+HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
+ std::shared_ptr<HalWrapper> hal = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ }
+ hal = mConnectedHal;
+ }
+ if (hal) {
+ return processHalResult(halFn(hal), functionName);
+ }
+
+ ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+ return HalResult<T>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HalController::ping() {
+ hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+ return apply(pingFn, "ping");
+}
+
+HalResult<void> HalController::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->on(timeout, completionCallback);
+ };
+ return apply(onFn, "on");
+}
+
+HalResult<void> HalController::off() {
+ hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); };
+ return apply(offFn, "off");
+}
+
+HalResult<void> HalController::setAmplitude(int32_t amplitude) {
+ hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->setAmplitude(amplitude);
+ };
+ return apply(setAmplitudeFn, "setAmplitude");
+}
+
+HalResult<void> HalController::setExternalControl(bool enabled) {
+ hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->setExternalControl(enabled);
+ };
+ return apply(setExternalControlFn, "setExternalControl");
+}
+
+HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->alwaysOnEnable(id, effect, strength);
+ };
+ return apply(alwaysOnEnableFn, "alwaysOnEnable");
+}
+
+HalResult<void> HalController::alwaysOnDisable(int32_t id) {
+ hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->alwaysOnDisable(id);
+ };
+ return apply(alwaysOnDisableFn, "alwaysOnDisable");
+}
+
+HalResult<Capabilities> HalController::getCapabilities() {
+ hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getCapabilities();
+ };
+ return apply(getCapabilitiesFn, "getCapabilities");
+}
+
+HalResult<std::vector<Effect>> HalController::getSupportedEffects() {
+ hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getSupportedEffects();
+ };
+ return apply(getSupportedEffectsFn, "getSupportedEffects");
+}
+
+HalResult<milliseconds> HalController::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->performEffect(effect, strength, completionCallback);
+ };
+ return apply(performEffectFn, "performEffect");
+}
+
+HalResult<void> HalController::performComposedEffect(
+ const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->performComposedEffect(primitiveEffects, completionCallback);
+ };
+ return apply(performComposedEffectFn, "performComposedEffect");
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 1420bf5..94db538 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -68,21 +68,6 @@
// -------------------------------------------------------------------------------------------------
template <typename T>
-HalResult<T> HalResult<T>::ok(T value) {
- return HalResult(value);
-}
-
-template <typename T>
-HalResult<T> HalResult<T>::failed() {
- return HalResult(/* unsupported= */ false);
-}
-
-template <typename T>
-HalResult<T> HalResult<T>::unsupported() {
- return HalResult(/* unsupported= */ true);
-}
-
-template <typename T>
HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) {
if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
return HalResult<T>::unsupported();
@@ -119,18 +104,6 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> HalResult<void>::ok() {
- return HalResult();
-}
-
-HalResult<void> HalResult<void>::failed() {
- return HalResult(/* failed= */ true);
-}
-
-HalResult<void> HalResult<void>::unsupported() {
- return HalResult(/* failed= */ false, /* unsupported= */ true);
-}
-
HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
return HalResult<void>::unsupported();
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
new file mode 100644
index 0000000..e254969
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -0,0 +1,98 @@
+/*
+ * 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_VIBRATORHALCONTROLLER_H
+#define ANDROID_OS_VIBRATORHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+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);
+};
+
+// 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.
+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)
+ : HalWrapper(std::move(callbackScheduler)),
+ mHalConnector(std::move(halConnector)),
+ mConnectedHal(nullptr) {}
+ virtual ~HalController() = default;
+
+ HalResult<void> ping() final override;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) final override;
+ HalResult<void> off() final override;
+
+ HalResult<void> setAmplitude(int32_t amplitude) final override;
+ HalResult<void> setExternalControl(bool enabled) final override;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) final override;
+ HalResult<void> alwaysOnDisable(int32_t id) final override;
+
+ HalResult<Capabilities> getCapabilities() final override;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) final override;
+
+ HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) final override;
+
+private:
+ std::unique_ptr<HalConnector> mHalConnector;
+ 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<HalWrapper>)>;
+
+ template <typename T>
+ HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALCONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 0f9aacb..5e3c275 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -33,9 +33,9 @@
template <typename T>
class HalResult {
public:
- static HalResult<T> ok(T value);
- static HalResult<T> failed();
- static HalResult<T> unsupported();
+ static HalResult<T> ok(T value) { return HalResult(value); }
+ static HalResult<T> failed() { return HalResult(/* unsupported= */ false); }
+ static HalResult<T> unsupported() { return HalResult(/* unsupported= */ true); }
static HalResult<T> fromStatus(binder::Status status, T data);
static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
@@ -65,9 +65,11 @@
template <>
class HalResult<void> {
public:
- static HalResult<void> ok();
- static HalResult<void> failed();
- static HalResult<void> unsupported();
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed() { return HalResult(/* failed= */ true); }
+ static HalResult<void> unsupported() {
+ return HalResult(/* failed= */ false, /* unsupported= */ true);
+ }
static HalResult<void> fromStatus(binder::Status status);
static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
@@ -262,9 +264,8 @@
class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
public:
HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
- : HidlHalWrapperV1_0(std::move(scheduler), handleV1_0),
- mHandleV1_1(hardware::vibrator::V1_1::IVibrator::castFrom(handleV1_0)) {}
+ sp<hardware::vibrator::V1_1::IVibrator> handle)
+ : HidlHalWrapperV1_0(std::move(scheduler), handle), mHandleV1_1(handle) {}
virtual ~HidlHalWrapperV1_1() = default;
virtual HalResult<std::chrono::milliseconds> performEffect(
@@ -283,9 +284,8 @@
class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
public:
HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
- : HidlHalWrapperV1_1(std::move(scheduler), handleV1_0),
- mHandleV1_2(hardware::vibrator::V1_2::IVibrator::castFrom(handleV1_0)) {}
+ sp<hardware::vibrator::V1_2::IVibrator> handle)
+ : HidlHalWrapperV1_1(std::move(scheduler), handle), mHandleV1_2(handle) {}
virtual ~HidlHalWrapperV1_2() = default;
virtual HalResult<std::chrono::milliseconds> performEffect(
@@ -304,9 +304,8 @@
class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
public:
HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
- : HidlHalWrapperV1_2(std::move(scheduler), handleV1_0),
- mHandleV1_3(hardware::vibrator::V1_3::IVibrator::castFrom(handleV1_0)) {}
+ sp<hardware::vibrator::V1_3::IVibrator> handle)
+ : HidlHalWrapperV1_2(std::move(scheduler), handle), mHandleV1_3(handle) {}
virtual ~HidlHalWrapperV1_3() = default;
virtual HalResult<void> setExternalControl(bool enabled) override;
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index fa399ad..9033124 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -17,6 +17,7 @@
test_suites: ["device-tests"],
srcs: [
"VibratorCallbackSchedulerTest.cpp",
+ "VibratorHalControllerTest.cpp",
"VibratorHalWrapperAidlTest.cpp",
"VibratorHalWrapperHidlV1_0Test.cpp",
"VibratorHalWrapperHidlV1_1Test.cpp",
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
new file mode 100644
index 0000000..3d8b069
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -0,0 +1,332 @@
+/*
+ * 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 "VibratorHalControllerTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+#include <cutils/atomic.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalWrapper : public vibrator::HalWrapper {
+public:
+ MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler)
+ : HalWrapper(scheduler) {}
+ virtual ~MockHalWrapper() = default;
+
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, on,
+ (milliseconds timeout, const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (int32_t amplitude), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
+ (int32_t id, Effect effect, EffectStrength strength), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
+ MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
+ (Effect effect, EffectStrength strength,
+ const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, performComposedEffect,
+ (const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback),
+ (override));
+
+ 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 {
+public:
+ void SetUp() override {
+ 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));
+ ASSERT_NE(mController, nullptr);
+ }
+
+protected:
+ int32_t mConnectCounter;
+ std::shared_ptr<MockHalWrapper> mMockHal;
+ std::unique_ptr<vibrator::HalController> mController;
+
+ void setHalExpectations(std::vector<CompositeEffect> compositeEffects,
+ vibrator::HalResult<void> voidResult,
+ vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
+ vibrator::HalResult<std::vector<Effect>> effectsResult,
+ vibrator::HalResult<milliseconds> durationResult) {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), ping()).Times(Exactly(1)).WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), off()).Times(Exactly(1)).WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(255)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), getCapabilities())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(capabilitiesResult));
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(effectsResult));
+ EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(durationResult));
+ EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(voidResult));
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+ std::vector<CompositeEffect> compositeEffects;
+ compositeEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ compositeEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ setHalExpectations(compositeEffects, vibrator::HalResult<void>::ok(),
+ vibrator::HalResult<vibrator::Capabilities>::ok(
+ vibrator::Capabilities::ON_CALLBACK),
+ vibrator::HalResult<std::vector<Effect>>::ok(supportedEffects),
+ vibrator::HalResult<milliseconds>::ok(100ms));
+
+ ASSERT_TRUE(mController->ping().isOk());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isOk());
+ ASSERT_TRUE(mController->off().isOk());
+ ASSERT_TRUE(mController->setAmplitude(255).isOk());
+ ASSERT_TRUE(mController->setExternalControl(true).isOk());
+ ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isOk());
+
+ auto getCapabilitiesResult = mController->getCapabilities();
+ ASSERT_TRUE(getCapabilitiesResult.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value());
+
+ auto getSupportedEffectsResult = mController->getSupportedEffects();
+ ASSERT_TRUE(getSupportedEffectsResult.isOk());
+ ASSERT_EQ(supportedEffects, getSupportedEffectsResult.value());
+
+ auto performEffectResult =
+ mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
+ ASSERT_TRUE(performEffectResult.isOk());
+ ASSERT_EQ(100ms, performEffectResult.value());
+
+ ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk());
+
+ // No reconnection attempt was made after the first one.
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+ setHalExpectations(std::vector<CompositeEffect>(), vibrator::HalResult<void>::unsupported(),
+ vibrator::HalResult<vibrator::Capabilities>::unsupported(),
+ vibrator::HalResult<std::vector<Effect>>::unsupported(),
+ vibrator::HalResult<milliseconds>::unsupported());
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+ ASSERT_TRUE(mController->off().isUnsupported());
+ ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+ ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(
+ mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+ .isUnsupported());
+ ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+ .isUnsupported());
+
+ // No reconnection attempt was made after the first one.
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
+ setHalExpectations(std::vector<CompositeEffect>(), vibrator::HalResult<void>::failed(),
+ vibrator::HalResult<vibrator::Capabilities>::failed(),
+ vibrator::HalResult<std::vector<Effect>>::failed(),
+ vibrator::HalResult<milliseconds>::failed());
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isFailed());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isFailed());
+ ASSERT_TRUE(mController->off().isFailed());
+ ASSERT_TRUE(mController->setAmplitude(255).isFailed());
+ ASSERT_TRUE(mController->setExternalControl(true).isFailed());
+ ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
+ ASSERT_TRUE(mController->getCapabilities().isFailed());
+ ASSERT_TRUE(mController->getSupportedEffects().isFailed());
+ ASSERT_TRUE(
+ mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
+ ASSERT_TRUE(
+ mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
+
+ // One reconnection attempt per api call.
+ ASSERT_EQ(11, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, 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);
+}
+
+TEST_F(VibratorHalControllerTest, TestNoHalReturnsUnsupportedAndAttemptsToReconnect) {
+ auto failingHalConnector = std::make_unique<FailingHalConnector>(&mConnectCounter);
+ mController =
+ std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr);
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+ ASSERT_TRUE(mController->off().isUnsupported());
+ ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+ ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(
+ mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+ .isUnsupported());
+ ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+ .isUnsupported());
+
+ // One reconnection attempt per api call.
+ ASSERT_EQ(11, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
+ mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
+ return vibrator::HalResult<void>::ok();
+ });
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mController->on(10ms, callback).isOk());
+ ASSERT_TRUE(mController->ping().isFailed()); // Delete connected HAL pointer from controller.
+ mMockHal.reset(); // Delete mock HAL pointer from test class.
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ // Callback triggered even after HalWrapper was completely destroyed.
+ std::this_thread::sleep_for(15ms);
+ ASSERT_EQ(1, *callbackCounter.get());
+}