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
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
index 32a3227..bb8e6ee 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
@@ -94,7 +94,7 @@
 
         // 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;
diff --git a/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java b/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
index a8c4ac8..df3b51c 100644
--- a/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
+++ b/services/core/java/com/android/server/vibrator/SplitPwleSegmentsAdapter.java
@@ -18,6 +18,7 @@
 
 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 @@
     @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;
         }
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/SplitPwleSegmentsAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/SplitPwleSegmentsAdapterTest.java
new file mode 100644
index 0000000..566b440
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/SplitPwleSegmentsAdapterTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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();
+    }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index b4345b6..5f2af0a 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -1001,6 +1001,48 @@
     }
 
     @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() {
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);