diff options
| author | 2021-06-22 16:08:22 +0000 | |
|---|---|---|
| committer | 2021-06-22 16:08:22 +0000 | |
| commit | 55a8db6450352d30da8478a8233bb7820e8bd40b (patch) | |
| tree | a773695a342d55fc6f2cd64712d34d058a2a33a6 | |
| parent | a9b80b098c46b26b6d14fd2398bcb8ad58f0f716 (diff) | |
| parent | a1167c2a064a0e5260e4d8cfa6161a3a79e76bd2 (diff) | |
Merge "Split long vibration composition using HAL limits" into sc-dev
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) { |