From bf3bc86bea21d6d2ee504619d3e939b856d06850 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 31 Jan 2017 18:34:35 +0000 Subject: [PATCH] Add amplitude control to vibrator HAL Providing control over the strength of the vibration allows the platform to provide a richer haptic experience to users. How this amplitude is modulated is left up to the vibrator implementation. This also adds an interface to ask the HAL to perform specific haptic effects. By exposing the intent of the haptic event to the HAL, we can let device and haptic driver manufacturers implement custom waveforms that more closely match the desired effect. Test: Manual testing with Marlin HAL + adb shell /data/nativetest/vibrator_hidl_hal_test/vibrator_hidl_hal_test Change-Id: Icfccb464c6c85adecdf354e2bd4cf422d7d31eb5 --- vibrator/1.0/Android.mk | 72 +++++++++++++++++++ vibrator/1.0/IVibrator.hal | 41 ++++++++++- vibrator/1.0/default/Vibrator.cpp | 19 ++++- vibrator/1.0/default/Vibrator.h | 15 ++-- vibrator/1.0/types.hal | 32 ++++++++- .../VtsHalVibratorV1_0TargetTest.cpp | 39 ++++++++++ 6 files changed, 201 insertions(+), 17 deletions(-) diff --git a/vibrator/1.0/Android.mk b/vibrator/1.0/Android.mk index 4e1ba6a623..d921a7e9e1 100644 --- a/vibrator/1.0/Android.mk +++ b/vibrator/1.0/Android.mk @@ -16,6 +16,25 @@ LOCAL_JAVA_LIBRARIES := \ android.hidl.base@1.0-java \ +# +# Build types.hal (Effect) +# +GEN := $(intermediates)/android/hardware/vibrator/V1_0/Effect.java +$(GEN): $(HIDL) +$(GEN): PRIVATE_HIDL := $(HIDL) +$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal +$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates) +$(GEN): PRIVATE_CUSTOM_TOOL = \ + $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \ + -Ljava \ + -randroid.hardware:hardware/interfaces \ + -randroid.hidl:system/libhidl/transport \ + android.hardware.vibrator@1.0::types.Effect + +$(GEN): $(LOCAL_PATH)/types.hal + $(transform-generated-source) +LOCAL_GENERATED_SOURCES += $(GEN) + # # Build types.hal (Status) # @@ -72,6 +91,25 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android.hidl.base@1.0-java-static \ +# +# Build types.hal (Effect) +# +GEN := $(intermediates)/android/hardware/vibrator/V1_0/Effect.java +$(GEN): $(HIDL) +$(GEN): PRIVATE_HIDL := $(HIDL) +$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal +$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates) +$(GEN): PRIVATE_CUSTOM_TOOL = \ + $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \ + -Ljava \ + -randroid.hardware:hardware/interfaces \ + -randroid.hidl:system/libhidl/transport \ + android.hardware.vibrator@1.0::types.Effect + +$(GEN): $(LOCAL_PATH)/types.hal + $(transform-generated-source) +LOCAL_GENERATED_SOURCES += $(GEN) + # # Build types.hal (Status) # @@ -114,5 +152,39 @@ LOCAL_GENERATED_SOURCES += $(GEN) include $(BUILD_STATIC_JAVA_LIBRARY) +################################################################################ + +include $(CLEAR_VARS) +LOCAL_MODULE := android.hardware.vibrator@1.0-java-constants +LOCAL_MODULE_CLASS := JAVA_LIBRARIES + +intermediates := $(local-generated-sources-dir) + +HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX) +# +GEN := $(intermediates)/android/hardware/vibrator/V1_0/Constants.java +$(GEN): $(HIDL) +$(GEN): $(LOCAL_PATH)/types.hal +$(GEN): $(LOCAL_PATH)/IVibrator.hal + +$(GEN): PRIVATE_HIDL := $(HIDL) +$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates) +$(GEN): PRIVATE_CUSTOM_TOOL = \ + $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \ + -Ljava-constants \ + -randroid.hardware:hardware/interfaces \ + -randroid.hidl:system/libhidl/transport \ + android.hardware.vibrator@1.0 + +$(GEN): + $(transform-generated-source) +LOCAL_GENERATED_SOURCES += $(GEN) +# Avoid dependency cycle of framework.jar -> this-library -> framework.jar +LOCAL_NO_STANDARD_LIBRARIES := true +LOCAL_JAVA_LIBRARIES := core-oj + +include $(BUILD_STATIC_JAVA_LIBRARY) + + include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/vibrator/1.0/IVibrator.hal b/vibrator/1.0/IVibrator.hal index 0a4ffca969..757ad0d74f 100644 --- a/vibrator/1.0/IVibrator.hal +++ b/vibrator/1.0/IVibrator.hal @@ -17,7 +17,8 @@ package android.hardware.vibrator@1.0; interface IVibrator { - /** Turn on vibrator + /** + * Turn on vibrator * * This function must only be called after the previous timeout has expired or * was canceled (through off()). @@ -26,10 +27,46 @@ interface IVibrator { */ on(uint32_t timeoutMs) generates (Status vibratorOnRet); - /** Turn off vibrator + /** + * Turn off vibrator * * Cancel a previously-started vibration, if any. * @return vibratorOffRet whether vibrator command was successful or not. */ off() generates (Status vibratorOffRet); + + /** + * Returns whether the vibrator supports changes to its vibrational amplitude. + */ + supportsAmplitudeControl() generates (bool supports); + + /** + * Sets the motor's vibrational amplitude. + * + * Changes the force being produced by the underlying motor. + * + * @param amplitude The unitless force setting. Note that this number must + * be between 1 and 255, inclusive. If the motor does not + * have exactly 255 steps, it must do it's best to map it + * onto the number of steps it does have. + * @return status Whether the command was successful or not. Must return + * Status::UNSUPPORTED_OPERATION if setting the amplitude is + * not supported by the device. + */ + setAmplitude(uint8_t amplitude) generates (Status status); + + /** + * Fire off a predefined haptic event. + * + * @param event The type of haptic event to trigger. + * @return status Whether the effect was successfully performed or not. Must + * return Status::UNSUPPORTED_OPERATION is the effect is not + * supported. + * @return lengthMs The length of time the event is expected to take in + * milliseconds. This doesn't need to be perfectly accurate, + * but should be a reasonable approximation. Should be a + * positive, non-zero value if the returned status is + * Status::OK, and set to 0 otherwise. + */ + perform(Effect effect, EffectStrength strength) generates (Status status, uint32_t lengthMs); }; diff --git a/vibrator/1.0/default/Vibrator.cpp b/vibrator/1.0/default/Vibrator.cpp index 8c82bcd5d6..19cf3dc850 100644 --- a/vibrator/1.0/default/Vibrator.cpp +++ b/vibrator/1.0/default/Vibrator.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "VibratorService" +#include + #include #include @@ -36,7 +38,7 @@ Return Vibrator::on(uint32_t timeout_ms) { int32_t ret = mDevice->vibrator_on(mDevice, timeout_ms); if (ret != 0) { ALOGE("on command failed : %s", strerror(-ret)); - return Status::ERR; + return Status::UNKNOWN_ERROR; } return Status::OK; } @@ -45,11 +47,24 @@ Return Vibrator::off() { int32_t ret = mDevice->vibrator_off(mDevice); if (ret != 0) { ALOGE("off command failed : %s", strerror(-ret)); - return Status::ERR; + return Status::UNKNOWN_ERROR; } return Status::OK; } +Return Vibrator::supportsAmplitudeControl() { + return false; +} + +Return Vibrator::setAmplitude(uint8_t) { + return Status::UNSUPPORTED_OPERATION; +} + +Return Vibrator::perform(Effect, EffectStrength, perform_cb _hidl_cb) { + _hidl_cb(Status::UNSUPPORTED_OPERATION, 0); + return Void(); +} + IVibrator* HIDL_FETCH_IVibrator(const char * /*hal*/) { vibrator_device_t *vib_device; const hw_module_t *hw_module = nullptr; diff --git a/vibrator/1.0/default/Vibrator.h b/vibrator/1.0/default/Vibrator.h index 061b36410e..bea6ea8f3a 100644 --- a/vibrator/1.0/default/Vibrator.h +++ b/vibrator/1.0/default/Vibrator.h @@ -26,23 +26,18 @@ namespace vibrator { namespace V1_0 { namespace implementation { -using ::android::hardware::vibrator::V1_0::IVibrator; -using ::android::hardware::vibrator::V1_0::Status; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hardware::hidl_vec; -using ::android::hardware::hidl_string; -using ::android::sp; - struct Vibrator : public IVibrator { Vibrator(vibrator_device_t *device); // Methods from ::android::hardware::vibrator::V1_0::IVibrator follow. Return on(uint32_t timeoutMs) override; Return off() override; + Return supportsAmplitudeControl() override; + Return setAmplitude(uint8_t amplitude) override; + Return perform(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override; - private: - vibrator_device_t *mDevice; +private: + vibrator_device_t *mDevice; }; extern "C" IVibrator* HIDL_FETCH_IVibrator(const char* name); diff --git a/vibrator/1.0/types.hal b/vibrator/1.0/types.hal index 8fc5683ddd..a080c07911 100644 --- a/vibrator/1.0/types.hal +++ b/vibrator/1.0/types.hal @@ -16,7 +16,33 @@ package android.hardware.vibrator@1.0; -enum Status: uint32_t { - OK = 0, - ERR = 1 +enum Status : uint32_t { + OK, + UNKNOWN_ERROR, + BAD_VALUE, + UNSUPPORTED_OPERATION +}; + +@export +enum Effect : uint32_t { + /** + * A single click effect. + * + * This effect should produce a sharp, crisp click sensation. + */ + CLICK, + /** + * A double click effect. + * + * This effect should produce two sequential sharp, crisp click sensations with a minimal + * amount of time between them. + */ + DOUBLE_CLICK +}; + +@export +enum EffectStrength : uint8_t { + LIGHT, + MEDIUM, + STRONG }; diff --git a/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp b/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp index a978f2c3e0..f415ad5191 100644 --- a/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp +++ b/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp @@ -22,6 +22,8 @@ #include #include +using ::android::hardware::vibrator::V1_0::Effect; +using ::android::hardware::vibrator::V1_0::EffectStrength; using ::android::hardware::vibrator::V1_0::IVibrator; using ::android::hardware::vibrator::V1_0::Status; using ::android::hardware::Return; @@ -50,12 +52,49 @@ class VibratorHidlEnvironment : public ::testing::Environment { private: }; +static void validatePerformEffect(Status status, uint32_t lengthMs) { + ASSERT_TRUE(status == Status::OK || status == Status::UNSUPPORTED_OPERATION); + if (status == Status::OK) { + ASSERT_GT(lengthMs, static_cast(0)); + } else { + ASSERT_EQ(lengthMs, static_cast(0)); + } +} + TEST_F(VibratorHidlTest, OnThenOffBeforeTimeout) { EXPECT_EQ(Status::OK, vibrator->on(2000)); sleep(1); EXPECT_EQ(Status::OK, vibrator->off()); } +TEST_F(VibratorHidlTest, PerformEffect) { + vibrator->perform(Effect::CLICK, EffectStrength::MEDIUM, validatePerformEffect); + vibrator->perform(Effect::DOUBLE_CLICK, EffectStrength::LIGHT, validatePerformEffect); +} + +TEST_F(VibratorHidlTest, ChangeVibrationalAmplitude) { + if (vibrator->supportsAmplitudeControl()) { + EXPECT_EQ(Status::OK, vibrator->setAmplitude(1)); + EXPECT_EQ(Status::OK, vibrator->on(2000)); + EXPECT_EQ(Status::OK, vibrator->setAmplitude(128)); + sleep(1); + EXPECT_EQ(Status::OK, vibrator->setAmplitude(255)); + sleep(1); + } +} + +TEST_F(VibratorHidlTest, AmplitudeOutsideRangeFails) { + if (vibrator->supportsAmplitudeControl()) { + EXPECT_EQ(Status::BAD_VALUE, vibrator->setAmplitude(0)); + } +} + +TEST_F(VibratorHidlTest, SetAmplitudeReturnUnsupportedOperationIfNotSupported) { + if (!vibrator->supportsAmplitudeControl()) { + EXPECT_EQ(Status::UNSUPPORTED_OPERATION, vibrator->setAmplitude(1)); + } +} + int main(int argc, char **argv) { ::testing::AddGlobalTestEnvironment(new VibratorHidlEnvironment); ::testing::InitGoogleTest(&argc, argv);