summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2021-06-22 16:08:22 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-06-22 16:08:22 +0000
commit55a8db6450352d30da8478a8233bb7820e8bd40b (patch)
treea773695a342d55fc6f2cd64712d34d058a2a33a6
parenta9b80b098c46b26b6d14fd2398bcb8ad58f0f716 (diff)
parenta1167c2a064a0e5260e4d8cfa6161a3a79e76bd2 (diff)
Merge "Split long vibration composition using HAL limits" into sc-dev
-rw-r--r--core/java/android/hardware/input/InputDeviceVibrator.java8
-rw-r--r--core/java/android/os/VibratorInfo.java275
-rw-r--r--core/tests/coretests/src/android/os/VibratorInfoTest.java63
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java14
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java38
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp139
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java69
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java12
9 files changed, 439 insertions, 213 deletions
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index d8150e483fd6..653c622386d6 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -56,10 +56,10 @@ final class InputDeviceVibrator extends Vibrator {
mDeviceId = deviceId;
mVibratorInfo = new VibratorInfo.Builder(vibratorId)
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
- // Set predefined support to empty as we know input devices do not support them.
- .setSupportedEffects()
- .setSupportedPrimitives()
- .setSupportedBraking()
+ // The supported effect and braking lists are known to be empty for input devices,
+ // which is different from not being set (that means the device support is unknown).
+ .setSupportedEffects(new int[0])
+ .setSupportedBraking(new int[0])
.build();
mToken = new Binder();
}
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 597df0811e20..486f9f139e0a 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -51,8 +51,11 @@ public class VibratorInfo implements Parcelable {
private final SparseBooleanArray mSupportedEffects;
@Nullable
private final SparseBooleanArray mSupportedBraking;
- @Nullable
private final SparseIntArray mSupportedPrimitives;
+ private final int mPrimitiveDelayMax;
+ private final int mCompositionSizeMax;
+ private final int mPwlePrimitiveDurationMax;
+ private final int mPwleSizeMax;
private final float mQFactor;
private final FrequencyMapping mFrequencyMapping;
@@ -62,6 +65,10 @@ public class VibratorInfo implements Parcelable {
mSupportedEffects = in.readSparseBooleanArray();
mSupportedBraking = in.readSparseBooleanArray();
mSupportedPrimitives = in.readSparseIntArray();
+ mPrimitiveDelayMax = in.readInt();
+ mCompositionSizeMax = in.readInt();
+ mPwlePrimitiveDurationMax = in.readInt();
+ mPwleSizeMax = in.readInt();
mQFactor = in.readFloat();
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
}
@@ -69,48 +76,50 @@ public class VibratorInfo implements Parcelable {
/**
* Default constructor.
*
- * @param id The vibrator id.
- * @param capabilities All capability flags of the vibrator, defined in IVibrator.CAP_*.
- * @param supportedEffects All supported predefined effects, enum values from {@link
- * android.hardware.vibrator.Effect}.
- * @param supportedBraking All supported braking types, enum values from {@link Braking}.
- * @param supportedPrimitives All supported primitive effects, enum values from {@link
- * android.hardware.vibrator.CompositePrimitive}.
- * @param primitiveDurations A mapping of primitive durations, where indexes are enum values
- * from {@link android.hardware.vibrator.CompositePrimitive} and the
- * values are estimated durations in milliseconds.
- * @param qFactor The vibrator quality factor.
- * @param frequencyMapping The description of the vibrator supported frequencies and max
- * amplitude mappings.
+ * @param id The vibrator id.
+ * @param capabilities All capability flags of the vibrator, defined in
+ * IVibrator.CAP_*.
+ * @param supportedEffects All supported predefined effects, enum values from
+ * {@link android.hardware.vibrator.Effect}.
+ * @param supportedBraking All supported braking types, enum values from {@link
+ * Braking}.
+ * @param supportedPrimitives All supported primitive effects, key are enum values from
+ * {@link android.hardware.vibrator.CompositePrimitive} and
+ * values are estimated durations in milliseconds.
+ * @param primitiveDelayMax The maximum delay that can be set to a composition primitive
+ * in milliseconds.
+ * @param compositionSizeMax The maximum number of primitives supported by a composition.
+ * @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds.
+ * @param pwleSizeMax The maximum number of primitives supported by a PWLE
+ * composition.
+ * @param qFactor The vibrator quality factor.
+ * @param frequencyMapping The description of the vibrator supported frequencies and max
+ * amplitude mappings.
* @hide
*/
- public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
- int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
- @NonNull FrequencyMapping frequencyMapping) {
+ public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
+ @Nullable SparseBooleanArray supportedBraking,
+ @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
+ int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
+ float qFactor, @NonNull FrequencyMapping frequencyMapping) {
mId = id;
mCapabilities = capabilities;
- mSupportedEffects = toSparseBooleanArray(supportedEffects);
- mSupportedBraking = toSparseBooleanArray(supportedBraking);
- mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
+ mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
+ mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
+ mSupportedPrimitives = supportedPrimitives.clone();
+ mPrimitiveDelayMax = primitiveDelayMax;
+ mCompositionSizeMax = compositionSizeMax;
+ mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+ mPwleSizeMax = pwleSizeMax;
mQFactor = qFactor;
mFrequencyMapping = frequencyMapping;
}
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
- mId = id;
- mCapabilities = capabilities;
- mSupportedEffects = baseVibrator.mSupportedEffects == null ? null :
- baseVibrator.mSupportedEffects.clone();
- mSupportedBraking = baseVibrator.mSupportedBraking == null ? null :
- baseVibrator.mSupportedBraking.clone();
- mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null :
- baseVibrator.mSupportedPrimitives.clone();
- mQFactor = baseVibrator.mQFactor;
- mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz,
- baseVibrator.mFrequencyMapping.mResonantFrequencyHz,
- baseVibrator.mFrequencyMapping.mFrequencyResolutionHz,
- baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz,
- baseVibrator.mFrequencyMapping.mMaxAmplitudes);
+ this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
+ baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
+ baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
+ baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
}
@Override
@@ -120,6 +129,10 @@ public class VibratorInfo implements Parcelable {
dest.writeSparseBooleanArray(mSupportedEffects);
dest.writeSparseBooleanArray(mSupportedBraking);
dest.writeSparseIntArray(mSupportedPrimitives);
+ dest.writeInt(mPrimitiveDelayMax);
+ dest.writeInt(mCompositionSizeMax);
+ dest.writeInt(mPwlePrimitiveDurationMax);
+ dest.writeInt(mPwleSizeMax);
dest.writeFloat(mQFactor);
dest.writeParcelable(mFrequencyMapping, flags);
}
@@ -138,24 +151,23 @@ public class VibratorInfo implements Parcelable {
return false;
}
VibratorInfo that = (VibratorInfo) o;
- if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
- if (mSupportedPrimitives != that.mSupportedPrimitives) {
+ int supportedPrimitivesCount = mSupportedPrimitives.size();
+ if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
+ return false;
+ }
+ for (int i = 0; i < supportedPrimitivesCount; i++) {
+ if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
return false;
}
- } else {
- if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
+ if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
return false;
}
- for (int i = 0; i < mSupportedPrimitives.size(); i++) {
- if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
- return false;
- }
- if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
- return false;
- }
- }
}
return mId == that.mId && mCapabilities == that.mCapabilities
+ && mPrimitiveDelayMax == that.mPrimitiveDelayMax
+ && mCompositionSizeMax == that.mCompositionSizeMax
+ && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
+ && mPwleSizeMax == that.mPwleSizeMax
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
&& Objects.equals(mQFactor, that.mQFactor)
@@ -166,11 +178,9 @@ public class VibratorInfo implements Parcelable {
public int hashCode() {
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mQFactor, mFrequencyMapping);
- if (mSupportedPrimitives != null) {
- for (int i = 0; i < mSupportedPrimitives.size(); i++) {
- hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
- hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
- }
+ for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
+ hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
}
return hashCode;
}
@@ -184,6 +194,10 @@ public class VibratorInfo implements Parcelable {
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
+ ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+ + ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
+ + ", mCompositionSizeMax=" + mCompositionSizeMax
+ + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
+ + ", mPwleSizeMax=" + mPwleSizeMax
+ ", mQFactor=" + mQFactor
+ ", mFrequencyMapping=" + mFrequencyMapping
+ '}';
@@ -247,7 +261,7 @@ public class VibratorInfo implements Parcelable {
*/
public boolean isPrimitiveSupported(
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
- return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
+ return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
&& (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
}
@@ -260,7 +274,43 @@ public class VibratorInfo implements Parcelable {
*/
public int getPrimitiveDuration(
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
- return mSupportedPrimitives != null ? mSupportedPrimitives.get(primitiveId) : 0;
+ return mSupportedPrimitives.get(primitiveId);
+ }
+
+ /**
+ * Query the maximum delay supported for a primitive in a composed effect.
+ *
+ * @return The max delay in milliseconds, or zero if unlimited.
+ */
+ public int getPrimitiveDelayMax() {
+ return mPrimitiveDelayMax;
+ }
+
+ /**
+ * Query the maximum number of primitives supported in a composed effect.
+ *
+ * @return The max number of primitives supported, or zero if unlimited.
+ */
+ public int getCompositionSizeMax() {
+ return mCompositionSizeMax;
+ }
+
+ /**
+ * Query the maximum duration supported for a primitive in a PWLE composition.
+ *
+ * @return The max duration in milliseconds, or zero if unlimited.
+ */
+ public int getPwlePrimitiveDurationMax() {
+ return mPwlePrimitiveDurationMax;
+ }
+
+ /**
+ * Query the maximum number of primitives supported in a PWLE composition.
+ *
+ * @return The max number of primitives supported, or zero if unlimited.
+ */
+ public int getPwleSizeMax() {
+ return mPwleSizeMax;
}
/**
@@ -408,52 +458,15 @@ public class VibratorInfo implements Parcelable {
}
private String[] getSupportedPrimitivesNames() {
- if (mSupportedPrimitives == null) {
- return new String[0];
- }
- String[] names = new String[mSupportedPrimitives.size()];
- for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ int supportedPrimitivesCount = mSupportedPrimitives.size();
+ String[] names = new String[supportedPrimitivesCount];
+ for (int i = 0; i < supportedPrimitivesCount; i++) {
names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
}
return names;
}
/**
- * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is mapped
- * to {@code true}.
- */
- @Nullable
- private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
- if (supportedKeys == null) {
- return null;
- }
- SparseBooleanArray array = new SparseBooleanArray();
- for (int key : supportedKeys) {
- array.put(key, true);
- }
- return array;
- }
-
- /**
- * Create a {@link SparseIntArray} from given {@code supportedKeys} where each key is mapped
- * to the value indexed by it.
- *
- * <p>If {@code values} is null or does not contain a given key as a index, then zero is stored
- * to the sparse array so it can still be used to query the supported keys.
- */
- @Nullable
- private static SparseIntArray toSparseIntArray(int[] supportedKeys, int[] values) {
- if (supportedKeys == null) {
- return null;
- }
- SparseIntArray array = new SparseIntArray();
- for (int key : supportedKeys) {
- array.put(key, (values == null || key >= values.length) ? 0 : values[key]);
- }
- return array;
- }
-
- /**
* Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
*
* <p>This mapping is defined by the following parameters:
@@ -675,11 +688,14 @@ public class VibratorInfo implements Parcelable {
/** @hide */
public static final class Builder {
private final int mId;
- private int mCapabilities = 0;
- private int[] mSupportedEffects = null;
- private int[] mSupportedBraking = null;
- private int[] mSupportedPrimitives = null;
- private int[] mPrimitiveDurations = new int[0];
+ private long mCapabilities;
+ private SparseBooleanArray mSupportedEffects;
+ private SparseBooleanArray mSupportedBraking;
+ private SparseIntArray mSupportedPrimitives = new SparseIntArray();
+ private int mPrimitiveDelayMax;
+ private int mCompositionSizeMax;
+ private int mPwlePrimitiveDurationMax;
+ private int mPwleSizeMax;
private float mQFactor = Float.NaN;
private FrequencyMapping mFrequencyMapping =
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
@@ -691,7 +707,7 @@ public class VibratorInfo implements Parcelable {
/** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
@NonNull
- public Builder setCapabilities(int capabilities) {
+ public Builder setCapabilities(long capabilities) {
mCapabilities = capabilities;
return this;
}
@@ -699,34 +715,49 @@ public class VibratorInfo implements Parcelable {
/** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
@NonNull
public Builder setSupportedEffects(int... supportedEffects) {
- mSupportedEffects = supportedEffects;
+ mSupportedEffects = toSparseBooleanArray(supportedEffects);
return this;
}
/** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
@NonNull
public Builder setSupportedBraking(int... supportedBraking) {
- mSupportedBraking = supportedBraking;
+ mSupportedBraking = toSparseBooleanArray(supportedBraking);
return this;
}
- /**
- * Configure the primitives supported with
- * {@link android.hardware.vibrator.CompositePrimitive} values.
- */
+ /** Configure maximum duration, in milliseconds, of a PWLE primitive. */
+ @NonNull
+ public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
+ mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+ return this;
+ }
+
+ /** Configure maximum number of primitives supported in a single PWLE composed effect. */
@NonNull
- public Builder setSupportedPrimitives(int... supportedPrimitives) {
- mSupportedPrimitives = supportedPrimitives;
+ public Builder setPwleSizeMax(int pwleSizeMax) {
+ mPwleSizeMax = pwleSizeMax;
return this;
}
/** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
@NonNull
- public Builder setPrimitiveDuration(int primitiveId, int duration) {
- if (mPrimitiveDurations.length <= primitiveId) {
- mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
- }
- mPrimitiveDurations[primitiveId] = duration;
+ public Builder setSupportedPrimitive(int primitiveId, int duration) {
+ mSupportedPrimitives.put(primitiveId, duration);
+ return this;
+ }
+
+ /** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */
+ @NonNull
+ public Builder setPrimitiveDelayMax(int primitiveDelayMax) {
+ mPrimitiveDelayMax = primitiveDelayMax;
+ return this;
+ }
+
+ /** Configure maximum number of primitives supported in a single composed effect. */
+ @NonNull
+ public Builder setCompositionSizeMax(int compositionSizeMax) {
+ mCompositionSizeMax = compositionSizeMax;
return this;
}
@@ -748,7 +779,25 @@ public class VibratorInfo implements Parcelable {
@NonNull
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mPrimitiveDurations, mQFactor, mFrequencyMapping);
+ mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
+ mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
+ }
+
+ /**
+ * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is
+ * mapped
+ * to {@code true}.
+ */
+ @Nullable
+ private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
+ if (supportedKeys == null) {
+ return null;
+ }
+ SparseBooleanArray array = new SparseBooleanArray();
+ for (int key : supportedKeys) {
+ array.put(key, true);
+ }
+ return array;
}
}
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 8c7d10c7a5ef..6e07fa264c1c 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -87,14 +87,14 @@ public class VibratorInfoTest {
public void testIsPrimitiveSupported() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
// Returns false when there is no compose capability.
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -103,8 +103,7 @@ public class VibratorInfoTest {
public void testGetPrimitiveDuration() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.build();
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
@@ -113,6 +112,26 @@ public class VibratorInfoTest {
}
@Test
+ public void testCompositionLimits() {
+ VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setPrimitiveDelayMax(100)
+ .setCompositionSizeMax(10)
+ .setPwlePrimitiveDurationMax(50)
+ .setPwleSizeMax(20)
+ .build();
+ assertEquals(100, info.getPrimitiveDelayMax());
+ assertEquals(10, info.getCompositionSizeMax());
+ assertEquals(50, info.getPwlePrimitiveDurationMax());
+ assertEquals(20, info.getPwleSizeMax());
+
+ VibratorInfo emptyInfo = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ assertEquals(0, emptyInfo.getPrimitiveDelayMax());
+ assertEquals(0, emptyInfo.getCompositionSizeMax());
+ assertEquals(0, emptyInfo.getPwlePrimitiveDurationMax());
+ assertEquals(0, emptyInfo.getPwleSizeMax());
+ }
+
+ @Test
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
assertEquals(Braking.NONE, new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getDefaultBraking());
@@ -263,8 +282,12 @@ public class VibratorInfoTest {
VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setPrimitiveDelayMax(100)
+ .setCompositionSizeMax(10)
+ .setSupportedBraking(Braking.CLAB)
+ .setPwlePrimitiveDurationMax(50)
+ .setPwleSizeMax(20)
.setQFactor(2f)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
VibratorInfo complete = completeBuilder.build();
@@ -279,8 +302,7 @@ public class VibratorInfoTest {
assertNotEquals(complete, completeWithComposeControl);
VibratorInfo completeWithNoEffects = completeBuilder
- .setSupportedEffects()
- .setSupportedPrimitives()
+ .setSupportedEffects(new int[0])
.build();
assertNotEquals(complete, completeWithNoEffects);
@@ -289,13 +311,8 @@ public class VibratorInfoTest {
.build();
assertNotEquals(complete, completeWithUnknownEffects);
- VibratorInfo completeWithUnknownPrimitives = completeBuilder
- .setSupportedPrimitives(null)
- .build();
- assertNotEquals(complete, completeWithUnknownPrimitives);
-
VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
@@ -321,12 +338,17 @@ public class VibratorInfoTest {
.build();
assertNotEquals(complete, completeWithDifferentQFactor);
- VibratorInfo empty = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
- VibratorInfo emptyWithKnownSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setSupportedEffects()
- .setSupportedPrimitives()
+ VibratorInfo unknownEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ VibratorInfo knownEmptyEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setSupportedEffects(new int[0])
+ .build();
+ assertNotEquals(unknownEffectSupport, knownEmptyEffectSupport);
+
+ VibratorInfo unknownBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ VibratorInfo knownEmptyBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setSupportedBraking(new int[0])
.build();
- assertNotEquals(empty, emptyWithKnownSupport);
+ assertNotEquals(unknownBrakingSupport, knownEmptyBrakingSupport);
}
@Test
@@ -334,8 +356,7 @@ public class VibratorInfoTest {
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(Float.NaN)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.build();
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 150fde99b706..45d511159e11 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -844,7 +844,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
public List<Step> play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
try {
- int segmentCount = effect.getSegments().size();
+ // Load the next PrimitiveSegments to create a single compose call to the vibrator,
+ // limited to the vibrator composition maximum size.
+ int limit = controller.getVibratorInfo().getCompositionSizeMax();
+ int segmentCount = limit > 0
+ ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+ : effect.getSegments().size();
List<PrimitiveSegment> primitives = new ArrayList<>();
for (int i = segmentIndex; i < segmentCount; i++) {
VibrationEffectSegment segment = effect.getSegments().get(i);
@@ -896,7 +901,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
public List<Step> play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
try {
- int segmentCount = effect.getSegments().size();
+ // Load the next RampSegments to create a single composePwle call to the vibrator,
+ // limited to the vibrator PWLE maximum size.
+ int limit = controller.getVibratorInfo().getPwleSizeMax();
+ int segmentCount = limit > 0
+ ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+ : effect.getSegments().size();
List<RampSegment> pwles = new ArrayList<>();
for (int i = segmentIndex; i < segmentCount; i++) {
VibrationEffectSegment segment = effect.getSegments().get(i);
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 5dceac2d066c..001d5c440d58 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -30,19 +30,24 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import libcore.util.NativeAllocationRegistry;
/** Controls a single vibrator. */
final class VibratorController {
private static final String TAG = "VibratorController";
+ // TODO(b/167947076): load suggested range from config
+ private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
private final Object mLock = new Object();
private final NativeWrapper mNativeWrapper;
- private final VibratorInfo mVibratorInfo;
+ private final VibratorInfo.Builder mVibratorInfoBuilder;
@GuardedBy("mLock")
+ private VibratorInfo mVibratorInfo;
+ @GuardedBy("mLock")
+ private boolean mVibratorInfoLoaded;
+ @GuardedBy("mLock")
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
new RemoteCallbackList<>();
@GuardedBy("mLock")
@@ -66,10 +71,10 @@ final class VibratorController {
NativeWrapper nativeWrapper) {
mNativeWrapper = nativeWrapper;
mNativeWrapper.init(vibratorId, listener);
- // TODO(b/167947076): load suggested range from config
- mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 200);
- Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
- vibratorId);
+ mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
+ mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+ mVibratorInfoBuilder);
+ mVibratorInfo = mVibratorInfoBuilder.build();
}
/** Register state listener for this vibrator. */
@@ -103,7 +108,15 @@ final class VibratorController {
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
public VibratorInfo getVibratorInfo() {
- return mVibratorInfo;
+ synchronized (mLock) {
+ if (!mVibratorInfoLoaded) {
+ // Try to load the vibrator metadata that has failed in the last attempt.
+ mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+ mVibratorInfoBuilder);
+ mVibratorInfo = mVibratorInfoBuilder.build();
+ }
+ return mVibratorInfo;
+ }
}
/**
@@ -361,7 +374,8 @@ final class VibratorController {
private static native void alwaysOnDisable(long nativePtr, long id);
- private static native VibratorInfo getInfo(long nativePtr, float suggestedFrequencyRange);
+ private static native boolean getInfo(long nativePtr, float suggestedFrequencyRange,
+ VibratorInfo.Builder infoBuilder);
private long mNativePtr = 0;
@@ -428,9 +442,11 @@ final class VibratorController {
alwaysOnDisable(mNativePtr, id);
}
- /** Return device vibrator metadata. */
- public VibratorInfo getInfo(float suggestedFrequencyRange) {
- return getInfo(mNativePtr, suggestedFrequencyRange);
+ /**
+ * Loads device vibrator metadata and returns true if all metadata was loaded successfully.
+ */
+ public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
+ return getInfo(mNativePtr, suggestedFrequencyRange, infoBuilder);
}
}
}
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 698e3f75d0ed..9029fe7cca66 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -41,8 +41,18 @@ static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnComplete;
static jclass sFrequencyMappingClass;
static jmethodID sFrequencyMappingCtor;
-static jclass sVibratorInfoClass;
-static jmethodID sVibratorInfoCtor;
+static struct {
+ jmethodID setCapabilities;
+ jmethodID setSupportedEffects;
+ jmethodID setSupportedBraking;
+ jmethodID setPwlePrimitiveDurationMax;
+ jmethodID setPwleSizeMax;
+ jmethodID setSupportedPrimitive;
+ jmethodID setPrimitiveDelayMax;
+ jmethodID setCompositionSizeMax;
+ jmethodID setQFactor;
+ jmethodID setFrequencyMapping;
+} sVibratorInfoBuilderClassInfo;
static struct {
jfieldID id;
jfieldID scale;
@@ -352,68 +362,88 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr,
wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable");
}
-static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jfloat suggestedSafeRange) {
+static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jfloat suggestedSafeRange, jobject vibratorInfoBuilder) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
- return nullptr;
+ return JNI_FALSE;
}
vibrator::Info info = wrapper->getVibratorInfo();
- jlong capabilities =
- static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE));
- jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
- jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
- jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
- jfloat qFactor = static_cast<jfloat>(info.qFactor.valueOr(NAN));
- jintArray supportedEffects = nullptr;
- jintArray supportedBraking = nullptr;
- jintArray supportedPrimitives = nullptr;
- jintArray primitiveDurations = nullptr;
- jfloatArray maxAmplitudes = nullptr;
-
+ if (info.capabilities.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setCapabilities,
+ static_cast<jlong>(info.capabilities.value()));
+ }
if (info.supportedEffects.isOk()) {
std::vector<aidl::Effect> effects = info.supportedEffects.value();
- supportedEffects = env->NewIntArray(effects.size());
+ jintArray supportedEffects = env->NewIntArray(effects.size());
env->SetIntArrayRegion(supportedEffects, 0, effects.size(),
reinterpret_cast<jint*>(effects.data()));
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setSupportedEffects, supportedEffects);
}
if (info.supportedBraking.isOk()) {
std::vector<aidl::Braking> braking = info.supportedBraking.value();
- supportedBraking = env->NewIntArray(braking.size());
+ jintArray supportedBraking = env->NewIntArray(braking.size());
env->SetIntArrayRegion(supportedBraking, 0, braking.size(),
reinterpret_cast<jint*>(braking.data()));
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setSupportedBraking, supportedBraking);
}
- if (info.supportedPrimitives.isOk()) {
- std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value();
- supportedPrimitives = env->NewIntArray(primitives.size());
- env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
- reinterpret_cast<jint*>(primitives.data()));
+ if (info.pwlePrimitiveDurationMax.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax,
+ static_cast<jint>(info.pwlePrimitiveDurationMax.value().count()));
+ }
+ if (info.pwleSizeMax.isOk()) {
+ // Use (pwleMaxSize - 1) to account for a possible extra braking segment added by the
+ // vibratorPerformPwleEffect method.
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setPwleSizeMax,
+ static_cast<jint>(info.pwleSizeMax.value() - 1));
}
- if (info.primitiveDurations.isOk()) {
- std::vector<int32_t> durations;
- for (auto duration : info.primitiveDurations.value()) {
- durations.push_back(duration.count());
+ if (info.supportedPrimitives.isOk()) {
+ auto durations = info.primitiveDurations.valueOr({});
+ for (auto& primitive : info.supportedPrimitives.value()) {
+ auto primitiveIdx = static_cast<size_t>(primitive);
+ auto duration = durations.size() > primitiveIdx ? durations[primitiveIdx].count() : 0;
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setSupportedPrimitive,
+ static_cast<jint>(primitive), static_cast<jint>(duration));
}
- primitiveDurations = env->NewIntArray(durations.size());
- env->SetIntArrayRegion(primitiveDurations, 0, durations.size(),
- reinterpret_cast<jint*>(durations.data()));
}
+ if (info.primitiveDelayMax.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax,
+ static_cast<jint>(info.primitiveDelayMax.value().count()));
+ }
+ if (info.compositionSizeMax.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setCompositionSizeMax,
+ static_cast<jint>(info.compositionSizeMax.value()));
+ }
+ if (info.qFactor.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setQFactor,
+ static_cast<jfloat>(info.qFactor.value()));
+ }
+
+ jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
+ jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
+ jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
+ jfloatArray maxAmplitudes = nullptr;
if (info.maxAmplitudes.isOk()) {
std::vector<float> amplitudes = info.maxAmplitudes.value();
maxAmplitudes = env->NewFloatArray(amplitudes.size());
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
reinterpret_cast<jfloat*>(amplitudes.data()));
}
-
jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
minFrequency, resonantFrequency, frequencyResolution,
suggestedSafeRange, maxAmplitudes);
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
+ frequencyMapping);
- return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
- capabilities, supportedEffects, supportedBraking, supportedPrimitives,
- primitiveDurations, qFactor, frequencyMapping);
+ return info.checkAndLogFailure("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
}
static const JNINativeMethod method_table[] = {
@@ -433,7 +463,7 @@ static const JNINativeMethod method_table[] = {
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
{"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
- {"getInfo", "(JF)Landroid/os/VibratorInfo;", (void*)vibratorGetInfo},
+ {"getInfo", "(JFLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
};
int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -459,11 +489,38 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
- jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
- sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
- sVibratorInfoCtor =
- GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
- "(IJ[I[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
+ jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
+ sVibratorInfoBuilderClassInfo.setCapabilities =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCapabilities",
+ "(J)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setSupportedEffects =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedEffects",
+ "([I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setSupportedBraking =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedBraking",
+ "([I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwlePrimitiveDurationMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setPwleSizeMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwleSizeMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setSupportedPrimitive =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedPrimitive",
+ "(II)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPrimitiveDelayMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setCompositionSizeMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCompositionSizeMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setQFactor =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setQFactor",
+ "(F)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setFrequencyMapping =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyMapping",
+ "(Landroid/os/VibratorInfo$FrequencyMapping;)"
+ "Landroid/os/VibratorInfo$Builder;");
return jniRegisterNativeMethods(env,
"com/android/server/vibrator/VibratorController$NativeWrapper",
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 058575817acf..0449e4450d06 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -56,6 +56,8 @@ final class FakeVibratorControllerProvider {
private int[] mSupportedEffects;
private int[] mSupportedBraking;
private int[] mSupportedPrimitives;
+ private int mCompositionSizeMax;
+ private int mPwleSizeMax;
private float mMinFrequency = Float.NaN;
private float mResonantFrequency = Float.NaN;
private float mFrequencyResolution = Float.NaN;
@@ -151,12 +153,22 @@ final class FakeVibratorControllerProvider {
}
@Override
- public VibratorInfo getInfo(float suggestedFrequencyRange) {
- VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
- mMinFrequency, mResonantFrequency, mFrequencyResolution,
- suggestedFrequencyRange, mMaxAmplitudes);
- return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, null, mQFactor, frequencyMapping);
+ public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
+ infoBuilder.setCapabilities(mCapabilities);
+ infoBuilder.setSupportedBraking(mSupportedBraking);
+ infoBuilder.setPwleSizeMax(mPwleSizeMax);
+ infoBuilder.setSupportedEffects(mSupportedEffects);
+ if (mSupportedPrimitives != null) {
+ for (int primitive : mSupportedPrimitives) {
+ infoBuilder.setSupportedPrimitive(primitive, EFFECT_DURATION);
+ }
+ }
+ infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
+ infoBuilder.setQFactor(mQFactor);
+ infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
+ mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
+ mMaxAmplitudes));
+ return true;
}
private void applyLatency() {
@@ -236,6 +248,16 @@ final class FakeVibratorControllerProvider {
mSupportedPrimitives = primitives;
}
+ /** Set the max number of primitives allowed in a composition by the fake vibrator hardware. */
+ public void setCompositionSizeMax(int compositionSizeMax) {
+ mCompositionSizeMax = compositionSizeMax;
+ }
+
+ /** Set the max number of PWLEs allowed in a composition by the fake vibrator hardware. */
+ public void setPwleSizeMax(int pwleSizeMax) {
+ mPwleSizeMax = pwleSizeMax;
+ }
+
/** Set the resonant frequency of the fake vibrator hardware. */
public void setResonantFrequency(float frequencyHz) {
mResonantFrequency = frequencyHz;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index f02e2f081e3b..b8fdb552e453 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -356,7 +356,8 @@ public class VibrationThreadTest {
@Test
public void vibrate_singleVibratorComposed_runsVibration() throws Exception {
- mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
@@ -374,7 +375,7 @@ public class VibrationThreadTest {
assertEquals(Arrays.asList(
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
+ fakeVibrator.getEffectSegments());
}
@Test
@@ -395,6 +396,27 @@ public class VibrationThreadTest {
}
@Test
+ public void vibrate_singleVibratorLargeComposition_splitsVibratorComposeCalls() {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setCompositionSizeMax(2);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
+ .compose();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+ // Vibrator compose called twice.
+ verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ assertEquals(3, fakeVibrator.getEffectSegments().size());
+ }
+
+ @Test
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
@@ -432,12 +454,13 @@ public class VibrationThreadTest {
@Test
public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
- mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedBraking(Braking.CLAB);
- mVibratorProviders.get(VIBRATOR_ID).setMinFrequency(100);
- mVibratorProviders.get(VIBRATOR_ID).setResonantFrequency(150);
- mVibratorProviders.get(VIBRATOR_ID).setFrequencyResolution(50);
- mVibratorProviders.get(VIBRATOR_ID).setMaxAmplitudes(
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ fakeVibrator.setSupportedBraking(Braking.CLAB);
+ fakeVibrator.setMinFrequency(100);
+ fakeVibrator.setResonantFrequency(150);
+ fakeVibrator.setFrequencyResolution(50);
+ fakeVibrator.setMaxAmplitudes(
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
long vibrationId = 1;
@@ -462,8 +485,34 @@ public class VibrationThreadTest {
expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
- assertEquals(Arrays.asList(Braking.CLAB), mVibratorProviders.get(VIBRATOR_ID).getBraking());
+ fakeVibrator.getEffectSegments());
+ assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking());
+ }
+
+ @Test
+ public void vibrate_singleVibratorLargePwle_splitsVibratorComposeCalls() {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ fakeVibrator.setMinFrequency(100);
+ fakeVibrator.setResonantFrequency(150);
+ fakeVibrator.setFrequencyResolution(50);
+ fakeVibrator.setMaxAmplitudes(1, 1, 1);
+ fakeVibrator.setPwleSizeMax(2);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startWaveform()
+ .addStep(1, 10)
+ .addRamp(0, 20)
+ .addStep(0.8f, 1, 30)
+ .addRamp(0.6f, -1, 40)
+ .build();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+ // Vibrator compose called twice.
+ verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ assertEquals(4, fakeVibrator.getEffectSegments().size());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index 9e98e7d0410c..a732bd18676a 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -298,11 +298,13 @@ public class VibratorControllerTest {
private void mockVibratorCapabilities(int capabilities) {
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
- when(mNativeWrapperMock.getInfo(anyFloat())).thenReturn(
- new VibratorInfo.Builder(VIBRATOR_ID)
- .setCapabilities(capabilities)
- .setFrequencyMapping(frequencyMapping)
- .build());
+ when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class)))
+ .then(invocation -> {
+ ((VibratorInfo.Builder) invocation.getArgument(1))
+ .setCapabilities(capabilities)
+ .setFrequencyMapping(frequencyMapping);
+ return true;
+ });
}
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {