Introduce IVibrator.performVendorEffect
Introduce HAL API to perform vibration effects defined by the vendor.
The new API accepts a PersistableBundle representing the effect and some
platform-provided parameters, e.g. EffectStrength and scale.
The callback support for completion is required for this API, since
effect duration is undefined.
Fix: 345409060
Test: VtsHalVibratorTargetTest
Flag: EXEMPT HAL interface change
Change-Id: I26379ede4b64e41e1fc85feae6de5105cb636511
diff --git a/vibrator/aidl/Android.bp b/vibrator/aidl/Android.bp
index fe76450..b2d98f5 100644
--- a/vibrator/aidl/Android.bp
+++ b/vibrator/aidl/Android.bp
@@ -15,6 +15,9 @@
srcs: [
"android/hardware/vibrator/*.aidl",
],
+ headers: [
+ "PersistableBundle_aidl",
+ ],
stability: "vintf",
backend: {
java: {
@@ -26,9 +29,19 @@
cpp: {
enabled: false,
},
+ rust: {
+ enabled: false,
+ },
},
- versions: [
- "1",
- "2",
+ versions_with_info: [
+ {
+ version: "1",
+ imports: [],
+ },
+ {
+ version: "2",
+ imports: [],
+ },
],
+ frozen: false,
}
diff --git a/vibrator/aidl/TEST_MAPPING b/vibrator/aidl/TEST_MAPPING
index 2414b84..e00b3e9 100644
--- a/vibrator/aidl/TEST_MAPPING
+++ b/vibrator/aidl/TEST_MAPPING
@@ -6,5 +6,10 @@
{
"name": "VtsHalVibratorManagerTargetTest"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "VtsHalVibratorPerformVendorEffectFuzzer"
+ }
]
}
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
index b7afb66..af619c6 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
@@ -58,15 +58,17 @@
int getPwleCompositionSizeMax();
android.hardware.vibrator.Braking[] getSupportedBraking();
void composePwle(in android.hardware.vibrator.PrimitivePwle[] composite, in android.hardware.vibrator.IVibratorCallback callback);
- const int CAP_ON_CALLBACK = 1;
- const int CAP_PERFORM_CALLBACK = 2;
- const int CAP_AMPLITUDE_CONTROL = 4;
- const int CAP_EXTERNAL_CONTROL = 8;
- const int CAP_EXTERNAL_AMPLITUDE_CONTROL = 16;
- const int CAP_COMPOSE_EFFECTS = 32;
- const int CAP_ALWAYS_ON_CONTROL = 64;
- const int CAP_GET_RESONANT_FREQUENCY = 128;
- const int CAP_GET_Q_FACTOR = 256;
- const int CAP_FREQUENCY_CONTROL = 512;
- const int CAP_COMPOSE_PWLE_EFFECTS = 1024;
+ void performVendorEffect(in android.hardware.vibrator.VendorEffect vendorEffect, in android.hardware.vibrator.IVibratorCallback callback);
+ const int CAP_ON_CALLBACK = (1 << 0) /* 1 */;
+ const int CAP_PERFORM_CALLBACK = (1 << 1) /* 2 */;
+ const int CAP_AMPLITUDE_CONTROL = (1 << 2) /* 4 */;
+ const int CAP_EXTERNAL_CONTROL = (1 << 3) /* 8 */;
+ const int CAP_EXTERNAL_AMPLITUDE_CONTROL = (1 << 4) /* 16 */;
+ const int CAP_COMPOSE_EFFECTS = (1 << 5) /* 32 */;
+ const int CAP_ALWAYS_ON_CONTROL = (1 << 6) /* 64 */;
+ const int CAP_GET_RESONANT_FREQUENCY = (1 << 7) /* 128 */;
+ const int CAP_GET_Q_FACTOR = (1 << 8) /* 256 */;
+ const int CAP_FREQUENCY_CONTROL = (1 << 9) /* 512 */;
+ const int CAP_COMPOSE_PWLE_EFFECTS = (1 << 10) /* 1024 */;
+ const int CAP_PERFORM_VENDOR_EFFECTS = (1 << 11) /* 2048 */;
}
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
index 290c68d..ef5794c 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
@@ -40,12 +40,12 @@
void prepareSynced(in int[] vibratorIds);
void triggerSynced(in android.hardware.vibrator.IVibratorCallback callback);
void cancelSynced();
- const int CAP_SYNC = 1;
- const int CAP_PREPARE_ON = 2;
- const int CAP_PREPARE_PERFORM = 4;
- const int CAP_PREPARE_COMPOSE = 8;
- const int CAP_MIXED_TRIGGER_ON = 16;
- const int CAP_MIXED_TRIGGER_PERFORM = 32;
- const int CAP_MIXED_TRIGGER_COMPOSE = 64;
- const int CAP_TRIGGER_CALLBACK = 128;
+ const int CAP_SYNC = (1 << 0) /* 1 */;
+ const int CAP_PREPARE_ON = (1 << 1) /* 2 */;
+ const int CAP_PREPARE_PERFORM = (1 << 2) /* 4 */;
+ const int CAP_PREPARE_COMPOSE = (1 << 3) /* 8 */;
+ const int CAP_MIXED_TRIGGER_ON = (1 << 4) /* 16 */;
+ const int CAP_MIXED_TRIGGER_PERFORM = (1 << 5) /* 32 */;
+ const int CAP_MIXED_TRIGGER_COMPOSE = (1 << 6) /* 64 */;
+ const int CAP_TRIGGER_CALLBACK = (1 << 7) /* 128 */;
}
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/VendorEffect.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/VendorEffect.aidl
new file mode 100644
index 0000000..1900084
--- /dev/null
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/VendorEffect.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.vibrator;
+@VintfStability
+parcelable VendorEffect {
+ android.os.PersistableBundle vendorData;
+ android.hardware.vibrator.EffectStrength strength = android.hardware.vibrator.EffectStrength.MEDIUM;
+ float scale;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/Braking.aidl b/vibrator/aidl/android/hardware/vibrator/Braking.aidl
index 2bc51db..f934ff2 100644
--- a/vibrator/aidl/android/hardware/vibrator/Braking.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/Braking.aidl
@@ -23,12 +23,12 @@
* No braking mechanism used.
* This is the default if the hardware does not support any braking mechanism.
*/
- NONE,
+ NONE = 0,
/**
* Closed-loop active braking.
*
* This effect should produce a sharp, crisp end to the waveform
* Support is optional.
*/
- CLAB,
+ CLAB = 1,
}
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
index 5314898..5f8ee8d 100644
--- a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
@@ -24,13 +24,13 @@
*
* Support is required.
*/
- NOOP,
+ NOOP = 0,
/**
* This effect should produce a sharp, crisp click sensation.
*
* Support is required.
*/
- CLICK,
+ CLICK = 1,
/**
* A haptic effect that simulates downwards movement with gravity. Often
* followed by extra energy of hitting and reverberation to augment
@@ -38,43 +38,43 @@
*
* Support is optional.
*/
- THUD,
+ THUD = 2,
/**
* A haptic effect that simulates spinning momentum.
*
* Support is optional.
*/
- SPIN,
+ SPIN = 3,
/**
* A haptic effect that simulates quick upward movement against gravity.
*
* Support is required.
*/
- QUICK_RISE,
+ QUICK_RISE = 4,
/**
* A haptic effect that simulates slow upward movement against gravity.
*
* Support is required.
*/
- SLOW_RISE,
+ SLOW_RISE = 5,
/**
* A haptic effect that simulates quick downwards movement with gravity.
*
* Support is required.
*/
- QUICK_FALL,
+ QUICK_FALL = 6,
/**
* This very short effect should produce a light crisp sensation intended
* to be used repetitively for dynamic feedback.
*
* Support is required.
*/
- LIGHT_TICK,
+ LIGHT_TICK = 7,
/**
* This very short low frequency effect should produce a light crisp sensation intended
* to be used repetitively for dynamic feedback.
*
* Support is required.
*/
- LOW_TICK,
+ LOW_TICK = 8,
}
diff --git a/vibrator/aidl/android/hardware/vibrator/Effect.aidl b/vibrator/aidl/android/hardware/vibrator/Effect.aidl
index c60bfe9..f5cf9e3 100644
--- a/vibrator/aidl/android/hardware/vibrator/Effect.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/Effect.aidl
@@ -24,57 +24,57 @@
*
* This effect should produce a sharp, crisp click sensation.
*/
- CLICK,
+ CLICK = 0,
/**
* A double click effect.
*
* This effect should produce two sequential sharp, crisp click sensations with a minimal
* amount of time between them.
*/
- DOUBLE_CLICK,
+ DOUBLE_CLICK = 1,
/**
* A tick effect.
*
* This effect should produce a soft, short sensation, like the tick of a clock.
*/
- TICK,
+ TICK = 2,
/**
* A thud effect.
*
* This effect should solid feeling bump, like the depression of a heavy mechanical button.
*/
- THUD,
+ THUD = 3,
/**
* A pop effect.
*
* A short, quick burst effect.
*/
- POP,
+ POP = 4,
/**
* A heavy click effect.
*
* This should produce a sharp striking sensation, like a click but stronger.
*/
- HEAVY_CLICK,
+ HEAVY_CLICK = 5,
/**
* Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
* pattern that can be played as a ringtone with any audio, depending on the device.
*/
- RINGTONE_1,
- RINGTONE_2,
- RINGTONE_3,
- RINGTONE_4,
- RINGTONE_5,
- RINGTONE_6,
- RINGTONE_7,
- RINGTONE_8,
- RINGTONE_9,
- RINGTONE_10,
- RINGTONE_11,
- RINGTONE_12,
- RINGTONE_13,
- RINGTONE_14,
- RINGTONE_15,
+ RINGTONE_1 = 6,
+ RINGTONE_2 = 7,
+ RINGTONE_3 = 8,
+ RINGTONE_4 = 9,
+ RINGTONE_5 = 10,
+ RINGTONE_6 = 11,
+ RINGTONE_7 = 12,
+ RINGTONE_8 = 13,
+ RINGTONE_9 = 14,
+ RINGTONE_10 = 15,
+ RINGTONE_11 = 16,
+ RINGTONE_12 = 17,
+ RINGTONE_13 = 18,
+ RINGTONE_14 = 19,
+ RINGTONE_15 = 20,
/**
* A soft tick effect meant to be played as a texture.
*
@@ -82,5 +82,5 @@
* are expected to be played multiple times in quick succession, replicating a specific
* texture to the user as a form of haptic feedback.
*/
- TEXTURE_TICK,
+ TEXTURE_TICK = 21,
}
diff --git a/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl b/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
index 66f70e5..c6a78d4 100644
--- a/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
@@ -19,7 +19,7 @@
@VintfStability
@Backing(type="byte")
enum EffectStrength {
- LIGHT,
- MEDIUM,
- STRONG,
+ LIGHT = 0,
+ MEDIUM = 1,
+ STRONG = 2,
}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index b4e7e44..768ec4f 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -16,13 +16,14 @@
package android.hardware.vibrator;
-import android.hardware.vibrator.IVibratorCallback;
import android.hardware.vibrator.Braking;
-import android.hardware.vibrator.Effect;
-import android.hardware.vibrator.EffectStrength;
import android.hardware.vibrator.CompositeEffect;
import android.hardware.vibrator.CompositePrimitive;
+import android.hardware.vibrator.Effect;
+import android.hardware.vibrator.EffectStrength;
+import android.hardware.vibrator.IVibratorCallback;
import android.hardware.vibrator.PrimitivePwle;
+import android.hardware.vibrator.VendorEffect;
@VintfStability
interface IVibrator {
@@ -70,6 +71,10 @@
* Whether composePwle is supported.
*/
const int CAP_COMPOSE_PWLE_EFFECTS = 1 << 10;
+ /**
+ * Whether perform w/ vendor effect is supported.
+ */
+ const int CAP_PERFORM_VENDOR_EFFECTS = 1 << 11;
/**
* Determine capabilities of the vibrator HAL (CAP_* mask)
@@ -359,4 +364,25 @@
* @param composite Array of PWLEs.
*/
void composePwle(in PrimitivePwle[] composite, in IVibratorCallback callback);
+
+ /**
+ * Fire off a vendor-defined haptic event.
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_PERFORM_VENDOR_EFFECTS).
+ *
+ * The duration of the effect is unknown and can be undefined for looping effects.
+ * IVibratorCallback.onComplete() support is required for this API.
+ *
+ * Doing this operation while the vibrator is already on is undefined behavior. Clients should
+ * explicitly call off.
+ *
+ * @param effect The vendor data representing the effect to be performed.
+ * @param callback A callback used to inform Frameworks of state change.
+ * @throws :
+ * - EX_UNSUPPORTED_OPERATION if unsupported, as reflected by getCapabilities.
+ * - EX_ILLEGAL_ARGUMENT for bad framework parameters, e.g. scale or effect strength.
+ * - EX_SERVICE_SPECIFIC for bad vendor data, vibration is not triggered.
+ */
+ void performVendorEffect(in VendorEffect vendorEffect, in IVibratorCallback callback);
}
diff --git a/vibrator/aidl/android/hardware/vibrator/VendorEffect.aidl b/vibrator/aidl/android/hardware/vibrator/VendorEffect.aidl
new file mode 100644
index 0000000..2155aca
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/VendorEffect.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+package android.hardware.vibrator;
+
+import android.hardware.vibrator.EffectStrength;
+import android.os.PersistableBundle;
+
+@VintfStability
+parcelable VendorEffect {
+ /**
+ * Vendor data describing the haptic effect. Expected fields should be defined by the vendor.
+ *
+ * Vendors can use this as a platform extension point for experimental hardware capabilities,
+ * but they are strongly discouraged from using it as an alternative to the AOSP support for
+ * stable vibrator APIs. Implemenitng vendor-specific custom effects outside the platform APIs
+ * will hinder portability for the code and overall user experience.
+ *
+ * Vendors are encouraged to upstream new capabilities to the IVibrator surface once it has
+ * matured into a stable interface.
+ */
+ PersistableBundle vendorData;
+
+ /**
+ * The intensity of the haptic effect.
+ */
+ EffectStrength strength = EffectStrength.MEDIUM;
+
+ /**
+ * A scale to be applied to the haptic effect intensity.
+ *
+ * This value represents a linear scale that should be applied on top of the effect strength to
+ * dynamically adapt to the device state.
+ *
+ * Values in [0,1) should scale down. Values > 1 should scale up within hardware bounds.
+ */
+ float scale;
+}
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index 0f342db..4b26640 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -15,7 +15,7 @@
shared_libs: [
"libbase",
"libbinder_ndk",
- "android.hardware.vibrator-V2-ndk",
+ "android.hardware.vibrator-V3-ndk",
],
export_include_dirs: ["include"],
srcs: [
@@ -49,7 +49,7 @@
shared_libs: [
"libbase",
"libbinder_ndk",
- "android.hardware.vibrator-V2-ndk",
+ "android.hardware.vibrator-V3-ndk",
],
static_libs: [
"libvibratorexampleimpl",
@@ -62,7 +62,7 @@
host_supported: true,
defaults: ["service_fuzzer_defaults"],
static_libs: [
- "android.hardware.vibrator-V2-ndk",
+ "android.hardware.vibrator-V3-ndk",
"liblog",
"libvibratorexampleimpl",
],
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index 01602ab..acf7d34 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -39,6 +39,9 @@
static constexpr float PWLE_BW_MAP_SIZE =
1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
+// Service specific error code used for vendor vibration effects.
+static constexpr int32_t ERROR_CODE_INVALID_DURATION = 1;
+
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
LOG(VERBOSE) << "Vibrator reporting capabilities";
*_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
@@ -46,7 +49,7 @@
IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
- IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
+ IVibrator::CAP_COMPOSE_PWLE_EFFECTS | IVibrator::CAP_PERFORM_VENDOR_EFFECTS;
return ndk::ScopedAStatus::ok();
}
@@ -102,6 +105,36 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Vibrator::performVendorEffect(
+ const VendorEffect& effect, const std::shared_ptr<IVibratorCallback>& callback) {
+ LOG(VERBOSE) << "Vibrator perform vendor effect";
+ EffectStrength strength = effect.strength;
+ if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
+ strength != EffectStrength::STRONG) {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+ }
+ float scale = effect.scale;
+ if (scale <= 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ int32_t durationMs = 0;
+ if (!effect.vendorData.getInt("DURATION_MS", &durationMs) || durationMs <= 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ERROR_CODE_INVALID_DURATION);
+ }
+
+ if (callback != nullptr) {
+ std::thread([callback, durationMs] {
+ LOG(VERBOSE) << "Starting perform on another thread for durationMs:" << durationMs;
+ usleep(durationMs * 1000);
+ LOG(VERBOSE) << "Notifying perform vendor effect complete";
+ callback->onComplete();
+ }).detach();
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
*_aidl_return = {Effect::CLICK, Effect::TICK};
return ndk::ScopedAStatus::ok();
diff --git a/vibrator/aidl/default/android.hardware.vibrator.xml b/vibrator/aidl/default/android.hardware.vibrator.xml
index b5bd3dd..b730046 100644
--- a/vibrator/aidl/default/android.hardware.vibrator.xml
+++ b/vibrator/aidl/default/android.hardware.vibrator.xml
@@ -1,12 +1,12 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.vibrator</name>
- <version>2</version>
+ <version>3</version>
<fqname>IVibrator/default</fqname>
</hal>
<hal format="aidl">
<name>android.hardware.vibrator</name>
- <version>2</version>
+ <version>3</version>
<fqname>IVibratorManager/default</fqname>
</hal>
</manifest>
diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
index 4203bf2..e8f64ca 100644
--- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
+++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
@@ -31,6 +31,9 @@
ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback>& callback,
int32_t* _aidl_return) override;
+ ndk::ScopedAStatus performVendorEffect(
+ const VendorEffect& effect,
+ const std::shared_ptr<IVibratorCallback>& callback) override;
ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _aidl_return) override;
ndk::ScopedAStatus setAmplitude(float amplitude) override;
ndk::ScopedAStatus setExternalControl(bool enabled) override;
diff --git a/vibrator/aidl/vts/Android.bp b/vibrator/aidl/vts/Android.bp
index 166b30b..a48bb2e 100644
--- a/vibrator/aidl/vts/Android.bp
+++ b/vibrator/aidl/vts/Android.bp
@@ -20,7 +20,7 @@
"libbinder_ndk",
],
static_libs: [
- "android.hardware.vibrator-V2-ndk",
+ "android.hardware.vibrator-V3-ndk",
],
test_suites: [
"general-tests",
@@ -39,7 +39,7 @@
"libbinder_ndk",
],
static_libs: [
- "android.hardware.vibrator-V2-ndk",
+ "android.hardware.vibrator-V3-ndk",
],
test_suites: [
"general-tests",
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index 65a1e84..706ab41 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -21,10 +21,14 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <android/persistable_bundle_aidl.h>
#include <cmath>
+#include <cstdlib>
+#include <ctime>
#include <future>
+#include "persistable_bundle_utils.h"
#include "test_utils.h"
using aidl::android::hardware::vibrator::ActivePwle;
@@ -38,6 +42,8 @@
using aidl::android::hardware::vibrator::IVibrator;
using aidl::android::hardware::vibrator::IVibratorManager;
using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::VendorEffect;
+using aidl::android::os::PersistableBundle;
using std::chrono::high_resolution_clock;
using namespace ::std::chrono_literals;
@@ -357,6 +363,122 @@
}
}
+TEST_P(VibratorAidl, PerformVendorEffectSupported) {
+ if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+ float scale = 0.5f;
+ for (EffectStrength strength : kEffectStrengths) {
+ PersistableBundle vendorData;
+ ::aidl::android::hardware::vibrator::testing::fillBasicData(&vendorData);
+
+ PersistableBundle nestedData;
+ ::aidl::android::hardware::vibrator::testing::fillBasicData(&nestedData);
+ vendorData.putPersistableBundle("test_nested_bundle", nestedData);
+
+ VendorEffect effect;
+ effect.vendorData = vendorData;
+ effect.strength = strength;
+ effect.scale = scale;
+ scale *= 1.5f;
+
+ auto callback = ndk::SharedRefBase::make<CompletionCallback>([] {});
+ ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, callback);
+
+ // No expectations on the actual status, the effect might be refused with illegal argument
+ // or the vendor might return a service-specific error code.
+ EXPECT_TRUE(status.getExceptionCode() != EX_UNSUPPORTED_OPERATION &&
+ status.getStatus() != STATUS_UNKNOWN_TRANSACTION)
+ << status << "\n For vendor effect with strength" << toString(strength)
+ << " and scale " << effect.scale;
+
+ if (status.isOk()) {
+ // Generic vendor data should not trigger vibrations, but if it does trigger one
+ // then we make sure the vibrator is reset by triggering off().
+ EXPECT_OK(vibrator->off());
+ }
+ }
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectStability) {
+ if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+ // Run some iterations of performVendorEffect with randomized vendor data to check basic
+ // stability of the implementation.
+ uint8_t iterations = 200;
+
+ for (EffectStrength strength : kEffectStrengths) {
+ float scale = 0.5f;
+ for (uint8_t i = 0; i < iterations; i++) {
+ PersistableBundle vendorData;
+ ::aidl::android::hardware::vibrator::testing::fillRandomData(&vendorData);
+
+ VendorEffect effect;
+ effect.vendorData = vendorData;
+ effect.strength = strength;
+ effect.scale = scale;
+ scale *= 2;
+
+ auto callback = ndk::SharedRefBase::make<CompletionCallback>([] {});
+ ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, callback);
+
+ // No expectations on the actual status, the effect might be refused with illegal
+ // argument or the vendor might return a service-specific error code.
+ EXPECT_TRUE(status.getExceptionCode() != EX_UNSUPPORTED_OPERATION &&
+ status.getStatus() != STATUS_UNKNOWN_TRANSACTION)
+ << status << "\n For random vendor effect with strength " << toString(strength)
+ << " and scale " << effect.scale;
+
+ if (status.isOk()) {
+ // Random vendor data should not trigger vibrations, but if it does trigger one
+ // then we make sure the vibrator is reset by triggering off().
+ EXPECT_OK(vibrator->off());
+ }
+ }
+ }
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectEmptyVendorData) {
+ if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+ for (EffectStrength strength : kEffectStrengths) {
+ VendorEffect effect;
+ effect.strength = strength;
+ effect.scale = 1.0f;
+
+ ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, nullptr /*callback*/);
+
+ EXPECT_TRUE(status.getExceptionCode() == EX_SERVICE_SPECIFIC)
+ << status << "\n For vendor effect with strength " << toString(strength)
+ << " and scale " << effect.scale;
+ }
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectInvalidScale) {
+ if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+ VendorEffect effect;
+ effect.strength = EffectStrength::MEDIUM;
+
+ effect.scale = 0.0f;
+ EXPECT_ILLEGAL_ARGUMENT(vibrator->performVendorEffect(effect, nullptr /*callback*/));
+
+ effect.scale = -1.0f;
+ EXPECT_ILLEGAL_ARGUMENT(vibrator->performVendorEffect(effect, nullptr /*callback*/));
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectUnsupported) {
+ if (capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) return;
+
+ for (EffectStrength strength : kEffectStrengths) {
+ VendorEffect effect;
+ effect.strength = strength;
+ effect.scale = 1.0f;
+
+ EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->performVendorEffect(effect, nullptr /*callback*/))
+ << "\n For vendor effect with strength " << toString(strength);
+ }
+}
+
TEST_P(VibratorAidl, ChangeVibrationAmplitude) {
if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
EXPECT_OK(vibrator->setAmplitude(0.1f));
@@ -940,6 +1062,9 @@
PrintGeneratedTest);
int main(int argc, char **argv) {
+ // Random values are used in the implementation.
+ std::srand(std::time(nullptr));
+
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
diff --git a/vibrator/aidl/vts/persistable_bundle_utils.h b/vibrator/aidl/vts/persistable_bundle_utils.h
new file mode 100644
index 0000000..a765a49
--- /dev/null
+++ b/vibrator/aidl/vts/persistable_bundle_utils.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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 VIBRATOR_HAL_PERSISTABLE_BUNDLE_UTILS_H
+#define VIBRATOR_HAL_PERSISTABLE_BUNDLE_UTILS_H
+
+#include <android/persistable_bundle_aidl.h>
+
+#include <cstdlib>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace testing {
+
+using aidl::android::os::PersistableBundle;
+
+namespace {
+
+template <typename T>
+T nextValue() {
+ return static_cast<T>(std::rand());
+}
+
+template <>
+std::string nextValue() {
+ std::string str;
+ uint8_t entryCount = nextValue<uint8_t>();
+ for (uint8_t i = 0; i < entryCount; i++) {
+ str.push_back(nextValue<char>());
+ }
+ return str;
+}
+
+template <typename T>
+T nextValue(T limit) {
+ assert(limit > 0);
+ return static_cast<T>(std::rand()) / (static_cast<T>(RAND_MAX / limit));
+}
+
+template <typename T>
+void fillVector(std::vector<T>* values) {
+ uint8_t entryCount = nextValue<uint8_t>();
+ for (uint8_t i = 0; i < entryCount; i++) {
+ values->push_back(nextValue<T>());
+ }
+}
+
+const std::vector<std::function<void(PersistableBundle*, const std::string&)>>
+ sPersistableBundleSetters = {[](PersistableBundle* bundle, const std::string& key) -> void {
+ bundle->putBoolean(key, nextValue<bool>());
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ bundle->putInt(key, nextValue<int32_t>());
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ bundle->putLong(key, nextValue<int64_t>());
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ bundle->putDouble(key, nextValue<double>());
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ bundle->putString(key, nextValue<std::string>());
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ std::vector<bool> value;
+ fillVector<bool>(&value);
+ bundle->putBooleanVector(key, value);
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ std::vector<int32_t> value;
+ fillVector<int32_t>(&value);
+ bundle->putIntVector(key, value);
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ std::vector<int64_t> value;
+ fillVector<int64_t>(&value);
+ bundle->putLongVector(key, value);
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ std::vector<double> value;
+ fillVector<double>(&value);
+ bundle->putDoubleVector(key, value);
+ },
+ [](PersistableBundle* bundle, const std::string& key) -> void {
+ std::vector<std::string> value;
+ fillVector<std::string>(&value);
+ bundle->putStringVector(key, value);
+ }};
+
+} // namespace
+
+void fillBasicData(PersistableBundle* bundle) {
+ bundle->putBoolean("test_bool", true);
+ bundle->putInt("test_int", 2147483647);
+ bundle->putLong("test_long", 2147483647L);
+ bundle->putDouble("test_double", 1.23);
+ bundle->putString("test_string", "test data");
+ bundle->putBooleanVector("test_bool_vector", {true, false, false});
+ bundle->putIntVector("test_int_vector", {1, 2, 3, 4});
+ bundle->putLongVector("test_long_vector", {100L, 200L, 300L});
+ bundle->putDoubleVector("test_double_vector", {1.1, 2.2});
+ bundle->putStringVector("test_string_vector", {"test", "val"});
+}
+
+void fillRandomData(PersistableBundle* bundle) {
+ uint8_t entryCount = nextValue<uint8_t>();
+ for (uint8_t i = 0; i < entryCount; i++) {
+ std::string key(nextValue<std::string>());
+ uint8_t setterIdx = nextValue<uint8_t>(sPersistableBundleSetters.size() - 1);
+ sPersistableBundleSetters[setterIdx](bundle, key);
+ }
+}
+
+} // namespace testing
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // VIBRATOR_HAL_PERSISTABLE_BUNDLE_UTILS_H
diff --git a/vibrator/bench/Android.bp b/vibrator/bench/Android.bp
index b31c719..cd56516 100644
--- a/vibrator/bench/Android.bp
+++ b/vibrator/bench/Android.bp
@@ -30,7 +30,7 @@
"benchmark.cpp",
],
shared_libs: [
- "android.hardware.vibrator-V2-ndk",
+ "android.hardware.vibrator-V3-ndk",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",