Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86445de1 authored by Ahmad Khalil's avatar Ahmad Khalil
Browse files

Fix vibration patterns with zero duration steps.

Removing steps with zero duration in the vibration pattern to prevent cases where it becomes un-synced with the requested vibration.

Bug: 287549459
Test: com.android.server.vibrator.VibrationThreadTest
Change-Id: I49874c64e8fe0f0a31adca023fe7273e32149a9b
parent ef187054
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -179,7 +179,9 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
        while (i < segmentCount) {
            VibrationEffectSegment segment = segments.get(i);
            if (!(segment instanceof StepSegment)
                    || ((StepSegment) segment).getAmplitude() == 0) {
                    // play() will ignore segments with zero duration, so it's important that
                    // zero-duration segments don't affect this method.
                    || (segment.getDuration() > 0 && ((StepSegment) segment).getAmplitude() == 0)) {
                break;
            }
            timing += segment.getDuration();
+82 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.server.vibrator;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -304,6 +306,80 @@ public class VibrationThreadTest {
                fakeVibrator.getEffectSegments(vibrationId));
    }

    @Test
    public void vibrate_singleVibratorPatternWithZeroDurationSteps_skipsZeroDurationSteps() {
        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);

        VibrationEffect effect = VibrationEffect.createWaveform(
                /* timings= */ new long[]{0, 100, 50, 100, 0, 0, 0, 50}, /* repeat= */ -1);
        VibrationStepConductor conductor = startThreadAndDispatcher(effect);
        long vibrationId = conductor.getVibration().id;
        waitForCompletion();

        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(300L));
        verify(mManagerHooks).noteVibratorOff(eq(UID));

        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
        assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();

        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
                .isEqualTo(expectedOneShots(100L, 150L));
    }

    @Test
    public void vibrate_singleVibratorPatternWithZeroDurationAndAmplitude_skipsZeroDurationSteps() {
        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);

        int[] amplitudes = new int[]{1, 2, 0, 3, 4, 5, 0, 6};
        VibrationEffect effect = VibrationEffect.createWaveform(
                /* timings= */ new long[]{0, 100, 0, 50, 50, 0, 100, 50}, amplitudes,
                /* repeat= */ -1);
        VibrationStepConductor conductor = startThreadAndDispatcher(effect);
        long vibrationId = conductor.getVibration().id;
        waitForCompletion();

        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(350L));
        verify(mManagerHooks).noteVibratorOff(eq(UID));

        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
        assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();

        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
                .isEqualTo(expectedOneShots(200L, 50L));
    }

    @LargeTest
    @Test
    public void vibrate_singleVibratorRepeatingPatternWithZeroDurationSteps_repeatsEffectCorrectly()
            throws Exception {
        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
        fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);

        VibrationEffect effect = VibrationEffect.createWaveform(
                /* timings= */ new long[]{0, 200, 50, 100, 0, 50, 50, 100}, /* repeat= */ 0);
        VibrationStepConductor conductor = startThreadAndDispatcher(effect);
        long vibrationId = conductor.getVibration().id;
        // We are expect this test to repeat the vibration effect twice, which would result in 5
        // segments being played:
        // 200ms ON
        // 150ms ON (100ms + 50ms, skips 0ms)
        // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms)
        // 150ms ON (100ms + 50ms, skips 0ms)
        // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms)
        assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() >= 5,
                5000L + TEST_TIMEOUT_MILLIS));
        conductor.notifyCancelled(
                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
                /* immediate= */ false);
        waitForCompletion();

        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
        assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();

        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).subList(0, 5))
                .isEqualTo(expectedOneShots(200L, 150L, 300L, 150L, 300L));
    }

    @Test
    public void vibrate_singleVibratorRepeatingPwle_generatesLargestPwles() throws Exception {
        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
@@ -1640,6 +1716,12 @@ public class VibrationThreadTest {
                /* frequencyHz= */ 0, (int) millis);
    }

    private List<VibrationEffectSegment> expectedOneShots(long... millis) {
        return Arrays.stream(millis)
                .mapToObj(this::expectedOneShot)
                .collect(Collectors.toList());
    }

    private VibrationEffectSegment expectedPrebaked(int effectId) {
        return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
    }