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;
|
mDeviceId = deviceId;
|
||||||
mVibratorInfo = new VibratorInfo.Builder(vibratorId)
|
mVibratorInfo = new VibratorInfo.Builder(vibratorId)
|
||||||
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
|
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
|
||||||
// Set predefined support to empty as we know input devices do not support them.
|
// The supported effect and braking lists are known to be empty for input devices,
|
||||||
.setSupportedEffects()
|
// which is different from not being set (that means the device support is unknown).
|
||||||
.setSupportedPrimitives()
|
.setSupportedEffects(new int[0])
|
||||||
.setSupportedBraking()
|
.setSupportedBraking(new int[0])
|
||||||
.build();
|
.build();
|
||||||
mToken = new Binder();
|
mToken = new Binder();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,11 @@ public class VibratorInfo implements Parcelable {
|
|||||||
private final SparseBooleanArray mSupportedEffects;
|
private final SparseBooleanArray mSupportedEffects;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final SparseBooleanArray mSupportedBraking;
|
private final SparseBooleanArray mSupportedBraking;
|
||||||
@Nullable
|
|
||||||
private final SparseIntArray mSupportedPrimitives;
|
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 float mQFactor;
|
||||||
private final FrequencyMapping mFrequencyMapping;
|
private final FrequencyMapping mFrequencyMapping;
|
||||||
|
|
||||||
@@ -62,6 +65,10 @@ public class VibratorInfo implements Parcelable {
|
|||||||
mSupportedEffects = in.readSparseBooleanArray();
|
mSupportedEffects = in.readSparseBooleanArray();
|
||||||
mSupportedBraking = in.readSparseBooleanArray();
|
mSupportedBraking = in.readSparseBooleanArray();
|
||||||
mSupportedPrimitives = in.readSparseIntArray();
|
mSupportedPrimitives = in.readSparseIntArray();
|
||||||
|
mPrimitiveDelayMax = in.readInt();
|
||||||
|
mCompositionSizeMax = in.readInt();
|
||||||
|
mPwlePrimitiveDurationMax = in.readInt();
|
||||||
|
mPwleSizeMax = in.readInt();
|
||||||
mQFactor = in.readFloat();
|
mQFactor = in.readFloat();
|
||||||
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
|
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
|
||||||
}
|
}
|
||||||
@@ -70,47 +77,49 @@ public class VibratorInfo implements Parcelable {
|
|||||||
* Default constructor.
|
* Default constructor.
|
||||||
*
|
*
|
||||||
* @param id The vibrator id.
|
* @param id The vibrator id.
|
||||||
* @param capabilities All capability flags of the vibrator, defined in IVibrator.CAP_*.
|
* @param capabilities All capability flags of the vibrator, defined in
|
||||||
* @param supportedEffects All supported predefined effects, enum values from {@link
|
* IVibrator.CAP_*.
|
||||||
* android.hardware.vibrator.Effect}.
|
* @param supportedEffects All supported predefined effects, enum values from
|
||||||
* @param supportedBraking All supported braking types, enum values from {@link Braking}.
|
* {@link android.hardware.vibrator.Effect}.
|
||||||
* @param supportedPrimitives All supported primitive effects, enum values from {@link
|
* @param supportedBraking All supported braking types, enum values from {@link
|
||||||
* android.hardware.vibrator.CompositePrimitive}.
|
* Braking}.
|
||||||
* @param primitiveDurations A mapping of primitive durations, where indexes are enum values
|
* @param supportedPrimitives All supported primitive effects, key are enum values from
|
||||||
* from {@link android.hardware.vibrator.CompositePrimitive} and the
|
* {@link android.hardware.vibrator.CompositePrimitive} and
|
||||||
* values are estimated durations in milliseconds.
|
* 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 qFactor The vibrator quality factor.
|
||||||
* @param frequencyMapping The description of the vibrator supported frequencies and max
|
* @param frequencyMapping The description of the vibrator supported frequencies and max
|
||||||
* amplitude mappings.
|
* amplitude mappings.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
|
public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
|
||||||
int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
|
@Nullable SparseBooleanArray supportedBraking,
|
||||||
@NonNull FrequencyMapping frequencyMapping) {
|
@NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
|
||||||
|
int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
|
||||||
|
float qFactor, @NonNull FrequencyMapping frequencyMapping) {
|
||||||
mId = id;
|
mId = id;
|
||||||
mCapabilities = capabilities;
|
mCapabilities = capabilities;
|
||||||
mSupportedEffects = toSparseBooleanArray(supportedEffects);
|
mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
|
||||||
mSupportedBraking = toSparseBooleanArray(supportedBraking);
|
mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
|
||||||
mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
|
mSupportedPrimitives = supportedPrimitives.clone();
|
||||||
|
mPrimitiveDelayMax = primitiveDelayMax;
|
||||||
|
mCompositionSizeMax = compositionSizeMax;
|
||||||
|
mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
|
||||||
|
mPwleSizeMax = pwleSizeMax;
|
||||||
mQFactor = qFactor;
|
mQFactor = qFactor;
|
||||||
mFrequencyMapping = frequencyMapping;
|
mFrequencyMapping = frequencyMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
|
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
|
||||||
mId = id;
|
this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
|
||||||
mCapabilities = capabilities;
|
baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
|
||||||
mSupportedEffects = baseVibrator.mSupportedEffects == null ? null :
|
baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
|
||||||
baseVibrator.mSupportedEffects.clone();
|
baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
|
||||||
mSupportedBraking = baseVibrator.mSupportedBraking == null ? null :
|
|
||||||
baseVibrator.mSupportedBraking.clone();
|
|
||||||
mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null :
|
|
||||||
baseVibrator.mSupportedPrimitives.clone();
|
|
||||||
mQFactor = baseVibrator.mQFactor;
|
|
||||||
mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz,
|
|
||||||
baseVibrator.mFrequencyMapping.mResonantFrequencyHz,
|
|
||||||
baseVibrator.mFrequencyMapping.mFrequencyResolutionHz,
|
|
||||||
baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz,
|
|
||||||
baseVibrator.mFrequencyMapping.mMaxAmplitudes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,6 +129,10 @@ public class VibratorInfo implements Parcelable {
|
|||||||
dest.writeSparseBooleanArray(mSupportedEffects);
|
dest.writeSparseBooleanArray(mSupportedEffects);
|
||||||
dest.writeSparseBooleanArray(mSupportedBraking);
|
dest.writeSparseBooleanArray(mSupportedBraking);
|
||||||
dest.writeSparseIntArray(mSupportedPrimitives);
|
dest.writeSparseIntArray(mSupportedPrimitives);
|
||||||
|
dest.writeInt(mPrimitiveDelayMax);
|
||||||
|
dest.writeInt(mCompositionSizeMax);
|
||||||
|
dest.writeInt(mPwlePrimitiveDurationMax);
|
||||||
|
dest.writeInt(mPwleSizeMax);
|
||||||
dest.writeFloat(mQFactor);
|
dest.writeFloat(mQFactor);
|
||||||
dest.writeParcelable(mFrequencyMapping, flags);
|
dest.writeParcelable(mFrequencyMapping, flags);
|
||||||
}
|
}
|
||||||
@@ -138,15 +151,11 @@ public class VibratorInfo implements Parcelable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
VibratorInfo that = (VibratorInfo) o;
|
VibratorInfo that = (VibratorInfo) o;
|
||||||
if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
|
int supportedPrimitivesCount = mSupportedPrimitives.size();
|
||||||
if (mSupportedPrimitives != that.mSupportedPrimitives) {
|
if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
for (int i = 0; i < supportedPrimitivesCount; i++) {
|
||||||
if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
|
||||||
if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
|
if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -154,8 +163,11 @@ public class VibratorInfo implements Parcelable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return mId == that.mId && mCapabilities == that.mCapabilities
|
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(mSupportedEffects, that.mSupportedEffects)
|
||||||
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
|
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
|
||||||
&& Objects.equals(mQFactor, that.mQFactor)
|
&& Objects.equals(mQFactor, that.mQFactor)
|
||||||
@@ -166,12 +178,10 @@ public class VibratorInfo implements Parcelable {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||||
mQFactor, mFrequencyMapping);
|
mQFactor, mFrequencyMapping);
|
||||||
if (mSupportedPrimitives != null) {
|
|
||||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
||||||
hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
|
hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
|
||||||
hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
|
hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +194,10 @@ public class VibratorInfo implements Parcelable {
|
|||||||
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
|
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
|
||||||
+ ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
|
+ ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
|
||||||
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
|
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
|
||||||
|
+ ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
|
||||||
|
+ ", mCompositionSizeMax=" + mCompositionSizeMax
|
||||||
|
+ ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
|
||||||
|
+ ", mPwleSizeMax=" + mPwleSizeMax
|
||||||
+ ", mQFactor=" + mQFactor
|
+ ", mQFactor=" + mQFactor
|
||||||
+ ", mFrequencyMapping=" + mFrequencyMapping
|
+ ", mFrequencyMapping=" + mFrequencyMapping
|
||||||
+ '}';
|
+ '}';
|
||||||
@@ -247,7 +261,7 @@ public class VibratorInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public boolean isPrimitiveSupported(
|
public boolean isPrimitiveSupported(
|
||||||
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
|
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
|
||||||
return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
|
return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||||
&& (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
|
&& (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +274,43 @@ public class VibratorInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public int getPrimitiveDuration(
|
public int getPrimitiveDuration(
|
||||||
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
|
@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() {
|
private String[] getSupportedPrimitivesNames() {
|
||||||
if (mSupportedPrimitives == null) {
|
int supportedPrimitivesCount = mSupportedPrimitives.size();
|
||||||
return new String[0];
|
String[] names = new String[supportedPrimitivesCount];
|
||||||
}
|
for (int i = 0; i < supportedPrimitivesCount; i++) {
|
||||||
String[] names = new String[mSupportedPrimitives.size()];
|
|
||||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
|
||||||
names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
|
names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
|
||||||
}
|
}
|
||||||
return names;
|
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}.
|
* Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
|
||||||
*
|
*
|
||||||
@@ -675,11 +688,14 @@ public class VibratorInfo implements Parcelable {
|
|||||||
/** @hide */
|
/** @hide */
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private final int mId;
|
private final int mId;
|
||||||
private int mCapabilities = 0;
|
private long mCapabilities;
|
||||||
private int[] mSupportedEffects = null;
|
private SparseBooleanArray mSupportedEffects;
|
||||||
private int[] mSupportedBraking = null;
|
private SparseBooleanArray mSupportedBraking;
|
||||||
private int[] mSupportedPrimitives = null;
|
private SparseIntArray mSupportedPrimitives = new SparseIntArray();
|
||||||
private int[] mPrimitiveDurations = new int[0];
|
private int mPrimitiveDelayMax;
|
||||||
|
private int mCompositionSizeMax;
|
||||||
|
private int mPwlePrimitiveDurationMax;
|
||||||
|
private int mPwleSizeMax;
|
||||||
private float mQFactor = Float.NaN;
|
private float mQFactor = Float.NaN;
|
||||||
private FrequencyMapping mFrequencyMapping =
|
private FrequencyMapping mFrequencyMapping =
|
||||||
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
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. */
|
/** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder setCapabilities(int capabilities) {
|
public Builder setCapabilities(long capabilities) {
|
||||||
mCapabilities = capabilities;
|
mCapabilities = capabilities;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -699,34 +715,49 @@ public class VibratorInfo implements Parcelable {
|
|||||||
/** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
|
/** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder setSupportedEffects(int... supportedEffects) {
|
public Builder setSupportedEffects(int... supportedEffects) {
|
||||||
mSupportedEffects = supportedEffects;
|
mSupportedEffects = toSparseBooleanArray(supportedEffects);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
|
/** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder setSupportedBraking(int... supportedBraking) {
|
public Builder setSupportedBraking(int... supportedBraking) {
|
||||||
mSupportedBraking = supportedBraking;
|
mSupportedBraking = toSparseBooleanArray(supportedBraking);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Configure maximum duration, in milliseconds, of a PWLE primitive. */
|
||||||
* Configure the primitives supported with
|
|
||||||
* {@link android.hardware.vibrator.CompositePrimitive} values.
|
|
||||||
*/
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder setSupportedPrimitives(int... supportedPrimitives) {
|
public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
|
||||||
mSupportedPrimitives = supportedPrimitives;
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
|
/** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder setPrimitiveDuration(int primitiveId, int duration) {
|
public Builder setSupportedPrimitive(int primitiveId, int duration) {
|
||||||
if (mPrimitiveDurations.length <= primitiveId) {
|
mSupportedPrimitives.put(primitiveId, duration);
|
||||||
mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
|
return this;
|
||||||
}
|
}
|
||||||
mPrimitiveDurations[primitiveId] = duration;
|
|
||||||
|
/** 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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,7 +779,25 @@ public class VibratorInfo implements Parcelable {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public VibratorInfo build() {
|
public VibratorInfo build() {
|
||||||
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
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() {
|
public void testIsPrimitiveSupported() {
|
||||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||||
.build();
|
.build();
|
||||||
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
|
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
|
||||||
|
|
||||||
// Returns false when there is no compose capability.
|
// Returns false when there is no compose capability.
|
||||||
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||||
.build();
|
.build();
|
||||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||||
}
|
}
|
||||||
@@ -103,8 +103,7 @@ public class VibratorInfoTest {
|
|||||||
public void testGetPrimitiveDuration() {
|
public void testGetPrimitiveDuration() {
|
||||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
|
||||||
.build();
|
.build();
|
||||||
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||||
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
|
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
|
||||||
@@ -112,6 +111,26 @@ public class VibratorInfoTest {
|
|||||||
.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
|
.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
|
@Test
|
||||||
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
|
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
|
||||||
assertEquals(Braking.NONE, new VibratorInfo.Builder(
|
assertEquals(Braking.NONE, new VibratorInfo.Builder(
|
||||||
@@ -263,8 +282,12 @@ public class VibratorInfoTest {
|
|||||||
VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||||
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
|
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
|
||||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
.setPrimitiveDelayMax(100)
|
||||||
|
.setCompositionSizeMax(10)
|
||||||
|
.setSupportedBraking(Braking.CLAB)
|
||||||
|
.setPwlePrimitiveDurationMax(50)
|
||||||
|
.setPwleSizeMax(20)
|
||||||
.setQFactor(2f)
|
.setQFactor(2f)
|
||||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
|
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
|
||||||
VibratorInfo complete = completeBuilder.build();
|
VibratorInfo complete = completeBuilder.build();
|
||||||
@@ -279,8 +302,7 @@ public class VibratorInfoTest {
|
|||||||
assertNotEquals(complete, completeWithComposeControl);
|
assertNotEquals(complete, completeWithComposeControl);
|
||||||
|
|
||||||
VibratorInfo completeWithNoEffects = completeBuilder
|
VibratorInfo completeWithNoEffects = completeBuilder
|
||||||
.setSupportedEffects()
|
.setSupportedEffects(new int[0])
|
||||||
.setSupportedPrimitives()
|
|
||||||
.build();
|
.build();
|
||||||
assertNotEquals(complete, completeWithNoEffects);
|
assertNotEquals(complete, completeWithNoEffects);
|
||||||
|
|
||||||
@@ -289,13 +311,8 @@ public class VibratorInfoTest {
|
|||||||
.build();
|
.build();
|
||||||
assertNotEquals(complete, completeWithUnknownEffects);
|
assertNotEquals(complete, completeWithUnknownEffects);
|
||||||
|
|
||||||
VibratorInfo completeWithUnknownPrimitives = completeBuilder
|
|
||||||
.setSupportedPrimitives(null)
|
|
||||||
.build();
|
|
||||||
assertNotEquals(complete, completeWithUnknownPrimitives);
|
|
||||||
|
|
||||||
VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
|
VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
|
||||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||||
.build();
|
.build();
|
||||||
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
|
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
|
||||||
|
|
||||||
@@ -321,12 +338,17 @@ public class VibratorInfoTest {
|
|||||||
.build();
|
.build();
|
||||||
assertNotEquals(complete, completeWithDifferentQFactor);
|
assertNotEquals(complete, completeWithDifferentQFactor);
|
||||||
|
|
||||||
VibratorInfo empty = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
VibratorInfo unknownEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||||
VibratorInfo emptyWithKnownSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
VibratorInfo knownEmptyEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||||
.setSupportedEffects()
|
.setSupportedEffects(new int[0])
|
||||||
.setSupportedPrimitives()
|
|
||||||
.build();
|
.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
|
@Test
|
||||||
@@ -334,8 +356,7 @@ public class VibratorInfoTest {
|
|||||||
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||||
.setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
|
||||||
.setQFactor(Float.NaN)
|
.setQFactor(Float.NaN)
|
||||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
|
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -844,7 +844,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
public List<Step> play() {
|
public List<Step> play() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
|
||||||
try {
|
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<>();
|
List<PrimitiveSegment> primitives = new ArrayList<>();
|
||||||
for (int i = segmentIndex; i < segmentCount; i++) {
|
for (int i = segmentIndex; i < segmentCount; i++) {
|
||||||
VibrationEffectSegment segment = effect.getSegments().get(i);
|
VibrationEffectSegment segment = effect.getSegments().get(i);
|
||||||
@@ -896,7 +901,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
public List<Step> play() {
|
public List<Step> play() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
|
||||||
try {
|
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<>();
|
List<RampSegment> pwles = new ArrayList<>();
|
||||||
for (int i = segmentIndex; i < segmentCount; i++) {
|
for (int i = segmentIndex; i < segmentCount; i++) {
|
||||||
VibrationEffectSegment segment = effect.getSegments().get(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.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.Preconditions;
|
|
||||||
|
|
||||||
import libcore.util.NativeAllocationRegistry;
|
import libcore.util.NativeAllocationRegistry;
|
||||||
|
|
||||||
/** Controls a single vibrator. */
|
/** Controls a single vibrator. */
|
||||||
final class VibratorController {
|
final class VibratorController {
|
||||||
private static final String TAG = "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 Object mLock = new Object();
|
||||||
private final NativeWrapper mNativeWrapper;
|
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")
|
@GuardedBy("mLock")
|
||||||
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
|
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
|
||||||
new RemoteCallbackList<>();
|
new RemoteCallbackList<>();
|
||||||
@@ -66,10 +71,10 @@ final class VibratorController {
|
|||||||
NativeWrapper nativeWrapper) {
|
NativeWrapper nativeWrapper) {
|
||||||
mNativeWrapper = nativeWrapper;
|
mNativeWrapper = nativeWrapper;
|
||||||
mNativeWrapper.init(vibratorId, listener);
|
mNativeWrapper.init(vibratorId, listener);
|
||||||
// TODO(b/167947076): load suggested range from config
|
mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
|
||||||
mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 200);
|
mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
|
||||||
Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
|
mVibratorInfoBuilder);
|
||||||
vibratorId);
|
mVibratorInfo = mVibratorInfoBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Register state listener for this vibrator. */
|
/** Register state listener for this vibrator. */
|
||||||
@@ -103,8 +108,16 @@ final class VibratorController {
|
|||||||
|
|
||||||
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
|
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
|
||||||
public VibratorInfo getVibratorInfo() {
|
public VibratorInfo getVibratorInfo() {
|
||||||
|
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;
|
return mVibratorInfo;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return {@code true} is this vibrator is currently vibrating, false otherwise.
|
* Return {@code true} is this vibrator is currently vibrating, false otherwise.
|
||||||
@@ -361,7 +374,8 @@ final class VibratorController {
|
|||||||
|
|
||||||
private static native void alwaysOnDisable(long nativePtr, long id);
|
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;
|
private long mNativePtr = 0;
|
||||||
|
|
||||||
@@ -428,9 +442,11 @@ final class VibratorController {
|
|||||||
alwaysOnDisable(mNativePtr, id);
|
alwaysOnDisable(mNativePtr, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return device vibrator metadata. */
|
/**
|
||||||
public VibratorInfo getInfo(float suggestedFrequencyRange) {
|
* Loads device vibrator metadata and returns true if all metadata was loaded successfully.
|
||||||
return getInfo(mNativePtr, suggestedFrequencyRange);
|
*/
|
||||||
|
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 jmethodID sMethodIdOnComplete;
|
||||||
static jclass sFrequencyMappingClass;
|
static jclass sFrequencyMappingClass;
|
||||||
static jmethodID sFrequencyMappingCtor;
|
static jmethodID sFrequencyMappingCtor;
|
||||||
static jclass sVibratorInfoClass;
|
static struct {
|
||||||
static jmethodID sVibratorInfoCtor;
|
jmethodID setCapabilities;
|
||||||
|
jmethodID setSupportedEffects;
|
||||||
|
jmethodID setSupportedBraking;
|
||||||
|
jmethodID setPwlePrimitiveDurationMax;
|
||||||
|
jmethodID setPwleSizeMax;
|
||||||
|
jmethodID setSupportedPrimitive;
|
||||||
|
jmethodID setPrimitiveDelayMax;
|
||||||
|
jmethodID setCompositionSizeMax;
|
||||||
|
jmethodID setQFactor;
|
||||||
|
jmethodID setFrequencyMapping;
|
||||||
|
} sVibratorInfoBuilderClassInfo;
|
||||||
static struct {
|
static struct {
|
||||||
jfieldID id;
|
jfieldID id;
|
||||||
jfieldID scale;
|
jfieldID scale;
|
||||||
@@ -352,68 +362,88 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
|||||||
wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable");
|
wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable");
|
||||||
}
|
}
|
||||||
|
|
||||||
static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
||||||
jfloat suggestedSafeRange) {
|
jfloat suggestedSafeRange, jobject vibratorInfoBuilder) {
|
||||||
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
|
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
|
||||||
if (wrapper == nullptr) {
|
if (wrapper == nullptr) {
|
||||||
ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
|
ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
|
||||||
return nullptr;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
vibrator::Info info = wrapper->getVibratorInfo();
|
vibrator::Info info = wrapper->getVibratorInfo();
|
||||||
|
|
||||||
jlong capabilities =
|
if (info.capabilities.isOk()) {
|
||||||
static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE));
|
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setCapabilities,
|
||||||
jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
|
static_cast<jlong>(info.capabilities.value()));
|
||||||
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.supportedEffects.isOk()) {
|
if (info.supportedEffects.isOk()) {
|
||||||
std::vector<aidl::Effect> effects = info.supportedEffects.value();
|
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(),
|
env->SetIntArrayRegion(supportedEffects, 0, effects.size(),
|
||||||
reinterpret_cast<jint*>(effects.data()));
|
reinterpret_cast<jint*>(effects.data()));
|
||||||
|
env->CallObjectMethod(vibratorInfoBuilder,
|
||||||
|
sVibratorInfoBuilderClassInfo.setSupportedEffects, supportedEffects);
|
||||||
}
|
}
|
||||||
if (info.supportedBraking.isOk()) {
|
if (info.supportedBraking.isOk()) {
|
||||||
std::vector<aidl::Braking> braking = info.supportedBraking.value();
|
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(),
|
env->SetIntArrayRegion(supportedBraking, 0, braking.size(),
|
||||||
reinterpret_cast<jint*>(braking.data()));
|
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()) {
|
if (info.supportedPrimitives.isOk()) {
|
||||||
std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value();
|
auto durations = info.primitiveDurations.valueOr({});
|
||||||
supportedPrimitives = env->NewIntArray(primitives.size());
|
for (auto& primitive : info.supportedPrimitives.value()) {
|
||||||
env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
|
auto primitiveIdx = static_cast<size_t>(primitive);
|
||||||
reinterpret_cast<jint*>(primitives.data()));
|
auto duration = durations.size() > primitiveIdx ? durations[primitiveIdx].count() : 0;
|
||||||
|
env->CallObjectMethod(vibratorInfoBuilder,
|
||||||
|
sVibratorInfoBuilderClassInfo.setSupportedPrimitive,
|
||||||
|
static_cast<jint>(primitive), static_cast<jint>(duration));
|
||||||
}
|
}
|
||||||
if (info.primitiveDurations.isOk()) {
|
|
||||||
std::vector<int32_t> durations;
|
|
||||||
for (auto duration : info.primitiveDurations.value()) {
|
|
||||||
durations.push_back(duration.count());
|
|
||||||
}
|
}
|
||||||
primitiveDurations = env->NewIntArray(durations.size());
|
if (info.primitiveDelayMax.isOk()) {
|
||||||
env->SetIntArrayRegion(primitiveDurations, 0, durations.size(),
|
env->CallObjectMethod(vibratorInfoBuilder,
|
||||||
reinterpret_cast<jint*>(durations.data()));
|
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()) {
|
if (info.maxAmplitudes.isOk()) {
|
||||||
std::vector<float> amplitudes = info.maxAmplitudes.value();
|
std::vector<float> amplitudes = info.maxAmplitudes.value();
|
||||||
maxAmplitudes = env->NewFloatArray(amplitudes.size());
|
maxAmplitudes = env->NewFloatArray(amplitudes.size());
|
||||||
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
|
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
|
||||||
reinterpret_cast<jfloat*>(amplitudes.data()));
|
reinterpret_cast<jfloat*>(amplitudes.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
|
jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
|
||||||
minFrequency, resonantFrequency, frequencyResolution,
|
minFrequency, resonantFrequency, frequencyResolution,
|
||||||
suggestedSafeRange, maxAmplitudes);
|
suggestedSafeRange, maxAmplitudes);
|
||||||
|
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
|
||||||
|
frequencyMapping);
|
||||||
|
|
||||||
return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
|
return info.checkAndLogFailure("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
|
||||||
capabilities, supportedEffects, supportedBraking, supportedPrimitives,
|
|
||||||
primitiveDurations, qFactor, frequencyMapping);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const JNINativeMethod method_table[] = {
|
static const JNINativeMethod method_table[] = {
|
||||||
@@ -433,7 +463,7 @@ static const JNINativeMethod method_table[] = {
|
|||||||
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
|
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
|
||||||
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
|
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
|
||||||
{"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
|
{"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) {
|
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));
|
sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
|
||||||
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
|
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
|
||||||
|
|
||||||
jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
|
jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
|
||||||
sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
|
sVibratorInfoBuilderClassInfo.setCapabilities =
|
||||||
sVibratorInfoCtor =
|
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCapabilities",
|
||||||
GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
|
"(J)Landroid/os/VibratorInfo$Builder;");
|
||||||
"(IJ[I[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
|
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,
|
return jniRegisterNativeMethods(env,
|
||||||
"com/android/server/vibrator/VibratorController$NativeWrapper",
|
"com/android/server/vibrator/VibratorController$NativeWrapper",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ final class FakeVibratorControllerProvider {
|
|||||||
private int[] mSupportedEffects;
|
private int[] mSupportedEffects;
|
||||||
private int[] mSupportedBraking;
|
private int[] mSupportedBraking;
|
||||||
private int[] mSupportedPrimitives;
|
private int[] mSupportedPrimitives;
|
||||||
|
private int mCompositionSizeMax;
|
||||||
|
private int mPwleSizeMax;
|
||||||
private float mMinFrequency = Float.NaN;
|
private float mMinFrequency = Float.NaN;
|
||||||
private float mResonantFrequency = Float.NaN;
|
private float mResonantFrequency = Float.NaN;
|
||||||
private float mFrequencyResolution = Float.NaN;
|
private float mFrequencyResolution = Float.NaN;
|
||||||
@@ -151,12 +153,22 @@ final class FakeVibratorControllerProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VibratorInfo getInfo(float suggestedFrequencyRange) {
|
public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
|
||||||
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
infoBuilder.setCapabilities(mCapabilities);
|
||||||
mMinFrequency, mResonantFrequency, mFrequencyResolution,
|
infoBuilder.setSupportedBraking(mSupportedBraking);
|
||||||
suggestedFrequencyRange, mMaxAmplitudes);
|
infoBuilder.setPwleSizeMax(mPwleSizeMax);
|
||||||
return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
infoBuilder.setSupportedEffects(mSupportedEffects);
|
||||||
mSupportedPrimitives, null, mQFactor, frequencyMapping);
|
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() {
|
private void applyLatency() {
|
||||||
@@ -236,6 +248,16 @@ final class FakeVibratorControllerProvider {
|
|||||||
mSupportedPrimitives = primitives;
|
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. */
|
/** Set the resonant frequency of the fake vibrator hardware. */
|
||||||
public void setResonantFrequency(float frequencyHz) {
|
public void setResonantFrequency(float frequencyHz) {
|
||||||
mResonantFrequency = frequencyHz;
|
mResonantFrequency = frequencyHz;
|
||||||
|
|||||||
@@ -356,7 +356,8 @@ public class VibrationThreadTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void vibrate_singleVibratorComposed_runsVibration() throws Exception {
|
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;
|
long vibrationId = 1;
|
||||||
VibrationEffect effect = VibrationEffect.startComposition()
|
VibrationEffect effect = VibrationEffect.startComposition()
|
||||||
@@ -374,7 +375,7 @@ public class VibrationThreadTest {
|
|||||||
assertEquals(Arrays.asList(
|
assertEquals(Arrays.asList(
|
||||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
||||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
|
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
fakeVibrator.getEffectSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -394,6 +395,27 @@ public class VibrationThreadTest {
|
|||||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
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
|
@Test
|
||||||
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
|
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
|
||||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||||
@@ -432,12 +454,13 @@ public class VibrationThreadTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
|
public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
|
||||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedBraking(Braking.CLAB);
|
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||||
mVibratorProviders.get(VIBRATOR_ID).setMinFrequency(100);
|
fakeVibrator.setSupportedBraking(Braking.CLAB);
|
||||||
mVibratorProviders.get(VIBRATOR_ID).setResonantFrequency(150);
|
fakeVibrator.setMinFrequency(100);
|
||||||
mVibratorProviders.get(VIBRATOR_ID).setFrequencyResolution(50);
|
fakeVibrator.setResonantFrequency(150);
|
||||||
mVibratorProviders.get(VIBRATOR_ID).setMaxAmplitudes(
|
fakeVibrator.setFrequencyResolution(50);
|
||||||
|
fakeVibrator.setMaxAmplitudes(
|
||||||
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
|
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
|
||||||
|
|
||||||
long vibrationId = 1;
|
long vibrationId = 1;
|
||||||
@@ -462,8 +485,34 @@ public class VibrationThreadTest {
|
|||||||
expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
|
expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
|
||||||
expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
|
expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
|
||||||
/* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
|
/* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
fakeVibrator.getEffectSegments());
|
||||||
assertEquals(Arrays.asList(Braking.CLAB), mVibratorProviders.get(VIBRATOR_ID).getBraking());
|
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
|
@Test
|
||||||
|
|||||||
@@ -298,11 +298,13 @@ public class VibratorControllerTest {
|
|||||||
private void mockVibratorCapabilities(int capabilities) {
|
private void mockVibratorCapabilities(int capabilities) {
|
||||||
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
||||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
||||||
when(mNativeWrapperMock.getInfo(anyFloat())).thenReturn(
|
when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class)))
|
||||||
new VibratorInfo.Builder(VIBRATOR_ID)
|
.then(invocation -> {
|
||||||
|
((VibratorInfo.Builder) invocation.getArgument(1))
|
||||||
.setCapabilities(capabilities)
|
.setCapabilities(capabilities)
|
||||||
.setFrequencyMapping(frequencyMapping)
|
.setFrequencyMapping(frequencyMapping);
|
||||||
.build());
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {
|
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {
|
||||||
|
|||||||
Reference in New Issue
Block a user