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);