Expose vibrator frequency bandwith to the service

Add hidden APIs to expose the supported frequency range and maximum
amplitudes at each frequency for the device in Vibrator.java.

Also add a hidden method hasFrequencyControl().

Bug: 167947076
Test: VibratorTest
Change-Id: If2d5f8f0aa92223b4a342893f5fd3a1668dfcff1
This commit is contained in:
Lais Andrade
2021-03-31 16:12:26 +00:00
parent f5195618f8
commit 853dcb5a41
9 changed files with 304 additions and 212 deletions

View File

@@ -20,11 +20,13 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.content.Context;
import android.hardware.vibrator.IVibrator;
import android.os.Binder;
import android.os.IVibratorStateListener;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
import android.util.ArrayMap;
import android.util.Log;
@@ -41,7 +43,7 @@ final class InputDeviceVibrator extends Vibrator {
// mDeviceId represents InputDevice ID the vibrator belongs to
private final int mDeviceId;
private final int mVibratorId;
private final VibratorInfo mVibratorInfo;
private final Binder mToken;
private final InputManager mInputManager;
@@ -52,7 +54,13 @@ final class InputDeviceVibrator extends Vibrator {
InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) {
mInputManager = inputManager;
mDeviceId = deviceId;
mVibratorId = vibratorId;
mVibratorInfo = new VibratorInfo.Builder(vibratorId)
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
// Set predefined support to empty as we know input devices do not support them.
.setSupportedEffects()
.setSupportedPrimitives()
.setSupportedBraking()
.build();
mToken = new Binder();
}
@@ -74,8 +82,8 @@ final class InputDeviceVibrator extends Vibrator {
}
@Override
public int getId() {
return mVibratorId;
protected VibratorInfo getInfo() {
return mVibratorInfo;
}
@Override
@@ -159,7 +167,7 @@ final class InputDeviceVibrator extends Vibrator {
@Override
public boolean hasAmplitudeControl() {
return true;
return mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
}
/**

View File

@@ -28,7 +28,6 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -50,6 +49,9 @@ public class SystemVibrator extends Vibrator {
private final ArrayMap<OnVibratorStateChangedListener, AllVibratorsStateListener>
mRegisteredListeners = new ArrayMap<>();
private final Object mLock = new Object();
private AllVibratorsInfo mVibratorInfo;
@UnsupportedAppUsage
public SystemVibrator(Context context) {
super(context);
@@ -57,6 +59,25 @@ public class SystemVibrator extends Vibrator {
mVibratorManager = mContext.getSystemService(VibratorManager.class);
}
@Override
protected VibratorInfo getInfo() {
synchronized (mLock) {
if (mVibratorInfo != null) {
return mVibratorInfo;
}
if (mVibratorManager == null) {
Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager.");
return VibratorInfo.EMPTY_VIBRATOR_INFO;
}
int[] vibratorIds = mVibratorManager.getVibratorIds();
VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
for (int i = 0; i < vibratorIds.length; i++) {
vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo();
}
return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
}
}
@Override
public boolean hasVibrator() {
if (mVibratorManager == null) {
@@ -144,20 +165,7 @@ public class SystemVibrator extends Vibrator {
@Override
public boolean hasAmplitudeControl() {
if (mVibratorManager == null) {
Log.w(TAG, "Failed to check vibrator has amplitude control; no vibrator manager.");
return false;
}
int[] vibratorIds = mVibratorManager.getVibratorIds();
if (vibratorIds.length == 0) {
return false;
}
for (int vibratorId : vibratorIds) {
if (!mVibratorManager.getVibrator(vibratorId).hasAmplitudeControl()) {
return false;
}
}
return true;
return getInfo().hasAmplitudeControl();
}
@Override
@@ -183,70 +191,6 @@ public class SystemVibrator extends Vibrator {
mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes);
}
@Override
public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) {
int[] supported = new int[effectIds.length];
if (mVibratorManager == null) {
Log.w(TAG, "Failed to check supported effects; no vibrator manager.");
Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
return supported;
}
int[] vibratorIds = mVibratorManager.getVibratorIds();
if (vibratorIds.length == 0) {
Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
return supported;
}
int[][] vibratorSupportMap = new int[vibratorIds.length][effectIds.length];
for (int i = 0; i < vibratorIds.length; i++) {
vibratorSupportMap[i] = mVibratorManager.getVibrator(
vibratorIds[i]).areEffectsSupported(effectIds);
}
Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_YES);
for (int effectIdx = 0; effectIdx < effectIds.length; effectIdx++) {
for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
int effectSupported = vibratorSupportMap[vibratorIdx][effectIdx];
if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
break;
} else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
}
}
}
return supported;
}
@Override
public boolean[] arePrimitivesSupported(
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
boolean[] supported = new boolean[primitiveIds.length];
if (mVibratorManager == null) {
Log.w(TAG, "Failed to check supported primitives; no vibrator manager.");
Arrays.fill(supported, false);
return supported;
}
int[] vibratorIds = mVibratorManager.getVibratorIds();
if (vibratorIds.length == 0) {
Arrays.fill(supported, false);
return supported;
}
boolean[][] vibratorSupportMap = new boolean[vibratorIds.length][primitiveIds.length];
for (int i = 0; i < vibratorIds.length; i++) {
vibratorSupportMap[i] = mVibratorManager.getVibrator(
vibratorIds[i]).arePrimitivesSupported(primitiveIds);
}
Arrays.fill(supported, true);
for (int primitiveIdx = 0; primitiveIdx < primitiveIds.length; primitiveIdx++) {
for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
if (!vibratorSupportMap[vibratorIdx][primitiveIdx]) {
supported[primitiveIdx] = false;
break;
}
}
}
return supported;
}
@Override
public void cancel() {
if (mVibratorManager == null) {
@@ -295,6 +239,58 @@ public class SystemVibrator extends Vibrator {
}
}
/**
* Represents all the vibrators information as a single {@link VibratorInfo}.
*
* <p>This uses the first vibrator on the list as the default one for all hardware spec, but
* uses an intersection of all vibrators to decide the capabilities and effect/primitive
* support.
*/
private static class AllVibratorsInfo extends VibratorInfo {
private final VibratorInfo[] mVibratorInfos;
AllVibratorsInfo(VibratorInfo[] vibrators) {
super(/* id= */ -1, capabilitiesIntersection(vibrators),
vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO);
mVibratorInfos = vibrators;
}
@Override
public int isEffectSupported(int effectId) {
int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES;
for (VibratorInfo info : mVibratorInfos) {
int effectSupported = info.isEffectSupported(effectId);
if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
return effectSupported;
} else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
supported = effectSupported;
}
}
return supported;
}
@Override
public boolean isPrimitiveSupported(int primitiveId) {
for (VibratorInfo info : mVibratorInfos) {
if (!info.isPrimitiveSupported(primitiveId)) {
return false;
}
}
return true;
}
private static int capabilitiesIntersection(VibratorInfo[] infos) {
if (infos.length == 0) {
return 0;
}
int intersection = ~0;
for (VibratorInfo info : infos) {
intersection &= info.getCapabilities();
}
return intersection;
}
}
/** Listener for all vibrators state change. */
private static class AllVibratorsStateListener {
private final Object mLock = new Object();

View File

@@ -185,8 +185,8 @@ public class SystemVibratorManager extends VibratorManager {
}
@Override
public int getId() {
return mVibratorInfo.getId();
protected VibratorInfo getInfo() {
return mVibratorInfo;
}
@Override
@@ -199,36 +199,6 @@ public class SystemVibratorManager extends VibratorManager {
return mVibratorInfo.hasAmplitudeControl();
}
@Override
public float getResonantFrequency() {
return mVibratorInfo.getResonantFrequency();
}
@Override
public float getQFactor() {
return mVibratorInfo.getQFactor();
}
@NonNull
@Override
public int[] areEffectsSupported(@NonNull int... effectIds) {
int[] supported = new int[effectIds.length];
for (int i = 0; i < effectIds.length; i++) {
supported[i] = mVibratorInfo.isEffectSupported(effectIds[i]);
}
return supported;
}
@Override
public boolean[] arePrimitivesSupported(
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
boolean[] supported = new boolean[primitiveIds.length];
for (int i = 0; i < primitiveIds.length; i++) {
supported[i] = mVibratorInfo.isPrimitiveSupported(primitiveIds[i]);
}
return supported;
}
@Override
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
@Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) {

View File

@@ -17,6 +17,7 @@
package android.os;
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,12 +27,13 @@ import android.annotation.SystemService;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.util.Log;
import android.util.Range;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.concurrent.Executor;
/**
@@ -156,6 +158,11 @@ public abstract class Vibrator {
return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM;
}
/** @hide */
protected VibratorInfo getInfo() {
return VibratorInfo.EMPTY_VIBRATOR_INFO;
}
/**
* Get the default vibration intensity for haptic feedback.
*
@@ -190,7 +197,7 @@ public abstract class Vibrator {
* service, or -1 this service is not attached to any physical vibrator.
*/
public int getId() {
return -1;
return getInfo().getId();
}
/**
@@ -207,15 +214,27 @@ public abstract class Vibrator {
*/
public abstract boolean hasAmplitudeControl();
/**
* Check whether the vibrator has independent frequency control.
*
* @return True if the hardware can control the frequency of the vibrations, otherwise false.
* @hide
*/
public boolean hasFrequencyControl() {
// We currently can only control frequency of the vibration using the compose PWLE method.
return getInfo().hasCapability(
IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
}
/**
* Gets the resonant frequency of the vibrator.
*
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
* this vibrator is a composite of multiple physical devices.
* this vibrator is a composite of multiple physical devices.
* @hide
*/
public float getResonantFrequency() {
return Float.NaN;
return getInfo().getResonantFrequency();
}
/**
@@ -226,7 +245,44 @@ public abstract class Vibrator {
* @hide
*/
public float getQFactor() {
return Float.NaN;
return getInfo().getQFactor();
}
/**
* Return a range of relative frequency values supported by the vibrator.
*
* <p>These values can be used to create waveforms that controls the vibration frequency via
* {@link VibrationEffect.WaveformBuilder}.
*
* @return A range of relative frequency values supported. The range will always contain the
* value 0, representing the device resonant frequency. Devices without frequency control will
* return the range [0,0]. Devices with frequency control will always return a range containing
* the safe range [-1, 1].
* @hide
*/
public Range<Float> getRelativeFrequencyRange() {
return getInfo().getFrequencyRange();
}
/**
* Return the maximum amplitude the vibrator can play at given relative frequency.
*
* <p>Devices without frequency control will return 1 for the input zero (resonant frequency),
* and 0 to any other input.
*
* <p>Devices with frequency control will return the supported value, for input in
* {@link #getRelativeFrequencyRange()}, and 0 for any other input.
*
* <p>These values can be used to create waveforms that plays vibrations outside the resonant
* frequency via {@link VibrationEffect.WaveformBuilder}.
*
* @return a value in [0,1] representing the maximum amplitude the device can play at given
* relative frequency.
* @hide
*/
@FloatRange(from = 0, to = 1)
public float getMaximumAmplitude(float relativeFrequency) {
return getInfo().getMaxAmplitude(relativeFrequency);
}
/**
@@ -414,9 +470,12 @@ public abstract class Vibrator {
@VibrationEffectSupport
public int[] areEffectsSupported(
@NonNull @VibrationEffect.EffectType int... effectIds) {
final int[] support = new int[effectIds.length];
Arrays.fill(support, VIBRATION_EFFECT_SUPPORT_NO);
return support;
VibratorInfo info = getInfo();
int[] supported = new int[effectIds.length];
for (int i = 0; i < effectIds.length; i++) {
supported[i] = info.isEffectSupported(effectIds[i]);
}
return supported;
}
/**
@@ -468,7 +527,12 @@ public abstract class Vibrator {
@NonNull
public boolean[] arePrimitivesSupported(
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
return new boolean[primitiveIds.length];
VibratorInfo info = getInfo();
boolean[] supported = new boolean[primitiveIds.length];
for (int i = 0; i < primitiveIds.length; i++) {
supported[i] = info.isPrimitiveSupported(primitiveIds[i]);
}
return supported;
}
/**

View File

@@ -38,9 +38,12 @@ import java.util.Objects;
*
* @hide
*/
public final class VibratorInfo implements Parcelable {
public class VibratorInfo implements Parcelable {
private static final String TAG = "VibratorInfo";
/** @hide */
public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build();
private final int mId;
private final long mCapabilities;
@Nullable
@@ -74,6 +77,23 @@ public final class VibratorInfo implements Parcelable {
mFrequencyMapping = frequencyMapping;
}
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
mId = id;
mCapabilities = capabilities;
mSupportedEffects = baseVibrator.mSupportedEffects == null ? null :
baseVibrator.mSupportedEffects.clone();
mSupportedBraking = baseVibrator.mSupportedBraking == null ? null :
baseVibrator.mSupportedBraking.clone();
mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null :
baseVibrator.mSupportedPrimitives.clone();
mQFactor = baseVibrator.mQFactor;
mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz,
baseVibrator.mFrequencyMapping.mResonantFrequencyHz,
baseVibrator.mFrequencyMapping.mFrequencyResolutionHz,
baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz,
baseVibrator.mFrequencyMapping.mMaxAmplitudes);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
@@ -145,6 +165,7 @@ public final class VibratorInfo implements Parcelable {
* Returns a default value to be applied to composed PWLE effects for braking.
*
* @return a supported braking value, one of android.hardware.vibrator.Braking.*
* @hide
*/
public int getDefaultBraking() {
if (mSupportedBraking != null) {
@@ -265,6 +286,10 @@ public final class VibratorInfo implements Parcelable {
return mFrequencyMapping.toHertz(relativeFrequency);
}
protected long getCapabilities() {
return mCapabilities;
}
private String[] getCapabilitiesNames() {
List<String> names = new ArrayList<>();
if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
@@ -370,7 +395,7 @@ public final class VibratorInfo implements Parcelable {
* <p>The mapping is defined linearly by the following points:
*
* <ol>
* <li>{@code toHertz(relativeMinFrequency} = minFrequency
* <li>{@code toHertz(relativeMinFrequency) = minFrequency}
* <li>{@code toHertz(-1) = resonantFrequency - safeRange / 2}
* <li>{@code toHertz(0) = resonantFrequency}
* <li>{@code toHertz(1) = resonantFrequency + safeRange / 2}
@@ -555,6 +580,75 @@ public final class VibratorInfo implements Parcelable {
};
}
/** @hide */
public static final class Builder {
private final int mId;
private int mCapabilities = 0;
private int[] mSupportedEffects = null;
private int[] mSupportedBraking = null;
private int[] mSupportedPrimitives = null;
private float mQFactor = Float.NaN;
private FrequencyMapping mFrequencyMapping =
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
/** A builder class for a {@link VibratorInfo}. */
public Builder(int id) {
mId = id;
}
/** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
@NonNull
public Builder setCapabilities(int capabilities) {
mCapabilities = capabilities;
return this;
}
/** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
@NonNull
public Builder setSupportedEffects(int... supportedEffects) {
mSupportedEffects = supportedEffects;
return this;
}
/** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
@NonNull
public Builder setSupportedBraking(int... supportedBraking) {
mSupportedBraking = supportedBraking;
return this;
}
/**
* Configure the primitives supported with
* {@link android.hardware.vibrator.CompositePrimitive} values.
*/
@NonNull
public Builder setSupportedPrimitives(int... supportedPrimitives) {
mSupportedPrimitives = supportedPrimitives;
return this;
}
/** Configure the vibrator quality factor. */
@NonNull
public Builder setQFactor(float qFactor) {
mQFactor = qFactor;
return this;
}
/** Configure the vibrator frequency information like resonant frequency and bandwidth. */
@NonNull
public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) {
mFrequencyMapping = frequencyMapping;
return this;
}
/** Build the configured {@link VibratorInfo}. */
@NonNull
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mSupportedPrimitives, mQFactor, mFrequencyMapping);
}
}
@NonNull
public static final Creator<VibratorInfo> CREATOR =
new Creator<VibratorInfo>() {

View File

@@ -35,6 +35,7 @@ import org.junit.runners.JUnit4;
public class VibratorInfoTest {
private static final float TEST_TOLERANCE = 1e-5f;
private static final int TEST_VIBRATOR_ID = 1;
private static final float TEST_MIN_FREQUENCY = 50;
private static final float TEST_RESONANT_FREQUENCY = 150;
private static final float TEST_FREQUENCY_RESOLUTION = 25;
@@ -50,9 +51,9 @@ public class VibratorInfoTest {
@Test
public void testHasAmplitudeControl() {
VibratorInfo noCapabilities = new InfoBuilder().build();
VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
assertFalse(noCapabilities.hasAmplitudeControl());
VibratorInfo composeAndAmplitudeControl = new InfoBuilder()
VibratorInfo composeAndAmplitudeControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS
| IVibrator.CAP_AMPLITUDE_CONTROL)
.build();
@@ -61,7 +62,7 @@ public class VibratorInfoTest {
@Test
public void testHasCapabilities() {
VibratorInfo info = new InfoBuilder()
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.build();
assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
@@ -70,8 +71,8 @@ public class VibratorInfoTest {
@Test
public void testIsEffectSupported() {
VibratorInfo noEffects = new InfoBuilder().build();
VibratorInfo canClick = new InfoBuilder()
VibratorInfo noEffects = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
VibratorInfo canClick = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.build();
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
@@ -84,7 +85,7 @@ public class VibratorInfoTest {
@Test
public void testIsPrimitiveSupported() {
VibratorInfo info = new InfoBuilder()
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
.build();
@@ -92,7 +93,7 @@ public class VibratorInfoTest {
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
// Returns false when there is no compose capability.
info = new InfoBuilder()
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
.build();
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
@@ -100,9 +101,10 @@ public class VibratorInfoTest {
@Test
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
assertEquals(Braking.NONE, new InfoBuilder().build().getDefaultBraking());
assertEquals(Braking.NONE, new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getDefaultBraking());
assertEquals(Braking.CLAB,
new InfoBuilder()
new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setSupportedBraking(Braking.NONE, Braking.CLAB)
.build()
.getDefaultBraking());
@@ -111,33 +113,34 @@ public class VibratorInfoTest {
@Test
public void testGetFrequencyRange_invalidFrequencyMappingReturnsEmptyRange() {
// Invalid, contains NaN values or empty array.
assertEquals(Range.create(0f, 0f), new InfoBuilder().build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new InfoBuilder()
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
Float.NaN, 150, 25, 50, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new InfoBuilder()
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, Float.NaN, 25, 50, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new InfoBuilder()
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, 150, Float.NaN, 50, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new InfoBuilder()
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, 150, 25, Float.NaN, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new InfoBuilder()
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(50, 150, 25, 50, null))
.build().getFrequencyRange());
// Invalid, minFrequency > resonantFrequency
assertEquals(Range.create(0f, 0f), new InfoBuilder()
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 250, /* resonantFrequency= */ 150, 25, 50, null))
.build().getFrequencyRange());
// Invalid, maxFrequency < resonantFrequency by changing resolution.
assertEquals(Range.create(0f, 0f), new InfoBuilder()
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, 150, /* frequencyResolutionHz= */10, 50, null))
.build().getFrequencyRange());
@@ -145,7 +148,7 @@ public class VibratorInfoTest {
@Test
public void testGetFrequencyRange_safeRangeLimitedByMaxFrequency() {
VibratorInfo info = new InfoBuilder()
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
/* frequencyResolutionHz= */ 25, /* suggestedSafeRangeHz= */ 200,
@@ -159,7 +162,7 @@ public class VibratorInfoTest {
@Test
public void testGetFrequencyRange_safeRangeLimitedByMinFrequency() {
VibratorInfo info = new InfoBuilder()
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
/* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 200,
@@ -173,7 +176,7 @@ public class VibratorInfoTest {
@Test
public void testGetFrequencyRange_validMappingReturnsFullRelativeRange() {
VibratorInfo info = new InfoBuilder()
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
/* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 100,
@@ -187,7 +190,7 @@ public class VibratorInfoTest {
@Test
public void testAbsoluteFrequency_emptyMappingReturnsNaN() {
VibratorInfo info = new InfoBuilder().build();
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
assertTrue(Float.isNaN(info.getAbsoluteFrequency(-1)));
assertTrue(Float.isNaN(info.getAbsoluteFrequency(0)));
assertTrue(Float.isNaN(info.getAbsoluteFrequency(1)));
@@ -195,7 +198,8 @@ public class VibratorInfoTest {
@Test
public void testAbsoluteFrequency_validRangeReturnsOriginalValue() {
VibratorInfo info = new InfoBuilder().setFrequencyMapping(TEST_FREQUENCY_MAPPING).build();
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).setFrequencyMapping(
TEST_FREQUENCY_MAPPING).build();
assertEquals(TEST_RESONANT_FREQUENCY, info.getAbsoluteFrequency(0), TEST_TOLERANCE);
// Safe range [-1, 1] = [125Hz, 175Hz] defined by suggested safe range 100Hz
@@ -213,7 +217,7 @@ public class VibratorInfoTest {
@Test
public void testGetMaxAmplitude_emptyMappingReturnsOnlyResonantFrequency() {
VibratorInfo info = new InfoBuilder().build();
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(0.1f), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(-1), TEST_TOLERANCE);
@@ -221,7 +225,7 @@ public class VibratorInfoTest {
@Test
public void testGetMaxAmplitude_validMappingReturnsMappedValues() {
VibratorInfo info = new InfoBuilder()
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(/* minFrequencyHz= */ 50,
/* resonantFrequencyHz= */ 150, /* frequencyResolutionHz= */ 25,
/* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP))
@@ -243,8 +247,7 @@ public class VibratorInfoTest {
@Test
public void testEquals() {
InfoBuilder completeBuilder = new InfoBuilder()
.setId(1)
VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
@@ -298,9 +301,8 @@ public class VibratorInfoTest {
.build();
assertNotEquals(complete, completeWithDifferentQFactor);
VibratorInfo empty = new InfoBuilder().setId(1).build();
VibratorInfo emptyWithKnownSupport = new InfoBuilder()
.setId(1)
VibratorInfo empty = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
VibratorInfo emptyWithKnownSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setSupportedEffects()
.setSupportedPrimitives()
.build();
@@ -309,8 +311,7 @@ public class VibratorInfoTest {
@Test
public void testParceling() {
VibratorInfo original = new InfoBuilder()
.setId(1)
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.setSupportedPrimitives(null)
@@ -324,54 +325,4 @@ public class VibratorInfoTest {
VibratorInfo restored = VibratorInfo.CREATOR.createFromParcel(parcel);
assertEquals(original, restored);
}
private static class InfoBuilder {
private int mId = 0;
private int mCapabilities = 0;
private int[] mSupportedEffects = null;
private int[] mSupportedBraking = null;
private int[] mSupportedPrimitives = null;
private float mQFactor = Float.NaN;
private VibratorInfo.FrequencyMapping mFrequencyMapping = EMPTY_FREQUENCY_MAPPING;
public InfoBuilder setId(int id) {
mId = id;
return this;
}
public InfoBuilder setCapabilities(int capabilities) {
mCapabilities = capabilities;
return this;
}
public InfoBuilder setSupportedEffects(int... supportedEffects) {
mSupportedEffects = supportedEffects;
return this;
}
public InfoBuilder setSupportedBraking(int... supportedBraking) {
mSupportedBraking = supportedBraking;
return this;
}
public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) {
mSupportedPrimitives = supportedPrimitives;
return this;
}
public InfoBuilder setQFactor(float qFactor) {
mQFactor = qFactor;
return this;
}
public InfoBuilder setFrequencyMapping(VibratorInfo.FrequencyMapping frequencyMapping) {
mFrequencyMapping = frequencyMapping;
return this;
}
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mSupportedPrimitives, mQFactor, mFrequencyMapping);
}
}
}

View File

@@ -54,6 +54,11 @@ public class VibratorTest {
mVibratorSpy = spy(InstrumentationRegistry.getContext().getSystemService(Vibrator.class));
}
@Test
public void getId_returnsDefaultId() {
assertEquals(-1, mVibratorSpy.getId());
}
@Test
public void areEffectsSupported_returnsArrayOfSameSize() {
assertEquals(0, mVibratorSpy.areEffectsSupported(new int[0]).length);

View File

@@ -182,7 +182,9 @@ public class DeviceVibrationEffectAdapterTest {
private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,
int... capabilities) {
int cap = IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0);
return new VibratorInfo(/* id= */ 0, cap, null, null, null, /* qFactor= */ Float.NaN,
frequencyMapping);
return new VibratorInfo.Builder(0)
.setCapabilities(cap)
.setFrequencyMapping(frequencyMapping)
.build();
}
}

View File

@@ -298,8 +298,10 @@ public class VibratorControllerTest {
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
when(mNativeWrapperMock.getInfo(/* suggestedFrequencyRange= */ 100)).thenReturn(
new VibratorInfo(VIBRATOR_ID, capabilities, null, null, null, Float.NaN,
frequencyMapping));
new VibratorInfo.Builder(VIBRATOR_ID)
.setCapabilities(capabilities)
.setFrequencyMapping(frequencyMapping)
.build());
}
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {