Merge "Create VibratorFrequencyProfile in Vibrator API."

This commit is contained in:
Lais Andrade
2022-01-24 20:08:18 +00:00
committed by Android (Google) Code Review
17 changed files with 833 additions and 282 deletions

View File

@@ -33045,9 +33045,13 @@ package android.os {
method @NonNull public int[] areEffectsSupported(@NonNull int...);
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
method public int getId();
method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
method public float getQFactor();
method public float getResonantFrequency();
method public abstract boolean hasAmplitudeControl();
method public boolean hasFrequencyControl();
method public abstract boolean hasVibrator();
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long, android.media.AudioAttributes);
@@ -33373,6 +33377,17 @@ package android.os.strictmode {
}
package android.os.vibrator {
public final class VibratorFrequencyProfile {
method public float getMaxAmplitudeMeasurementInterval();
method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements();
method public float getMaxFrequency();
method public float getMinFrequency();
}
}
package android.preference {
@Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference {

View File

@@ -18,18 +18,25 @@ package android.os;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Range;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Function;
/**
* Vibrator implementation that controls the main system vibrator.
@@ -51,7 +58,7 @@ public class SystemVibrator extends Vibrator {
private final Object mLock = new Object();
@GuardedBy("mLock")
private AllVibratorsInfo mVibratorInfo;
private VibratorInfo mVibratorInfo;
@UnsupportedAppUsage
public SystemVibrator(Context context) {
@@ -71,6 +78,11 @@ public class SystemVibrator extends Vibrator {
return VibratorInfo.EMPTY_VIBRATOR_INFO;
}
int[] vibratorIds = mVibratorManager.getVibratorIds();
if (vibratorIds.length == 0) {
// It is known that the device has no vibrator, so cache and return info that
// reflects the lack of support for effects/primitives.
return mVibratorInfo = new NoVibratorInfo();
}
VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
for (int i = 0; i < vibratorIds.length; i++) {
Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]);
@@ -83,7 +95,12 @@ public class SystemVibrator extends Vibrator {
}
vibratorInfos[i] = vibrator.getInfo();
}
return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
if (vibratorInfos.length == 1) {
// Device has a single vibrator info, cache and return successfully loaded info.
return mVibratorInfo = new VibratorInfo(/* id= */ -1, vibratorInfos[0]);
}
// Device has multiple vibrators, generate a single info representing all of them.
return mVibratorInfo = new MultiVibratorInfo(vibratorInfos);
}
}
@@ -257,77 +274,282 @@ public class SystemVibrator extends Vibrator {
}
/**
* Represents all the vibrators information as a single {@link VibratorInfo}.
* Represents a device with no vibrator as a single {@link VibratorInfo}.
*
* <p>This uses the first vibrator on the list as the default one for all hardware spec, but
* uses an intersection of all vibrators to decide the capabilities and effect/primitive
* @hide
*/
@VisibleForTesting
public static class NoVibratorInfo extends VibratorInfo {
public NoVibratorInfo() {
// Use empty arrays to indicate no support, while null would indicate support unknown.
super(/* id= */ -1,
/* capabilities= */ 0,
/* supportedEffects= */ new SparseBooleanArray(),
/* supportedBraking= */ new SparseBooleanArray(),
/* supportedPrimitives= */ new SparseIntArray(),
/* primitiveDelayMax= */ 0,
/* compositionSizeMax= */ 0,
/* pwlePrimitiveDurationMax= */ 0,
/* pwleSizeMax= */ 0,
/* qFactor= */ Float.NaN,
new FrequencyProfile(/* resonantFrequencyHz= */ Float.NaN,
/* minFrequencyHz= */ Float.NaN,
/* frequencyResolutionHz= */ Float.NaN,
/* maxAmplitudes= */ null));
}
}
/**
* Represents multiple vibrator information as a single {@link VibratorInfo}.
*
* <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive
* support.
*
* @hide
*/
@VisibleForTesting
public static class AllVibratorsInfo extends VibratorInfo {
private final VibratorInfo[] mVibratorInfos;
public static class MultiVibratorInfo extends VibratorInfo {
// Epsilon used for float comparison applied in calculations for the merged info.
private static final float EPSILON = 1e-5f;
public AllVibratorsInfo(VibratorInfo[] vibrators) {
super(/* id= */ -1, capabilitiesIntersection(vibrators),
vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO);
mVibratorInfos = vibrators;
}
@Override
public int isEffectSupported(int effectId) {
if (mVibratorInfos.length == 0) {
return Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
}
int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES;
for (VibratorInfo info : mVibratorInfos) {
int effectSupported = info.isEffectSupported(effectId);
if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
return effectSupported;
} else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
supported = effectSupported;
}
}
return supported;
}
@Override
public boolean isPrimitiveSupported(int primitiveId) {
if (mVibratorInfos.length == 0) {
return false;
}
for (VibratorInfo info : mVibratorInfos) {
if (!info.isPrimitiveSupported(primitiveId)) {
return false;
}
}
return true;
}
@Override
public int getPrimitiveDuration(int primitiveId) {
int maxDuration = 0;
for (VibratorInfo info : mVibratorInfos) {
int duration = info.getPrimitiveDuration(primitiveId);
if (duration == 0) {
return 0;
}
maxDuration = Math.max(maxDuration, duration);
}
return maxDuration;
public MultiVibratorInfo(VibratorInfo[] vibrators) {
super(/* id= */ -1,
capabilitiesIntersection(vibrators),
supportedEffectsIntersection(vibrators),
supportedBrakingIntersection(vibrators),
supportedPrimitivesAndDurationsIntersection(vibrators),
integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax),
integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax),
integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
frequencyProfileIntersection(vibrators));
}
private static int capabilitiesIntersection(VibratorInfo[] infos) {
if (infos.length == 0) {
return 0;
}
int intersection = ~0;
for (VibratorInfo info : infos) {
intersection &= info.getCapabilities();
}
return intersection;
}
@Nullable
private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) {
for (VibratorInfo info : infos) {
if (!info.isBrakingSupportKnown()) {
// If one vibrator support is unknown, then the intersection is also unknown.
return null;
}
}
SparseBooleanArray intersection = new SparseBooleanArray();
SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking();
brakingIdLoop:
for (int i = 0; i < firstVibratorBraking.size(); i++) {
int brakingId = firstVibratorBraking.keyAt(i);
if (!firstVibratorBraking.valueAt(i)) {
// The first vibrator already doesn't support this braking, so skip it.
continue brakingIdLoop;
}
for (int j = 1; j < infos.length; j++) {
if (!infos[j].hasBrakingSupport(brakingId)) {
// One vibrator doesn't support this braking, so the intersection doesn't.
continue brakingIdLoop;
}
}
intersection.put(brakingId, true);
}
return intersection;
}
@Nullable
private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) {
for (VibratorInfo info : infos) {
if (!info.isEffectSupportKnown()) {
// If one vibrator support is unknown, then the intersection is also unknown.
return null;
}
}
SparseBooleanArray intersection = new SparseBooleanArray();
SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects();
effectIdLoop:
for (int i = 0; i < firstVibratorEffects.size(); i++) {
int effectId = firstVibratorEffects.keyAt(i);
if (!firstVibratorEffects.valueAt(i)) {
// The first vibrator already doesn't support this effect, so skip it.
continue effectIdLoop;
}
for (int j = 1; j < infos.length; j++) {
if (infos[j].isEffectSupported(effectId) != VIBRATION_EFFECT_SUPPORT_YES) {
// One vibrator doesn't support this effect, so the intersection doesn't.
continue effectIdLoop;
}
}
intersection.put(effectId, true);
}
return intersection;
}
@NonNull
private static SparseIntArray supportedPrimitivesAndDurationsIntersection(
VibratorInfo[] infos) {
SparseIntArray intersection = new SparseIntArray();
SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives();
primitiveIdLoop:
for (int i = 0; i < firstVibratorPrimitives.size(); i++) {
int primitiveId = firstVibratorPrimitives.keyAt(i);
int primitiveDuration = firstVibratorPrimitives.valueAt(i);
if (primitiveDuration == 0) {
// The first vibrator already doesn't support this primitive, so skip it.
continue primitiveIdLoop;
}
for (int j = 1; j < infos.length; j++) {
int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId);
if (vibratorPrimitiveDuration == 0) {
// One vibrator doesn't support this primitive, so the intersection doesn't.
continue primitiveIdLoop;
} else {
// The primitive vibration duration is the maximum among all vibrators.
primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration);
}
}
intersection.put(primitiveId, primitiveDuration);
}
return intersection;
}
private static int integerLimitIntersection(VibratorInfo[] infos,
Function<VibratorInfo, Integer> propertyGetter) {
int limit = 0; // Limit 0 means unlimited
for (VibratorInfo info : infos) {
int vibratorLimit = propertyGetter.apply(info);
if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) {
// This vibrator is limited and intersection is unlimited or has a larger limit:
// use smaller limit here for the intersection.
limit = vibratorLimit;
}
}
return limit;
}
private static float floatPropertyIntersection(VibratorInfo[] infos,
Function<VibratorInfo, Float> propertyGetter) {
float property = propertyGetter.apply(infos[0]);
if (Float.isNaN(property)) {
// If one vibrator is undefined then the intersection is undefined.
return Float.NaN;
}
for (int i = 1; i < infos.length; i++) {
if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) {
// If one vibrator has a different value then the intersection is undefined.
return Float.NaN;
}
}
return property;
}
@NonNull
private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) {
float freqResolution = floatPropertyIntersection(infos,
info -> info.getFrequencyProfile().getFrequencyResolutionHz());
float resonantFreq = floatPropertyIntersection(infos,
VibratorInfo::getResonantFrequencyHz);
Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution);
if ((freqRange == null) || Float.isNaN(freqResolution)) {
return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null);
}
int amplitudeCount =
Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution);
float[] maxAmplitudes = new float[amplitudeCount];
// Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this
// will fail if the loop below is broken and do not replace filled values with actual
// vibrator measurements.
Arrays.fill(maxAmplitudes, Float.MAX_VALUE);
for (VibratorInfo info : infos) {
Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz();
float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes();
int vibratorStartIdx = Math.round(
(freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution);
int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1;
if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) {
Slog.w(TAG, "Error calculating the intersection of vibrator frequency"
+ " profiles: attempted to fetch from vibrator "
+ info.getId() + " max amplitude with bad index " + vibratorStartIdx);
return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null);
}
for (int i = 0; i < maxAmplitudes.length; i++) {
maxAmplitudes[i] = Math.min(maxAmplitudes[i],
vibratorMaxAmplitudes[vibratorStartIdx + i]);
}
}
return new FrequencyProfile(resonantFreq, freqRange.getLower(),
freqResolution, maxAmplitudes);
}
@Nullable
private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos,
float frequencyResolution) {
Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz();
if (firstRange == null) {
// If one vibrator is undefined then the intersection is undefined.
return null;
}
float intersectionLower = firstRange.getLower();
float intersectionUpper = firstRange.getUpper();
// Generate the intersection of all vibrator supported ranges, making sure that both
// min supported frequencies are aligned w.r.t. the frequency resolution.
for (int i = 1; i < infos.length; i++) {
Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz();
if (vibratorRange == null) {
// If one vibrator is undefined then the intersection is undefined.
return null;
}
if ((vibratorRange.getLower() >= intersectionUpper)
|| (vibratorRange.getUpper() <= intersectionLower)) {
// If the range and intersection are disjoint then the intersection is undefined
return null;
}
float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower());
if ((frequencyDelta % frequencyResolution) > EPSILON) {
// If the intersection is not aligned with one vibrator then it's undefined
return null;
}
intersectionLower = Math.max(intersectionLower, vibratorRange.getLower());
intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper());
}
if ((intersectionUpper - intersectionLower) < frequencyResolution) {
// If the intersection is empty then it's undefined.
return null;
}
return Range.create(intersectionLower, intersectionUpper);
}
}
/** Listener for all vibrators state change. */

View File

@@ -31,6 +31,7 @@ import android.content.res.Resources;
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibratorFrequencyProfile;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -208,8 +209,8 @@ public abstract class Vibrator {
/**
* Check whether the vibrator has independent frequency control.
*
* @return True if the hardware can control the frequency of the vibrations, otherwise false.
* @hide
* @return True if the hardware can control the frequency of the vibrations independently of
* the vibration amplitude, false otherwise.
*/
public boolean hasFrequencyControl() {
// We currently can only control frequency of the vibration using the compose PWLE method.
@@ -229,27 +230,47 @@ public abstract class Vibrator {
}
/**
* Gets the resonant frequency of the vibrator.
* Gets the resonant frequency of the vibrator, if applicable.
*
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
* this vibrator is a composite of multiple physical devices.
* @hide
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
* applicable, or if this vibrator is a composite of multiple physical devices with different
* frequencies.
*/
public float getResonantFrequency() {
return getInfo().getResonantFrequency();
return getInfo().getResonantFrequencyHz();
}
/**
* Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
*
* @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
* this vibrator is a composite of multiple physical devices.
* @hide
* @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
* applicable, or if this vibrator is a composite of multiple physical devices with different
* Q factors.
*/
public float getQFactor() {
return getInfo().getQFactor();
}
/**
* Gets the profile that describes the vibrator output across the supported frequency range.
*
* <p>The profile describes the relative output acceleration that the device can reach when it
* vibrates at different frequencies.
*
* @return The frequency profile for this vibrator, or null if the vibrator does not have
* frequency control. If this vibrator is a composite of multiple physical devices then this
* will return a profile supported in all devices, or null if the intersection is empty or not
* available.
*/
@Nullable
public VibratorFrequencyProfile getFrequencyProfile() {
VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile();
if (frequencyProfile.isEmpty()) {
return null;
}
return new VibratorFrequencyProfile(frequencyProfile);
}
/**
* Return the maximum amplitude the vibrator can play using the audio haptic channels.
*

View File

@@ -16,7 +16,6 @@
package android.os;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.Braking;
@@ -26,6 +25,8 @@ import android.util.Range;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -56,7 +57,7 @@ public class VibratorInfo implements Parcelable {
private final int mPwlePrimitiveDurationMax;
private final int mPwleSizeMax;
private final float mQFactor;
private final FrequencyMapping mFrequencyMapping;
private final FrequencyProfile mFrequencyProfile;
VibratorInfo(Parcel in) {
mId = in.readInt();
@@ -69,7 +70,15 @@ public class VibratorInfo implements Parcelable {
mPwlePrimitiveDurationMax = in.readInt();
mPwleSizeMax = in.readInt();
mQFactor = in.readFloat();
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader(), android.os.VibratorInfo.FrequencyMapping.class);
mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
}
public VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo) {
this(id, baseVibratorInfo.mCapabilities, baseVibratorInfo.mSupportedEffects,
baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives,
baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfile);
}
/**
@@ -92,7 +101,7 @@ public class VibratorInfo implements Parcelable {
* @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
* @param frequencyProfile The description of the vibrator supported frequencies and max
* amplitude mappings.
* @hide
*/
@@ -100,7 +109,9 @@ public class VibratorInfo implements Parcelable {
@Nullable SparseBooleanArray supportedBraking,
@NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
float qFactor, @NonNull FrequencyMapping frequencyMapping) {
float qFactor, @NonNull FrequencyProfile frequencyProfile) {
Preconditions.checkNotNull(supportedPrimitives);
Preconditions.checkNotNull(frequencyProfile);
mId = id;
mCapabilities = capabilities;
mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
@@ -111,14 +122,7 @@ public class VibratorInfo implements Parcelable {
mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
mPwleSizeMax = pwleSizeMax;
mQFactor = qFactor;
mFrequencyMapping = frequencyMapping;
}
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
mFrequencyProfile = frequencyProfile;
}
@Override
@@ -133,7 +137,7 @@ public class VibratorInfo implements Parcelable {
dest.writeInt(mPwlePrimitiveDurationMax);
dest.writeInt(mPwleSizeMax);
dest.writeFloat(mQFactor);
dest.writeParcelable(mFrequencyMapping, flags);
mFrequencyProfile.writeToParcel(dest, flags);
}
@Override
@@ -170,13 +174,13 @@ public class VibratorInfo implements Parcelable {
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
&& Objects.equals(mQFactor, that.mQFactor)
&& Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
&& Objects.equals(mFrequencyProfile, that.mFrequencyProfile);
}
@Override
public int hashCode() {
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mQFactor, mFrequencyMapping);
mQFactor, mFrequencyProfile);
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
@@ -198,7 +202,7 @@ public class VibratorInfo implements Parcelable {
+ ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
+ ", mPwleSizeMax=" + mPwleSizeMax
+ ", mQFactor=" + mQFactor
+ ", mFrequencyMapping=" + mFrequencyMapping
+ ", mFrequencyProfile=" + mFrequencyProfile
+ '}';
}
@@ -234,6 +238,30 @@ public class VibratorInfo implements Parcelable {
return Braking.NONE;
}
/** @hide */
@Nullable
public SparseBooleanArray getSupportedBraking() {
if (mSupportedBraking == null) {
return null;
}
return mSupportedBraking.clone();
}
/** @hide */
public boolean isBrakingSupportKnown() {
return mSupportedBraking != null;
}
/** @hide */
public boolean hasBrakingSupport(@Braking int braking) {
return (mSupportedBraking != null) && mSupportedBraking.get(braking);
}
/** @hide */
public boolean isEffectSupportKnown() {
return mSupportedEffects != null;
}
/**
* Query whether the vibrator supports the given effect.
*
@@ -252,6 +280,15 @@ public class VibratorInfo implements Parcelable {
: Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
}
/** @hide */
@Nullable
public SparseBooleanArray getSupportedEffects() {
if (mSupportedEffects == null) {
return null;
}
return mSupportedEffects.clone();
}
/**
* Query whether the vibrator supports the given primitive.
*
@@ -276,6 +313,11 @@ public class VibratorInfo implements Parcelable {
return mSupportedPrimitives.get(primitiveId);
}
/** @hide */
public SparseIntArray getSupportedPrimitives() {
return mSupportedPrimitives.clone();
}
/**
* Query the maximum delay supported for a primitive in a composed effect.
*
@@ -329,8 +371,8 @@ public class VibratorInfo implements Parcelable {
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
* this vibrator is a composite of multiple physical devices.
*/
public float getResonantFrequency() {
return mFrequencyMapping.mResonantFrequencyHz;
public float getResonantFrequencyHz() {
return mFrequencyProfile.mResonantFrequencyHz;
}
/**
@@ -344,31 +386,14 @@ public class VibratorInfo implements Parcelable {
}
/**
* Return a range of frequency values supported by the vibrator.
* Gets the profile of supported frequencies, including the measurements of maximum relative
* output acceleration for supported vibration frequencies.
*
* @return A range of frequency values supported, in hertz. The range will always contain the
* device resonant frequency. Devices without frequency control will return null.
* @hide
* <p>If the devices does not have frequency control then the profile should be empty.
*/
@Nullable
public Range<Float> getFrequencyRangeHz() {
return mFrequencyMapping.mFrequencyRangeHz;
}
/**
* Return the maximum amplitude the vibrator can play at given frequency.
*
* @param frequencyHz The frequency, in hertz, for query.
* @return a value in [0,1] representing the maximum amplitude the device can play at given
* frequency. Devices without frequency control will return 0 to any input. Devices with
* frequency control will return the supported value, for input in
* {@link #getFrequencyRangeHz()}, and 0 for any other input.
* @hide
*/
@FloatRange(from = 0, to = 1)
public float getMaxAmplitude(float frequencyHz) {
return mFrequencyMapping.getMaxAmplitude(frequencyHz);
@NonNull
public FrequencyProfile getFrequencyProfile() {
return mFrequencyProfile;
}
protected long getCapabilities() {
@@ -452,7 +477,7 @@ public class VibratorInfo implements Parcelable {
* Describes the maximum relative output acceleration that can be achieved for each supported
* frequency in a specific vibrator.
*
* <p>This mapping is defined by the following parameters:
* <p>This profile is defined by the following parameters:
*
* <ol>
* <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz}
@@ -466,7 +491,7 @@ public class VibratorInfo implements Parcelable {
*
* @hide
*/
public static final class FrequencyMapping implements Parcelable {
public static final class FrequencyProfile implements Parcelable {
@Nullable
private final Range<Float> mFrequencyRangeHz;
private final float mMinFrequencyHz;
@@ -474,7 +499,7 @@ public class VibratorInfo implements Parcelable {
private final float mFrequencyResolutionHz;
private final float[] mMaxAmplitudes;
FrequencyMapping(Parcel in) {
FrequencyProfile(Parcel in) {
this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray());
}
@@ -484,13 +509,13 @@ public class VibratorInfo implements Parcelable {
* @param resonantFrequencyHz The vibrator resonant frequency, in hertz.
* @param minFrequencyHz Minimum supported frequency, in hertz.
* @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
* amplitudes mapping.
* amplitude measurements.
* @param maxAmplitudes The max amplitude supported by each supported frequency,
* starting at minimum frequency with jumps of frequency
* resolution.
* @hide
*/
public FrequencyMapping(float resonantFrequencyHz, float minFrequencyHz,
public FrequencyProfile(float resonantFrequencyHz, float minFrequencyHz,
float frequencyResolutionHz, float[] maxAmplitudes) {
mMinFrequencyHz = minFrequencyHz;
mResonantFrequencyHz = resonantFrequencyHz;
@@ -500,18 +525,25 @@ public class VibratorInfo implements Parcelable {
System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
}
// If any required field is undefined then leave this mapping empty.
// If any required field is undefined or has a bad value then this profile is invalid.
boolean isValid = !Float.isNaN(resonantFrequencyHz)
&& (resonantFrequencyHz > 0)
&& !Float.isNaN(minFrequencyHz)
&& (minFrequencyHz > 0)
&& !Float.isNaN(frequencyResolutionHz)
&& (frequencyResolutionHz > 0)
&& (mMaxAmplitudes.length > 0);
// If any max amplitude is outside the allowed range then this profile is invalid.
for (int i = 0; i < mMaxAmplitudes.length; i++) {
isValid &= (mMaxAmplitudes[i] >= 0) && (mMaxAmplitudes[i] <= 1);
}
float maxFrequencyHz = isValid
? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1)
: Float.NaN;
// If the non-empty mapping does not have min < resonant < max frequency respected
// then leave this mapping empty.
// If the constraint min < resonant < max is not met then it is invalid.
isValid &= !Float.isNaN(maxFrequencyHz)
&& (resonantFrequencyHz >= minFrequencyHz)
&& (resonantFrequencyHz <= maxFrequencyHz)
@@ -520,14 +552,17 @@ public class VibratorInfo implements Parcelable {
mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null;
}
/**
* Returns true if this frequency mapping is empty, i.e. the only supported is the resonant
* frequency.
*/
/** Returns true if the supported frequency range is empty. */
public boolean isEmpty() {
return mFrequencyRangeHz == null;
}
/** Returns the supported frequency range, in hertz. */
@Nullable
public Range<Float> getFrequencyRangeHz() {
return mFrequencyRangeHz;
}
/**
* Returns the maximum relative amplitude the vibrator can reach while playing at the
* given frequency.
@@ -535,7 +570,7 @@ public class VibratorInfo implements Parcelable {
* @param frequencyHz frequency, in hertz, for query.
* @return A value in [0,1] representing the max relative amplitude supported at the given
* frequency. This will return 0 if the frequency is outside the supported range, or if the
* mapping is empty.
* supported frequency range is empty.
*/
public float getMaxAmplitude(float frequencyHz) {
if (isEmpty() || Float.isNaN(frequencyHz)) {
@@ -555,6 +590,17 @@ public class VibratorInfo implements Parcelable {
return mMaxAmplitudes[floorIndex];
}
/** Returns the raw list of maximum relative output accelerations from the vibrator. */
@NonNull
public float[] getMaxAmplitudes() {
return Arrays.copyOf(mMaxAmplitudes, mMaxAmplitudes.length);
}
/** Returns the raw frequency resolution used for max amplitude measurements, in hertz. */
public float getFrequencyResolutionHz() {
return mFrequencyResolutionHz;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloat(mResonantFrequencyHz);
@@ -573,10 +619,10 @@ public class VibratorInfo implements Parcelable {
if (this == o) {
return true;
}
if (!(o instanceof FrequencyMapping)) {
if (!(o instanceof FrequencyProfile)) {
return false;
}
FrequencyMapping that = (FrequencyMapping) o;
FrequencyProfile that = (FrequencyProfile) o;
return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
&& Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
&& Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
@@ -593,7 +639,7 @@ public class VibratorInfo implements Parcelable {
@Override
public String toString() {
return "FrequencyMapping{"
return "FrequencyProfile{"
+ "mFrequencyRange=" + mFrequencyRangeHz
+ ", mMinFrequency=" + mMinFrequencyHz
+ ", mResonantFrequency=" + mResonantFrequencyHz
@@ -603,16 +649,16 @@ public class VibratorInfo implements Parcelable {
}
@NonNull
public static final Creator<FrequencyMapping> CREATOR =
new Creator<FrequencyMapping>() {
public static final Creator<FrequencyProfile> CREATOR =
new Creator<FrequencyProfile>() {
@Override
public FrequencyMapping createFromParcel(Parcel in) {
return new FrequencyMapping(in);
public FrequencyProfile createFromParcel(Parcel in) {
return new FrequencyProfile(in);
}
@Override
public FrequencyMapping[] newArray(int size) {
return new FrequencyMapping[size];
public FrequencyProfile[] newArray(int size) {
return new FrequencyProfile[size];
}
};
}
@@ -629,8 +675,8 @@ public class VibratorInfo implements Parcelable {
private int mPwlePrimitiveDurationMax;
private int mPwleSizeMax;
private float mQFactor = Float.NaN;
private FrequencyMapping mFrequencyMapping =
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
private FrequencyProfile mFrequencyProfile =
new FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
/** A builder class for a {@link VibratorInfo}. */
public Builder(int id) {
@@ -702,8 +748,8 @@ public class VibratorInfo implements Parcelable {
/** Configure the vibrator frequency information like resonant frequency and bandwidth. */
@NonNull
public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) {
mFrequencyMapping = frequencyMapping;
public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) {
mFrequencyProfile = frequencyProfile;
return this;
}
@@ -712,7 +758,7 @@ public class VibratorInfo implements Parcelable {
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfile);
}
/**

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os.vibrator;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.os.VibratorInfo;
import com.android.internal.util.Preconditions;
/**
* Describes the output of a {@link android.os.Vibrator} for different vibration frequencies.
*
* <p>The profile contains the minimum and maximum supported vibration frequencies, if the device
* supports independent frequency control.
*
* <p>It also describes the relative output acceleration of a vibration at different supported
* frequencies. The acceleration is defined by a relative amplitude value between 0 and 1,
* inclusive, where 0 represents the vibrator off state and 1 represents the maximum output
* acceleration that the vibrator can reach across all supported frequencies.
*
* <p>The measurements are returned as an array of uniformly distributed amplitude values for
* frequencies between the minimum and maximum supported ones. The measurement interval is the
* frequency increment between each pair of amplitude values.
*
* <p>Vibrators without independent frequency control do not have a frequency profile.
*/
public final class VibratorFrequencyProfile {
private final VibratorInfo.FrequencyProfile mFrequencyProfile;
/** @hide */
public VibratorFrequencyProfile(@NonNull VibratorInfo.FrequencyProfile frequencyProfile) {
Preconditions.checkArgument(!frequencyProfile.isEmpty(),
"Frequency profile must have a non-empty frequency range");
mFrequencyProfile = frequencyProfile;
}
/**
* Measurements of the maximum relative amplitude the vibrator can achieve for each supported
* frequency.
*
* <p>The frequency of a measurement is determined as:
*
* {@code getMinFrequency() + measurementIndex * getMaxAmplitudeMeasurementInterval()}
*
* <p>The returned list will not be empty, and will have entries representing frequencies from
* {@link #getMinFrequency()} to {@link #getMaxFrequency()}, inclusive.
*
* @return Array of maximum relative amplitude measurements, each value is between 0 and 1,
* inclusive.
*/
@NonNull
@FloatRange(from = 0, to = 1)
public float[] getMaxAmplitudeMeasurements() {
// VibratorInfo getters always return a copy or clone of the data objects.
return mFrequencyProfile.getMaxAmplitudes();
}
/**
* Gets the frequency interval used to measure the maximum relative amplitudes.
*
* @return the frequency interval used for the measurement, in hertz.
*/
public float getMaxAmplitudeMeasurementInterval() {
return mFrequencyProfile.getFrequencyResolutionHz();
}
/**
* Gets the minimum frequency supported by the vibrator.
*
* @return the minimum frequency supported by the vibrator, in hertz.
*/
public float getMinFrequency() {
return mFrequencyProfile.getFrequencyRangeHz().getLower();
}
/**
* Gets the maximum frequency supported by the vibrator.
*
* @return the maximum frequency supported by the vibrator, in hertz.
*/
public float getMaxFrequency() {
return mFrequencyProfile.getFrequencyRangeHz().getUpper();
}
}

View File

@@ -19,13 +19,11 @@ package android.os;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.platform.test.annotations.Presubmit;
import android.util.Range;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,10 +41,10 @@ public class VibratorInfoTest {
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE =
new VibratorInfo.FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
@Test
@@ -142,95 +140,76 @@ public class VibratorInfoTest {
}
@Test
public void testGetFrequencyRangeHz_invalidFrequencyMappingReturnsNull() {
public void testGetFrequencyProfile_unsetProfileIsEmpty() {
assertTrue(
new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyProfile().isEmpty());
}
@Test
public void testFrequencyProfile_invalidValuesCreatesEmptyProfile() {
// Invalid, contains NaN values or empty array.
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyRangeHz());
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
Float.NaN, 50, 25, TEST_AMPLITUDE_MAP))
.build().getFrequencyRangeHz());
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
150, Float.NaN, 25, TEST_AMPLITUDE_MAP))
.build().getFrequencyRangeHz());
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
150, 50, Float.NaN, TEST_AMPLITUDE_MAP))
.build().getFrequencyRangeHz());
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(150, 50, 25, null))
.build().getFrequencyRangeHz());
assertTrue(new VibratorInfo.FrequencyProfile(
Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).isEmpty());
assertTrue(new VibratorInfo.FrequencyProfile(
150, Float.NaN, 25, TEST_AMPLITUDE_MAP).isEmpty());
assertTrue(new VibratorInfo.FrequencyProfile(
150, 50, Float.NaN, TEST_AMPLITUDE_MAP).isEmpty());
assertTrue(new VibratorInfo.FrequencyProfile(150, 50, 25, null).isEmpty());
// Invalid, contains zero or negative frequency values.
assertTrue(new VibratorInfo.FrequencyProfile(-1, 50, 25, TEST_AMPLITUDE_MAP).isEmpty());
assertTrue(new VibratorInfo.FrequencyProfile(150, 0, 25, TEST_AMPLITUDE_MAP).isEmpty());
assertTrue(new VibratorInfo.FrequencyProfile(150, 50, -2, TEST_AMPLITUDE_MAP).isEmpty());
// Invalid max amplitude entries.
assertTrue(new VibratorInfo.FrequencyProfile(
150, 50, 50, new float[] { -1, 0, 1, 1, 0 }).isEmpty());
assertTrue(new VibratorInfo.FrequencyProfile(
150, 50, 50, new float[] { 0, 1, 2, 1, 0 }).isEmpty());
// Invalid, minFrequency > resonantFrequency
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, null))
.build().getFrequencyRangeHz());
assertTrue(new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, TEST_AMPLITUDE_MAP)
.isEmpty());
// Invalid, maxFrequency < resonantFrequency by changing resolution.
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
150, 50, /* frequencyResolutionHz= */ 10, null))
.build().getFrequencyRangeHz());
assertTrue(new VibratorInfo.FrequencyProfile(
150, 50, /* frequencyResolutionHz= */ 10, TEST_AMPLITUDE_MAP).isEmpty());
}
@Test
public void testGetFrequencyRangeHz_resultRangeDerivedFromHalMapping() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ 50,
/* frequencyResolutionHz= */ 25,
new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
/* 200Hz= */ 0.8f}))
.build();
public void testGetMaxAmplitude_emptyProfileReturnsAlwaysZero() {
VibratorInfo.FrequencyProfile profile = EMPTY_FREQUENCY_PROFILE;
assertEquals(0f, profile.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
assertEquals(0f, profile.getMaxAmplitude(100f), TEST_TOLERANCE);
assertEquals(0f, profile.getMaxAmplitude(200f), TEST_TOLERANCE);
assertEquals(Range.create(50f, 200f), info.getFrequencyRangeHz());
}
@Test
public void testGetMaxAmplitude_emptyMappingReturnsAlwaysZero() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(200f), TEST_TOLERANCE);
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
profile = new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ Float.NaN,
/* frequencyResolutionHz= */ Float.NaN,
null))
.build();
/* maxAmplitudes= */ null);
assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
assertEquals(0f, profile.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
assertEquals(0f, profile.getMaxAmplitude(100f), TEST_TOLERANCE);
assertEquals(0f, profile.getMaxAmplitude(150f), TEST_TOLERANCE);
}
@Test
public void testGetMaxAmplitude_validMappingReturnsMappedValues() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
public void testGetMaxAmplitude_validprofileReturnsMappedValues() {
VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ 50,
/* frequencyResolutionHz= */ 25,
new float[]{
/* maxAmplitudes= */ new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
/* 200Hz= */ 0.8f}))
.build();
/* 200Hz= */ 0.8f});
assertEquals(1f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
assertEquals(0.9f, info.getMaxAmplitude(175f), TEST_TOLERANCE);
assertEquals(0.8f, info.getMaxAmplitude(125f), TEST_TOLERANCE);
assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRangeHz().getUpper()),
TEST_TOLERANCE); // 200Hz
assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRangeHz().getLower()),
TEST_TOLERANCE); // 50Hz
assertEquals(1f, profile.getMaxAmplitude(150f), TEST_TOLERANCE);
assertEquals(0.9f, profile.getMaxAmplitude(175f), TEST_TOLERANCE);
assertEquals(0.8f, profile.getMaxAmplitude(125f), TEST_TOLERANCE);
assertEquals(0.8f, profile.getMaxAmplitude(200f), TEST_TOLERANCE);
assertEquals(0.1f, profile.getMaxAmplitude(50f), TEST_TOLERANCE);
// 145Hz maps to the max amplitude for 125Hz, which is lower.
assertEquals(0.8f, info.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz
assertEquals(0.8f, profile.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz
// 185Hz maps to the max amplitude for 200Hz, which is lower.
assertEquals(0.8f, info.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz
assertEquals(0.8f, profile.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz
}
@Test
@@ -245,7 +224,7 @@ public class VibratorInfoTest {
.setPwlePrimitiveDurationMax(50)
.setPwleSizeMax(20)
.setQFactor(2f)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
.setFrequencyProfile(TEST_FREQUENCY_PROFILE);
VibratorInfo complete = completeBuilder.build();
assertEquals(complete, complete);
@@ -272,23 +251,21 @@ public class VibratorInfoTest {
.build();
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
TEST_RESONANT_FREQUENCY + 20,
TEST_MIN_FREQUENCY + 10,
TEST_FREQUENCY_RESOLUTION + 5,
TEST_AMPLITUDE_MAP))
.build();
assertNotEquals(complete, completeWithDifferentFrequencyMapping);
assertNotEquals(complete, completeWithDifferentFrequencyProfile);
VibratorInfo completeWithEmptyFrequencyMapping = completeBuilder
.setFrequencyMapping(EMPTY_FREQUENCY_MAPPING)
VibratorInfo completeWithEmptyFrequencyProfile = completeBuilder
.setFrequencyProfile(EMPTY_FREQUENCY_PROFILE)
.build();
assertNotEquals(complete, completeWithEmptyFrequencyMapping);
assertNotEquals(complete, completeWithEmptyFrequencyProfile);
VibratorInfo completeWithUnknownQFactor = completeBuilder
.setQFactor(Float.NaN)
.build();
VibratorInfo completeWithUnknownQFactor = completeBuilder.setQFactor(Float.NaN).build();
assertNotEquals(complete, completeWithUnknownQFactor);
VibratorInfo completeWithDifferentQFactor = completeBuilder
@@ -316,7 +293,7 @@ public class VibratorInfoTest {
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(Float.NaN)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
Parcel parcel = Parcel.obtain();

View File

@@ -61,6 +61,8 @@ public class VibratorTest {
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
private static final float TEST_TOLERANCE = 1e-5f;
private Context mContextSpy;
private Vibrator mVibratorSpy;
@@ -76,6 +78,9 @@ public class VibratorTest {
@Test
public void getId_returnsDefaultId() {
assertEquals(-1, mVibratorSpy.getId());
assertEquals(-1, new SystemVibrator.NoVibratorInfo().getId());
assertEquals(-1, new SystemVibrator.MultiVibratorInfo(new VibratorInfo[] {
VibratorInfo.EMPTY_VIBRATOR_INFO, VibratorInfo.EMPTY_VIBRATOR_INFO }).getId());
}
@Test
@@ -90,8 +95,7 @@ public class VibratorTest {
@Test
public void areEffectsSupported_noVibrator_returnsAlwaysNo() {
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
new VibratorInfo[0]);
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
}
@@ -104,7 +108,7 @@ public class VibratorTest {
VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setSupportedEffects(new int[0])
.build();
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
@@ -116,7 +120,7 @@ public class VibratorTest {
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.build();
VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unknownSupportVibrator});
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
@@ -130,7 +134,7 @@ public class VibratorTest {
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.build();
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
@@ -148,8 +152,7 @@ public class VibratorTest {
@Test
public void arePrimitivesSupported_noVibrator_returnsAlwaysFalse() {
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
new VibratorInfo[0]);
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -160,7 +163,7 @@ public class VibratorTest {
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -175,7 +178,7 @@ public class VibratorTest {
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15)
.build();
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -192,8 +195,7 @@ public class VibratorTest {
@Test
public void getPrimitivesDurations_noVibrator_returnsAlwaysZero() {
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
new VibratorInfo[0]);
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -204,7 +206,7 @@ public class VibratorTest {
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -219,11 +221,179 @@ public class VibratorTest {
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.build();
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@Test
public void getQFactorAndResonantFrequency_noVibrator_returnsNaN() {
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertTrue(Float.isNaN(info.getQFactor()));
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
}
@Test
public void getQFactorAndResonantFrequency_differentValues_returnsNaN() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setQFactor(1f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setQFactor(2f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertTrue(Float.isNaN(info.getQFactor()));
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
// One vibrator with values undefined.
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, thirdVibrator});
assertTrue(Float.isNaN(info.getQFactor()));
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
}
@Test
public void getQFactorAndResonantFrequency_sameValues_returnsValue() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setQFactor(10f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 11, 10, 0.5f, null))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setQFactor(10f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 11, 5, 1, null))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertEquals(10f, info.getQFactor(), TEST_TOLERANCE);
assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE);
}
@Test
public void getFrequencyProfile_noVibrator_returnsEmpty() {
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertTrue(info.getFrequencyProfile().isEmpty());
}
@Test
public void getFrequencyProfile_differentResonantFrequencyOrResolutionValues_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, differentResonantFrequency});
assertTrue(info.getFrequencyProfile().isEmpty());
VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, differentFrequencyResolution});
assertTrue(info.getFrequencyProfile().isEmpty());
}
@Test
public void getFrequencyProfile_missingValues_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingResonantFrequency});
assertTrue(info.getFrequencyProfile().isEmpty());
VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingMinFrequency});
assertTrue(info.getFrequencyProfile().isEmpty());
VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingFrequencyResolution});
assertTrue(info.getFrequencyProfile().isEmpty());
VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingMaxAmplitudes});
assertTrue(info.getFrequencyProfile().isEmpty());
}
@Test
public void getFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, unalignedMinFrequency, thirdVibrator});
assertTrue(info.getFrequencyProfile().isEmpty());
}
@Test
public void getFrequencyProfile_alignedProfiles_returnsIntersection() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
new float[] { 0.5f, 1, 1, 0.5f }))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 1, 1, 1 }))
.build();
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 0.8f, 1, 0.8f, 0.5f }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator});
assertEquals(
new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
info.getFrequencyProfile());
}
@Test
public void vibrate_withVibrationAttributes_usesGivenAttributes() {
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);

View File

@@ -26,8 +26,8 @@ import android.util.Range;
import java.util.List;
/**
* Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRangeHz()} and
* amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
* Adapter that clips frequency values to the ones specified by the
* {@link VibratorInfo.FrequencyProfile}.
*
* <p>Devices with no frequency control will collapse all frequencies to the resonant frequency and
* leave amplitudes unchanged.
@@ -69,20 +69,20 @@ final class ClippingAmplitudeAndFrequencyAdapter
}
private float clampFrequency(VibratorInfo info, float frequencyHz) {
Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
Range<Float> frequencyRangeHz = info.getFrequencyProfile().getFrequencyRangeHz();
if (frequencyHz == 0 || frequencyRangeHz == null) {
return info.getResonantFrequency();
return info.getResonantFrequencyHz();
}
return frequencyRangeHz.clamp(frequencyHz);
}
private float clampAmplitude(VibratorInfo info, float frequencyHz, float amplitude) {
Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
if (frequencyRangeHz == null) {
// No frequency range was specified, leave amplitude unchanged, the frequency will be
// clamped to the device's resonant frequency.
VibratorInfo.FrequencyProfile mapping = info.getFrequencyProfile();
if (mapping.isEmpty()) {
// No frequency mapping was specified so leave amplitude unchanged.
// The frequency will be clamped to the device's resonant frequency.
return amplitude;
}
return MathUtils.min(amplitude, info.getMaxAmplitude(frequencyHz));
return MathUtils.min(amplitude, mapping.getMaxAmplitude(frequencyHz));
}
}

View File

@@ -94,6 +94,6 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter
}
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
}
}

View File

@@ -148,6 +148,6 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
}
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
}
}

View File

@@ -39,8 +39,8 @@ namespace android {
static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnComplete;
static jclass sFrequencyMappingClass;
static jmethodID sFrequencyMappingCtor;
static jclass sFrequencyProfileClass;
static jmethodID sFrequencyProfileCtor;
static struct {
jmethodID setCapabilities;
jmethodID setSupportedEffects;
@@ -51,7 +51,7 @@ static struct {
jmethodID setPrimitiveDelayMax;
jmethodID setCompositionSizeMax;
jmethodID setQFactor;
jmethodID setFrequencyMapping;
jmethodID setFrequencyProfile;
} sVibratorInfoBuilderClassInfo;
static struct {
jfieldID id;
@@ -437,11 +437,11 @@ static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
reinterpret_cast<jfloat*>(amplitudes.data()));
}
jobject frequencyMapping =
env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, resonantFrequency,
jobject frequencyProfile =
env->NewObject(sFrequencyProfileClass, sFrequencyProfileCtor, resonantFrequency,
minFrequency, frequencyResolution, maxAmplitudes);
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
frequencyMapping);
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyProfile,
frequencyProfile);
return info.isFailedLogged("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
}
@@ -485,9 +485,9 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
sRampClassInfo.endFrequencyHz = GetFieldIDOrDie(env, rampClass, "mEndFrequencyHz", "F");
sRampClassInfo.duration = GetFieldIDOrDie(env, rampClass, "mDuration", "I");
jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping");
sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFF[F)V");
jclass frequencyProfileClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyProfile");
sFrequencyProfileClass = static_cast<jclass>(env->NewGlobalRef(frequencyProfileClass));
sFrequencyProfileCtor = GetMethodIDOrDie(env, sFrequencyProfileClass, "<init>", "(FFF[F)V");
jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
sVibratorInfoBuilderClassInfo.setCapabilities =
@@ -517,9 +517,9 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
sVibratorInfoBuilderClassInfo.setQFactor =
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setQFactor",
"(F)Landroid/os/VibratorInfo$Builder;");
sVibratorInfoBuilderClassInfo.setFrequencyMapping =
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyMapping",
"(Landroid/os/VibratorInfo$FrequencyMapping;)"
sVibratorInfoBuilderClassInfo.setFrequencyProfile =
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyProfile",
"(Landroid/os/VibratorInfo$FrequencyProfile;)"
"Landroid/os/VibratorInfo$Builder;");
return jniRegisterNativeMethods(env,

View File

@@ -53,10 +53,10 @@ public class DeviceVibrationEffectAdapterTest {
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE =
new VibratorInfo.FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
private DeviceVibrationEffectAdapter mAdapter;
@@ -79,8 +79,8 @@ public class DeviceVibrationEffectAdapterTest {
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.5f, 100)),
/* repeatIndex= */ -1);
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_MAPPING)));
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_MAPPING)));
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_PROFILE)));
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_PROFILE)));
}
@Test
@@ -97,7 +97,7 @@ public class DeviceVibrationEffectAdapterTest {
/* repeatIndex= */ 3);
VibrationEffect.Composed adaptedEffect = (VibrationEffect.Composed) mAdapter.apply(effect,
createVibratorInfo(EMPTY_FREQUENCY_MAPPING));
createVibratorInfo(EMPTY_FREQUENCY_PROFILE));
assertTrue(adaptedEffect.getSegments().size() > effect.getSegments().size());
assertTrue(adaptedEffect.getRepeatIndex() >= effect.getRepeatIndex());
@@ -128,7 +128,7 @@ public class DeviceVibrationEffectAdapterTest {
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
/* repeatIndex= */ 2);
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_PROFILE,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(expected, mAdapter.apply(effect, info));
}
@@ -159,7 +159,7 @@ public class DeviceVibrationEffectAdapterTest {
/* duration= */ 20)),
/* repeatIndex= */ 2);
VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_MAPPING,
VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_PROFILE,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(expected, mAdapter.apply(effect, info));
}
@@ -188,17 +188,17 @@ public class DeviceVibrationEffectAdapterTest {
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
/* repeatIndex= */ 2);
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_PROFILE,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(expected, mAdapter.apply(effect, info));
}
private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,
private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyProfile frequencyProfile,
int... capabilities) {
int cap = IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0);
return new VibratorInfo.Builder(0)
.setCapabilities(cap)
.setFrequencyMapping(frequencyMapping)
.setFrequencyProfile(frequencyProfile)
.build();
}
}

View File

@@ -170,7 +170,7 @@ final class FakeVibratorControllerProvider {
}
infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
infoBuilder.setQFactor(mQFactor);
infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
infoBuilder.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes));
return mIsInfoLoadSuccessful;
}

View File

@@ -47,8 +47,8 @@ public class RampToStepAdapterTest {
private static final int TEST_STEP_DURATION = 5;
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
@@ -124,7 +124,7 @@ public class RampToStepAdapterTest {
private static VibratorInfo createVibratorInfo(int... capabilities) {
return new VibratorInfo.Builder(0)
.setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
}
}

View File

@@ -46,8 +46,8 @@ import java.util.stream.IntStream;
public class StepToRampAdapterTest {
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
@@ -99,7 +99,7 @@ public class StepToRampAdapterTest {
VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
.setPwlePrimitiveDurationMax(10)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
// Update repeat index to skip the ramp splits.
@@ -191,7 +191,7 @@ public class StepToRampAdapterTest {
private static VibratorInfo createVibratorInfo(int... capabilities) {
return new VibratorInfo.Builder(0)
.setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
}
}

View File

@@ -310,13 +310,13 @@ public class VibratorControllerTest {
}
private void mockVibratorCapabilities(int capabilities) {
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
VibratorInfo.FrequencyProfile frequencyProfile = new VibratorInfo.FrequencyProfile(
Float.NaN, Float.NaN, Float.NaN, null);
when(mNativeWrapperMock.getInfo(any(VibratorInfo.Builder.class)))
.then(invocation -> {
((VibratorInfo.Builder) invocation.getArgument(0))
.setCapabilities(capabilities)
.setFrequencyMapping(frequencyMapping);
.setFrequencyProfile(frequencyProfile);
return true;
});
}

View File

@@ -316,7 +316,7 @@ public class VibratorManagerServiceTest {
assertNotNull(info);
assertEquals(1, info.getId());
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
}
@Test
@@ -341,7 +341,7 @@ public class VibratorManagerServiceTest {
info.isEffectSupported(VibrationEffect.EFFECT_TICK));
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
assertTrue(Float.isNaN(info.getQFactor()));
}
@@ -360,7 +360,7 @@ public class VibratorManagerServiceTest {
VibratorInfo info = createService().getVibratorInfo(1);
assertNotNull(info);
assertEquals(1, info.getId());
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
}
@Test