summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/VibrationEffect.java22
-rw-r--r--core/java/android/os/Vibrator.java22
-rw-r--r--core/java/android/os/vibrator/PrebakedSegment.java26
-rw-r--r--core/java/android/os/vibrator/PrimitiveSegment.java7
-rw-r--r--core/java/android/os/vibrator/RampSegment.java24
-rw-r--r--core/java/android/os/vibrator/StepSegment.java14
-rw-r--r--core/java/android/os/vibrator/VibrationEffectSegment.java32
-rw-r--r--core/tests/coretests/src/android/os/VibrationEffectTest.java54
-rw-r--r--core/tests/coretests/src/android/os/VibratorTest.java200
-rw-r--r--core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java145
-rw-r--r--core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java56
-rw-r--r--core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java139
-rw-r--r--core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java101
13 files changed, 841 insertions, 1 deletions
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index fa6efa886d89..4366c2811c65 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.hardware.vibrator.V1_3.Effect;
import android.net.Uri;
+import android.os.Vibrator;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
@@ -515,6 +516,16 @@ public abstract class VibrationEffect implements Parcelable {
public abstract long getDuration();
/**
+ * Checks if a given {@link Vibrator} can play this effect as intended.
+ *
+ * <p>See @link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information
+ * about what counts as supported by a vibrator, and what counts as not.
+ *
+ * @hide
+ */
+ public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+
+ /**
* Returns true if this effect could represent a touch haptic feedback.
*
* <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
@@ -758,6 +769,17 @@ public abstract class VibrationEffect implements Parcelable {
/** @hide */
@Override
+ public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ for (VibrationEffectSegment segment : mSegments) {
+ if (!segment.areVibrationFeaturesSupported(vibrator)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** @hide */
+ @Override
public boolean isHapticFeedbackCandidate() {
long totalDuration = getDuration();
if (totalDuration > MAX_HAPTIC_FEEDBACK_DURATION) {
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 7edcdd754f06..4e852e333ec8 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -222,6 +222,28 @@ public abstract class Vibrator {
}
/**
+ * Checks whether or not the vibrator supports all components of a given {@link VibrationEffect}
+ * (i.e. the vibrator can play the given effect as intended).
+ *
+ * <p>If this method returns {@code true}, then the VibrationEffect should play as expected.
+ * If {@code false}, playing the VibrationEffect might still make a vibration, but the vibration
+ * may be significantly degraded from the intention.
+ *
+ * <p>This method aggregates the results of feature check methods such as
+ * {@link #hasAmplitudeControl}, {@link #areAllPrimitivesSupported(int...)}, etc, depending
+ * on the features that are actually used by the VibrationEffect.
+ *
+ * @param effect the {@link VibrationEffect} to check if it is supported
+ * @return {@code true} if the vibrator can play the given {@code effect} as intended,
+ * {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) {
+ return effect.areVibrationFeaturesSupported(this);
+ }
+
+ /**
* Check whether the vibrator can be controlled by an external service with the
* {@link IExternalVibratorService}.
*
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 30f5a5ca86c5..cc76ffa39149 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -22,6 +22,7 @@ import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
+import android.os.Vibrator;
import java.util.Objects;
@@ -69,6 +70,31 @@ public final class PrebakedSegment extends VibrationEffectSegment {
/** @hide */
@Override
+ public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ if (vibrator.areAllEffectsSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
+ return true;
+ }
+ if (!mFallback) {
+ // If the Vibrator's support is not `VIBRATION_EFFECT_SUPPORT_YES`, and this effect does
+ // not support fallbacks, the effect is considered not supported by the vibrator.
+ return false;
+ }
+ // The vibrator does not have hardware support for the effect, but the effect has fallback
+ // support. Check if a fallback will be available for the effect ID.
+ switch (mEffectId) {
+ case VibrationEffect.EFFECT_CLICK:
+ case VibrationEffect.EFFECT_DOUBLE_CLICK:
+ case VibrationEffect.EFFECT_HEAVY_CLICK:
+ case VibrationEffect.EFFECT_TICK:
+ // Any of these effects are always supported via some form of fallback.
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** @hide */
+ @Override
public boolean isHapticFeedbackCandidate() {
switch (mEffectId) {
case VibrationEffect.EFFECT_CLICK:
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 2d287e96ef60..cde0ff3aa2f7 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -22,6 +22,7 @@ import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
+import android.os.Vibrator;
import com.android.internal.util.Preconditions;
@@ -69,6 +70,12 @@ public final class PrimitiveSegment extends VibrationEffectSegment {
/** @hide */
@Override
+ public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ return vibrator.areAllPrimitivesSupported(mPrimitiveId);
+ }
+
+ /** @hide */
+ @Override
public boolean isHapticFeedbackCandidate() {
return true;
}
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index d7d8c49fe28c..034962a5f91b 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.VibrationEffect;
+import android.os.Vibrator;
import com.android.internal.util.Preconditions;
@@ -95,6 +96,29 @@ public final class RampSegment extends VibrationEffectSegment {
/** @hide */
@Override
+ public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ boolean areFeaturesSupported = true;
+ // If the start/end frequencies are not the same, require frequency control since we need to
+ // ramp up/down the frequency.
+ if ((mStartFrequencyHz != mEndFrequencyHz)
+ // If there is no frequency ramping, make sure that the one frequency used does not
+ // require frequency control.
+ || frequencyRequiresFrequencyControl(mStartFrequencyHz)) {
+ areFeaturesSupported &= vibrator.hasFrequencyControl();
+ }
+ // If the start/end amplitudes are not the same, require amplitude control since we need to
+ // ramp up/down the amplitude.
+ if ((mStartAmplitude != mEndAmplitude)
+ // If there is no amplitude ramping, make sure that the amplitude used does not
+ // require amplitude control.
+ || amplitudeRequiresAmplitudeControl(mStartAmplitude)) {
+ areFeaturesSupported &= vibrator.hasAmplitudeControl();
+ }
+ return areFeaturesSupported;
+ }
+
+ /** @hide */
+ @Override
public boolean isHapticFeedbackCandidate() {
return true;
}
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 5a0bbf7d9436..115a66c5580a 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -21,6 +21,7 @@ import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
+import android.os.Vibrator;
import com.android.internal.util.Preconditions;
@@ -81,6 +82,19 @@ public final class StepSegment extends VibrationEffectSegment {
/** @hide */
@Override
+ public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ boolean areFeaturesSupported = true;
+ if (frequencyRequiresFrequencyControl(mFrequencyHz)) {
+ areFeaturesSupported &= vibrator.hasFrequencyControl();
+ }
+ if (amplitudeRequiresAmplitudeControl(mAmplitude)) {
+ areFeaturesSupported &= vibrator.hasAmplitudeControl();
+ }
+ return areFeaturesSupported;
+ }
+
+ /** @hide */
+ @Override
public boolean isHapticFeedbackCandidate() {
return true;
}
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index be1055362f1c..75a055fa5273 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -21,6 +21,7 @@ import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
+import android.os.Vibrator;
/**
* Representation of a single segment of a {@link VibrationEffect}.
@@ -57,6 +58,15 @@ public abstract class VibrationEffectSegment implements Parcelable {
*/
public abstract long getDuration();
+ /**
+ * Checks if a given {@link Vibrator} can play this segment as intended. See
+ * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about
+ * what counts as supported by a vibrator, and what counts as not.
+ *
+ * @hide
+ */
+ public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+
/**
* Returns true if this segment could be a haptic feedback effect candidate.
*
@@ -149,6 +159,28 @@ public abstract class VibrationEffectSegment implements Parcelable {
}
}
+ /**
+ * Helper method to check if an amplitude requires a vibrator to have amplitude control to play.
+ *
+ * @hide
+ */
+ protected static boolean amplitudeRequiresAmplitudeControl(float amplitude) {
+ return (amplitude != 0)
+ && (amplitude != 1)
+ && (amplitude != VibrationEffect.DEFAULT_AMPLITUDE);
+ }
+
+ /**
+ * Helper method to check if a frequency requires a vibrator to have frequency control to play.
+ *
+ * @hide
+ */
+ protected static boolean frequencyRequiresFrequencyControl(float frequency) {
+ // Anything other than the default frequency value (represented with "0") requires frequency
+ // control.
+ return frequency != 0;
+ }
+
@NonNull
public static final Creator<VibrationEffectSegment> CREATOR =
new Creator<VibrationEffectSegment>() {
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index 0c7ff4a762b5..627feaba1422 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -35,13 +35,19 @@ import android.content.ContentInterface;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.vibrator.IVibrator;
import android.net.Uri;
+import android.os.SystemVibrator;
import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException;
+import android.os.Vibrator;
+import android.os.VibratorInfo;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.platform.test.annotations.Presubmit;
+import androidx.test.InstrumentationRegistry;
+
import com.android.internal.R;
import org.junit.Test;
@@ -770,6 +776,45 @@ public class VibrationEffectTest {
}
@Test
+ public void testAreVibrationFeaturesSupported_allSegmentsSupported() {
+ Vibrator vibrator =
+ createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build());
+
+ assertTrue(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 40},
+ /* repeatIndex= */ 2)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(
+ VibrationEffect.startComposition()
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .repeatEffectIndefinitely(TEST_ONE_SHOT)
+ .compose()
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
+ public void testAreVibrationFeaturesSupported_withUnsupportedSegments() {
+ Vibrator vibrator =
+ createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1).build());
+
+ assertFalse(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 40},
+ /* repeatIndex= */ -1))
+ .compose()
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
public void testIsHapticFeedbackCandidate_repeatingEffects_notCandidates() {
assertFalse(VibrationEffect.createWaveform(
new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).isHapticFeedbackCandidate());
@@ -890,4 +935,13 @@ public class VibrationEffectTest {
return context;
}
+
+ private Vibrator createVibratorWithCustomInfo(VibratorInfo info) {
+ return new SystemVibrator(InstrumentationRegistry.getContext()) {
+ @Override
+ public VibratorInfo getInfo() {
+ return info;
+ }
+ };
+ }
}
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index c59a3f518da8..375fdac2e223 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -586,6 +586,189 @@ public class VibratorTest {
assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes);
}
+ @Test
+ public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudes() {
+ Vibrator vibrator =
+ createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedEffects(VibrationEffect.EFFECT_THUD)
+ .build());
+
+ // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT).
+ assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30)));
+ assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255)));
+ assertFalse(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudes() {
+ Vibrator vibrator =
+ createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedEffects(VibrationEffect.EFFECT_THUD)
+ .build());
+
+ // All amplitudes are min, max, or default. Requires no amplitude control.
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_withAmplitudeControl() {
+ Vibrator vibrator =
+ createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build());
+
+ // All forms of amplitudes are valid when amplitude control is available.
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(7, 255, 0, 0, 60)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_primitiveCompositionsWithSupportedPrimitives() {
+ Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build());
+
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose()));
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ /* scale= */ 0.2f,
+ /* delay= */ 200)
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_primitiveCompositionsWithUnupportedPrimitives() {
+ Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build());
+
+ assertFalse(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .compose()));
+ assertFalse(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() {
+ Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
+ .build());
+
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .compose()));
+
+ vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK)
+ .build());
+
+ assertTrue(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addEffect(VibrationEffect.createWaveform(
+ // These timings are given either 0 or default amplitudes, which
+ // do not require vibrator's amplitude control.
+ /* timings= */ new long[] {1, 2, 3},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() {
+ Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
+ .build());
+
+ // Not supported due to the TICK primitive, which the vibrator has no support for.
+ assertFalse(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .compose()));
+ // Not supported due to the THUD effect, which the vibrator has no support for.
+ assertFalse(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD))
+ .compose()));
+
+ vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP)
+ .build());
+
+ // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or
+ // DEFAULT), because the vibrator has no amplitude control.
+ assertFalse(vibrator.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .compose()));
+ }
+
/**
* Asserts that the frequency profile is empty, and therefore frequency control isn't supported.
*/
@@ -593,4 +776,21 @@ public class VibratorTest {
assertTrue(info.getFrequencyProfile().isEmpty());
assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
}
+
+ private Vibrator createVibratorWithCustomInfo(VibratorInfo info) {
+ return new SystemVibrator(mContextSpy) {
+ @Override
+ public VibratorInfo getInfo() {
+ return info;
+ }
+ };
+ }
+
+ private static VibrationEffect waveformWithAmplitudes(int...amplitudes) {
+ long[] timings = new long[amplitudes.length];
+ for (int i = 0; i < timings.length; i++) {
+ timings[i] = i * 2; // Arbitrary timings.
+ }
+ return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1);
+ }
}
diff --git a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
index a0e1f437f1da..9099274e2767 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -25,9 +25,14 @@ import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertThrows;
import android.os.Parcel;
+import android.os.SystemVibrator;
import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.os.VibratorInfo;
import android.platform.test.annotations.Presubmit;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@@ -146,9 +151,149 @@ public class PrebakedSegmentTest {
}
@Test
+ public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_vibratorSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibrationEffect.EFFECT_TICK,
+ VibrationEffect.EFFECT_CLICK,
+ VibrationEffect.EFFECT_DOUBLE_CLICK,
+ VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_noVibratorSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_vibratorSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibrationEffect.EFFECT_TICK,
+ VibrationEffect.EFFECT_CLICK,
+ VibrationEffect.EFFECT_DOUBLE_CLICK,
+ VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_noVibratorSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+
+ assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_vibratorSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibrationEffect.EFFECT_THUD,
+ VibrationEffect.EFFECT_POP,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_noVibratorSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+
+ assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+ .areVibrationFeaturesSupported(vibrator));
+ assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+ .areVibrationFeaturesSupported(vibrator));
+ assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_vibratorSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibrationEffect.EFFECT_THUD,
+ VibrationEffect.EFFECT_POP,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP)
+ .areVibrationFeaturesSupported(vibrator));
+ assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_noVibSupport() {
+ Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+
+ assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD)
+ .areVibrationFeaturesSupported(vibrator));
+ assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP)
+ .areVibrationFeaturesSupported(vibrator));
+ assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+ .areVibrationFeaturesSupported(vibrator));
+ }
+
+ @Test
public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() {
assertFalse(new PrebakedSegment(
VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
.isHapticFeedbackCandidate());
}
+
+ private static PrebakedSegment createSegmentWithFallback(int effectId) {
+ // note: arbitrary effect strength being used.
+ return new PrebakedSegment(effectId, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ }
+
+ private static PrebakedSegment createSegmentWithoutFallback(int effectId) {
+ // note: arbitrary effect strength being used.
+ return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ }
+
+ private static Vibrator createVibratorWithSupportedEffects(int... supportedEffects) {
+ return new SystemVibrator(InstrumentationRegistry.getContext()) {
+ @Override
+ public VibratorInfo getInfo() {
+ return new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(supportedEffects)
+ .build();
+ }
+ };
+ }
}
diff --git a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
index a69055335663..298438fdc243 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -17,15 +17,22 @@
package android.os.vibrator;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
+import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
+import android.os.SystemVibrator;
import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.os.VibratorInfo;
import android.platform.test.annotations.Presubmit;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@@ -139,6 +146,38 @@ public class PrimitiveSegmentTest {
}
@Test
+ public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() {
+ assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .areVibrationFeaturesSupported(
+ createVibratorWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK)));
+ assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .areVibrationFeaturesSupported(
+ createVibratorWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_THUD)));
+ assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
+ .areVibrationFeaturesSupported(
+ createVibratorWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_primitiveNotSupportedByVibrator() {
+ assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .areVibrationFeaturesSupported(
+ createVibratorWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_THUD)));
+ assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .areVibrationFeaturesSupported(
+ createVibratorWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK)));
+ assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .areVibrationFeaturesSupported(
+ createVibratorWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)));
+ }
+
+ @Test
public void testIsHapticFeedbackCandidate_returnsTrue() {
assertTrue(new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 10).isHapticFeedbackCandidate());
@@ -151,4 +190,21 @@ public class PrimitiveSegmentTest {
assertTrue(new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_SPIN, 1, 10).isHapticFeedbackCandidate());
}
+
+ private static PrimitiveSegment createSegment(int primitiveId) {
+ // note: arbitrary scale and delay values being used.
+ return new PrimitiveSegment(primitiveId, 0.2f, 10);
+ }
+
+ private static Vibrator createVibratorWithSupportedPrimitive(int primitiveId) {
+ return new SystemVibrator(InstrumentationRegistry.getContext()) {
+ @Override
+ public VibratorInfo getInfo() {
+ return new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(primitiveId, 10)
+ .build();
+ }
+ };
+ }
}
diff --git a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
index 3291b2d8edd9..6f8c20558ddc 100644
--- a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
@@ -16,26 +16,40 @@
package android.os.vibrator;
+import static android.os.VibrationEffect.DEFAULT_AMPLITUDE;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.os.Parcel;
import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.platform.test.annotations.Presubmit;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class RampSegmentTest {
private static final float TOLERANCE = 1e-2f;
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private Vibrator mVibrator;
+
@Test
public void testCreation() {
RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
@@ -66,7 +80,7 @@ public class RampSegmentTest {
new RampSegment(0, 0, 0, 0, 0).validate();
assertThrows(IllegalArgumentException.class,
- () -> new RampSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate());
+ () -> new RampSegment(DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate());
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(/* startAmplitude= */ -2, 0, 0, 0, 0).validate());
assertThrows(IllegalArgumentException.class,
@@ -142,6 +156,129 @@ public class RampSegmentTest {
}
@Test
+ public void testVibrationFeaturesSupport_amplitudeAndFrequencyControls_supported() {
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+
+ // Increasing amplitude
+ assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ // Increasing frequency
+ assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(mVibrator));
+ // Decreasing amplitude
+ assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ // Decreasing frequency
+ assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ // Zero duration
+ assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_noAmplitudeControl_unsupportedForChangingAmplitude() {
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+
+ // Test with increasing/decreasing amplitudes.
+ assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_noAmplitudeControl_fractionalAmplitudeUnsupported() {
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+
+ assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_unchangingZeroAmplitude_supported() {
+ RampSegment amplitudeZeroWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10);
+ RampSegment amplitudeZeroWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10);
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+
+ assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+
+ assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_unchangingOneAmplitude_supported() {
+ RampSegment amplitudeOneWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10);
+ RampSegment amplitudeOneWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10);
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+
+ assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+
+ assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_unchangingDefaultAmplitude_supported() {
+ RampSegment defaultAmplitudeIncreasingFrequency =
+ new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.5f, 0.8f, 10);
+ RampSegment defaultAmplitudeDecreasingFrequency =
+ new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.8f, 0.5f, 10);
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+
+ assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+
+ assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_noFrequencyControl_unsupportedForChangingFrequency() {
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ when(mVibrator.hasFrequencyControl()).thenReturn(false);
+
+ // Test with increasing/decreasing frequencies.
+ assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_noFrequencyControl_fractionalFrequencyUnsupported() {
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ when(mVibrator.hasFrequencyControl()).thenReturn(false);
+
+ assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_unchangingZeroFrequency_supported() {
+ RampSegment frequencyZeroWithIncreasingAmplitude = new RampSegment(0.1f, 1, 0, 0, 10);
+ RampSegment frequencyZeroWithDecreasingAmplitude = new RampSegment(1, 0.1f, 0, 0, 10);
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ when(mVibrator.hasFrequencyControl()).thenReturn(false);
+
+ assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+
+ assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
public void testIsHapticFeedbackCandidate_returnsTrue() {
// A single ramp segment duration is not checked here, but contributes to the effect known
// duration checked in VibrationEffect implementations.
diff --git a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
index 44241273d9e3..ade21613013f 100644
--- a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
@@ -21,21 +21,33 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.os.Parcel;
import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.platform.test.annotations.Presubmit;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class StepSegmentTest {
private static final float TOLERANCE = 1e-2f;
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private Vibrator mVibrator;
+
@Test
public void testCreation() {
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f,
@@ -156,6 +168,95 @@ public class StepSegmentTest {
}
@Test
+ public void testVibrationFeaturesSupport_zeroAmplitude_supported() {
+ StepSegment segment =
+ new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 0);
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_maxAmplitude_supported() {
+ StepSegment segment =
+ new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 0);
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_defaultAmplitude_supported() {
+ StepSegment segment =
+ new StepSegment(
+ /* amplitude= */ VibrationEffect.DEFAULT_AMPLITUDE,
+ /* frequencyHz= */ 0,
+ /* duration= */ 0);
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_fractionalAmplitude_hasAmplitudeCtrl_supported() {
+ when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+
+ assertTrue(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0)
+ .areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_fractionalAmplitude_hasNoAmplitudeCtrl_notSupported() {
+ when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+
+ assertFalse(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0)
+ .areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_zeroFrequency_supported() {
+ StepSegment segment =
+ new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0, /* duration= */ 0);
+ when(mVibrator.hasFrequencyControl()).thenReturn(false);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_nonZeroFrequency_hasFrequencyCtrl_supported() {
+ StepSegment segment =
+ new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0);
+ when(mVibrator.hasFrequencyControl()).thenReturn(true);
+
+ assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
+ public void testVibrationFeaturesSupport_nonZeroFrequency_hasNoFrequencyCtrl_notSupported() {
+ StepSegment segment =
+ new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0);
+ when(mVibrator.hasFrequencyControl()).thenReturn(false);
+
+ assertFalse(segment.areVibrationFeaturesSupported(mVibrator));
+ }
+
+ @Test
public void testIsHapticFeedbackCandidate_returnsTrue() {
// A single step segment duration is not checked here, but contributes to the effect known
// duration checked in VibrationEffect implementations.