Loading services/vibratorservice/Android.bp +3 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ cc_library_shared { srcs: [ "VibratorCallbackScheduler.cpp", "VibratorController.cpp", "VibratorHalController.cpp", "VibratorHalWrapper.cpp", "VibratorManagerHalController.cpp", Loading @@ -41,17 +42,17 @@ cc_library_shared { }, shared_libs: [ "android.hardware.vibrator-V3-ndk", "libbinder_ndk", "liblog", "libutils", "android.hardware.vibrator-V3-ndk", ], cflags: [ "-Wall", "-Werror", "-Wunused", "-Wunreachable-code", "-Wunused", ], local_include_dirs: ["include"], Loading services/vibratorservice/VibratorController.cpp 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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 "VibratorController" #ifndef qDoWithRetries #define qDoWithRetries(op) doWithRetries(op, __FUNCTION__) #endif #include <aidl/android/hardware/vibrator/IVibrator.h> #include <android/binder_manager.h> #include <binder/IServiceManager.h> #include <utils/Log.h> #include <vibratorservice/VibratorController.h> using ::aidl::android::hardware::vibrator::Effect; using ::aidl::android::hardware::vibrator::EffectStrength; using ::aidl::android::hardware::vibrator::IVibrator; using Status = ::ndk::ScopedAStatus; using namespace std::placeholders; namespace android { namespace vibrator { // ------------------------------------------------------------------------------------------------- inline bool isStatusUnsupported(const Status& status) { // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. return status.getStatus() == STATUS_UNKNOWN_TRANSACTION || status.getExceptionCode() == EX_UNSUPPORTED_OPERATION; } inline bool isStatusTransactionFailed(const Status& status) { // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. return status.getStatus() != STATUS_UNKNOWN_TRANSACTION && status.getExceptionCode() == EX_TRANSACTION_FAILED; } // ------------------------------------------------------------------------------------------------- bool VibratorProvider::isDeclared() { std::lock_guard<std::mutex> lock(mMutex); if (mIsDeclared.has_value()) { return *mIsDeclared; } bool isDeclared = AServiceManager_isDeclared(mServiceName.c_str()); if (!isDeclared) { ALOGV("Vibrator HAL service not declared."); } mIsDeclared.emplace(isDeclared); return isDeclared; } std::shared_ptr<IVibrator> VibratorProvider::waitForVibrator() { if (!isDeclared()) { return nullptr; } auto vibrator = IVibrator::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(mServiceName.c_str()))); if (vibrator) { ALOGV("Successfully connected to Vibrator HAL service."); } else { ALOGE("Error connecting to declared Vibrator HAL service."); } return vibrator; } std::shared_ptr<IVibrator> VibratorProvider::checkForVibrator() { if (!isDeclared()) { return nullptr; } auto vibrator = IVibrator::fromBinder( ndk::SpAIBinder(AServiceManager_checkService(mServiceName.c_str()))); if (vibrator) { ALOGV("Successfully reconnected to Vibrator HAL service."); } else { ALOGE("Error reconnecting to declared Vibrator HAL service."); } return vibrator; } // ------------------------------------------------------------------------------------------------- bool VibratorController::init() { if (!mVibratorProvider->isDeclared()) { return false; } std::lock_guard<std::mutex> lock(mMutex); if (mVibrator == nullptr) { mVibrator = mVibratorProvider->waitForVibrator(); } return mVibratorProvider->isDeclared(); } Status VibratorController::off() { return qDoWithRetries(std::bind(&IVibrator::off, _1)); } Status VibratorController::setAmplitude(float amplitude) { return qDoWithRetries(std::bind(&IVibrator::setAmplitude, _1, amplitude)); } Status VibratorController::setExternalControl(bool enabled) { return qDoWithRetries(std::bind(&IVibrator::setExternalControl, _1, enabled)); } Status VibratorController::alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength) { return qDoWithRetries(std::bind(&IVibrator::alwaysOnEnable, _1, id, effect, strength)); } Status VibratorController::alwaysOnDisable(int32_t id) { return qDoWithRetries(std::bind(&IVibrator::alwaysOnDisable, _1, id)); } // ------------------------------------------------------------------------------------------------- std::shared_ptr<IVibrator> VibratorController::reconnectToVibrator() { std::lock_guard<std::mutex> lock(mMutex); mVibrator = mVibratorProvider->checkForVibrator(); return mVibrator; } Status VibratorController::doWithRetries(const VibratorController::VibratorOp& op, const char* logLabel) { if (!init()) { ALOGV("Skipped %s because Vibrator HAL is not declared", logLabel); return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator not declared"); } std::shared_ptr<IVibrator> vibrator; { std::lock_guard<std::mutex> lock(mMutex); vibrator = mVibrator; } if (!vibrator) { ALOGE("Skipped %s because Vibrator HAL is declared but failed to load", logLabel); return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator declared but failed to load"); } auto status = doOnce(vibrator.get(), op, logLabel); for (int i = 1; i < MAX_ATTEMPTS && isStatusTransactionFailed(status); i++) { vibrator = reconnectToVibrator(); if (!vibrator) { // Failed to reconnect to vibrator HAL after a transaction failed, skip retries. break; } status = doOnce(vibrator.get(), op, logLabel); } return status; } Status VibratorController::doOnce(IVibrator* vibrator, const VibratorController::VibratorOp& op, const char* logLabel) { auto status = op(vibrator); if (!status.isOk()) { if (isStatusUnsupported(status)) { ALOGV("Vibrator HAL %s is unsupported: %s", logLabel, status.getMessage()); } else { ALOGE("Vibrator HAL %s failed: %s", logLabel, status.getMessage()); } } return status; } // ------------------------------------------------------------------------------------------------- }; // namespace vibrator }; // namespace android services/vibratorservice/include/vibratorservice/VibratorController.h 0 → 100644 +118 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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_CONTROLLER_H #define ANDROID_OS_VIBRATOR_CONTROLLER_H #include <aidl/android/hardware/vibrator/IVibrator.h> #include <android-base/thread_annotations.h> namespace android { namespace vibrator { // ------------------------------------------------------------------------------------------------- /* Provider for IVibrator HAL service instances. */ class VibratorProvider { public: using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; VibratorProvider() : mServiceName(std::string(IVibrator::descriptor) + "/default") {} virtual ~VibratorProvider() = default; /* Returns true if vibrator HAL service is declared in the device, false otherwise. */ virtual bool isDeclared(); /* Connects to vibrator HAL, possibly waiting for the declared service to become available. */ virtual std::shared_ptr<IVibrator> waitForVibrator(); /* Connects to vibrator HAL if declared and available, without waiting. */ virtual std::shared_ptr<IVibrator> checkForVibrator(); private: std::mutex mMutex; const std::string mServiceName; std::optional<bool> mIsDeclared GUARDED_BY(mMutex); }; // ------------------------------------------------------------------------------------------------- /* Controller for Vibrator HAL handle. * This relies on VibratorProvider to connect to the underlying Vibrator HAL service and reconnects * after each transaction failed call. This also ensures connecting to the service is thread-safe. */ class VibratorController { public: using Effect = ::aidl::android::hardware::vibrator::Effect; using EffectStrength = ::aidl::android::hardware::vibrator::EffectStrength; using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; using Status = ::ndk::ScopedAStatus; using VibratorOp = std::function<Status(IVibrator*)>; VibratorController() : VibratorController(std::make_shared<VibratorProvider>()) {} VibratorController(std::shared_ptr<VibratorProvider> vibratorProvider) : mVibratorProvider(std::move(vibratorProvider)), mVibrator(nullptr) {} virtual ~VibratorController() = default; /* Connects HAL service, possibly waiting for the declared service to become available. * This will automatically be called at the first API usage if it was not manually called * beforehand. Call this manually during the setup phase to avoid slowing the first API call. * Returns true if HAL service is declared, false otherwise. */ bool init(); /* Turn vibrator off. */ Status off(); /* Set vibration amplitude in [0,1]. */ Status setAmplitude(float amplitude); /* Enable/disable external control. */ Status setExternalControl(bool enabled); /* Enable always-on for given id, with given effect and strength. */ Status alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength); /* Disable always-on for given id. */ Status alwaysOnDisable(int32_t id); private: /* Max number of attempts to perform an operation when it fails with transaction error. */ static constexpr int MAX_ATTEMPTS = 2; std::mutex mMutex; std::shared_ptr<VibratorProvider> mVibratorProvider; std::shared_ptr<IVibrator> mVibrator GUARDED_BY(mMutex); /* Reconnects HAL service without waiting for the service to become available. */ std::shared_ptr<IVibrator> reconnectToVibrator(); /* Perform given operation on HAL with retries on transaction failures. */ Status doWithRetries(const VibratorOp& op, const char* logLabel); /* Perform given operation on HAL with logs for error/unsupported results. */ static Status doOnce(IVibrator* vibrator, const VibratorOp& op, const char* logLabel); }; // ------------------------------------------------------------------------------------------------- }; // namespace vibrator }; // namespace android #endif // ANDROID_OS_VIBRATOR_CONTROLLER_H services/vibratorservice/include/vibratorservice/VibratorHalController.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ // TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed #ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H #define ANDROID_OS_VIBRATORHALCONTROLLER_H Loading services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ // TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed #ifndef ANDROID_OS_VIBRATORHALWRAPPER_H #define ANDROID_OS_VIBRATORHALWRAPPER_H Loading Loading
services/vibratorservice/Android.bp +3 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ cc_library_shared { srcs: [ "VibratorCallbackScheduler.cpp", "VibratorController.cpp", "VibratorHalController.cpp", "VibratorHalWrapper.cpp", "VibratorManagerHalController.cpp", Loading @@ -41,17 +42,17 @@ cc_library_shared { }, shared_libs: [ "android.hardware.vibrator-V3-ndk", "libbinder_ndk", "liblog", "libutils", "android.hardware.vibrator-V3-ndk", ], cflags: [ "-Wall", "-Werror", "-Wunused", "-Wunreachable-code", "-Wunused", ], local_include_dirs: ["include"], Loading
services/vibratorservice/VibratorController.cpp 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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 "VibratorController" #ifndef qDoWithRetries #define qDoWithRetries(op) doWithRetries(op, __FUNCTION__) #endif #include <aidl/android/hardware/vibrator/IVibrator.h> #include <android/binder_manager.h> #include <binder/IServiceManager.h> #include <utils/Log.h> #include <vibratorservice/VibratorController.h> using ::aidl::android::hardware::vibrator::Effect; using ::aidl::android::hardware::vibrator::EffectStrength; using ::aidl::android::hardware::vibrator::IVibrator; using Status = ::ndk::ScopedAStatus; using namespace std::placeholders; namespace android { namespace vibrator { // ------------------------------------------------------------------------------------------------- inline bool isStatusUnsupported(const Status& status) { // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. return status.getStatus() == STATUS_UNKNOWN_TRANSACTION || status.getExceptionCode() == EX_UNSUPPORTED_OPERATION; } inline bool isStatusTransactionFailed(const Status& status) { // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. return status.getStatus() != STATUS_UNKNOWN_TRANSACTION && status.getExceptionCode() == EX_TRANSACTION_FAILED; } // ------------------------------------------------------------------------------------------------- bool VibratorProvider::isDeclared() { std::lock_guard<std::mutex> lock(mMutex); if (mIsDeclared.has_value()) { return *mIsDeclared; } bool isDeclared = AServiceManager_isDeclared(mServiceName.c_str()); if (!isDeclared) { ALOGV("Vibrator HAL service not declared."); } mIsDeclared.emplace(isDeclared); return isDeclared; } std::shared_ptr<IVibrator> VibratorProvider::waitForVibrator() { if (!isDeclared()) { return nullptr; } auto vibrator = IVibrator::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(mServiceName.c_str()))); if (vibrator) { ALOGV("Successfully connected to Vibrator HAL service."); } else { ALOGE("Error connecting to declared Vibrator HAL service."); } return vibrator; } std::shared_ptr<IVibrator> VibratorProvider::checkForVibrator() { if (!isDeclared()) { return nullptr; } auto vibrator = IVibrator::fromBinder( ndk::SpAIBinder(AServiceManager_checkService(mServiceName.c_str()))); if (vibrator) { ALOGV("Successfully reconnected to Vibrator HAL service."); } else { ALOGE("Error reconnecting to declared Vibrator HAL service."); } return vibrator; } // ------------------------------------------------------------------------------------------------- bool VibratorController::init() { if (!mVibratorProvider->isDeclared()) { return false; } std::lock_guard<std::mutex> lock(mMutex); if (mVibrator == nullptr) { mVibrator = mVibratorProvider->waitForVibrator(); } return mVibratorProvider->isDeclared(); } Status VibratorController::off() { return qDoWithRetries(std::bind(&IVibrator::off, _1)); } Status VibratorController::setAmplitude(float amplitude) { return qDoWithRetries(std::bind(&IVibrator::setAmplitude, _1, amplitude)); } Status VibratorController::setExternalControl(bool enabled) { return qDoWithRetries(std::bind(&IVibrator::setExternalControl, _1, enabled)); } Status VibratorController::alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength) { return qDoWithRetries(std::bind(&IVibrator::alwaysOnEnable, _1, id, effect, strength)); } Status VibratorController::alwaysOnDisable(int32_t id) { return qDoWithRetries(std::bind(&IVibrator::alwaysOnDisable, _1, id)); } // ------------------------------------------------------------------------------------------------- std::shared_ptr<IVibrator> VibratorController::reconnectToVibrator() { std::lock_guard<std::mutex> lock(mMutex); mVibrator = mVibratorProvider->checkForVibrator(); return mVibrator; } Status VibratorController::doWithRetries(const VibratorController::VibratorOp& op, const char* logLabel) { if (!init()) { ALOGV("Skipped %s because Vibrator HAL is not declared", logLabel); return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator not declared"); } std::shared_ptr<IVibrator> vibrator; { std::lock_guard<std::mutex> lock(mMutex); vibrator = mVibrator; } if (!vibrator) { ALOGE("Skipped %s because Vibrator HAL is declared but failed to load", logLabel); return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator declared but failed to load"); } auto status = doOnce(vibrator.get(), op, logLabel); for (int i = 1; i < MAX_ATTEMPTS && isStatusTransactionFailed(status); i++) { vibrator = reconnectToVibrator(); if (!vibrator) { // Failed to reconnect to vibrator HAL after a transaction failed, skip retries. break; } status = doOnce(vibrator.get(), op, logLabel); } return status; } Status VibratorController::doOnce(IVibrator* vibrator, const VibratorController::VibratorOp& op, const char* logLabel) { auto status = op(vibrator); if (!status.isOk()) { if (isStatusUnsupported(status)) { ALOGV("Vibrator HAL %s is unsupported: %s", logLabel, status.getMessage()); } else { ALOGE("Vibrator HAL %s failed: %s", logLabel, status.getMessage()); } } return status; } // ------------------------------------------------------------------------------------------------- }; // namespace vibrator }; // namespace android
services/vibratorservice/include/vibratorservice/VibratorController.h 0 → 100644 +118 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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_CONTROLLER_H #define ANDROID_OS_VIBRATOR_CONTROLLER_H #include <aidl/android/hardware/vibrator/IVibrator.h> #include <android-base/thread_annotations.h> namespace android { namespace vibrator { // ------------------------------------------------------------------------------------------------- /* Provider for IVibrator HAL service instances. */ class VibratorProvider { public: using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; VibratorProvider() : mServiceName(std::string(IVibrator::descriptor) + "/default") {} virtual ~VibratorProvider() = default; /* Returns true if vibrator HAL service is declared in the device, false otherwise. */ virtual bool isDeclared(); /* Connects to vibrator HAL, possibly waiting for the declared service to become available. */ virtual std::shared_ptr<IVibrator> waitForVibrator(); /* Connects to vibrator HAL if declared and available, without waiting. */ virtual std::shared_ptr<IVibrator> checkForVibrator(); private: std::mutex mMutex; const std::string mServiceName; std::optional<bool> mIsDeclared GUARDED_BY(mMutex); }; // ------------------------------------------------------------------------------------------------- /* Controller for Vibrator HAL handle. * This relies on VibratorProvider to connect to the underlying Vibrator HAL service and reconnects * after each transaction failed call. This also ensures connecting to the service is thread-safe. */ class VibratorController { public: using Effect = ::aidl::android::hardware::vibrator::Effect; using EffectStrength = ::aidl::android::hardware::vibrator::EffectStrength; using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; using Status = ::ndk::ScopedAStatus; using VibratorOp = std::function<Status(IVibrator*)>; VibratorController() : VibratorController(std::make_shared<VibratorProvider>()) {} VibratorController(std::shared_ptr<VibratorProvider> vibratorProvider) : mVibratorProvider(std::move(vibratorProvider)), mVibrator(nullptr) {} virtual ~VibratorController() = default; /* Connects HAL service, possibly waiting for the declared service to become available. * This will automatically be called at the first API usage if it was not manually called * beforehand. Call this manually during the setup phase to avoid slowing the first API call. * Returns true if HAL service is declared, false otherwise. */ bool init(); /* Turn vibrator off. */ Status off(); /* Set vibration amplitude in [0,1]. */ Status setAmplitude(float amplitude); /* Enable/disable external control. */ Status setExternalControl(bool enabled); /* Enable always-on for given id, with given effect and strength. */ Status alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength); /* Disable always-on for given id. */ Status alwaysOnDisable(int32_t id); private: /* Max number of attempts to perform an operation when it fails with transaction error. */ static constexpr int MAX_ATTEMPTS = 2; std::mutex mMutex; std::shared_ptr<VibratorProvider> mVibratorProvider; std::shared_ptr<IVibrator> mVibrator GUARDED_BY(mMutex); /* Reconnects HAL service without waiting for the service to become available. */ std::shared_ptr<IVibrator> reconnectToVibrator(); /* Perform given operation on HAL with retries on transaction failures. */ Status doWithRetries(const VibratorOp& op, const char* logLabel); /* Perform given operation on HAL with logs for error/unsupported results. */ static Status doOnce(IVibrator* vibrator, const VibratorOp& op, const char* logLabel); }; // ------------------------------------------------------------------------------------------------- }; // namespace vibrator }; // namespace android #endif // ANDROID_OS_VIBRATOR_CONTROLLER_H
services/vibratorservice/include/vibratorservice/VibratorHalController.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ // TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed #ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H #define ANDROID_OS_VIBRATORHALCONTROLLER_H Loading
services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +2 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ // TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed #ifndef ANDROID_OS_VIBRATORHALWRAPPER_H #define ANDROID_OS_VIBRATORHALWRAPPER_H Loading