Split long vibration composition using HAL limits
Load composition and pwle limits from the HAL and apply them on VibrationThread to split long vibrations in multiple calls. This ensures that long vibration will be performed by the HAL. Bug: 188431691 Fix: 183517124 Test: VibrationThreadTest & VibrationInfoTest Change-Id: I09a04c7b1de331320d453899f59b01e4bbb42ac3
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