Merge "Split long vibration composition using HAL limits" into sc-dev am: 55a8db6450
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14807584 Change-Id: Id21e2dd1e3f42b0cb39229a503de313f6eb70c1a
This commit is contained in:
@@ -56,10 +56,10 @@ final class InputDeviceVibrator extends Vibrator {
|
||||
mDeviceId = deviceId;
|
||||
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()
|
||||
// The supported effect and braking lists are known to be empty for input devices,
|
||||
// which is different from not being set (that means the device support is unknown).
|
||||
.setSupportedEffects(new int[0])
|
||||
.setSupportedBraking(new int[0])
|
||||
.build();
|
||||
mToken = new Binder();
|
||||
}
|
||||
|
||||
@@ -51,8 +51,11 @@ public class VibratorInfo implements Parcelable {
|
||||
private final SparseBooleanArray mSupportedEffects;
|
||||
@Nullable
|
||||
private final SparseBooleanArray mSupportedBraking;
|
||||
@Nullable
|
||||
private final SparseIntArray mSupportedPrimitives;
|
||||
private final int mPrimitiveDelayMax;
|
||||
private final int mCompositionSizeMax;
|
||||
private final int mPwlePrimitiveDurationMax;
|
||||
private final int mPwleSizeMax;
|
||||
private final float mQFactor;
|
||||
private final FrequencyMapping mFrequencyMapping;
|
||||
|
||||
@@ -62,6 +65,10 @@ public class VibratorInfo implements Parcelable {
|
||||
mSupportedEffects = in.readSparseBooleanArray();
|
||||
mSupportedBraking = in.readSparseBooleanArray();
|
||||
mSupportedPrimitives = in.readSparseIntArray();
|
||||
mPrimitiveDelayMax = in.readInt();
|
||||
mCompositionSizeMax = in.readInt();
|
||||
mPwlePrimitiveDurationMax = in.readInt();
|
||||
mPwleSizeMax = in.readInt();
|
||||
mQFactor = in.readFloat();
|
||||
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
|
||||
}
|
||||
@@ -69,48 +76,50 @@ public class VibratorInfo implements Parcelable {
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param id The vibrator id.
|
||||
* @param capabilities All capability flags of the vibrator, defined in IVibrator.CAP_*.
|
||||
* @param supportedEffects All supported predefined effects, enum values from {@link
|
||||
* android.hardware.vibrator.Effect}.
|
||||
* @param supportedBraking All supported braking types, enum values from {@link Braking}.
|
||||
* @param supportedPrimitives All supported primitive effects, enum values from {@link
|
||||
* android.hardware.vibrator.CompositePrimitive}.
|
||||
* @param primitiveDurations A mapping of primitive durations, where indexes are enum values
|
||||
* from {@link android.hardware.vibrator.CompositePrimitive} and the
|
||||
* values are estimated durations in milliseconds.
|
||||
* @param qFactor The vibrator quality factor.
|
||||
* @param frequencyMapping The description of the vibrator supported frequencies and max
|
||||
* amplitude mappings.
|
||||
* @param id The vibrator id.
|
||||
* @param capabilities All capability flags of the vibrator, defined in
|
||||
* IVibrator.CAP_*.
|
||||
* @param supportedEffects All supported predefined effects, enum values from
|
||||
* {@link android.hardware.vibrator.Effect}.
|
||||
* @param supportedBraking All supported braking types, enum values from {@link
|
||||
* Braking}.
|
||||
* @param supportedPrimitives All supported primitive effects, key are enum values from
|
||||
* {@link android.hardware.vibrator.CompositePrimitive} and
|
||||
* values are estimated durations in milliseconds.
|
||||
* @param primitiveDelayMax The maximum delay that can be set to a composition primitive
|
||||
* in milliseconds.
|
||||
* @param compositionSizeMax The maximum number of primitives supported by a composition.
|
||||
* @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds.
|
||||
* @param pwleSizeMax The maximum number of primitives supported by a PWLE
|
||||
* composition.
|
||||
* @param qFactor The vibrator quality factor.
|
||||
* @param frequencyMapping The description of the vibrator supported frequencies and max
|
||||
* amplitude mappings.
|
||||
* @hide
|
||||
*/
|
||||
public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
|
||||
int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
|
||||
@NonNull FrequencyMapping frequencyMapping) {
|
||||
public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
|
||||
@Nullable SparseBooleanArray supportedBraking,
|
||||
@NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
|
||||
int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
|
||||
float qFactor, @NonNull FrequencyMapping frequencyMapping) {
|
||||
mId = id;
|
||||
mCapabilities = capabilities;
|
||||
mSupportedEffects = toSparseBooleanArray(supportedEffects);
|
||||
mSupportedBraking = toSparseBooleanArray(supportedBraking);
|
||||
mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
|
||||
mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
|
||||
mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
|
||||
mSupportedPrimitives = supportedPrimitives.clone();
|
||||
mPrimitiveDelayMax = primitiveDelayMax;
|
||||
mCompositionSizeMax = compositionSizeMax;
|
||||
mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
|
||||
mPwleSizeMax = pwleSizeMax;
|
||||
mQFactor = qFactor;
|
||||
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);
|
||||
this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
|
||||
baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
|
||||
baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
|
||||
baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,6 +129,10 @@ public class VibratorInfo implements Parcelable {
|
||||
dest.writeSparseBooleanArray(mSupportedEffects);
|
||||
dest.writeSparseBooleanArray(mSupportedBraking);
|
||||
dest.writeSparseIntArray(mSupportedPrimitives);
|
||||
dest.writeInt(mPrimitiveDelayMax);
|
||||
dest.writeInt(mCompositionSizeMax);
|
||||
dest.writeInt(mPwlePrimitiveDurationMax);
|
||||
dest.writeInt(mPwleSizeMax);
|
||||
dest.writeFloat(mQFactor);
|
||||
dest.writeParcelable(mFrequencyMapping, flags);
|
||||
}
|
||||
@@ -138,24 +151,23 @@ public class VibratorInfo implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
VibratorInfo that = (VibratorInfo) o;
|
||||
if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
|
||||
if (mSupportedPrimitives != that.mSupportedPrimitives) {
|
||||
int supportedPrimitivesCount = mSupportedPrimitives.size();
|
||||
if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < supportedPrimitivesCount; i++) {
|
||||
if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
|
||||
if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
||||
if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
|
||||
return false;
|
||||
}
|
||||
if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mId == that.mId && mCapabilities == that.mCapabilities
|
||||
&& mPrimitiveDelayMax == that.mPrimitiveDelayMax
|
||||
&& mCompositionSizeMax == that.mCompositionSizeMax
|
||||
&& mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
|
||||
&& mPwleSizeMax == that.mPwleSizeMax
|
||||
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
|
||||
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
|
||||
&& Objects.equals(mQFactor, that.mQFactor)
|
||||
@@ -166,11 +178,9 @@ public class VibratorInfo implements Parcelable {
|
||||
public int hashCode() {
|
||||
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||
mQFactor, mFrequencyMapping);
|
||||
if (mSupportedPrimitives != null) {
|
||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
||||
hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
|
||||
hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
|
||||
}
|
||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
||||
hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
|
||||
hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
@@ -184,6 +194,10 @@ public class VibratorInfo implements Parcelable {
|
||||
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
|
||||
+ ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
|
||||
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
|
||||
+ ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
|
||||
+ ", mCompositionSizeMax=" + mCompositionSizeMax
|
||||
+ ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
|
||||
+ ", mPwleSizeMax=" + mPwleSizeMax
|
||||
+ ", mQFactor=" + mQFactor
|
||||
+ ", mFrequencyMapping=" + mFrequencyMapping
|
||||
+ '}';
|
||||
@@ -247,7 +261,7 @@ public class VibratorInfo implements Parcelable {
|
||||
*/
|
||||
public boolean isPrimitiveSupported(
|
||||
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
|
||||
return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
|
||||
return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
&& (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
|
||||
}
|
||||
|
||||
@@ -260,7 +274,43 @@ public class VibratorInfo implements Parcelable {
|
||||
*/
|
||||
public int getPrimitiveDuration(
|
||||
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
|
||||
return mSupportedPrimitives != null ? mSupportedPrimitives.get(primitiveId) : 0;
|
||||
return mSupportedPrimitives.get(primitiveId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the maximum delay supported for a primitive in a composed effect.
|
||||
*
|
||||
* @return The max delay in milliseconds, or zero if unlimited.
|
||||
*/
|
||||
public int getPrimitiveDelayMax() {
|
||||
return mPrimitiveDelayMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the maximum number of primitives supported in a composed effect.
|
||||
*
|
||||
* @return The max number of primitives supported, or zero if unlimited.
|
||||
*/
|
||||
public int getCompositionSizeMax() {
|
||||
return mCompositionSizeMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the maximum duration supported for a primitive in a PWLE composition.
|
||||
*
|
||||
* @return The max duration in milliseconds, or zero if unlimited.
|
||||
*/
|
||||
public int getPwlePrimitiveDurationMax() {
|
||||
return mPwlePrimitiveDurationMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the maximum number of primitives supported in a PWLE composition.
|
||||
*
|
||||
* @return The max number of primitives supported, or zero if unlimited.
|
||||
*/
|
||||
public int getPwleSizeMax() {
|
||||
return mPwleSizeMax;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,51 +458,14 @@ public class VibratorInfo implements Parcelable {
|
||||
}
|
||||
|
||||
private String[] getSupportedPrimitivesNames() {
|
||||
if (mSupportedPrimitives == null) {
|
||||
return new String[0];
|
||||
}
|
||||
String[] names = new String[mSupportedPrimitives.size()];
|
||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
||||
int supportedPrimitivesCount = mSupportedPrimitives.size();
|
||||
String[] names = new String[supportedPrimitivesCount];
|
||||
for (int i = 0; i < supportedPrimitivesCount; i++) {
|
||||
names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is mapped
|
||||
* to {@code true}.
|
||||
*/
|
||||
@Nullable
|
||||
private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
|
||||
if (supportedKeys == null) {
|
||||
return null;
|
||||
}
|
||||
SparseBooleanArray array = new SparseBooleanArray();
|
||||
for (int key : supportedKeys) {
|
||||
array.put(key, true);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SparseIntArray} from given {@code supportedKeys} where each key is mapped
|
||||
* to the value indexed by it.
|
||||
*
|
||||
* <p>If {@code values} is null or does not contain a given key as a index, then zero is stored
|
||||
* to the sparse array so it can still be used to query the supported keys.
|
||||
*/
|
||||
@Nullable
|
||||
private static SparseIntArray toSparseIntArray(int[] supportedKeys, int[] values) {
|
||||
if (supportedKeys == null) {
|
||||
return null;
|
||||
}
|
||||
SparseIntArray array = new SparseIntArray();
|
||||
for (int key : supportedKeys) {
|
||||
array.put(key, (values == null || key >= values.length) ? 0 : values[key]);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
|
||||
*
|
||||
@@ -675,11 +688,14 @@ public 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 int[] mPrimitiveDurations = new int[0];
|
||||
private long mCapabilities;
|
||||
private SparseBooleanArray mSupportedEffects;
|
||||
private SparseBooleanArray mSupportedBraking;
|
||||
private SparseIntArray mSupportedPrimitives = new SparseIntArray();
|
||||
private int mPrimitiveDelayMax;
|
||||
private int mCompositionSizeMax;
|
||||
private int mPwlePrimitiveDurationMax;
|
||||
private int mPwleSizeMax;
|
||||
private float mQFactor = Float.NaN;
|
||||
private FrequencyMapping mFrequencyMapping =
|
||||
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
||||
@@ -691,7 +707,7 @@ public class VibratorInfo implements Parcelable {
|
||||
|
||||
/** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
|
||||
@NonNull
|
||||
public Builder setCapabilities(int capabilities) {
|
||||
public Builder setCapabilities(long capabilities) {
|
||||
mCapabilities = capabilities;
|
||||
return this;
|
||||
}
|
||||
@@ -699,34 +715,49 @@ public class VibratorInfo implements Parcelable {
|
||||
/** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
|
||||
@NonNull
|
||||
public Builder setSupportedEffects(int... supportedEffects) {
|
||||
mSupportedEffects = supportedEffects;
|
||||
mSupportedEffects = toSparseBooleanArray(supportedEffects);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
|
||||
@NonNull
|
||||
public Builder setSupportedBraking(int... supportedBraking) {
|
||||
mSupportedBraking = supportedBraking;
|
||||
mSupportedBraking = toSparseBooleanArray(supportedBraking);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the primitives supported with
|
||||
* {@link android.hardware.vibrator.CompositePrimitive} values.
|
||||
*/
|
||||
/** Configure maximum duration, in milliseconds, of a PWLE primitive. */
|
||||
@NonNull
|
||||
public Builder setSupportedPrimitives(int... supportedPrimitives) {
|
||||
mSupportedPrimitives = supportedPrimitives;
|
||||
public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
|
||||
mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure maximum number of primitives supported in a single PWLE composed effect. */
|
||||
@NonNull
|
||||
public Builder setPwleSizeMax(int pwleSizeMax) {
|
||||
mPwleSizeMax = pwleSizeMax;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
|
||||
@NonNull
|
||||
public Builder setPrimitiveDuration(int primitiveId, int duration) {
|
||||
if (mPrimitiveDurations.length <= primitiveId) {
|
||||
mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
|
||||
}
|
||||
mPrimitiveDurations[primitiveId] = duration;
|
||||
public Builder setSupportedPrimitive(int primitiveId, int duration) {
|
||||
mSupportedPrimitives.put(primitiveId, duration);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */
|
||||
@NonNull
|
||||
public Builder setPrimitiveDelayMax(int primitiveDelayMax) {
|
||||
mPrimitiveDelayMax = primitiveDelayMax;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure maximum number of primitives supported in a single composed effect. */
|
||||
@NonNull
|
||||
public Builder setCompositionSizeMax(int compositionSizeMax) {
|
||||
mCompositionSizeMax = compositionSizeMax;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -748,7 +779,25 @@ public class VibratorInfo implements Parcelable {
|
||||
@NonNull
|
||||
public VibratorInfo build() {
|
||||
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||
mSupportedPrimitives, mPrimitiveDurations, mQFactor, mFrequencyMapping);
|
||||
mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
|
||||
mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is
|
||||
* mapped
|
||||
* to {@code true}.
|
||||
*/
|
||||
@Nullable
|
||||
private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
|
||||
if (supportedKeys == null) {
|
||||
return null;
|
||||
}
|
||||
SparseBooleanArray array = new SparseBooleanArray();
|
||||
for (int key : supportedKeys) {
|
||||
array.put(key, true);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,14 +87,14 @@ public class VibratorInfoTest {
|
||||
public void testIsPrimitiveSupported() {
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||
.build();
|
||||
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
|
||||
|
||||
// Returns false when there is no compose capability.
|
||||
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||
.build();
|
||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
}
|
||||
@@ -103,8 +103,7 @@ public class VibratorInfoTest {
|
||||
public void testGetPrimitiveDuration() {
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.build();
|
||||
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
|
||||
@@ -112,6 +111,26 @@ public class VibratorInfoTest {
|
||||
.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompositionLimits() {
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setPrimitiveDelayMax(100)
|
||||
.setCompositionSizeMax(10)
|
||||
.setPwlePrimitiveDurationMax(50)
|
||||
.setPwleSizeMax(20)
|
||||
.build();
|
||||
assertEquals(100, info.getPrimitiveDelayMax());
|
||||
assertEquals(10, info.getCompositionSizeMax());
|
||||
assertEquals(50, info.getPwlePrimitiveDurationMax());
|
||||
assertEquals(20, info.getPwleSizeMax());
|
||||
|
||||
VibratorInfo emptyInfo = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
assertEquals(0, emptyInfo.getPrimitiveDelayMax());
|
||||
assertEquals(0, emptyInfo.getCompositionSizeMax());
|
||||
assertEquals(0, emptyInfo.getPwlePrimitiveDurationMax());
|
||||
assertEquals(0, emptyInfo.getPwleSizeMax());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
|
||||
assertEquals(Braking.NONE, new VibratorInfo.Builder(
|
||||
@@ -263,8 +282,12 @@ public class VibratorInfoTest {
|
||||
VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.setPrimitiveDelayMax(100)
|
||||
.setCompositionSizeMax(10)
|
||||
.setSupportedBraking(Braking.CLAB)
|
||||
.setPwlePrimitiveDurationMax(50)
|
||||
.setPwleSizeMax(20)
|
||||
.setQFactor(2f)
|
||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
|
||||
VibratorInfo complete = completeBuilder.build();
|
||||
@@ -279,8 +302,7 @@ public class VibratorInfoTest {
|
||||
assertNotEquals(complete, completeWithComposeControl);
|
||||
|
||||
VibratorInfo completeWithNoEffects = completeBuilder
|
||||
.setSupportedEffects()
|
||||
.setSupportedPrimitives()
|
||||
.setSupportedEffects(new int[0])
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithNoEffects);
|
||||
|
||||
@@ -289,13 +311,8 @@ public class VibratorInfoTest {
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithUnknownEffects);
|
||||
|
||||
VibratorInfo completeWithUnknownPrimitives = completeBuilder
|
||||
.setSupportedPrimitives(null)
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithUnknownPrimitives);
|
||||
|
||||
VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
|
||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
|
||||
|
||||
@@ -321,12 +338,17 @@ public class VibratorInfoTest {
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithDifferentQFactor);
|
||||
|
||||
VibratorInfo empty = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
VibratorInfo emptyWithKnownSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedEffects()
|
||||
.setSupportedPrimitives()
|
||||
VibratorInfo unknownEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
VibratorInfo knownEmptyEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedEffects(new int[0])
|
||||
.build();
|
||||
assertNotEquals(empty, emptyWithKnownSupport);
|
||||
assertNotEquals(unknownEffectSupport, knownEmptyEffectSupport);
|
||||
|
||||
VibratorInfo unknownBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
VibratorInfo knownEmptyBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedBraking(new int[0])
|
||||
.build();
|
||||
assertNotEquals(unknownBrakingSupport, knownEmptyBrakingSupport);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -334,8 +356,7 @@ public class VibratorInfoTest {
|
||||
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.setQFactor(Float.NaN)
|
||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
|
||||
.build();
|
||||
|
||||
@@ -844,7 +844,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
public List<Step> play() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
|
||||
try {
|
||||
int segmentCount = effect.getSegments().size();
|
||||
// Load the next PrimitiveSegments to create a single compose call to the vibrator,
|
||||
// limited to the vibrator composition maximum size.
|
||||
int limit = controller.getVibratorInfo().getCompositionSizeMax();
|
||||
int segmentCount = limit > 0
|
||||
? Math.min(effect.getSegments().size(), segmentIndex + limit)
|
||||
: effect.getSegments().size();
|
||||
List<PrimitiveSegment> primitives = new ArrayList<>();
|
||||
for (int i = segmentIndex; i < segmentCount; i++) {
|
||||
VibrationEffectSegment segment = effect.getSegments().get(i);
|
||||
@@ -896,7 +901,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
public List<Step> play() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
|
||||
try {
|
||||
int segmentCount = effect.getSegments().size();
|
||||
// Load the next RampSegments to create a single composePwle call to the vibrator,
|
||||
// limited to the vibrator PWLE maximum size.
|
||||
int limit = controller.getVibratorInfo().getPwleSizeMax();
|
||||
int segmentCount = limit > 0
|
||||
? Math.min(effect.getSegments().size(), segmentIndex + limit)
|
||||
: effect.getSegments().size();
|
||||
List<RampSegment> pwles = new ArrayList<>();
|
||||
for (int i = segmentIndex; i < segmentCount; i++) {
|
||||
VibrationEffectSegment segment = effect.getSegments().get(i);
|
||||
|
||||
@@ -30,18 +30,23 @@ import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import libcore.util.NativeAllocationRegistry;
|
||||
|
||||
/** Controls a single vibrator. */
|
||||
final class VibratorController {
|
||||
private static final String TAG = "VibratorController";
|
||||
// TODO(b/167947076): load suggested range from config
|
||||
private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final NativeWrapper mNativeWrapper;
|
||||
private final VibratorInfo mVibratorInfo;
|
||||
private final VibratorInfo.Builder mVibratorInfoBuilder;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private VibratorInfo mVibratorInfo;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mVibratorInfoLoaded;
|
||||
@GuardedBy("mLock")
|
||||
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
|
||||
new RemoteCallbackList<>();
|
||||
@@ -66,10 +71,10 @@ final class VibratorController {
|
||||
NativeWrapper nativeWrapper) {
|
||||
mNativeWrapper = nativeWrapper;
|
||||
mNativeWrapper.init(vibratorId, listener);
|
||||
// TODO(b/167947076): load suggested range from config
|
||||
mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 200);
|
||||
Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
|
||||
vibratorId);
|
||||
mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
|
||||
mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
|
||||
mVibratorInfoBuilder);
|
||||
mVibratorInfo = mVibratorInfoBuilder.build();
|
||||
}
|
||||
|
||||
/** Register state listener for this vibrator. */
|
||||
@@ -103,7 +108,15 @@ final class VibratorController {
|
||||
|
||||
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
|
||||
public VibratorInfo getVibratorInfo() {
|
||||
return mVibratorInfo;
|
||||
synchronized (mLock) {
|
||||
if (!mVibratorInfoLoaded) {
|
||||
// Try to load the vibrator metadata that has failed in the last attempt.
|
||||
mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
|
||||
mVibratorInfoBuilder);
|
||||
mVibratorInfo = mVibratorInfoBuilder.build();
|
||||
}
|
||||
return mVibratorInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,7 +374,8 @@ final class VibratorController {
|
||||
|
||||
private static native void alwaysOnDisable(long nativePtr, long id);
|
||||
|
||||
private static native VibratorInfo getInfo(long nativePtr, float suggestedFrequencyRange);
|
||||
private static native boolean getInfo(long nativePtr, float suggestedFrequencyRange,
|
||||
VibratorInfo.Builder infoBuilder);
|
||||
|
||||
private long mNativePtr = 0;
|
||||
|
||||
@@ -428,9 +442,11 @@ final class VibratorController {
|
||||
alwaysOnDisable(mNativePtr, id);
|
||||
}
|
||||
|
||||
/** Return device vibrator metadata. */
|
||||
public VibratorInfo getInfo(float suggestedFrequencyRange) {
|
||||
return getInfo(mNativePtr, suggestedFrequencyRange);
|
||||
/**
|
||||
* Loads device vibrator metadata and returns true if all metadata was loaded successfully.
|
||||
*/
|
||||
public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
|
||||
return getInfo(mNativePtr, suggestedFrequencyRange, infoBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,18 @@ static JavaVM* sJvm = nullptr;
|
||||
static jmethodID sMethodIdOnComplete;
|
||||
static jclass sFrequencyMappingClass;
|
||||
static jmethodID sFrequencyMappingCtor;
|
||||
static jclass sVibratorInfoClass;
|
||||
static jmethodID sVibratorInfoCtor;
|
||||
static struct {
|
||||
jmethodID setCapabilities;
|
||||
jmethodID setSupportedEffects;
|
||||
jmethodID setSupportedBraking;
|
||||
jmethodID setPwlePrimitiveDurationMax;
|
||||
jmethodID setPwleSizeMax;
|
||||
jmethodID setSupportedPrimitive;
|
||||
jmethodID setPrimitiveDelayMax;
|
||||
jmethodID setCompositionSizeMax;
|
||||
jmethodID setQFactor;
|
||||
jmethodID setFrequencyMapping;
|
||||
} sVibratorInfoBuilderClassInfo;
|
||||
static struct {
|
||||
jfieldID id;
|
||||
jfieldID scale;
|
||||
@@ -352,68 +362,88 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
||||
wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable");
|
||||
}
|
||||
|
||||
static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
||||
jfloat suggestedSafeRange) {
|
||||
static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
||||
jfloat suggestedSafeRange, jobject vibratorInfoBuilder) {
|
||||
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
|
||||
if (wrapper == nullptr) {
|
||||
ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
|
||||
return nullptr;
|
||||
return JNI_FALSE;
|
||||
}
|
||||
vibrator::Info info = wrapper->getVibratorInfo();
|
||||
|
||||
jlong capabilities =
|
||||
static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE));
|
||||
jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
|
||||
jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
|
||||
jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
|
||||
jfloat qFactor = static_cast<jfloat>(info.qFactor.valueOr(NAN));
|
||||
jintArray supportedEffects = nullptr;
|
||||
jintArray supportedBraking = nullptr;
|
||||
jintArray supportedPrimitives = nullptr;
|
||||
jintArray primitiveDurations = nullptr;
|
||||
jfloatArray maxAmplitudes = nullptr;
|
||||
|
||||
if (info.capabilities.isOk()) {
|
||||
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setCapabilities,
|
||||
static_cast<jlong>(info.capabilities.value()));
|
||||
}
|
||||
if (info.supportedEffects.isOk()) {
|
||||
std::vector<aidl::Effect> effects = info.supportedEffects.value();
|
||||
supportedEffects = env->NewIntArray(effects.size());
|
||||
jintArray supportedEffects = env->NewIntArray(effects.size());
|
||||
env->SetIntArrayRegion(supportedEffects, 0, effects.size(),
|
||||
reinterpret_cast<jint*>(effects.data()));
|
||||
env->CallObjectMethod(vibratorInfoBuilder,
|
||||
sVibratorInfoBuilderClassInfo.setSupportedEffects, supportedEffects);
|
||||
}
|
||||
if (info.supportedBraking.isOk()) {
|
||||
std::vector<aidl::Braking> braking = info.supportedBraking.value();
|
||||
supportedBraking = env->NewIntArray(braking.size());
|
||||
jintArray supportedBraking = env->NewIntArray(braking.size());
|
||||
env->SetIntArrayRegion(supportedBraking, 0, braking.size(),
|
||||
reinterpret_cast<jint*>(braking.data()));
|
||||
env->CallObjectMethod(vibratorInfoBuilder,
|
||||
sVibratorInfoBuilderClassInfo.setSupportedBraking, supportedBraking);
|
||||
}
|
||||
if (info.pwlePrimitiveDurationMax.isOk()) {
|
||||
env->CallObjectMethod(vibratorInfoBuilder,
|
||||
sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax,
|
||||
static_cast<jint>(info.pwlePrimitiveDurationMax.value().count()));
|
||||
}
|
||||
if (info.pwleSizeMax.isOk()) {
|
||||
// Use (pwleMaxSize - 1) to account for a possible extra braking segment added by the
|
||||
// vibratorPerformPwleEffect method.
|
||||
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setPwleSizeMax,
|
||||
static_cast<jint>(info.pwleSizeMax.value() - 1));
|
||||
}
|
||||
if (info.supportedPrimitives.isOk()) {
|
||||
std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value();
|
||||
supportedPrimitives = env->NewIntArray(primitives.size());
|
||||
env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
|
||||
reinterpret_cast<jint*>(primitives.data()));
|
||||
}
|
||||
if (info.primitiveDurations.isOk()) {
|
||||
std::vector<int32_t> durations;
|
||||
for (auto duration : info.primitiveDurations.value()) {
|
||||
durations.push_back(duration.count());
|
||||
auto durations = info.primitiveDurations.valueOr({});
|
||||
for (auto& primitive : info.supportedPrimitives.value()) {
|
||||
auto primitiveIdx = static_cast<size_t>(primitive);
|
||||
auto duration = durations.size() > primitiveIdx ? durations[primitiveIdx].count() : 0;
|
||||
env->CallObjectMethod(vibratorInfoBuilder,
|
||||
sVibratorInfoBuilderClassInfo.setSupportedPrimitive,
|
||||
static_cast<jint>(primitive), static_cast<jint>(duration));
|
||||
}
|
||||
primitiveDurations = env->NewIntArray(durations.size());
|
||||
env->SetIntArrayRegion(primitiveDurations, 0, durations.size(),
|
||||
reinterpret_cast<jint*>(durations.data()));
|
||||
}
|
||||
if (info.primitiveDelayMax.isOk()) {
|
||||
env->CallObjectMethod(vibratorInfoBuilder,
|
||||
sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax,
|
||||
static_cast<jint>(info.primitiveDelayMax.value().count()));
|
||||
}
|
||||
if (info.compositionSizeMax.isOk()) {
|
||||
env->CallObjectMethod(vibratorInfoBuilder,
|
||||
sVibratorInfoBuilderClassInfo.setCompositionSizeMax,
|
||||
static_cast<jint>(info.compositionSizeMax.value()));
|
||||
}
|
||||
if (info.qFactor.isOk()) {
|
||||
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setQFactor,
|
||||
static_cast<jfloat>(info.qFactor.value()));
|
||||
}
|
||||
|
||||
jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
|
||||
jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
|
||||
jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
|
||||
jfloatArray maxAmplitudes = nullptr;
|
||||
if (info.maxAmplitudes.isOk()) {
|
||||
std::vector<float> amplitudes = info.maxAmplitudes.value();
|
||||
maxAmplitudes = env->NewFloatArray(amplitudes.size());
|
||||
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
|
||||
reinterpret_cast<jfloat*>(amplitudes.data()));
|
||||
}
|
||||
|
||||
jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
|
||||
minFrequency, resonantFrequency, frequencyResolution,
|
||||
suggestedSafeRange, maxAmplitudes);
|
||||
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
|
||||
frequencyMapping);
|
||||
|
||||
return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
|
||||
capabilities, supportedEffects, supportedBraking, supportedPrimitives,
|
||||
primitiveDurations, qFactor, frequencyMapping);
|
||||
return info.checkAndLogFailure("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
|
||||
}
|
||||
|
||||
static const JNINativeMethod method_table[] = {
|
||||
@@ -433,7 +463,7 @@ static const JNINativeMethod method_table[] = {
|
||||
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
|
||||
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
|
||||
{"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
|
||||
{"getInfo", "(JF)Landroid/os/VibratorInfo;", (void*)vibratorGetInfo},
|
||||
{"getInfo", "(JFLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
|
||||
};
|
||||
|
||||
int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
|
||||
@@ -459,11 +489,38 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
|
||||
sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
|
||||
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
|
||||
|
||||
jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
|
||||
sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
|
||||
sVibratorInfoCtor =
|
||||
GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
|
||||
"(IJ[I[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
|
||||
jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
|
||||
sVibratorInfoBuilderClassInfo.setCapabilities =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCapabilities",
|
||||
"(J)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setSupportedEffects =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedEffects",
|
||||
"([I)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setSupportedBraking =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedBraking",
|
||||
"([I)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwlePrimitiveDurationMax",
|
||||
"(I)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setPwleSizeMax =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwleSizeMax",
|
||||
"(I)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setSupportedPrimitive =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedPrimitive",
|
||||
"(II)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPrimitiveDelayMax",
|
||||
"(I)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setCompositionSizeMax =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCompositionSizeMax",
|
||||
"(I)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setQFactor =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setQFactor",
|
||||
"(F)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setFrequencyMapping =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyMapping",
|
||||
"(Landroid/os/VibratorInfo$FrequencyMapping;)"
|
||||
"Landroid/os/VibratorInfo$Builder;");
|
||||
|
||||
return jniRegisterNativeMethods(env,
|
||||
"com/android/server/vibrator/VibratorController$NativeWrapper",
|
||||
|
||||
@@ -56,6 +56,8 @@ final class FakeVibratorControllerProvider {
|
||||
private int[] mSupportedEffects;
|
||||
private int[] mSupportedBraking;
|
||||
private int[] mSupportedPrimitives;
|
||||
private int mCompositionSizeMax;
|
||||
private int mPwleSizeMax;
|
||||
private float mMinFrequency = Float.NaN;
|
||||
private float mResonantFrequency = Float.NaN;
|
||||
private float mFrequencyResolution = Float.NaN;
|
||||
@@ -151,12 +153,22 @@ final class FakeVibratorControllerProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public VibratorInfo getInfo(float suggestedFrequencyRange) {
|
||||
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
||||
mMinFrequency, mResonantFrequency, mFrequencyResolution,
|
||||
suggestedFrequencyRange, mMaxAmplitudes);
|
||||
return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||
mSupportedPrimitives, null, mQFactor, frequencyMapping);
|
||||
public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
|
||||
infoBuilder.setCapabilities(mCapabilities);
|
||||
infoBuilder.setSupportedBraking(mSupportedBraking);
|
||||
infoBuilder.setPwleSizeMax(mPwleSizeMax);
|
||||
infoBuilder.setSupportedEffects(mSupportedEffects);
|
||||
if (mSupportedPrimitives != null) {
|
||||
for (int primitive : mSupportedPrimitives) {
|
||||
infoBuilder.setSupportedPrimitive(primitive, EFFECT_DURATION);
|
||||
}
|
||||
}
|
||||
infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
|
||||
infoBuilder.setQFactor(mQFactor);
|
||||
infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
|
||||
mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
|
||||
mMaxAmplitudes));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void applyLatency() {
|
||||
@@ -236,6 +248,16 @@ final class FakeVibratorControllerProvider {
|
||||
mSupportedPrimitives = primitives;
|
||||
}
|
||||
|
||||
/** Set the max number of primitives allowed in a composition by the fake vibrator hardware. */
|
||||
public void setCompositionSizeMax(int compositionSizeMax) {
|
||||
mCompositionSizeMax = compositionSizeMax;
|
||||
}
|
||||
|
||||
/** Set the max number of PWLEs allowed in a composition by the fake vibrator hardware. */
|
||||
public void setPwleSizeMax(int pwleSizeMax) {
|
||||
mPwleSizeMax = pwleSizeMax;
|
||||
}
|
||||
|
||||
/** Set the resonant frequency of the fake vibrator hardware. */
|
||||
public void setResonantFrequency(float frequencyHz) {
|
||||
mResonantFrequency = frequencyHz;
|
||||
|
||||
@@ -356,7 +356,8 @@ public class VibrationThreadTest {
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorComposed_runsVibration() throws Exception {
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
@@ -374,7 +375,7 @@ public class VibrationThreadTest {
|
||||
assertEquals(Arrays.asList(
|
||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||
fakeVibrator.getEffectSegments());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -394,6 +395,27 @@ public class VibrationThreadTest {
|
||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorLargeComposition_splitsVibratorComposeCalls() {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
fakeVibrator.setCompositionSizeMax(2);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
|
||||
.compose();
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
// Vibrator compose called twice.
|
||||
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
assertEquals(3, fakeVibrator.getEffectSegments().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
|
||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||
@@ -432,12 +454,13 @@ public class VibrationThreadTest {
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedBraking(Braking.CLAB);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setMinFrequency(100);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setResonantFrequency(150);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setFrequencyResolution(50);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setMaxAmplitudes(
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
fakeVibrator.setSupportedBraking(Braking.CLAB);
|
||||
fakeVibrator.setMinFrequency(100);
|
||||
fakeVibrator.setResonantFrequency(150);
|
||||
fakeVibrator.setFrequencyResolution(50);
|
||||
fakeVibrator.setMaxAmplitudes(
|
||||
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
|
||||
|
||||
long vibrationId = 1;
|
||||
@@ -462,8 +485,34 @@ public class VibrationThreadTest {
|
||||
expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
|
||||
expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
|
||||
/* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||
assertEquals(Arrays.asList(Braking.CLAB), mVibratorProviders.get(VIBRATOR_ID).getBraking());
|
||||
fakeVibrator.getEffectSegments());
|
||||
assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorLargePwle_splitsVibratorComposeCalls() {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
fakeVibrator.setMinFrequency(100);
|
||||
fakeVibrator.setResonantFrequency(150);
|
||||
fakeVibrator.setFrequencyResolution(50);
|
||||
fakeVibrator.setMaxAmplitudes(1, 1, 1);
|
||||
fakeVibrator.setPwleSizeMax(2);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startWaveform()
|
||||
.addStep(1, 10)
|
||||
.addRamp(0, 20)
|
||||
.addStep(0.8f, 1, 30)
|
||||
.addRamp(0.6f, -1, 40)
|
||||
.build();
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
// Vibrator compose called twice.
|
||||
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
assertEquals(4, fakeVibrator.getEffectSegments().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -298,11 +298,13 @@ public class VibratorControllerTest {
|
||||
private void mockVibratorCapabilities(int capabilities) {
|
||||
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
||||
when(mNativeWrapperMock.getInfo(anyFloat())).thenReturn(
|
||||
new VibratorInfo.Builder(VIBRATOR_ID)
|
||||
.setCapabilities(capabilities)
|
||||
.setFrequencyMapping(frequencyMapping)
|
||||
.build());
|
||||
when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class)))
|
||||
.then(invocation -> {
|
||||
((VibratorInfo.Builder) invocation.getArgument(1))
|
||||
.setCapabilities(capabilities)
|
||||
.setFrequencyMapping(frequencyMapping);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {
|
||||
|
||||
Reference in New Issue
Block a user