Merge "Use composition size limit on repeating effects" into tm-dev am: 48a15efcc7 am: 1a9e68638d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17474926
Change-Id: I07311446e1ec39e3a37f4c25150816a2dc956c1e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index 3550bda..12e68b1 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -141,8 +141,16 @@
*/
protected List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
int segmentsPlayed) {
+ int nextSegmentIndex = segmentIndex + segmentsPlayed;
+ int effectSize = effect.getSegments().size();
+ int repeatIndex = effect.getRepeatIndex();
+ if (nextSegmentIndex >= effectSize && repeatIndex >= 0) {
+ // Count the loops that were played.
+ int loopSize = effectSize - repeatIndex;
+ nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
+ }
Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
- segmentIndex + segmentsPlayed, vibratorOffTimeout);
+ nextSegmentIndex, vibratorOffTimeout);
return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
}
}
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
index d1ea805..3bc11c8 100644
--- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -32,6 +32,11 @@
* {@link PrimitiveSegment} starting at the current index.
*/
final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
+ /**
+ * Default limit to the number of primitives in a composition, if none is defined by the HAL,
+ * to prevent repeating effects from generating an infinite list.
+ */
+ private static final int DEFAULT_COMPOSITION_SIZE_LIMIT = 100;
ComposePrimitivesVibratorStep(VibrationStepConductor conductor, long startTime,
VibratorController controller, VibrationEffect.Composed effect, int index,
@@ -49,18 +54,8 @@
// 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);
- if (segment instanceof PrimitiveSegment) {
- primitives.add((PrimitiveSegment) segment);
- } else {
- break;
- }
- }
+ List<PrimitiveSegment> primitives = unrollPrimitiveSegments(effect, segmentIndex,
+ limit > 0 ? limit : DEFAULT_COMPOSITION_SIZE_LIMIT);
if (primitives.isEmpty()) {
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
@@ -81,4 +76,44 @@
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
+
+ /**
+ * Get the primitive segments to be played by this step as a single composition, starting at
+ * {@code startIndex} until:
+ *
+ * <ol>
+ * <li>There are no more segments in the effect;
+ * <li>The first non-primitive segment is found;
+ * <li>The given limit to the composition size is reached.
+ * </ol>
+ *
+ * <p>If the effect is repeating then this method will generate the largest composition within
+ * given limit.
+ */
+ private List<PrimitiveSegment> unrollPrimitiveSegments(VibrationEffect.Composed effect,
+ int startIndex, int limit) {
+ List<PrimitiveSegment> segments = new ArrayList<>(limit);
+ int segmentCount = effect.getSegments().size();
+ int repeatIndex = effect.getRepeatIndex();
+
+ for (int i = startIndex; segments.size() < limit; i++) {
+ if (i == segmentCount) {
+ if (repeatIndex >= 0) {
+ i = repeatIndex;
+ } else {
+ // Non-repeating effect, stop collecting primitives.
+ break;
+ }
+ }
+ VibrationEffectSegment segment = effect.getSegments().get(i);
+ if (segment instanceof PrimitiveSegment) {
+ segments.add((PrimitiveSegment) segment);
+ } else {
+ // First non-primitive segment, stop collecting primitives.
+ break;
+ }
+ }
+
+ return segments;
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
index 73bf933..919f1be 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -33,6 +33,11 @@
* {@link StepSegment} or {@link RampSegment} starting at the current index.
*/
final class ComposePwleVibratorStep extends AbstractVibratorStep {
+ /**
+ * Default limit to the number of PWLE segments, if none is defined by the HAL, to prevent
+ * repeating effects from generating an infinite list.
+ */
+ private static final int DEFAULT_PWLE_SIZE_LIMIT = 100;
ComposePwleVibratorStep(VibrationStepConductor conductor, long startTime,
VibratorController controller, VibrationEffect.Composed effect, int index,
@@ -50,18 +55,8 @@
// 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);
- if (segment instanceof RampSegment) {
- pwles.add((RampSegment) segment);
- } else {
- break;
- }
- }
+ List<RampSegment> pwles = unrollRampSegments(effect, segmentIndex,
+ limit > 0 ? limit : DEFAULT_PWLE_SIZE_LIMIT);
if (pwles.isEmpty()) {
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePwleStep: "
@@ -81,4 +76,88 @@
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
+
+ /**
+ * Get the ramp segments to be played by this step for a waveform, starting at
+ * {@code startIndex} until:
+ *
+ * <ol>
+ * <li>There are no more segments in the effect;
+ * <li>The first non-ramp segment is found;
+ * <li>The given limit to the PWLE size is reached.
+ * </ol>
+ *
+ * <p>If the effect is repeating then this method will generate the largest PWLE within given
+ * limit. This will also optimize to end the list at a ramp to zero-amplitude, if possible, and
+ * avoid braking down the effect in non-zero amplitude.
+ */
+ private List<RampSegment> unrollRampSegments(VibrationEffect.Composed effect, int startIndex,
+ int limit) {
+ List<RampSegment> segments = new ArrayList<>(limit);
+ float bestBreakAmplitude = 1;
+ int bestBreakPosition = limit; // Exclusive index.
+
+ int segmentCount = effect.getSegments().size();
+ int repeatIndex = effect.getRepeatIndex();
+
+ // Loop once after reaching the limit to see if breaking it will really be necessary, then
+ // apply the best break position found, otherwise return the full list as it fits the limit.
+ for (int i = startIndex; segments.size() <= limit; i++) {
+ if (i == segmentCount) {
+ if (repeatIndex >= 0) {
+ i = repeatIndex;
+ } else {
+ // Non-repeating effect, stop collecting ramps.
+ break;
+ }
+ }
+ VibrationEffectSegment segment = effect.getSegments().get(i);
+ if (segment instanceof RampSegment) {
+ RampSegment rampSegment = (RampSegment) segment;
+ segments.add(rampSegment);
+
+ if (isBetterBreakPosition(segments, bestBreakAmplitude, limit)) {
+ // Mark this position as the best one so far to break a long waveform.
+ bestBreakAmplitude = rampSegment.getEndAmplitude();
+ bestBreakPosition = segments.size(); // Break after this ramp ends.
+ }
+ } else {
+ // First non-ramp segment, stop collecting ramps.
+ break;
+ }
+ }
+
+ return segments.size() > limit
+ // Remove excessive segments, using the best breaking position recorded.
+ ? segments.subList(0, bestBreakPosition)
+ // Return all collected ramp segments.
+ : segments;
+ }
+
+ /**
+ * Returns true if the current segment list represents a better break position for a PWLE,
+ * given the current amplitude being used for breaking it at a smaller size and the size limit.
+ */
+ private boolean isBetterBreakPosition(List<RampSegment> segments,
+ float currentBestBreakAmplitude, int limit) {
+ RampSegment lastSegment = segments.get(segments.size() - 1);
+ float breakAmplitudeCandidate = lastSegment.getEndAmplitude();
+ int breakPositionCandidate = segments.size();
+
+ if (breakPositionCandidate > limit) {
+ // We're beyond limit, last break position found should be used.
+ return false;
+ }
+ if (breakAmplitudeCandidate == 0) {
+ // Breaking at amplitude zero at any position is always preferable.
+ return true;
+ }
+ if (breakPositionCandidate < limit / 2) {
+ // Avoid breaking at the first half of the allowed maximum size, even if amplitudes are
+ // lower, to avoid creating PWLEs that are too small unless it's to break at zero.
+ return false;
+ }
+ // Prefer lower amplitudes at a later position for breaking the PWLE in a more subtle way.
+ return breakAmplitudeCandidate <= currentBestBreakAmplitude;
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
index d5c1116..1f0d2d7 100644
--- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -33,6 +33,12 @@
* and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
*/
final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
+ /**
+ * The repeating waveform keeps the vibrator ON all the time. Use a minimum duration to
+ * prevent short patterns from turning the vibrator ON too frequently.
+ */
+ private static final int REPEATING_EFFECT_ON_DURATION = 5000; // 5s
+
private long mNextOffTime;
SetAmplitudeVibratorStep(VibrationStepConductor conductor, long startTime,
@@ -170,10 +176,7 @@
repeatIndex = -1;
}
if (i == startIndex) {
- // The repeating waveform keeps the vibrator ON all the time. Use a minimum
- // of 1s duration to prevent short patterns from turning the vibrator ON too
- // frequently.
- return Math.max(timing, 1000);
+ return Math.max(timing, REPEATING_EFFECT_ON_DURATION);
}
}
if (i == segmentCount && effect.getRepeatIndex() < 0) {
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 9f13591..de5f6ed 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -276,7 +276,7 @@
}
@Test
- public void vibrate_singleVibratorRepeatingShortAlwaysOnWaveform_turnsVibratorOnForASecond()
+ public void vibrate_singleVibratorRepeatingShortAlwaysOnWaveform_turnsVibratorOnForLonger()
throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -293,11 +293,71 @@
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(expectedOneShot(1000)),
+ assertEquals(Arrays.asList(expectedOneShot(5000)),
fakeVibrator.getEffectSegments(vibrationId));
}
@Test
+ public void vibrate_singleVibratorRepeatingPwle_generatesLargestPwles() throws Exception {
+ 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(10);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startWaveform(targetAmplitude(1))
+ // Very long segment so thread will be cancelled after first PWLE is triggered.
+ .addTransition(Duration.ofMillis(100), targetFrequency(100))
+ .build();
+ VibrationEffect repeatingEffect = VibrationEffect.startComposition()
+ .repeatEffectIndefinitely(effect)
+ .compose();
+ VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, repeatingEffect);
+
+ assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
+ TEST_TIMEOUT_MILLIS));
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
+ waitForCompletion();
+
+ // PWLE size max was used to generate a single vibrate call with 10 segments.
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
+ }
+
+ @Test
+ public void vibrate_singleVibratorRepeatingPrimitives_generatesLargestComposition()
+ throws Exception {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
+ fakeVibrator.setCompositionSizeMax(10);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startComposition()
+ // Very long delay so thread will be cancelled after first PWLE is triggered.
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+ .compose();
+ VibrationEffect repeatingEffect = VibrationEffect.startComposition()
+ .repeatEffectIndefinitely(effect)
+ .compose();
+ VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, repeatingEffect);
+
+ assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
+ TEST_TIMEOUT_MILLIS));
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false);
+ waitForCompletion();
+
+ // Composition size max was used to generate a single vibrate call with 10 primitives.
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
+ }
+
+ @Test
public void vibrate_singleVibratorRepeatingLongAlwaysOnWaveform_turnsVibratorOnForACycle()
throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
@@ -319,7 +379,7 @@
fakeVibrator.getEffectSegments(vibrationId));
}
-
+ @LargeTest
@Test
public void vibrate_singleVibratorRepeatingAlwaysOnWaveform_turnsVibratorBackOn()
throws Exception {
@@ -329,22 +389,21 @@
long vibrationId = 1;
int[] amplitudes = new int[]{1, 2};
VibrationEffect effect = VibrationEffect.createWaveform(
- new long[]{900, 50}, amplitudes, 0);
+ new long[]{4900, 50}, amplitudes, 0);
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
- assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
- 1000 + TEST_TIMEOUT_MILLIS));
+ assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1,
+ 5000 + TEST_TIMEOUT_MILLIS));
conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(2, fakeVibrator.getEffectSegments(vibrationId).size());
- // First time turn vibrator ON for minimum of 1s.
- assertEquals(1000L, fakeVibrator.getEffectSegments(vibrationId).get(0).getDuration());
+ // First time turn vibrator ON for minimum of 5s.
+ assertEquals(5000L, fakeVibrator.getEffectSegments(vibrationId).get(0).getDuration());
// Vibrator turns off in the middle of the second execution of first step, turn it back ON
- // for another 1s + remaining of 850ms.
- assertEquals(1850,
+ // for another 5s + remaining of 850ms.
+ assertEquals(4900 + 50 + 4900,
fakeVibrator.getEffectSegments(vibrationId).get(1).getDuration(), /* delta= */ 20);
// Set amplitudes for a cycle {1, 2}, start second loop then turn it back on to same value.
assertEquals(expectedAmplitudes(1, 2, 1, 1),
@@ -530,12 +589,18 @@
@Test
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
- mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ fakeVibrator.setSupportedPrimitives(
VibrationEffect.Composition.PRIMITIVE_CLICK,
VibrationEffect.Composition.PRIMITIVE_TICK);
- mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
- IVibrator.CAP_AMPLITUDE_CONTROL);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
+ IVibrator.CAP_COMPOSE_PWLE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
+ fakeVibrator.setMinFrequency(100);
+ fakeVibrator.setResonantFrequency(150);
+ fakeVibrator.setFrequencyResolution(50);
+ fakeVibrator.setMaxAmplitudes(
+ 0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
@@ -543,7 +608,11 @@
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
- .addOffDuration(Duration.ofMillis(100))
+ .addEffect(VibrationEffect.startWaveform()
+ .addTransition(Duration.ofMillis(10),
+ targetAmplitude(1), targetFrequency(100))
+ .addTransition(Duration.ofMillis(20), targetFrequency(120))
+ .build())
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.compose();
startThreadAndDispatcher(vibrationId, effect);
@@ -552,7 +621,7 @@
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
@@ -560,6 +629,10 @@
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0),
expectedPrebaked(VibrationEffect.EFFECT_CLICK),
+ expectedRamp(/* startAmplitude= */ 0, /* endAmplitude= */ 0.5f,
+ /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 100, /* duration= */ 10),
+ expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.7f,
+ /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 120, /* duration= */ 20),
expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
@@ -605,30 +678,36 @@
}
@Test
- public void vibrate_singleVibratorLargePwle_splitsVibratorComposeCalls() {
+ public void vibrate_singleVibratorLargePwle_splitsComposeCallWhenAmplitudeIsLowest() {
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);
+ fakeVibrator.setPwleSizeMax(3);
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startWaveform(targetAmplitude(1))
.addSustain(Duration.ofMillis(10))
.addTransition(Duration.ofMillis(20), targetAmplitude(0))
+ // Waveform will be split here, after vibration goes to zero amplitude
.addTransition(Duration.ZERO, targetAmplitude(0.8f), targetFrequency(100))
.addSustain(Duration.ofMillis(30))
.addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
+ // Waveform will be split here at lowest amplitude.
+ .addTransition(Duration.ofMillis(40), targetAmplitude(0.7f), targetFrequency(200))
+ .addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
.build();
startThreadAndDispatcher(vibrationId, effect);
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
- // Vibrator compose called twice.
- verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- assertEquals(4, fakeVibrator.getEffectSegments(vibrationId).size());
+
+ // Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
+ // Using best split points instead of max-packing PWLEs.
+ verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ assertEquals(6, fakeVibrator.getEffectSegments(vibrationId).size());
}
@Test