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

Commit b5cdd53a authored by Ahmad Khalil's avatar Ahmad Khalil
Browse files

Add tests for SplitPwleSegmentsAdapter

Adding tests to validate the Pwle segment splitting is working properly.

Bug: 388239434
Flag: android.os.vibrator.normalized_pwle_effects
Test: atest SplitPwleSegmentsAdapterTest
Change-Id: I01ad692224d86a908c5fdfb30726f01fae20d40f
parent a39afd3f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -94,7 +94,7 @@ final class ComposePwleV2VibratorStep extends AbstractComposedVibratorStep {

        // 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; pwlePoints.size() < limit; i++) {
        for (int i = startIndex; pwlePoints.size() <= limit; i++) {
            if (i == segmentCount) {
                if (repeatIndex >= 0) {
                    i = repeatIndex;
+3 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.vibrator;

import android.hardware.vibrator.IVibrator;
import android.os.VibratorInfo;
import android.os.vibrator.Flags;
import android.os.vibrator.PwleSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.MathUtils;
@@ -40,7 +41,8 @@ final class SplitPwleSegmentsAdapter implements VibrationSegmentsAdapter {
    @Override
    public int adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments,
            int repeatIndex) {
        if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
        if (!Flags.normalizedPwleEffects()
                || !info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
            // The vibrator does not have PWLE v2 capability, so keep the segments unchanged.
            return repeatIndex;
        }
+150 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.vibrator;

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

import android.hardware.vibrator.IVibrator;
import android.os.VibratorInfo;
import android.os.vibrator.Flags;
import android.os.vibrator.PwleSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;

public class SplitPwleSegmentsAdapterTest {
    private static final float TEST_RESONANT_FREQUENCY = 150;
    private static final float[] TEST_FREQUENCIES =
            new float[]{90f, 120f, 150f, 60f, 30f, 210f, 270f, 300f, 240f, 180f};
    private static final float[] TEST_OUTPUT_ACCELERATIONS =
            new float[]{1.2f, 1.8f, 2.4f, 0.6f, 0.1f, 2.2f, 1.0f, 0.5f, 1.9f, 3.0f};

    private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
            new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_FREQUENCIES,
                    TEST_OUTPUT_ACCELERATIONS);

    private SplitPwleSegmentsAdapter mAdapter;

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Before
    public void setUp() throws Exception {
        mAdapter = new SplitPwleSegmentsAdapter();
    }

    @Test
    @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
    public void testSplitPwleSegmentsAdapter_withFeatureFlagDisabled_returnsOriginalSegments() {
        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                //  startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz, duration
                new PwleSegment(0.0f, 1.0f, 300.0f, 300.0f, 5000)));
        List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);

        VibratorInfo vibratorInfo = createVibratorInfo(
                /*maxEnvelopeControlPointDuration=*/1000, TEST_FREQUENCY_PROFILE,
                IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);

        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ -1))
                .isEqualTo(-1);
        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 1))
                .isEqualTo(1);

        assertThat(segments).isEqualTo(originalSegments);
    }

    @Test
    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
    public void testSplitPwleSegmentsAdapter_noPwleCapability_returnsOriginalSegments() {
        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                //  startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz, duration
                new PwleSegment(0.0f, 1.0f, 300.0f, 300.0f, 5000)));
        List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);

        VibratorInfo vibratorInfo = createVibratorInfo(
                /*maxEnvelopeControlPointDuration=*/1000, TEST_FREQUENCY_PROFILE);

        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ -1))
                .isEqualTo(-1);
        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 1))
                .isEqualTo(1);

        assertThat(segments).isEqualTo(originalSegments);
    }

    @Test
    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
    public void testSplitPwleSegmentsAdapter_noMaxEnvelopeEffectSize_returnsOriginalSegments() {
        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                //  startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz, duration
                new PwleSegment(0.0f, 1.0f, 300.0f, 300.0f, 5000)));
        List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
        VibratorInfo vibratorInfo = createVibratorInfo(/*maxEnvelopeControlPointDuration=*/ 0,
                TEST_FREQUENCY_PROFILE, IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);

        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ -1))
                .isEqualTo(-1);
        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 1))
                .isEqualTo(1);

        assertThat(segments).isEqualTo(originalSegments);
    }

    @Test
    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
    public void testSplitPwleSegmentsAdapter_withPwleCapability_adaptSegmentsCorrectly() {
        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                //  startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz, duration
                new PwleSegment(0.0f, 1.0f, 200.0f, 200.0f, 1000),
                new PwleSegment(0.0f, 1.0f, 300.0f, 300.0f, 5000)));
        List<VibrationEffectSegment> expectedSegments = Arrays.asList(
                //  startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz, duration
                new PwleSegment(0.0f, 1.0f, 200.0f, 200.0f, 1000),
                new PwleSegment(0.0f, 0.2f, 300.0f, 300.0f, 1000),
                new PwleSegment(0.2f, 0.4f, 300.0f, 300.0f, 1000),
                new PwleSegment(0.4f, 0.6f, 300.0f, 300.0f, 1000),
                new PwleSegment(0.6f, 0.8f, 300.0f, 300.0f, 1000),
                new PwleSegment(0.8f, 1.0f, 300.0f, 300.0f, 1000));
        VibratorInfo vibratorInfo = createVibratorInfo(
                /*maxEnvelopeControlPointDuration=*/1000, TEST_FREQUENCY_PROFILE,
                IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);

        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 0))
                .isEqualTo(0);
        assertThat(segments).isEqualTo(expectedSegments);
    }

    private static VibratorInfo createVibratorInfo(int maxEnvelopeControlPointDuration,
            VibratorInfo.FrequencyProfile frequencyProfile, int... capabilities) {
        return new VibratorInfo.Builder(0)
                .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
                .setFrequencyProfile(frequencyProfile)
                .setMaxEnvelopeEffectSize(1)
                .setMaxEnvelopeEffectControlPointDurationMillis(maxEnvelopeControlPointDuration)
                .build();
    }
}
+42 −0
Original line number Diff line number Diff line
@@ -1000,6 +1000,48 @@ public class VibrationThreadTest {

    }

    @Test
    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
    public void vibrate_singleVibratorPwle_TooManyControlPoints_splitsAndRunsComposePwleV2() {
        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
        fakeVibrator.setResonantFrequency(150);
        fakeVibrator.setFrequenciesHz(new float[]{30f, 50f, 100f, 120f, 150f});
        fakeVibrator.setOutputAccelerationsGs(new float[]{0.3f, 0.5f, 1.0f, 0.8f, 0.6f});
        fakeVibrator.setMaxEnvelopeEffectSize(3);
        fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20);

        VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
                .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*durationMillis=*/ 30)
                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 100f, /*durationMillis=*/ 30)
                // Waveform will be split here, after vibration goes to zero amplitude
                .addControlPoint(/*amplitude=*/ 0.9f, /*frequencyHz=*/ 100f, /*durationMillis=*/ 30)
                .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 100f, /*durationMillis=*/ 30)
                // Waveform will be split here at lowest amplitude.
                .addControlPoint(/*amplitude=*/ 0.6f, /*frequencyHz=*/ 100f, /*durationMillis=*/ 30)
                .addControlPoint(/*amplitude=*/ 0.7f, /*frequencyHz=*/ 100f, /*durationMillis=*/ 30)
                .build();
        HalVibration vibration = startThreadAndDispatcher(effect);
        waitForCompletion();

        verifyCallbacksTriggered(vibration, Status.FINISHED);
        // 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(vibration.id));
        assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());

        assertEquals(Arrays.asList(
                expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0),
                expectedPwle(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
                expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
                expectedPwle(/*amplitude=*/ 0.9f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0),
                expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
                expectedPwle(/*amplitude=*/ 0.6f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0),
                expectedPwle(/*amplitude=*/ 0.7f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30)
        ), fakeVibrator.getEffectPwlePoints(vibration.id));

    }

    @Test
    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
    public void vibrate_singleVibratorPwle_runsComposePwle() {