summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml9
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java12
-rw-r--r--services/core/java/com/android/server/vibrator/RampToStepAdapter.java2
-rw-r--r--services/core/java/com/android/server/vibrator/StepToRampAdapter.java196
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java10
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java122
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java312
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java11
11 files changed, 673 insertions, 71 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5ac23365eaee..ee33d48768c5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3354,6 +3354,15 @@
(e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
<bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
+ <!-- The duration (in milliseconds) that should be used to convert vibration ramps to a sequence
+ of fixed amplitude steps on devices without PWLE support. -->
+ <integer name="config_vibrationWaveformRampStepDuration">5</integer>
+
+ <!-- The duration (in milliseconds) that should be applied to waveform vibrations that ends in
+ non-zero amplitudes, . The waveform will
+ be played as a PWLE instead of on/off calls if this value is set. -->
+ <integer name="config_vibrationWaveformRampDownDuration">0</integer>
+
<!-- Number of retries Cell Data should attempt for a given error code before
restarting the modem.
Error codes not listed will not lead to modem restarts.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b574415c0a08..7d685a202538 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2012,6 +2012,8 @@
<java-symbol type="integer" name="config_notificationServiceArchiveSize" />
<java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
+ <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
+ <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
<java-symbol type="integer" name="config_radioScanningTimeout" />
<java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
diff --git a/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java b/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
index b695150d9ba3..e8ce4f336caa 100644
--- a/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
+++ b/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.content.Context;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
@@ -26,17 +27,16 @@ import java.util.List;
final class DeviceVibrationEffectAdapter
implements VibrationEffectAdapters.EffectAdapter<VibratorInfo> {
- /** Duration of each step created to simulate a ramp segment. */
- private static final int RAMP_STEP_DURATION_MILLIS = 5;
-
private final List<VibrationEffectAdapters.SegmentsAdapter<VibratorInfo>> mSegmentAdapters;
- DeviceVibrationEffectAdapter() {
+ DeviceVibrationEffectAdapter(Context context) {
mSegmentAdapters = Arrays.asList(
// TODO(b/167947076): add filter that removes unsupported primitives
// TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
- new RampToStepAdapter(RAMP_STEP_DURATION_MILLIS),
- new StepToRampAdapter(),
+ new RampToStepAdapter(context.getResources().getInteger(
+ com.android.internal.R.integer.config_vibrationWaveformRampStepDuration)),
+ new StepToRampAdapter(context.getResources().getInteger(
+ com.android.internal.R.integer.config_vibrationWaveformRampDownDuration)),
new ClippingAmplitudeAndFrequencyAdapter()
);
}
diff --git a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
index 1e05bdbdf082..64624a28c5da 100644
--- a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
+++ b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
@@ -29,7 +29,7 @@ import java.util.List;
/**
* Adapter that converts ramp segments that to a sequence of fixed step segments.
*
- * <p>This leaves the list unchanged if the device have compose PWLE capability.
+ * <p>This leaves the list unchanged if the device has compose PWLE capability.
*/
final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
index f78df9208fbd..d439b94ec2fc 100644
--- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
+++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
@@ -27,39 +27,221 @@ import java.util.List;
/**
* Adapter that converts step segments that should be handled as PWLEs to ramp segments.
*
- * <p>This leaves the list unchanged if the device do not have compose PWLE capability.
+ * <p>Each replaced {@link StepSegment} will be represented by a {@link RampSegment} with same
+ * start and end amplitudes/frequencies, which can then be converted to PWLE compositions. This
+ * adapter leaves the segments unchanged if the device doesn't have the PWLE composition capability.
+ *
+ * <p>This adapter also applies the ramp down duration config on devices with PWLE support. This
+ * prevents the device from ringing when it cannot handle abrupt changes between ON and OFF states.
+ * This will not change other types of abrupt amplitude changes in the original effect.
+ *
+ * <p>The effect overall duration is preserved by this transformation. Waveforms with ON/OFF
+ * segments are handled gracefully by the ramp down changes. Each OFF segment preceded by an ON
+ * segment will be shortened, and a ramp down will be added to the transition between ON and OFF.
+ * The ramps can be shorter than the configured duration in order to preserve the waveform timings,
+ * but they will still soften the ringing effect.
*/
final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
+
+ private final int mRampDownDuration;
+
+ StepToRampAdapter(int rampDownDuration) {
+ mRampDownDuration = rampDownDuration;
+ }
+
@Override
public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
VibratorInfo info) {
if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
- // The vibrator do not have PWLE capability, so keep the segments unchanged.
+ // The vibrator does not have PWLE capability, so keep the segments unchanged.
return repeatIndex;
}
+ convertStepsToRamps(segments);
+ int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex);
+ newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex);
+ return newRepeatIndex;
+ }
+
+ private void convertStepsToRamps(List<VibrationEffectSegment> segments) {
int segmentCount = segments.size();
+ if (mRampDownDuration > 0) {
+ // Convert all steps to ramps if the device requires ramp down.
+ for (int i = 0; i < segmentCount; i++) {
+ if (isStep(segments.get(i))) {
+ segments.set(i, apply((StepSegment) segments.get(i)));
+ }
+ }
+ return;
+ }
// Convert steps that require frequency control to ramps.
for (int i = 0; i < segmentCount; i++) {
VibrationEffectSegment segment = segments.get(i);
- if ((segment instanceof StepSegment)
- && ((StepSegment) segment).getFrequency() != 0) {
+ if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) {
segments.set(i, apply((StepSegment) segment));
}
}
// Convert steps that are next to ramps to also become ramps, so they can be composed
// together in the same PWLE waveform.
- for (int i = 1; i < segmentCount; i++) {
+ for (int i = 0; i < segmentCount; i++) {
if (segments.get(i) instanceof RampSegment) {
- for (int j = i - 1; j >= 0 && (segments.get(j) instanceof StepSegment); j--) {
+ for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) {
segments.set(j, apply((StepSegment) segments.get(j)));
}
+ for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); j++) {
+ segments.set(j, apply((StepSegment) segments.get(j)));
+ }
+ }
+ }
+ }
+
+ /**
+ * This will add a ramp to zero as follows:
+ *
+ * <ol>
+ * <li>Remove the {@link VibrationEffectSegment} that starts and ends at zero amplitude
+ * and follows a segment that ends at non-zero amplitude;
+ * <li>Add a ramp down to zero starting at the previous segment end amplitude and frequency,
+ * with min between the removed segment duration and the configured ramp down duration;
+ * <li>Add a zero amplitude segment following the ramp with the remaining duration, if
+ * necessary;
+ * </ol>
+ */
+ private int addRampDownToZeroAmplitudeSegments(List<VibrationEffectSegment> segments,
+ int repeatIndex) {
+ if (mRampDownDuration <= 0) {
+ // Nothing to do, no ramp down duration configured.
+ return repeatIndex;
+ }
+ int newRepeatIndex = repeatIndex;
+ int newSegmentCount = segments.size();
+ for (int i = 1; i < newSegmentCount; i++) {
+ if (!isOffRampSegment(segments.get(i))
+ || !endsWithNonZeroAmplitude(segments.get(i - 1))) {
+ continue;
+ }
+
+ // We know the previous segment is a ramp that ends at non-zero amplitude.
+ float previousAmplitude = ((RampSegment) segments.get(i - 1)).getEndAmplitude();
+ float previousFrequency = ((RampSegment) segments.get(i - 1)).getEndFrequency();
+ RampSegment ramp = (RampSegment) segments.get(i);
+
+ if (ramp.getDuration() <= mRampDownDuration) {
+ // Replace the zero amplitude segment with a ramp down of same duration, to
+ // preserve waveform timings and still soften the transition to zero.
+ segments.set(i, createRampDown(previousAmplitude, previousFrequency,
+ ramp.getDuration()));
+ } else {
+ // Make the zero amplitude segment shorter, to preserve waveform timings, and add a
+ // ramp down to zero segment right before it.
+ segments.set(i, updateDuration(ramp, ramp.getDuration() - mRampDownDuration));
+ segments.add(i, createRampDown(previousAmplitude, previousFrequency,
+ mRampDownDuration));
+ if (repeatIndex > i) {
+ newRepeatIndex++;
+ }
+ i++;
+ newSegmentCount++;
+ }
+ }
+ return newRepeatIndex;
+ }
+
+ /**
+ * This will add a ramp to zero at the repeating index of the given effect, if set, only if
+ * the last segment ends at a non-zero amplitude and the repeating segment starts and ends at
+ * zero amplitude. The update is described as:
+ *
+ * <ol>
+ * <li>Add a ramp down to zero following the last segment, with the min between the
+ * removed segment duration and the configured ramp down duration;
+ * <li>Skip the zero-amplitude segment by incrementing the repeat index, splitting it if
+ * necessary to skip the correct amount;
+ * </ol>
+ */
+ private int addRampDownToLoop(List<VibrationEffectSegment> segments, int repeatIndex) {
+ if (repeatIndex < 0) {
+ // Non-repeating compositions should remain unchanged so duration will be preserved.
+ return repeatIndex;
+ }
+
+ int segmentCount = segments.size();
+ if (mRampDownDuration <= 0 || !endsWithNonZeroAmplitude(segments.get(segmentCount - 1))) {
+ // Nothing to do, no ramp down duration configured or composition already ends at zero.
+ return repeatIndex;
+ }
+
+ // We know the last segment is a ramp that ends at non-zero amplitude.
+ RampSegment lastRamp = (RampSegment) segments.get(segmentCount - 1);
+ float previousAmplitude = lastRamp.getEndAmplitude();
+ float previousFrequency = lastRamp.getEndFrequency();
+
+ if (isOffRampSegment(segments.get(repeatIndex))) {
+ // Repeating from a non-zero to a zero amplitude segment, we know the next segment is a
+ // ramp with zero amplitudes.
+ RampSegment nextRamp = (RampSegment) segments.get(repeatIndex);
+
+ if (nextRamp.getDuration() <= mRampDownDuration) {
+ // Skip the zero amplitude segment and append a ramp down of same duration to the
+ // end of the composition, to preserve waveform timings and still soften the
+ // transition to zero.
+ // This will update the waveform as follows:
+ // R R+1
+ // | ____ | ____
+ // _|_/ => __|/ \
+ segments.add(createRampDown(previousAmplitude, previousFrequency,
+ nextRamp.getDuration()));
+ repeatIndex++;
+ } else {
+ // Append a ramp down to the end of the composition, split the zero amplitude
+ // segment and start repeating from the second half, to preserve waveform timings.
+ // This will update the waveform as follows:
+ // R R+1
+ // | ____ | ____
+ // _|__/ => __|_/ \
+ segments.add(createRampDown(previousAmplitude, previousFrequency,
+ mRampDownDuration));
+ segments.set(repeatIndex, updateDuration(nextRamp,
+ nextRamp.getDuration() - mRampDownDuration));
+ segments.add(repeatIndex, updateDuration(nextRamp, mRampDownDuration));
+ repeatIndex++;
}
}
+
return repeatIndex;
}
- private RampSegment apply(StepSegment segment) {
+ private static RampSegment apply(StepSegment segment) {
return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
}
+
+ private static RampSegment createRampDown(float amplitude, float frequency, long duration) {
+ return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency,
+ (int) duration);
+ }
+
+ private static RampSegment updateDuration(RampSegment ramp, long newDuration) {
+ return new RampSegment(ramp.getStartAmplitude(), ramp.getEndAmplitude(),
+ ramp.getStartFrequency(), ramp.getEndFrequency(), (int) newDuration);
+ }
+
+ private static boolean isStep(VibrationEffectSegment segment) {
+ return segment instanceof StepSegment;
+ }
+
+ /** Returns true if the segment is a ramp that starts and ends at zero amplitude. */
+ private static boolean isOffRampSegment(VibrationEffectSegment segment) {
+ if (segment instanceof RampSegment) {
+ RampSegment ramp = (RampSegment) segment;
+ return ramp.getStartAmplitude() == 0 && ramp.getEndAmplitude() == 0;
+ }
+ return false;
+ }
+
+ private static boolean endsWithNonZeroAmplitude(VibrationEffectSegment segment) {
+ if (segment instanceof RampSegment) {
+ return ((RampSegment) segment).getEndAmplitude() != 0;
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index e3672f4d497c..150fde99b706 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -95,8 +95,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
private final WorkSource mWorkSource = new WorkSource();
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
- private final DeviceVibrationEffectAdapter mDeviceEffectAdapter =
- new DeviceVibrationEffectAdapter();
+ private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
private final Vibration mVibration;
private final VibrationCallbacks mCallbacks;
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
@@ -104,10 +103,11 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
private volatile boolean mForceStop;
- VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators,
- PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
- VibrationCallbacks callbacks) {
+ VibrationThread(Vibration vib, DeviceVibrationEffectAdapter effectAdapter,
+ SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
+ IBatteryStats batteryStatsService, VibrationCallbacks callbacks) {
mVibration = vib;
+ mDeviceEffectAdapter = effectAdapter;
mCallbacks = callbacks;
mWakeLock = wakeLock;
mWorkSource.set(vib.uid);
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 06a5077bec82..2f0ed19d867a 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -132,6 +132,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private final VibrationSettings mVibrationSettings;
private final VibrationScaler mVibrationScaler;
private final InputDeviceDelegate mInputDeviceDelegate;
+ private final DeviceVibrationEffectAdapter mDeviceVibrationEffectAdapter;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -175,6 +176,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibrationSettings = new VibrationSettings(mContext, mHandler);
mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
+ mDeviceVibrationEffectAdapter = new DeviceVibrationEffectAdapter(mContext);
VibrationCompleteListener listener = new VibrationCompleteListener(this);
mNativeWrapper = injector.getNativeWrapper();
@@ -512,8 +514,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
}
- VibrationThread vibThread = new VibrationThread(vib, mVibrators, mWakeLock,
- mBatteryStatsService, mVibrationCallbacks);
+ VibrationThread vibThread = new VibrationThread(vib, mDeviceVibrationEffectAdapter,
+ mVibrators, mWakeLock, mBatteryStatsService, mVibrationCallbacks);
if (mCurrentVibration == null) {
return startVibrationThreadLocked(vibThread);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
index 00b05d4ad10c..14cab021edb0 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
@@ -17,6 +17,7 @@
package com.android.server.vibrator;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.hardware.vibrator.IVibrator;
import android.os.VibrationEffect;
@@ -25,8 +26,11 @@ import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.Presubmit;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.Before;
import org.junit.Test;
@@ -58,7 +62,7 @@ public class DeviceVibrationEffectAdapterTest {
@Before
public void setUp() throws Exception {
- mAdapter = new DeviceVibrationEffectAdapter();
+ mAdapter = new DeviceVibrationEffectAdapter(InstrumentationRegistry.getContext());
}
@Test
@@ -84,28 +88,19 @@ public class DeviceVibrationEffectAdapterTest {
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
- /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 11),
+ /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
- /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 200)),
+ /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 1000)),
/* repeatIndex= */ 3);
- VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
- new StepSegment(/* amplitude= */ 0, Float.NaN, /* duration= */ 10),
- new StepSegment(/* amplitude= */ 0.5f, Float.NaN, /* duration= */ 100),
- // 10ms ramp becomes 2 steps
- new StepSegment(/* amplitude= */ 1, Float.NaN, /* duration= */ 5),
- new StepSegment(/* amplitude= */ 0.2f, Float.NaN, /* duration= */ 5),
- // 11ms ramp becomes 3 steps
- new StepSegment(/* amplitude= */ 0.8f, Float.NaN, /* duration= */ 5),
- new StepSegment(/* amplitude= */ 0.6f, Float.NaN, /* duration= */ 5),
- new StepSegment(/* amplitude= */ 0.2f, Float.NaN, /* duration= */ 1),
- // 200ms ramp with same amplitude becomes a single step
- new StepSegment(/* amplitude= */ 0.65f, Float.NaN, /* duration= */ 200)),
- // Repeat index fixed after intermediate steps added
- /* repeatIndex= */ 4);
-
- VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_MAPPING);
- assertEquals(expected, mAdapter.apply(effect, info));
+ VibrationEffect.Composed adaptedEffect = (VibrationEffect.Composed) mAdapter.apply(effect,
+ createVibratorInfo(EMPTY_FREQUENCY_MAPPING));
+ assertTrue(adaptedEffect.getSegments().size() > effect.getSegments().size());
+ assertTrue(adaptedEffect.getRepeatIndex() >= effect.getRepeatIndex());
+
+ for (VibrationEffectSegment adaptedSegment : adaptedEffect.getSegments()) {
+ assertTrue(adaptedSegment instanceof StepSegment);
+ }
}
@Test
@@ -136,33 +131,6 @@ public class DeviceVibrationEffectAdapterTest {
}
@Test
- public void testStepAndRampSegments_withPwleCapabilityAndNoFrequency_keepsOriginalSteps() {
- VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
- new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
- new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
- new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10),
- new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
- /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
- new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
- /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
- /* repeatIndex= */ 2);
-
- VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
- new StepSegment(/* amplitude= */ 0, /* frequency= */ 150, /* duration= */ 10),
- new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 150, /* duration= */ 100),
- new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10),
- new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
- /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
- new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
- /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
- /* repeatIndex= */ 2);
-
- VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
- IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
- assertEquals(expected, mAdapter.apply(effect, info));
- }
-
- @Test
public void testStepAndRampSegments_withEmptyFreqMapping_returnsSameAmplitudesAndZeroFreq() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
new file mode 100644
index 000000000000..95c3bd93e69b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 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 org.junit.Assert.assertEquals;
+
+import android.hardware.vibrator.IVibrator;
+import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * Tests for {@link RampToStepAdapter}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:RampToStepAdapterTest
+ */
+@Presubmit
+public class RampToStepAdapterTest {
+ private static final int TEST_STEP_DURATION = 5;
+
+ private RampToStepAdapter mAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ mAdapter = new RampToStepAdapter(TEST_STEP_DURATION);
+ }
+
+ @Test
+ public void testStepAndPrebakedAndPrimitiveSegments_keepsListUnchanged() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+ new PrebakedSegment(
+ VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT),
+ new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
+ List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+
+ assertEquals(-1, mAdapter.apply(segments, -1, createVibratorInfo()));
+ assertEquals(1, mAdapter.apply(segments, 1, createVibratorInfo()));
+
+ assertEquals(originalSegments, segments);
+ }
+
+ @Test
+ public void testRampSegments_withPwleCapability_keepsListUnchanged() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
+ List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
+ assertEquals(0, mAdapter.apply(segments, 0, vibratorInfo));
+
+ assertEquals(originalSegments, segments);
+ }
+
+ @Test
+ public void testRampSegments_withoutPwleCapability_convertsRampsToSteps() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ -3, /* endFrequency= */ 0, /* duration= */ 11),
+ new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
+ /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 200)));
+
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+ // 10ms ramp becomes 2 steps
+ new StepSegment(/* amplitude= */ 1, /* frequency= */ -4, /* duration= */ 5),
+ new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 2, /* duration= */ 5),
+ // 11ms ramp becomes 3 steps
+ new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -3, /* duration= */ 5),
+ new StepSegment(/* amplitude= */ 0.6f, /* frequency= */ -2, /* duration= */ 5),
+ new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 0, /* duration= */ 1),
+ // 200ms ramp with same amplitude becomes a single step
+ new StepSegment(/* amplitude= */ 0.65f, /* frequency= */ 0, /* duration= */ 200));
+
+ // Repeat index fixed after intermediate steps added
+ assertEquals(4, mAdapter.apply(segments, 3, createVibratorInfo()));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ private static VibratorInfo createVibratorInfo(int... capabilities) {
+ return new VibratorInfo.Builder(0)
+ .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
new file mode 100644
index 000000000000..f4eb2ded5a9d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2021 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 org.junit.Assert.assertEquals;
+
+import android.hardware.vibrator.IVibrator;
+import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * Tests for {@link StepToRampAdapter}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:StepToRampAdapterTest
+ */
+@Presubmit
+public class StepToRampAdapterTest {
+ private StepToRampAdapter mAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ mAdapter = new StepToRampAdapter(/* rampDownDuration= */ 0);
+ }
+
+ @Test
+ public void testRampAndPrebakedAndPrimitiveSegments_returnsOriginalSegments() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
+ new PrebakedSegment(
+ VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT),
+ new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
+ List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+
+ assertEquals(-1, mAdapter.apply(segments, -1, createVibratorInfo()));
+ assertEquals(1, mAdapter.apply(segments, 1, createVibratorInfo()));
+
+ assertEquals(originalSegments, segments);
+ }
+
+ @Test
+ public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
+ mAdapter = new StepToRampAdapter(50);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
+ List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+
+ assertEquals(-1, mAdapter.apply(segments, -1, createVibratorInfo()));
+ assertEquals(0, mAdapter.apply(segments, 0, createVibratorInfo()));
+
+ assertEquals(originalSegments, segments);
+ }
+
+ @Test
+ public void testStepAndRampSegments_withPwleCapabilityAndNoFrequency_keepsOriginalSteps() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+ new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
+ List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
+ assertEquals(3, mAdapter.apply(segments, 3, vibratorInfo));
+
+ assertEquals(originalSegments, segments);
+ }
+
+ @Test
+ public void testStepAndRampSegments_withPwleCapabilityAndStepNextToRamp_convertsStepsToRamps() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
+ new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -1, /* duration= */ 60)));
+
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
+ /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 60));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
+ assertEquals(2, mAdapter.apply(segments, 2, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withPwleCapabilityAndFrequency_convertsStepsToRamps() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ -1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 1, /* duration= */ 100)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 100));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
+ assertEquals(0, mAdapter.apply(segments, 0, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withRampDownEndingAtNonZero_noRampDownAdded() {
+ int rampDownDuration = 50;
+ mAdapter = new StepToRampAdapter(rampDownDuration);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 1, /* duration= */ 100)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 100));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withRampDownAndShortZeroSegment_replaceWithRampDown() {
+ mAdapter = new StepToRampAdapter(50);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ -1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 20),
+ new StepSegment(/* amplitude= */ 1, /* frequency= */ 1, /* duration= */ 30)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(2, mAdapter.apply(segments, 2, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withRampDownAndLongZeroSegment_splitAndAddRampDown() {
+ mAdapter = new StepToRampAdapter(50);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ -1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 150),
+ new StepSegment(/* amplitude= */ 1, /* frequency= */ 1, /* duration= */ 30)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ // Repeat index fixed after intermediate steps added
+ assertEquals(3, mAdapter.apply(segments, 2, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withRampDownAndNoZeroSegment_noRampDownAdded() {
+ mAdapter = new StepToRampAdapter(50);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ -1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 1, /* frequency= */ 1, /* duration= */ 30),
+ new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30),
+ new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withRampDownAndRepeatToNonZeroSegment_noRampDownAdded() {
+ mAdapter = new StepToRampAdapter(50);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ -1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 1, /* frequency= */ 1, /* duration= */ 30)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(0, mAdapter.apply(segments, 0, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withRampDownAndRepeatToShortZeroSegment_skipAndAppendRampDown() {
+ mAdapter = new StepToRampAdapter(50);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 20),
+ new StepSegment(/* amplitude= */ 1, /* frequency= */ 1, /* duration= */ 30)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ // Shift repeat index to the right to use append instead of zero segment.
+ assertEquals(1, mAdapter.apply(segments, 0, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
+ public void testStepSegments_withRampDownAndRepeatToLongZeroSegment_splitAndAppendRampDown() {
+ mAdapter = new StepToRampAdapter(50);
+
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 120),
+ new StepSegment(/* amplitude= */ 1, /* frequency= */ 1, /* duration= */ 30)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ // Split long zero segment to skip part of it.
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 70),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
+ /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 50));
+
+ VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ // Shift repeat index to the right to use append with part of the zero segment.
+ assertEquals(1, mAdapter.apply(segments, 0, vibratorInfo));
+
+ assertEquals(expectedSegments, segments);
+ }
+
+ private static VibratorInfo createVibratorInfo(int... capabilities) {
+ return new VibratorInfo.Builder(0)
+ .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index ac3e05d27962..f02e2f081e3b 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibratorManager;
@@ -98,13 +99,17 @@ public class VibrationThreadTest {
private IBatteryStats mIBatteryStatsMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
+ private DeviceVibrationEffectAdapter mEffectAdapter;
private PowerManager.WakeLock mWakeLock;
private TestLooper mTestLooper;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
- mWakeLock = InstrumentationRegistry.getContext().getSystemService(
+
+ Context context = InstrumentationRegistry.getContext();
+ mEffectAdapter = new DeviceVibrationEffectAdapter(context);
+ mWakeLock = context.getSystemService(
PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mockVibrators(VIBRATOR_ID);
@@ -985,8 +990,8 @@ public class VibrationThreadTest {
}
private VibrationThread startThreadAndDispatcher(Vibration vib) {
- VibrationThread thread = new VibrationThread(vib, createVibratorControllers(), mWakeLock,
- mIBatteryStatsMock, mThreadCallbacks);
+ VibrationThread thread = new VibrationThread(vib, mEffectAdapter,
+ createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks);
doAnswer(answer -> {
thread.vibratorComplete(answer.getArgument(0));
return null;