summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java84
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java59
2 files changed, 129 insertions, 14 deletions
diff --git a/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java b/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
index 7f2b07b6f367..8eff2b9aab9e 100644
--- a/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
+++ b/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
@@ -26,11 +26,14 @@ import android.util.MathUtils;
import android.util.Range;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/** Adapts a {@link VibrationEffect} to a specific device, taking into account its capabilities. */
final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<VibratorInfo> {
+ private static final int RAMP_STEP_DURATION_MILLIS = 5;
+
/** Adapts a sequence of {@link VibrationEffectSegment} to device's capabilities. */
interface SegmentsAdapter {
@@ -38,16 +41,17 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
* Modifies the given segments list by adding/removing segments to it based on the
* device capabilities specified by given {@link VibratorInfo}.
*
- * @param segments List of {@link VibrationEffectSegment} to be adapter to the device.
- * @param repeatIndex Repeat index on the current segment list.
+ * @param segments List of {@link VibrationEffectSegment} to be modified.
+ * @param repeatIndex Repeat index of the vibration with given segment list.
* @param info The device vibrator info that the segments must be adapted to.
- * @return The new repeat index on the modifies list.
+ * @return The new repeat index to be used for the modified list.
*/
int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info);
}
private final SegmentsAdapter mAmplitudeFrequencyAdapter;
private final SegmentsAdapter mStepToRampAdapter;
+ private final SegmentsAdapter mRampToStepsAdapter;
DeviceVibrationEffectAdapter() {
this(new ClippingAmplitudeFrequencyAdapter());
@@ -56,6 +60,7 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
DeviceVibrationEffectAdapter(SegmentsAdapter amplitudeFrequencyAdapter) {
mAmplitudeFrequencyAdapter = amplitudeFrequencyAdapter;
mStepToRampAdapter = new StepToRampAdapter();
+ mRampToStepsAdapter = new RampToStepsAdapter(RAMP_STEP_DURATION_MILLIS);
}
@Override
@@ -68,7 +73,10 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments());
int newRepeatIndex = composed.getRepeatIndex();
- // Maps steps that should be handled by PWLE to ramps.
+ // Replace ramps with a sequence of fixed steps, or no-op if PWLE capability present.
+ newRepeatIndex = mRampToStepsAdapter.apply(newSegments, newRepeatIndex, info);
+
+ // Replace steps that should be handled by PWLE to ramps, or no-op if capability missing.
// This should be done before frequency is converted from relative to absolute values.
newRepeatIndex = mStepToRampAdapter.apply(newSegments, newRepeatIndex, info);
@@ -76,7 +84,6 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
// to absolute values in Hertz.
newRepeatIndex = mAmplitudeFrequencyAdapter.apply(newSegments, newRepeatIndex, info);
- // TODO(b/167947076): add ramp to step adapter
// TODO(b/167947076): add filter that removes unsupported primitives
// TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
@@ -86,7 +93,7 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
/**
* Adapter that converts step segments that should be handled as PWLEs to ramp segments.
*
- * <p>This leves the list unchanged if the device do not have compose PWLE capability.
+ * <p>This leaves the list unchanged if the device do not have compose PWLE capability.
*/
private static final class StepToRampAdapter implements SegmentsAdapter {
@Override
@@ -124,6 +131,71 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
}
/**
+ * 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.
+ */
+ private static final class RampToStepsAdapter implements SegmentsAdapter {
+ private final int mStepDuration;
+
+ RampToStepsAdapter(int stepDuration) {
+ mStepDuration = stepDuration;
+ }
+
+ @Override
+ public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
+ VibratorInfo info) {
+ if (info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+ // The vibrator have PWLE capability, so keep the segments unchanged.
+ return repeatIndex;
+ }
+ int segmentCount = segments.size();
+ for (int i = 0; i < segmentCount; i++) {
+ VibrationEffectSegment segment = segments.get(i);
+ if (!(segment instanceof RampSegment)) {
+ continue;
+ }
+ List<StepSegment> steps = apply((RampSegment) segment);
+ segments.remove(i);
+ segments.addAll(i, steps);
+ int addedSegments = steps.size() - 1;
+ if (repeatIndex > i) {
+ repeatIndex += addedSegments;
+ }
+ i += addedSegments;
+ segmentCount += addedSegments;
+ }
+ return repeatIndex;
+ }
+
+ private List<StepSegment> apply(RampSegment ramp) {
+ if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) {
+ // Amplitude is the same, so return a single step to simulate this ramp.
+ return Arrays.asList(
+ new StepSegment(ramp.getStartAmplitude(), ramp.getStartFrequency(),
+ (int) ramp.getDuration()));
+ }
+
+ List<StepSegment> steps = new ArrayList<>();
+ int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration;
+ for (int i = 0; i < stepCount - 1; i++) {
+ float pos = (float) i / stepCount;
+ steps.add(new StepSegment(
+ interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
+ interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), pos),
+ mStepDuration));
+ }
+ int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1);
+ steps.add(new StepSegment(ramp.getEndAmplitude(), ramp.getEndFrequency(), duration));
+ return steps;
+ }
+
+ private static float interpolate(float start, float end, float position) {
+ return start + position * (end - start);
+ }
+ }
+
+ /**
* Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRange()} and
* amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
*
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 f65969833521..00b05d4ad10c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
@@ -77,6 +77,38 @@ public class DeviceVibrationEffectAdapterTest {
}
@Test
+ public void testStepAndRampSegments_withoutPwleCapability_convertsRampsToSteps() {
+ VibrationEffect.Composed effect = new VibrationEffect.Composed(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= */ 0, /* endFrequency= */ 0, /* duration= */ 11),
+ new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
+ /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 200)),
+ /* 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));
+ }
+
+ @Test
public void testStepAndRampSegments_withPwleCapability_convertsStepsToRamps() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
@@ -131,7 +163,7 @@ public class DeviceVibrationEffectAdapterTest {
}
@Test
- public void testStepAndRampSegments_emptyMapping_returnsSameAmplitudesAndFrequencyZero() {
+ public void testStepAndRampSegments_withEmptyFreqMapping_returnsSameAmplitudesAndZeroFreq() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
@@ -142,8 +174,11 @@ public class DeviceVibrationEffectAdapterTest {
/* repeatIndex= */ 2);
VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
- new StepSegment(/* amplitude= */ 0, /* frequency= */ Float.NaN, /* duration= */ 10),
- new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ Float.NaN,
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
+ /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
+ /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
+ /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
/* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1,
/* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
@@ -153,11 +188,13 @@ public class DeviceVibrationEffectAdapterTest {
/* duration= */ 20)),
/* repeatIndex= */ 2);
- assertEquals(expected, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_MAPPING)));
+ VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_MAPPING,
+ IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(expected, mAdapter.apply(effect, info));
}
@Test
- public void testStepAndRampSegments_nonEmptyMapping_returnsClippedValues() {
+ public void testStepAndRampSegments_withValidFreqMapping_returnsClippedValues() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequency= */ -1, /* duration= */ 100),
@@ -168,15 +205,21 @@ public class DeviceVibrationEffectAdapterTest {
/* repeatIndex= */ 2);
VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
- new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 150, /* duration= */ 10),
- new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 125, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
+ /* startFrequency= */ 150, /* endFrequency= */ 150,
+ /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
+ /* startFrequency= */ 125, /* endFrequency= */ 125,
+ /* duration= */ 100),
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);
- assertEquals(expected, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_MAPPING)));
+ VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
+ IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(expected, mAdapter.apply(effect, info));
}
private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,