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

Commit d78c21e6 authored by Ahmad Khalil's avatar Ahmad Khalil Committed by Android (Google) Code Review
Browse files

Merge "Fix vibration patterns with zero duration steps." into main

parents 6284a7cf 86445de1
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);
    }