Use absolute frequency in WaveformBuilder

Remove the concept of relative frequency and the mapping logic from
VibratorInfo and receive absolute frequency values, in hertz, from
WaveformBuilder.

The clipping logic to make all PWLE amplitudes fit the HAL bandwidth map
still remains.

Bug: 203785430
Test: com.android.server.vibrator.*
Change-Id: Ic01a08d84e66d88acad6db5c704251394afbf1f6
This commit is contained in:
Lais Andrade
2021-12-23 18:36:26 +00:00
parent 4b10475f9a
commit 0eebf348e4
26 changed files with 572 additions and 629 deletions

View File

@@ -1822,9 +1822,9 @@ package android.os {
public static final class VibrationEffect.WaveformBuilder {
method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=-1.0F, to=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=-1.0F, to=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect build();
method @NonNull public android.os.VibrationEffect build(int);
}
@@ -1982,9 +1982,9 @@ package android.os.vibrator {
method public int describeContents();
method public long getDuration();
method public float getEndAmplitude();
method public float getEndFrequency();
method public float getEndFrequencyHz();
method public float getStartAmplitude();
method public float getStartFrequency();
method public float getStartFrequencyHz();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.RampSegment> CREATOR;
}
@@ -1993,7 +1993,7 @@ package android.os.vibrator {
method public int describeContents();
method public float getAmplitude();
method public long getDuration();
method public float getFrequency();
method public float getFrequencyHz();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.StepSegment> CREATOR;
}

View File

@@ -260,7 +260,7 @@ public abstract class VibrationEffect implements Parcelable {
for (int i = 0; i < timings.length; i++) {
float parsedAmplitude = amplitudes[i] == DEFAULT_AMPLITUDE
? DEFAULT_AMPLITUDE : (float) amplitudes[i] / MAX_AMPLITUDE;
segments.add(new StepSegment(parsedAmplitude, /* frequency= */ 0, (int) timings[i]));
segments.add(new StepSegment(parsedAmplitude, /* frequencyHz= */ 0, (int) timings[i]));
}
VibrationEffect effect = new Composed(segments, repeat);
effect.validate();
@@ -866,7 +866,7 @@ public abstract class VibrationEffect implements Parcelable {
Preconditions.checkArgumentNonnegative(delay);
if (delay > 0) {
// Created a segment sustaining the zero amplitude to represent the delay.
addSegment(new StepSegment(/* amplitude= */ 0, /* frequency= */ 0,
addSegment(new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0,
/* duration= */ delay));
}
return addSegments(effect);
@@ -1033,26 +1033,27 @@ public abstract class VibrationEffect implements Parcelable {
@NonNull
public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude,
@IntRange(from = 0) int duration) {
return addStep(amplitude, getPreviousFrequency(), duration);
mSegments.add(new StepSegment(amplitude, getPreviousFrequencyHz(), duration));
return this;
}
/**
* Vibrate with given amplitude for the given duration, in millis, keeping the previous
* vibration frequency the same.
* Vibrate with given amplitude and frequency for the given duration, in millis.
*
* <p>If the duration is zero the vibrator will jump to new amplitude.
*
* @param amplitude The amplitude for this step
* @param frequency The frequency for this step
* @param frequencyHz The frequency for this step, in hertz
* @param duration The duration of this step in milliseconds
* @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude,
@FloatRange(from = -1f, to = 1f) float frequency,
@FloatRange(from = 1f) float frequencyHz,
@IntRange(from = 0) int duration) {
mSegments.add(new StepSegment(amplitude, frequency, duration));
Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1");
mSegments.add(new StepSegment(amplitude, frequencyHz, duration));
return this;
}
@@ -1070,7 +1071,9 @@ public abstract class VibrationEffect implements Parcelable {
@NonNull
public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude,
@IntRange(from = 0) int duration) {
return addRamp(amplitude, getPreviousFrequency(), duration);
mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude,
getPreviousFrequencyHz(), getPreviousFrequencyHz(), duration));
return this;
}
/**
@@ -1080,22 +1083,23 @@ public abstract class VibrationEffect implements Parcelable {
* <p>If the duration is zero the vibrator will jump to new amplitude and frequency.
*
* @param amplitude The final amplitude this ramp should reach
* @param frequency The final frequency this ramp should reach
* @param frequencyHz The final frequency this ramp should reach, in hertz
* @param duration The duration of this ramp in milliseconds
* @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude,
@FloatRange(from = -1f, to = 1f) float frequency,
@FloatRange(from = 1f) float frequencyHz,
@IntRange(from = 0) int duration) {
mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude, getPreviousFrequency(),
frequency, duration));
Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1");
mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude,
getPreviousFrequencyHz(), frequencyHz, duration));
return this;
}
/**
* Compose all of the steps together into a single {@link VibrationEffect}.
* Compose all the steps together into a single {@link VibrationEffect}.
*
* The {@link WaveformBuilder} object is still valid after this call, so you can
* continue adding more primitives to it and generating more {@link VibrationEffect}s by
@@ -1109,7 +1113,7 @@ public abstract class VibrationEffect implements Parcelable {
}
/**
* Compose all of the steps together into a single {@link VibrationEffect}.
* Compose all the steps together into a single {@link VibrationEffect}.
*
* <p>To cause the pattern to repeat, pass the index at which to start the repetition
* (starting at 0), or -1 to disable repeating.
@@ -1131,13 +1135,13 @@ public abstract class VibrationEffect implements Parcelable {
return effect;
}
private float getPreviousFrequency() {
private float getPreviousFrequencyHz() {
if (!mSegments.isEmpty()) {
VibrationEffectSegment segment = mSegments.get(mSegments.size() - 1);
if (segment instanceof StepSegment) {
return ((StepSegment) segment).getFrequency();
return ((StepSegment) segment).getFrequencyHz();
} else if (segment instanceof RampSegment) {
return ((RampSegment) segment).getEndFrequency();
return ((RampSegment) segment).getEndFrequencyHz();
}
}
return 0;

View File

@@ -17,7 +17,6 @@
package android.os;
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,7 +29,6 @@ import android.content.Context;
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.util.Log;
import android.util.Range;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -270,43 +268,6 @@ public abstract class Vibrator {
return getInfo().getQFactor();
}
/**
* Return a range of relative frequency values supported by the vibrator.
*
* <p>These values can be used to create waveforms that controls the vibration frequency via
* {@link VibrationEffect.WaveformBuilder}.
*
* @return A range of relative frequency values supported. The range will always contain the
* value 0, representing the device resonant frequency. Devices without frequency control will
* return the range [0,0]. Devices with frequency control will always return a range containing
* the safe range [-1, 1].
* @hide
*/
public Range<Float> getRelativeFrequencyRange() {
return getInfo().getFrequencyRange();
}
/**
* Return the maximum amplitude the vibrator can play at given relative frequency.
*
* <p>Devices without frequency control will return 1 for the input zero (resonant frequency),
* and 0 to any other input.
*
* <p>Devices with frequency control will return the supported value, for input in
* {@link #getRelativeFrequencyRange()}, and 0 for any other input.
*
* <p>These values can be used to create waveforms that plays vibrations outside the resonant
* frequency via {@link VibrationEffect.WaveformBuilder}.
*
* @return a value in [0,1] representing the maximum amplitude the device can play at given
* relative frequency.
* @hide
*/
@FloatRange(from = 0, to = 1)
public float getMaximumAmplitude(float relativeFrequency) {
return getInfo().getMaxAmplitude(relativeFrequency);
}
/**
* Return the maximum amplitude the vibrator can play using the audio haptic channels.
*

View File

@@ -21,7 +21,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.util.Log;
import android.util.MathUtils;
import android.util.Range;
import android.util.SparseBooleanArray;
@@ -345,49 +344,31 @@ public class VibratorInfo implements Parcelable {
}
/**
* Return a range of relative frequency values supported by the vibrator.
* Return a range of frequency values supported by the vibrator.
*
* @return A range of relative frequency values supported. The range will always contain the
* value 0, representing the device resonant frequency. Devices without frequency control will
* return the range [0,0]. Devices with frequency control will always return a range containing
* the safe range [-1, 1].
* @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
*/
public Range<Float> getFrequencyRange() {
return mFrequencyMapping.mRelativeFrequencyRange;
@Nullable
public Range<Float> getFrequencyRangeHz() {
return mFrequencyMapping.mFrequencyRangeHz;
}
/**
* Return the maximum amplitude the vibrator can play at given relative frequency.
* 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
* relative frequency. Devices without frequency control will return 1 for the input zero
* (resonant frequency), and 0 to any other input. Devices with frequency control will return
* the supported value, for input in {@code #getFrequencyRange()}, and 0 for any other input.
* 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 relativeFrequency) {
if (mFrequencyMapping.isEmpty()) {
// The vibrator has not provided values for frequency mapping.
// Return the expected behavior for devices without frequency control.
return Float.compare(relativeFrequency, 0) == 0 ? 1 : 0;
}
return mFrequencyMapping.getMaxAmplitude(relativeFrequency);
}
/**
* Return absolute frequency value for this vibrator, in hertz, that corresponds to given
* relative frequency.
*
* @retur a value in hertz that corresponds to given relative frequency. Input values outside
* {@link #getFrequencyRange()} will return {@link Float#NaN}. Devices without frequency control
* will return {@link Float#NaN} for any input.
* @hide
*/
@FloatRange(from = 0)
public float getAbsoluteFrequency(float relativeFrequency) {
return mFrequencyMapping.toHertz(relativeFrequency);
public float getMaxAmplitude(float frequencyHz) {
return mFrequencyMapping.getMaxAmplitude(frequencyHz);
}
protected long getCapabilities() {
@@ -468,134 +449,96 @@ public class VibratorInfo implements Parcelable {
}
/**
* Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
* 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:
*
* <ol>
* <li>{@code minFrequency}, {@code resonantFrequency} and {@code frequencyResolution}, in
* hertz, provided by the vibrator.
* <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz}
* provided by the vibrator in hertz.
* <li>{@code maxAmplitudes} a list of values in [0,1] provided by the vibrator, where
* {@code maxAmplitudes[i]} represents max supported amplitude at frequency
* {@code minFrequency + frequencyResolution * i}.
* <li>{@code maxFrequency = minFrequency + frequencyResolution * (maxAmplitudes.length-1)}
* <li>{@code suggestedSafeRangeHz} is the suggested frequency range in hertz that should be
* mapped to relative values -1 and 1, where 0 maps to {@code resonantFrequency}.
* </ol>
*
* <p>The mapping is defined linearly by the following points:
*
* <ol>
* <li>{@code toHertz(relativeMinFrequency) = minFrequency}
* <li>{@code toHertz(-1) = resonantFrequency - safeRange / 2}
* <li>{@code toHertz(0) = resonantFrequency}
* <li>{@code toHertz(1) = resonantFrequency + safeRange / 2}
* <li>{@code toHertz(relativeMaxFrequency) = maxFrequency}
* {@code minFrequencyHz + frequencyResolutionHz * i}.
* <li>{@code maxFrequencyHz = minFrequencyHz
* + frequencyResolutionHz * (maxAmplitudes.length-1)}
* </ol>
*
* @hide
*/
public static final class FrequencyMapping implements Parcelable {
@Nullable
private final Range<Float> mFrequencyRangeHz;
private final float mMinFrequencyHz;
private final float mResonantFrequencyHz;
private final float mFrequencyResolutionHz;
private final float mSuggestedSafeRangeHz;
private final float[] mMaxAmplitudes;
// Relative fields calculated from input values:
private final Range<Float> mRelativeFrequencyRange;
FrequencyMapping(Parcel in) {
this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(),
in.createFloatArray());
this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray());
}
/**
* Default constructor.
*
* @param minFrequencyHz Minimum supported frequency, in hertz.
* @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.
* @param suggestedSafeRangeHz The suggested range, in hertz, for the safe relative
* frequency range represented by [-1, 1].
* @param maxAmplitudes The max amplitude supported by each supported frequency,
* starting at minimum frequency with jumps of frequency
* resolution.
* @hide
*/
public FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz,
float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes) {
public FrequencyMapping(float resonantFrequencyHz, float minFrequencyHz,
float frequencyResolutionHz, float[] maxAmplitudes) {
mMinFrequencyHz = minFrequencyHz;
mResonantFrequencyHz = resonantFrequencyHz;
mFrequencyResolutionHz = frequencyResolutionHz;
mSuggestedSafeRangeHz = suggestedSafeRangeHz;
mMaxAmplitudes = new float[maxAmplitudes == null ? 0 : maxAmplitudes.length];
if (maxAmplitudes != null) {
System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
}
float maxFrequencyHz =
minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1);
if (Float.isNaN(resonantFrequencyHz) || Float.isNaN(minFrequencyHz)
|| Float.isNaN(frequencyResolutionHz) || Float.isNaN(suggestedSafeRangeHz)
|| resonantFrequencyHz < minFrequencyHz
|| resonantFrequencyHz > maxFrequencyHz) {
// Some required fields are undefined or have bad values.
// Leave this mapping empty.
mRelativeFrequencyRange = Range.create(0f, 0f);
return;
}
// If any required field is undefined then leave this mapping empty.
boolean isValid = !Float.isNaN(resonantFrequencyHz)
&& !Float.isNaN(minFrequencyHz)
&& !Float.isNaN(frequencyResolutionHz)
&& (mMaxAmplitudes.length > 0);
// Calculate actual safe range, limiting the suggested one by the device supported range
float safeDelta = MathUtils.min(
suggestedSafeRangeHz / 2,
resonantFrequencyHz - minFrequencyHz,
maxFrequencyHz - resonantFrequencyHz);
mRelativeFrequencyRange = Range.create(
(minFrequencyHz - resonantFrequencyHz) / safeDelta,
(maxFrequencyHz - resonantFrequencyHz) / safeDelta);
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.
isValid &= !Float.isNaN(maxFrequencyHz)
&& (resonantFrequencyHz >= minFrequencyHz)
&& (resonantFrequencyHz <= maxFrequencyHz)
&& (minFrequencyHz < maxFrequencyHz);
mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null;
}
/**
* Returns true if this frequency mapping is empty, i.e. the only supported relative
* frequency is 0 (resonant frequency).
* Returns true if this frequency mapping is empty, i.e. the only supported is the resonant
* frequency.
*/
public boolean isEmpty() {
return Float.compare(mRelativeFrequencyRange.getLower(),
mRelativeFrequencyRange.getUpper()) == 0;
return mFrequencyRangeHz == null;
}
/**
* Returns the frequency value in hertz that is mapped to the given relative frequency.
* Returns the maximum relative amplitude the vibrator can reach while playing at the
* given frequency.
*
* @return The mapped frequency, in hertz, or {@link Float#NaN} is value outside the device
* supported range.
* @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.
*/
public float toHertz(float relativeFrequency) {
if (!mRelativeFrequencyRange.contains(relativeFrequency)) {
return Float.NaN;
}
float relativeMinFrequency = mRelativeFrequencyRange.getLower();
if (Float.compare(relativeMinFrequency, 0) == 0) {
// relative supported range is [0,0], so toHertz(0) should be the resonant frequency
return mResonantFrequencyHz;
}
float shift = (mMinFrequencyHz - mResonantFrequencyHz) / relativeMinFrequency;
return mResonantFrequencyHz + relativeFrequency * shift;
}
/**
* Returns the maximum amplitude the vibrator can reach while playing at given relative
* frequency.
*
* @return A value in [0,1] representing the max amplitude supported at given relative
* frequency. This will return 0 if frequency is outside supported range, or if max
* amplitude mapping is empty.
*/
public float getMaxAmplitude(float relativeFrequency) {
float frequencyHz = toHertz(relativeFrequency);
if (Float.isNaN(frequencyHz)) {
public float getMaxAmplitude(float frequencyHz) {
if (isEmpty() || Float.isNaN(frequencyHz)) {
// Unsupported frequency requested, vibrator cannot play at this frequency.
return 0;
}
@@ -603,13 +546,6 @@ public class VibratorInfo implements Parcelable {
int floorIndex = (int) Math.floor(position);
int ceilIndex = (int) Math.ceil(position);
if (floorIndex < 0 || floorIndex >= mMaxAmplitudes.length) {
if (mMaxAmplitudes.length > 0) {
// This should never happen if the setup of relative frequencies was correct.
Log.w(TAG, "Max amplitudes has " + mMaxAmplitudes.length
+ " entries and was expected to cover the frequency " + frequencyHz
+ " Hz when starting at min frequency of " + mMinFrequencyHz
+ " Hz with resolution of " + mFrequencyResolutionHz + " Hz.");
}
return 0;
}
if (floorIndex != ceilIndex && ceilIndex < mMaxAmplitudes.length) {
@@ -621,10 +557,9 @@ public class VibratorInfo implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloat(mMinFrequencyHz);
dest.writeFloat(mResonantFrequencyHz);
dest.writeFloat(mMinFrequencyHz);
dest.writeFloat(mFrequencyResolutionHz);
dest.writeFloat(mSuggestedSafeRangeHz);
dest.writeFloatArray(mMaxAmplitudes);
}
@@ -645,14 +580,13 @@ public class VibratorInfo implements Parcelable {
return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
&& Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
&& Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
&& Float.compare(mSuggestedSafeRangeHz, that.mSuggestedSafeRangeHz) == 0
&& Arrays.equals(mMaxAmplitudes, that.mMaxAmplitudes);
}
@Override
public int hashCode() {
int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
mFrequencyResolutionHz, mSuggestedSafeRangeHz);
mFrequencyResolutionHz);
hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
return hashCode;
}
@@ -660,13 +594,10 @@ public class VibratorInfo implements Parcelable {
@Override
public String toString() {
return "FrequencyMapping{"
+ "mRelativeFrequencyRange=" + mRelativeFrequencyRange
+ "mFrequencyRange=" + mFrequencyRangeHz
+ ", mMinFrequency=" + mMinFrequencyHz
+ ", mResonantFrequency=" + mResonantFrequencyHz
+ ", mMaxFrequency="
+ (mMinFrequencyHz + mFrequencyResolutionHz * (mMaxAmplitudes.length - 1))
+ ", mFrequencyResolution=" + mFrequencyResolutionHz
+ ", mSuggestedSafeRange=" + mSuggestedSafeRangeHz
+ ", mMaxAmplitudes count=" + mMaxAmplitudes.length
+ '}';
}
@@ -699,7 +630,7 @@ public class VibratorInfo implements Parcelable {
private int mPwleSizeMax;
private float mQFactor = Float.NaN;
private FrequencyMapping mFrequencyMapping =
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
/** A builder class for a {@link VibratorInfo}. */
public Builder(int id) {

View File

@@ -29,14 +29,20 @@ import java.util.Objects;
* Representation of {@link VibrationEffectSegment} that ramps vibration amplitude and/or frequency
* for a specified duration.
*
* <p>The amplitudes are expressed by float values in the range [0, 1], representing the relative
* output acceleration for the vibrator. The frequencies are expressed in hertz by positive finite
* float values. The special value zero is used here for an unspecified frequency, and will be
* automatically mapped to the device's default vibration frequency (usually the resonant
* frequency).
*
* @hide
*/
@TestApi
public final class RampSegment extends VibrationEffectSegment {
private final float mStartAmplitude;
private final float mStartFrequency;
private final float mStartFrequencyHz;
private final float mEndAmplitude;
private final float mEndFrequency;
private final float mEndFrequencyHz;
private final int mDuration;
RampSegment(@NonNull Parcel in) {
@@ -44,12 +50,12 @@ public final class RampSegment extends VibrationEffectSegment {
}
/** @hide */
public RampSegment(float startAmplitude, float endAmplitude, float startFrequency,
float endFrequency, int duration) {
public RampSegment(float startAmplitude, float endAmplitude, float startFrequencyHz,
float endFrequencyHz, int duration) {
mStartAmplitude = startAmplitude;
mEndAmplitude = endAmplitude;
mStartFrequency = startFrequency;
mEndFrequency = endFrequency;
mStartFrequencyHz = startFrequencyHz;
mEndFrequencyHz = endFrequencyHz;
mDuration = duration;
}
@@ -61,8 +67,8 @@ public final class RampSegment extends VibrationEffectSegment {
RampSegment other = (RampSegment) o;
return Float.compare(mStartAmplitude, other.mStartAmplitude) == 0
&& Float.compare(mEndAmplitude, other.mEndAmplitude) == 0
&& Float.compare(mStartFrequency, other.mStartFrequency) == 0
&& Float.compare(mEndFrequency, other.mEndFrequency) == 0
&& Float.compare(mStartFrequencyHz, other.mStartFrequencyHz) == 0
&& Float.compare(mEndFrequencyHz, other.mEndFrequencyHz) == 0
&& mDuration == other.mDuration;
}
@@ -74,12 +80,12 @@ public final class RampSegment extends VibrationEffectSegment {
return mEndAmplitude;
}
public float getStartFrequency() {
return mStartFrequency;
public float getStartFrequencyHz() {
return mStartFrequencyHz;
}
public float getEndFrequency() {
return mEndFrequency;
public float getEndFrequencyHz() {
return mEndFrequencyHz;
}
@Override
@@ -102,6 +108,12 @@ public final class RampSegment extends VibrationEffectSegment {
/** @hide */
@Override
public void validate() {
Preconditions.checkArgumentNonNegative(mStartFrequencyHz,
"Frequencies must all be >= 0, got start frequency of " + mStartFrequencyHz);
Preconditions.checkArgumentFinite(mStartFrequencyHz, "startFrequencyHz");
Preconditions.checkArgumentNonNegative(mEndFrequencyHz,
"Frequencies must all be >= 0, got end frequency of " + mEndFrequencyHz);
Preconditions.checkArgumentFinite(mEndFrequencyHz, "endFrequencyHz");
Preconditions.checkArgumentNonnegative(mDuration,
"Durations must all be >= 0, got " + mDuration);
Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude");
@@ -126,7 +138,8 @@ public final class RampSegment extends VibrationEffectSegment {
&& Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
return this;
}
return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequency, mEndFrequency,
return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
mEndFrequencyHz,
mDuration);
}
@@ -139,7 +152,7 @@ public final class RampSegment extends VibrationEffectSegment {
@Override
public int hashCode() {
return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequency, mEndFrequency,
return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequencyHz, mEndFrequencyHz,
mDuration);
}
@@ -147,8 +160,8 @@ public final class RampSegment extends VibrationEffectSegment {
public String toString() {
return "Ramp{startAmplitude=" + mStartAmplitude
+ ", endAmplitude=" + mEndAmplitude
+ ", startFrequency=" + mStartFrequency
+ ", endFrequency=" + mEndFrequency
+ ", startFrequencyHz=" + mStartFrequencyHz
+ ", endFrequencyHz=" + mEndFrequencyHz
+ ", duration=" + mDuration
+ "}";
}
@@ -163,8 +176,8 @@ public final class RampSegment extends VibrationEffectSegment {
out.writeInt(PARCEL_TOKEN_RAMP);
out.writeFloat(mStartAmplitude);
out.writeFloat(mEndAmplitude);
out.writeFloat(mStartFrequency);
out.writeFloat(mEndFrequency);
out.writeFloat(mStartFrequencyHz);
out.writeFloat(mEndFrequencyHz);
out.writeInt(mDuration);
}

View File

@@ -30,12 +30,18 @@ import java.util.Objects;
* Representation of {@link VibrationEffectSegment} that holds a fixed vibration amplitude and
* frequency for a specified duration.
*
* <p>The amplitude is expressed by a float value in the range [0, 1], representing the relative
* output acceleration for the vibrator. The frequency is expressed in hertz by a positive finite
* float value. The special value zero is used here for an unspecified frequency, and will be
* automatically mapped to the device's default vibration frequency (usually the resonant
* frequency).
*
* @hide
*/
@TestApi
public final class StepSegment extends VibrationEffectSegment {
private final float mAmplitude;
private final float mFrequency;
private final float mFrequencyHz;
private final int mDuration;
StepSegment(@NonNull Parcel in) {
@@ -43,9 +49,9 @@ public final class StepSegment extends VibrationEffectSegment {
}
/** @hide */
public StepSegment(float amplitude, float frequency, int duration) {
public StepSegment(float amplitude, float frequencyHz, int duration) {
mAmplitude = amplitude;
mFrequency = frequency;
mFrequencyHz = frequencyHz;
mDuration = duration;
}
@@ -56,7 +62,7 @@ public final class StepSegment extends VibrationEffectSegment {
}
StepSegment other = (StepSegment) o;
return Float.compare(mAmplitude, other.mAmplitude) == 0
&& Float.compare(mFrequency, other.mFrequency) == 0
&& Float.compare(mFrequencyHz, other.mFrequencyHz) == 0
&& mDuration == other.mDuration;
}
@@ -64,8 +70,8 @@ public final class StepSegment extends VibrationEffectSegment {
return mAmplitude;
}
public float getFrequency() {
return mFrequency;
public float getFrequencyHz() {
return mFrequencyHz;
}
@Override
@@ -89,6 +95,9 @@ public final class StepSegment extends VibrationEffectSegment {
/** @hide */
@Override
public void validate() {
Preconditions.checkArgumentNonNegative(mFrequencyHz,
"Frequencies must all be >= 0, got " + mFrequencyHz);
Preconditions.checkArgumentFinite(mFrequencyHz, "frequencyHz");
Preconditions.checkArgumentNonnegative(mDuration,
"Durations must all be >= 0, got " + mDuration);
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
@@ -108,7 +117,8 @@ public final class StepSegment extends VibrationEffectSegment {
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
return this;
}
return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, mFrequency,
return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE,
mFrequencyHz,
mDuration);
}
@@ -119,7 +129,7 @@ public final class StepSegment extends VibrationEffectSegment {
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
return this;
}
return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequency,
return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz,
mDuration);
}
@@ -132,13 +142,13 @@ public final class StepSegment extends VibrationEffectSegment {
@Override
public int hashCode() {
return Objects.hash(mAmplitude, mFrequency, mDuration);
return Objects.hash(mAmplitude, mFrequencyHz, mDuration);
}
@Override
public String toString() {
return "Step{amplitude=" + mAmplitude
+ ", frequency=" + mFrequency
+ ", frequencyHz=" + mFrequencyHz
+ ", duration=" + mDuration
+ "}";
}
@@ -152,7 +162,7 @@ public final class StepSegment extends VibrationEffectSegment {
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_STEP);
out.writeFloat(mAmplitude);
out.writeFloat(mFrequency);
out.writeFloat(mFrequencyHz);
out.writeInt(mDuration);
}

View File

@@ -125,8 +125,8 @@ public class VibrationEffectTest {
VibrationEffect.startWaveform()
.addStep(/* amplitude= */ 1, /* duration= */ 10)
.addRamp(/* amplitude= */ 0, /* duration= */ 20)
.addStep(/* amplitude= */ 1, /* frequency*/ 1, /* duration= */ 100)
.addRamp(/* amplitude= */ 0.5f, /* frequency*/ -1, /* duration= */ 50)
.addStep(/* amplitude= */ 1, /* frequencyHz= */ 1, /* duration= */ 100)
.addRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 50)
.build()
.validate();
@@ -148,12 +148,24 @@ public class VibrationEffectTest {
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startWaveform()
.addStep(/* amplitude= */ -2, 10).build().validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startWaveform()
.addStep(1, /* frequencyHz= */ -1f, 10).build().validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startWaveform()
.addStep(1, /* duration= */ -1).build().validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startWaveform()
.addStep(1, 0, /* duration= */ -1).build().validate());
.addStep(1, 100f, /* duration= */ -1).build().validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startWaveform()
.addRamp(/* amplitude= */ -3, 10).build().validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startWaveform()
.addRamp(1, /* frequencyHz= */ 0, 10).build().validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startWaveform()
.addRamp(1, 10f, /* duration= */ -3).build().validate());
}
@Test

View File

@@ -19,6 +19,7 @@ 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;
@@ -43,19 +44,17 @@ public class VibratorInfoTest {
/* 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, Float.NaN, null);
new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY,
TEST_RESONANT_FREQUENCY, TEST_FREQUENCY_RESOLUTION,
/* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP);
new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
@Test
public void testHasAmplitudeControl() {
VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
assertFalse(noCapabilities.hasAmplitudeControl());
VibratorInfo composeAndAmplitudeControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS
| IVibrator.CAP_AMPLITUDE_CONTROL)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
.build();
assertTrue(composeAndAmplitudeControl.hasAmplitudeControl());
}
@@ -143,138 +142,95 @@ public class VibratorInfoTest {
}
@Test
public void testGetFrequencyRange_invalidFrequencyMappingReturnsEmptyRange() {
public void testGetFrequencyRangeHz_invalidFrequencyMappingReturnsNull() {
// Invalid, contains NaN values or empty array.
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyRangeHz());
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
Float.NaN, 150, 25, 50, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
Float.NaN, 50, 25, TEST_AMPLITUDE_MAP))
.build().getFrequencyRangeHz());
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, Float.NaN, 25, 50, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
150, Float.NaN, 25, TEST_AMPLITUDE_MAP))
.build().getFrequencyRangeHz());
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, 150, Float.NaN, 50, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, 150, 25, Float.NaN, TEST_AMPLITUDE_MAP))
.build().getFrequencyRange());
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(50, 150, 25, 50, null))
.build().getFrequencyRange());
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());
// Invalid, minFrequency > resonantFrequency
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 250, /* resonantFrequency= */ 150, 25, 50, null))
.build().getFrequencyRange());
/* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, null))
.build().getFrequencyRangeHz());
// Invalid, maxFrequency < resonantFrequency by changing resolution.
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
50, 150, /* frequencyResolutionHz= */10, 50, null))
.build().getFrequencyRange());
150, 50, /* frequencyResolutionHz= */ 10, null))
.build().getFrequencyRangeHz());
}
@Test
public void testGetFrequencyRange_safeRangeLimitedByMaxFrequency() {
public void testGetFrequencyRangeHz_resultRangeDerivedFromHalMapping() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
/* frequencyResolutionHz= */ 25, /* suggestedSafeRangeHz= */ 200,
TEST_AMPLITUDE_MAP))
/* 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();
// Mapping should range from 50Hz = -2 to 200Hz = 1
// Safe range [-1, 1] = [100Hz, 200Hz] defined by max - resonant = 50Hz
assertEquals(Range.create(-2f, 1f), info.getFrequencyRange());
assertEquals(Range.create(50f, 200f), info.getFrequencyRangeHz());
}
@Test
public void testGetFrequencyRange_safeRangeLimitedByMinFrequency() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
/* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 200,
TEST_AMPLITUDE_MAP))
.build();
// Mapping should range from 50Hz = -1 to 350Hz = 2
// Safe range [-1, 1] = [50Hz, 250Hz] defined by resonant - min = 100Hz
assertEquals(Range.create(-1f, 2f), info.getFrequencyRange());
}
@Test
public void testGetFrequencyRange_validMappingReturnsFullRelativeRange() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
/* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 100,
TEST_AMPLITUDE_MAP))
.build();
// Mapping should range from 50Hz = -2 to 350Hz = 4
// Safe range [-1, 1] = [100Hz, 200Hz] defined by suggested safe range 100Hz
assertEquals(Range.create(-2f, 4f), info.getFrequencyRange());
}
@Test
public void testAbsoluteFrequency_emptyMappingReturnsNaN() {
public void testGetMaxAmplitude_emptyMappingReturnsAlwaysZero() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
assertTrue(Float.isNaN(info.getAbsoluteFrequency(-1)));
assertTrue(Float.isNaN(info.getAbsoluteFrequency(0)));
assertTrue(Float.isNaN(info.getAbsoluteFrequency(1)));
}
assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(200f), TEST_TOLERANCE);
@Test
public void testAbsoluteFrequency_validRangeReturnsOriginalValue() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).setFrequencyMapping(
TEST_FREQUENCY_MAPPING).build();
assertEquals(TEST_RESONANT_FREQUENCY, info.getAbsoluteFrequency(0), TEST_TOLERANCE);
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ Float.NaN,
/* frequencyResolutionHz= */ Float.NaN,
null))
.build();
// Safe range [-1, 1] = [125Hz, 175Hz] defined by suggested safe range 100Hz
assertEquals(125, info.getAbsoluteFrequency(-1), TEST_TOLERANCE);
assertEquals(175, info.getAbsoluteFrequency(1), TEST_TOLERANCE);
assertEquals(155, info.getAbsoluteFrequency(0.2f), TEST_TOLERANCE);
assertEquals(140, info.getAbsoluteFrequency(-0.4f), TEST_TOLERANCE);
// Full range [-4, 2] = [50Hz, 200Hz] defined by min frequency and amplitude mapping size
assertEquals(50, info.getAbsoluteFrequency(info.getFrequencyRange().getLower()),
TEST_TOLERANCE);
assertEquals(200, info.getAbsoluteFrequency(info.getFrequencyRange().getUpper()),
TEST_TOLERANCE);
}
@Test
public void testGetMaxAmplitude_emptyMappingReturnsOnlyResonantFrequency() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(0.1f), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(-1), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
assertEquals(0f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
}
@Test
public void testGetMaxAmplitude_validMappingReturnsMappedValues() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(/* minFrequencyHz= */ 50,
/* resonantFrequencyHz= */ 150, /* frequencyResolutionHz= */ 25,
/* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP))
.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();
assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE); // 150Hz
assertEquals(0.9f, info.getMaxAmplitude(1), TEST_TOLERANCE); // 175Hz
assertEquals(0.8f, info.getMaxAmplitude(-1), TEST_TOLERANCE); // 125Hz
assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRange().getUpper()),
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.getFrequencyRange().getLower()),
assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRangeHz().getLower()),
TEST_TOLERANCE); // 50Hz
// Rounds 145Hz to the max amplitude for 125Hz, which is lower.
assertEquals(0.8f, info.getMaxAmplitude(-0.1f), TEST_TOLERANCE); // 145Hz
// Rounds 185Hz to the max amplitude for 200Hz, which is lower.
assertEquals(0.8f, info.getMaxAmplitude(1.2f), TEST_TOLERANCE); // 185Hz
// 145Hz maps to the max amplitude for 125Hz, which is lower.
assertEquals(0.8f, info.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
}
@Test
@@ -317,9 +273,11 @@ public class VibratorInfoTest {
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY + 10,
TEST_RESONANT_FREQUENCY + 20, TEST_FREQUENCY_RESOLUTION + 5,
/* suggestedSafeRangeHz= */ 100, TEST_AMPLITUDE_MAP))
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
TEST_RESONANT_FREQUENCY + 20,
TEST_MIN_FREQUENCY + 10,
TEST_FREQUENCY_RESOLUTION + 5,
TEST_AMPLITUDE_MAP))
.build();
assertNotEquals(complete, completeWithDifferentFrequencyMapping);

View File

@@ -39,19 +39,19 @@ public class RampSegmentTest {
@Test
public void testCreation() {
RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
/* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100);
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 100);
assertEquals(100L, ramp.getDuration());
assertTrue(ramp.hasNonZeroAmplitude());
assertEquals(1f, ramp.getStartAmplitude());
assertEquals(0f, ramp.getEndAmplitude());
assertEquals(-1f, ramp.getStartFrequency());
assertEquals(1f, ramp.getEndFrequency());
assertEquals(100f, ramp.getStartFrequencyHz());
assertEquals(200f, ramp.getEndFrequencyHz());
}
@Test
public void testSerialization() {
RampSegment original = new RampSegment(0, 1, 0, 0.5f, 10);
RampSegment original = new RampSegment(0, 1, 10, 20.5f, 10);
Parcel parcel = Parcel.obtain();
original.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
@@ -61,7 +61,9 @@ public class RampSegmentTest {
@Test
public void testValidate() {
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
/* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100).validate();
/* startFrequencyHz= */ 2, /* endFrequencyHz= */ 1, /* duration= */ 100).validate();
// Zero frequency is still used internally for unset frequency.
new RampSegment(0, 0, 0, 0, 0).validate();
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate());
@@ -69,8 +71,16 @@ public class RampSegmentTest {
() -> new RampSegment(/* startAmplitude= */ -2, 0, 0, 0, 0).validate());
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(0, /* endAmplitude= */ 2, 0, 0, 0).validate());
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(0, 0, /* startFrequencyHz= */ -1, 0, 0).validate());
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(0, 0, 0, /* endFrequencyHz= */ -3, 0).validate());
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(0, 0, 0, 0, /* duration= */ -1).validate());
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(/* startAmplitude= */ Float.NaN, 0, 0, 0, 0).validate());
assertThrows(IllegalArgumentException.class,
() -> new RampSegment(0, 0, /* startFrequencyHz= */ Float.NaN, 0, 0).validate());
}
@Test

View File

@@ -38,13 +38,13 @@ public class StepSegmentTest {
@Test
public void testCreation() {
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequency= */ -1f,
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f,
/* duration= */ 100);
assertEquals(100, step.getDuration());
assertTrue(step.hasNonZeroAmplitude());
assertEquals(1f, step.getAmplitude());
assertEquals(-1f, step.getFrequency());
assertEquals(1f, step.getFrequencyHz());
}
@Test
@@ -58,14 +58,22 @@ public class StepSegmentTest {
@Test
public void testValidate() {
new StepSegment(/* amplitude= */ 0f, /* frequency= */ -1f, /* duration= */ 100).validate();
new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 10f, /* duration= */ 10).validate();
// Zero frequency is still used internally for unset frequency.
new StepSegment(0, 0, 0).validate();
assertThrows(IllegalArgumentException.class,
() -> new StepSegment(/* amplitude= */ -2, 1f, 10).validate());
assertThrows(IllegalArgumentException.class,
() -> new StepSegment(/* amplitude= */ 2, 1f, 10).validate());
assertThrows(IllegalArgumentException.class,
() -> new StepSegment(1, /* frequencyHz*/ -1f, 10).validate());
assertThrows(IllegalArgumentException.class,
() -> new StepSegment(2, 1f, /* duration= */ -1).validate());
assertThrows(IllegalArgumentException.class,
() -> new StepSegment(/* amplitude= */ Float.NaN, 1f, 10).validate());
assertThrows(IllegalArgumentException.class,
() -> new StepSegment(1, /* frequencyHz*/ Float.NaN, 10).validate());
}
@Test

View File

@@ -125,10 +125,10 @@ public final class VibratorHelper {
private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) {
VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
.addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
.addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration)
.addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS)
.addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration);
.addStep(/* amplitude= */ 0, /* frequencyHz= */ 60f, /* duration= */ 0)
.addRamp(/* amplitude= */ 1, /* frequencyHz= */ 120f, rampDuration)
.addStep(/* amplitude= */ 1, /* frequencyHz= */ 120f, CHIRP_LEVEL_DURATION_MILLIS)
.addRamp(/* amplitude= */ 0, /* frequencyHz= */ 60f, rampDuration);
if (insistent) {
return waveformBuilder

View File

@@ -21,18 +21,16 @@ import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.MathUtils;
import android.util.Range;
import java.util.List;
/**
* Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRange()} and
* Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRangeHz()} and
* amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
*
* <p>Devices with no frequency control will collapse all frequencies to zero and leave
* amplitudes unchanged.
*
* <p>The frequency value returned in segments will be absolute, converted with
* {@link VibratorInfo#getAbsoluteFrequency(float)}.
* <p>Devices with no frequency control will collapse all frequencies to the resonant frequency and
* leave amplitudes unchanged.
*/
final class ClippingAmplitudeAndFrequencyAdapter
implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
@@ -52,29 +50,39 @@ final class ClippingAmplitudeAndFrequencyAdapter
}
private StepSegment apply(StepSegment segment, VibratorInfo info) {
float clampedFrequency = clampFrequency(info, segment.getFrequency());
float clampedFrequency = clampFrequency(info, segment.getFrequencyHz());
return new StepSegment(
clampAmplitude(info, clampedFrequency, segment.getAmplitude()),
info.getAbsoluteFrequency(clampedFrequency),
clampedFrequency,
(int) segment.getDuration());
}
private RampSegment apply(RampSegment segment, VibratorInfo info) {
float clampedStartFrequency = clampFrequency(info, segment.getStartFrequency());
float clampedEndFrequency = clampFrequency(info, segment.getEndFrequency());
float clampedStartFrequency = clampFrequency(info, segment.getStartFrequencyHz());
float clampedEndFrequency = clampFrequency(info, segment.getEndFrequencyHz());
return new RampSegment(
clampAmplitude(info, clampedStartFrequency, segment.getStartAmplitude()),
clampAmplitude(info, clampedEndFrequency, segment.getEndAmplitude()),
info.getAbsoluteFrequency(clampedStartFrequency),
info.getAbsoluteFrequency(clampedEndFrequency),
clampedStartFrequency,
clampedEndFrequency,
(int) segment.getDuration());
}
private float clampFrequency(VibratorInfo info, float frequency) {
return info.getFrequencyRange().clamp(frequency);
private float clampFrequency(VibratorInfo info, float frequencyHz) {
Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
if (frequencyHz == 0 || frequencyRangeHz == null) {
return info.getResonantFrequency();
}
return frequencyRangeHz.clamp(frequencyHz);
}
private float clampAmplitude(VibratorInfo info, float frequency, float amplitude) {
return MathUtils.min(amplitude, info.getMaxAmplitude(frequency));
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.
return amplitude;
}
return MathUtils.min(amplitude, info.getMaxAmplitude(frequencyHz));
}
}

View File

@@ -90,13 +90,13 @@ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<V
if (previousSegment instanceof StepSegment) {
float previousAmplitude = ((StepSegment) previousSegment).getAmplitude();
float previousFrequency = ((StepSegment) previousSegment).getFrequency();
float previousFrequency = ((StepSegment) previousSegment).getFrequencyHz();
replacementSegments =
createStepsDown(previousAmplitude, previousFrequency, offDuration);
} else if (previousSegment instanceof RampSegment) {
float previousAmplitude = ((RampSegment) previousSegment).getEndAmplitude();
float previousFrequency = ((RampSegment) previousSegment).getEndFrequency();
float previousFrequency = ((RampSegment) previousSegment).getEndFrequencyHz();
if (offDuration <= mRampDownDuration) {
// Replace the zero amplitude segment with a ramp down of same duration, to
@@ -177,12 +177,12 @@ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<V
repeatIndex++;
if (lastSegment instanceof StepSegment) {
float previousAmplitude = ((StepSegment) lastSegment).getAmplitude();
float previousFrequency = ((StepSegment) lastSegment).getFrequency();
float previousFrequency = ((StepSegment) lastSegment).getFrequencyHz();
segments.addAll(createStepsDown(previousAmplitude, previousFrequency,
Math.min(offDuration, mRampDownDuration)));
} else if (lastSegment instanceof RampSegment) {
float previousAmplitude = ((RampSegment) lastSegment).getEndAmplitude();
float previousFrequency = ((RampSegment) lastSegment).getEndFrequency();
float previousFrequency = ((RampSegment) lastSegment).getEndFrequencyHz();
segments.add(createRampDown(previousAmplitude, previousFrequency,
Math.min(offDuration, mRampDownDuration)));
}
@@ -214,10 +214,10 @@ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<V
if (segment instanceof RampSegment) {
RampSegment ramp = (RampSegment) segment;
return new RampSegment(ramp.getStartAmplitude(), ramp.getEndAmplitude(),
ramp.getStartFrequency(), ramp.getEndFrequency(), (int) newDuration);
ramp.getStartFrequencyHz(), ramp.getEndFrequencyHz(), (int) newDuration);
} else if (segment instanceof StepSegment) {
StepSegment step = (StepSegment) segment;
return new StepSegment(step.getAmplitude(), step.getFrequency(), (int) newDuration);
return new StepSegment(step.getAmplitude(), step.getFrequencyHz(), (int) newDuration);
}
return segment;
}

View File

@@ -21,6 +21,7 @@ import android.os.VibratorInfo;
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.MathUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -52,7 +53,7 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter
if (!(segment instanceof RampSegment)) {
continue;
}
List<StepSegment> steps = apply((RampSegment) segment);
List<StepSegment> steps = apply(info, (RampSegment) segment);
segments.remove(i);
segments.addAll(i, steps);
int addedSegments = steps.size() - 1;
@@ -65,11 +66,12 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter
return repeatIndex;
}
private List<StepSegment> apply(RampSegment ramp) {
private List<StepSegment> apply(VibratorInfo info, RampSegment ramp) {
if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) {
// Amplitude is the same, so return a single step to simulate this ramp.
return Arrays.asList(
new StepSegment(ramp.getStartAmplitude(), ramp.getStartFrequency(),
new StepSegment(ramp.getStartAmplitude(),
fillEmptyFrequency(info, ramp.getStartFrequencyHz()),
(int) ramp.getDuration()));
}
@@ -77,17 +79,21 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter
int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration;
for (int i = 0; i < stepCount - 1; i++) {
float pos = (float) i / stepCount;
// Fill zero frequency values with the device resonant frequency before interpolating.
float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz());
float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
steps.add(new StepSegment(
interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), pos),
MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
MathUtils.lerp(startFrequencyHz, endFrequencyHz, pos),
mStepDuration));
}
int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1);
steps.add(new StepSegment(ramp.getEndAmplitude(), ramp.getEndFrequency(), duration));
float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
steps.add(new StepSegment(ramp.getEndAmplitude(), endFrequencyHz, duration));
return steps;
}
private static float interpolate(float start, float end, float position) {
return start + position * (end - start);
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
}
}

View File

@@ -21,6 +21,7 @@ import android.os.VibratorInfo;
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.MathUtils;
import java.util.ArrayList;
import java.util.List;
@@ -41,18 +42,18 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
// The vibrator does not have PWLE capability, so keep the segments unchanged.
return repeatIndex;
}
convertStepsToRamps(segments);
convertStepsToRamps(info, segments);
repeatIndex = splitLongRampSegments(info, segments, repeatIndex);
return repeatIndex;
}
private void convertStepsToRamps(List<VibrationEffectSegment> segments) {
private void convertStepsToRamps(VibratorInfo info, List<VibrationEffectSegment> segments) {
int segmentCount = segments.size();
// Convert steps that require frequency control to ramps.
for (int i = 0; i < segmentCount; i++) {
VibrationEffectSegment segment = segments.get(i);
if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) {
segments.set(i, convertStepToRamp((StepSegment) segment));
if (isStep(segment) && ((StepSegment) segment).getFrequencyHz() != 0) {
segments.set(i, convertStepToRamp(info, (StepSegment) segment));
}
}
// Convert steps that are next to ramps to also become ramps, so they can be composed
@@ -60,10 +61,10 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
for (int i = 0; i < segmentCount; i++) {
if (segments.get(i) instanceof RampSegment) {
for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) {
segments.set(j, convertStepToRamp((StepSegment) segments.get(j)));
segments.set(j, convertStepToRamp(info, (StepSegment) segments.get(j)));
}
for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); j++) {
segments.set(j, convertStepToRamp((StepSegment) segments.get(j)));
segments.set(j, convertStepToRamp(info, (StepSegment) segments.get(j)));
}
}
}
@@ -92,7 +93,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
continue;
}
segments.remove(i);
segments.addAll(i, splitRampSegment(ramp, splits));
segments.addAll(i, splitRampSegment(info, ramp, splits));
int addedSegments = splits - 1;
if (repeatIndex > i) {
repeatIndex += addedSegments;
@@ -104,31 +105,40 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
return repeatIndex;
}
private static RampSegment convertStepToRamp(StepSegment segment) {
private static RampSegment convertStepToRamp(VibratorInfo info, StepSegment segment) {
float frequencyHz = fillEmptyFrequency(info, segment.getFrequencyHz());
return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
frequencyHz, frequencyHz, (int) segment.getDuration());
}
private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) {
private static List<RampSegment> splitRampSegment(VibratorInfo info, RampSegment ramp,
int splits) {
List<RampSegment> ramps = new ArrayList<>(splits);
float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz());
float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
long splitDuration = ramp.getDuration() / splits;
float previousAmplitude = ramp.getStartAmplitude();
float previousFrequency = ramp.getStartFrequency();
float previousFrequency = startFrequencyHz;
long accumulatedDuration = 0;
for (int i = 1; i < splits; i++) {
accumulatedDuration += splitDuration;
float durationRatio = (float) accumulatedDuration / ramp.getDuration();
float interpolatedFrequency =
MathUtils.lerp(startFrequencyHz, endFrequencyHz, durationRatio);
float interpolatedAmplitude =
MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), durationRatio);
RampSegment rampSplit = new RampSegment(
previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration),
previousFrequency, interpolateFrequency(ramp, accumulatedDuration),
previousAmplitude, interpolatedAmplitude,
previousFrequency, interpolatedFrequency,
(int) splitDuration);
ramps.add(rampSplit);
previousAmplitude = rampSplit.getEndAmplitude();
previousFrequency = rampSplit.getEndFrequency();
previousFrequency = rampSplit.getEndFrequencyHz();
}
ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency,
ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration)));
endFrequencyHz, (int) (ramp.getDuration() - accumulatedDuration)));
return ramps;
}
@@ -137,18 +147,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
return segment instanceof StepSegment;
}
private static float interpolateAmplitude(RampSegment ramp, long duration) {
return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration,
ramp.getDuration());
}
private static float interpolateFrequency(RampSegment ramp, long duration) {
return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration,
ramp.getDuration());
}
private static float interpolate(float start, float end, long duration, long totalDuration) {
float position = (float) duration / totalDuration;
return start + position * (end - start);
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
}
}

View File

@@ -373,7 +373,7 @@ final class Vibration {
final long token = proto.start(fieldId);
proto.write(StepSegmentProto.DURATION, segment.getDuration());
proto.write(StepSegmentProto.AMPLITUDE, segment.getAmplitude());
proto.write(StepSegmentProto.FREQUENCY, segment.getFrequency());
proto.write(StepSegmentProto.FREQUENCY, segment.getFrequencyHz());
proto.end(token);
}
@@ -382,8 +382,8 @@ final class Vibration {
proto.write(RampSegmentProto.DURATION, segment.getDuration());
proto.write(RampSegmentProto.START_AMPLITUDE, segment.getStartAmplitude());
proto.write(RampSegmentProto.END_AMPLITUDE, segment.getEndAmplitude());
proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequency());
proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequency());
proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequencyHz());
proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequencyHz());
proto.end(token);
}

View File

@@ -36,8 +36,6 @@ import libcore.util.NativeAllocationRegistry;
/** Controls a single vibrator. */
final class VibratorController {
private static final String TAG = "VibratorController";
// TODO(b/167947076): load suggested range from config
private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
private final Object mLock = new Object();
@@ -74,8 +72,7 @@ final class VibratorController {
mNativeWrapper = nativeWrapper;
mNativeWrapper.init(vibratorId, listener);
VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
vibratorInfoBuilder);
mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder);
mVibratorInfo = vibratorInfoBuilder.build();
if (!mVibratorInfoLoadSuccessful) {
@@ -126,8 +123,7 @@ final class VibratorController {
}
int vibratorId = mVibratorInfo.getId();
VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
vibratorInfoBuilder);
mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder);
mVibratorInfo = vibratorInfoBuilder.build();
if (!mVibratorInfoLoadSuccessful) {
Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId);
@@ -419,8 +415,7 @@ final class VibratorController {
private static native void alwaysOnDisable(long nativePtr, long id);
private static native boolean getInfo(long nativePtr, float suggestedFrequencyRange,
VibratorInfo.Builder infoBuilder);
private static native boolean getInfo(long nativePtr, VibratorInfo.Builder infoBuilder);
private long mNativePtr = 0;
@@ -490,8 +485,8 @@ final class VibratorController {
/**
* Loads device vibrator metadata and returns true if all metadata was loaded successfully.
*/
public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
return getInfo(mNativePtr, suggestedFrequencyRange, infoBuilder);
public boolean getInfo(VibratorInfo.Builder infoBuilder) {
return getInfo(mNativePtr, infoBuilder);
}
}
}

View File

@@ -1756,17 +1756,23 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
if (hasFrequencies) {
frequencies.add(Float.parseFloat(getNextArgRequired()));
} else {
frequencies.add(0f);
}
}
VibrationEffect.WaveformBuilder waveform = VibrationEffect.startWaveform();
for (int i = 0; i < durations.size(); i++) {
if (isContinuous) {
waveform.addRamp(amplitudes.get(i), frequencies.get(i), durations.get(i));
if (hasFrequencies) {
waveform.addRamp(amplitudes.get(i), frequencies.get(i), durations.get(i));
} else {
waveform.addRamp(amplitudes.get(i), durations.get(i));
}
} else {
waveform.addStep(amplitudes.get(i), frequencies.get(i), durations.get(i));
if (hasFrequencies) {
waveform.addStep(amplitudes.get(i), frequencies.get(i), durations.get(i));
} else {
waveform.addStep(amplitudes.get(i), durations.get(i));
}
}
}
composition.addEffect(waveform.build(repeat), delay);
@@ -1865,7 +1871,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println(" If -c is provided, the waveform is continuous and will ramp");
pw.println(" between values; otherwise each entry is a fixed step.");
pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;");
pw.println(" frequency is a relative value around resonant frequency 0;");
pw.println(" frequency is an absolute value in hertz;");
pw.println(" prebaked [-w delay] [-b] <effect-id>");
pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");

View File

@@ -61,8 +61,8 @@ static struct {
static struct {
jfieldID startAmplitude;
jfieldID endAmplitude;
jfieldID startFrequency;
jfieldID endFrequency;
jfieldID startFrequencyHz;
jfieldID endFrequencyHz;
jfieldID duration;
} sRampClassInfo;
@@ -157,8 +157,8 @@ static aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) {
static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startAmplitude));
pwle.endAmplitude = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endAmplitude));
pwle.startFrequency =
static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startFrequency));
pwle.endFrequency = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endFrequency));
static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startFrequencyHz));
pwle.endFrequency = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endFrequencyHz));
pwle.duration = static_cast<int32_t>(env->GetIntField(ramp, sRampClassInfo.duration));
return pwle;
}
@@ -363,7 +363,7 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr,
}
static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
jfloat suggestedSafeRange, jobject vibratorInfoBuilder) {
jobject vibratorInfoBuilder) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
@@ -437,9 +437,9 @@ 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,
minFrequency, resonantFrequency, frequencyResolution,
suggestedSafeRange, maxAmplitudes);
jobject frequencyMapping =
env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, resonantFrequency,
minFrequency, frequencyResolution, maxAmplitudes);
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
frequencyMapping);
@@ -463,7 +463,7 @@ static const JNINativeMethod method_table[] = {
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
{"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
{"getInfo", "(JFLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
{"getInfo", "(JLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
};
int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -481,13 +481,13 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
jclass rampClass = FindClassOrDie(env, "android/os/vibrator/RampSegment");
sRampClassInfo.startAmplitude = GetFieldIDOrDie(env, rampClass, "mStartAmplitude", "F");
sRampClassInfo.endAmplitude = GetFieldIDOrDie(env, rampClass, "mEndAmplitude", "F");
sRampClassInfo.startFrequency = GetFieldIDOrDie(env, rampClass, "mStartFrequency", "F");
sRampClassInfo.endFrequency = GetFieldIDOrDie(env, rampClass, "mEndFrequency", "F");
sRampClassInfo.startFrequencyHz = GetFieldIDOrDie(env, rampClass, "mStartFrequencyHz", "F");
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>", "(FFFF[F)V");
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFF[F)V");
jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
sVibratorInfoBuilderClassInfo.setCapabilities =

View File

@@ -54,11 +54,10 @@ public class DeviceVibrationEffectAdapterTest {
/* 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, Float.NaN, null);
new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY,
TEST_RESONANT_FREQUENCY, TEST_FREQUENCY_RESOLUTION,
/* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP);
new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
private DeviceVibrationEffectAdapter mAdapter;
@@ -87,14 +86,14 @@ public class DeviceVibrationEffectAdapterTest {
@Test
public void testStepAndRampSegments_withoutPwleCapability_convertsRampsToSteps() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100),
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
/* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 1000)),
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 1, /* duration= */ 1000)),
/* repeatIndex= */ 3);
VibrationEffect.Composed adaptedEffect = (VibrationEffect.Composed) mAdapter.apply(effect,
@@ -110,23 +109,23 @@ public class DeviceVibrationEffectAdapterTest {
@Test
public void testStepAndRampSegments_withPwleCapability_convertsStepsToRamps() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 175, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 60),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
/* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
/* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)),
/* repeatIndex= */ 2);
VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
/* startFrequency= */ 175, /* endFrequency= */ 175, /* duration= */ 10),
/* startFrequencyHz= */ 175, /* endFrequencyHz= */ 175, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 100),
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 60),
new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
/* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
/* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
/* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
/* repeatIndex= */ 2);
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
@@ -135,28 +134,28 @@ public class DeviceVibrationEffectAdapterTest {
}
@Test
public void testStepAndRampSegments_withEmptyFreqMapping_returnsSameAmplitudesAndZeroFreq() {
public void testStepAndRampSegments_withEmptyFreqMapping_returnsAmplitudesWithResonantFreq() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 175, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1,
/* startFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 50),
/* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.7f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
/* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)),
/* repeatIndex= */ 2);
VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
/* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
/* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
/* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
/* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
/* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1,
/* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
/* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
/* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.7f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
/* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN,
/* duration= */ 20)),
/* repeatIndex= */ 2);
@@ -168,25 +167,25 @@ public class DeviceVibrationEffectAdapterTest {
@Test
public void testStepAndRampSegments_withValidFreqMapping_returnsClippedValues() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequency= */ -1, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 125, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
/* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
/* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)),
/* repeatIndex= */ 2);
VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 150, /* endFrequency= */ 150,
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150,
/* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
/* startFrequency= */ 125, /* endFrequency= */ 125,
/* startFrequencyHz= */ 125, /* endFrequencyHz= */ 125,
/* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
/* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
/* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
/* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
/* repeatIndex= */ 2);
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,

View File

@@ -87,7 +87,7 @@ final class FakeVibratorControllerProvider {
@Override
public long on(long milliseconds, long vibrationId) {
mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
/* frequency= */ 0, (int) milliseconds));
/* frequencyHz= */ 0, (int) milliseconds));
applyLatency();
scheduleListener(milliseconds, vibrationId);
return milliseconds;
@@ -158,7 +158,7 @@ final class FakeVibratorControllerProvider {
}
@Override
public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
public boolean getInfo(VibratorInfo.Builder infoBuilder) {
infoBuilder.setCapabilities(mCapabilities);
infoBuilder.setSupportedBraking(mSupportedBraking);
infoBuilder.setPwleSizeMax(mPwleSizeMax);
@@ -170,9 +170,8 @@ final class FakeVibratorControllerProvider {
}
infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
infoBuilder.setQFactor(mQFactor);
infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
mMaxAmplitudes));
infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes));
return mIsInfoLoadSuccessful;
}

View File

@@ -70,9 +70,9 @@ public class RampDownAdapterTest {
@Test
public void testRampAndStepSegments_withNoOffSegment_keepsListUnchanged() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
@@ -86,12 +86,12 @@ public class RampDownAdapterTest {
mAdapter = new RampDownAdapter(/* rampDownDuration= */ 0, TEST_STEP_DURATION);
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
/* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 50)));
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 50)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
@@ -102,12 +102,12 @@ public class RampDownAdapterTest {
@Test
public void testStepSegments_withShortZeroSegment_replaceWithStepsDown() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10)));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5));
assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
assertEquals(expectedSegments, segments);
@@ -116,17 +116,17 @@ public class RampDownAdapterTest {
@Test
public void testStepSegments_withLongZeroSegment_replaceWithStepsDownWithRemainingOffSegment() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
/* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 50),
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 50),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100));
assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO));
assertEquals(expectedSegments, segments);
@@ -135,16 +135,16 @@ public class RampDownAdapterTest {
@Test
public void testStepSegments_withZeroSegmentBeforeRepeat_fixesRepeat() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50),
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 50),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100));
// Repeat index fixed after intermediate steps added
assertEquals(5, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
@@ -154,14 +154,14 @@ public class RampDownAdapterTest {
@Test
public void testStepSegments_withZeroSegmentAfterRepeat_preservesRepeat() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100));
assertEquals(3, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
assertEquals(expectedSegments, segments);
@@ -170,22 +170,22 @@ public class RampDownAdapterTest {
@Test
public void testStepSegments_withZeroSegmentAtRepeat_fixesRepeatAndAppendOriginalToListEnd() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50),
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100)));
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 50),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 100)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35),
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 100),
// Original zero segment appended to the end of new looping vibration,
// then converted to ramp down as well.
new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35));
new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35));
// Repeat index fixed after intermediate steps added
assertEquals(5, mAdapter.apply(segments, 1, TEST_VIBRATOR_INFO));
@@ -195,8 +195,8 @@ public class RampDownAdapterTest {
@Test
public void testStepSegments_withRepeatToNonZeroSegment_keepsOriginalSteps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100)));
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
assertEquals(0, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -208,14 +208,14 @@ public class RampDownAdapterTest {
public void testStepSegments_withRepeatToShortZeroSegment_skipAndAppendRampDown() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
/* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30)));
/* startfrequencyHz= */ 0, /* endfrequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
/* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5));
/* startfrequencyHz= */ 0, /* endfrequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5));
// Shift repeat index to the right to use append instead of zero segment.
assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -226,17 +226,17 @@ public class RampDownAdapterTest {
@Test
public void testStepSegments_withRepeatToLongZeroSegment_splitAndAppendRampDown() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 120),
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30)));
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 120),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
// Split long zero segment to skip part of it.
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 20),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30),
new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5));
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 20),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30),
new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5));
// Shift repeat index to the right to use append with part of the zero segment.
assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -248,18 +248,20 @@ public class RampDownAdapterTest {
public void testRampSegments_withShortZeroSegment_replaceWithRampDown() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
/* duration= */ 30)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30));
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
/* duration= */ 30));
assertEquals(2, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
@@ -269,20 +271,23 @@ public class RampDownAdapterTest {
@Test
public void testRampSegments_withLongZeroSegment_splitAndAddRampDown() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 150),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 150, /* duration= */ 150),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
/* duration= */ 30)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 130),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100,
/* duration= */ 130),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30));
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
/* duration= */ 30));
// Repeat index fixed after intermediate steps added
assertEquals(3, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO));
@@ -294,9 +299,10 @@ public class RampDownAdapterTest {
public void testRampSegments_withRepeatToNonZeroSegment_keepsOriginalSteps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200,
/* duration= */ 30)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
assertEquals(0, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -307,15 +313,15 @@ public class RampDownAdapterTest {
@Test
public void testRampSegments_withRepeatToShortZeroSegment_skipAndAppendRampDown() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 20),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 1,
/* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 20)));
/* startFrequencyHz= */ 40, /* endFrequencyHz= */ 80, /* duration= */ 20)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 20),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 20),
/* startFrequencyHz= */ 40, /* endFrequencyHz= */ 80, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20));
/* startFrequencyHz= */ 80, /* endFrequencyHz= */ 80, /* duration= */ 20));
// Shift repeat index to the right to use append instead of zero segment.
assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));
@@ -327,19 +333,19 @@ public class RampDownAdapterTest {
public void testRampSegments_withRepeatToLongZeroSegment_splitAndAppendRampDown() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 70),
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 70),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)));
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 30)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
// Split long zero segment to skip part of it.
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20),
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 50),
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30),
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 30),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20));
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 20));
// Shift repeat index to the right to use append with part of the zero segment.
assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO));

View File

@@ -45,6 +45,12 @@ import java.util.stream.IntStream;
@Presubmit
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(
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
private RampToStepAdapter mAdapter;
@@ -56,7 +62,7 @@ public class RampToStepAdapterTest {
@Test
public void testStepAndPrebakedAndPrimitiveSegments_keepsListUnchanged() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
new PrebakedSegment(
VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT),
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
@@ -71,9 +77,9 @@ public class RampToStepAdapterTest {
@Test
public void testRampSegments_withPwleCapability_keepsListUnchanged() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 1, /* duration= */ 20)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
@@ -86,27 +92,28 @@ public class RampToStepAdapterTest {
@Test
public void testRampSegments_withoutPwleCapability_convertsRampsToSteps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 10, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 0, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ -3, /* endFrequency= */ 0, /* duration= */ 11),
/* startFrequencyHz= */ 30, /* endFrequencyHz= */ 60, /* duration= */ 11),
new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
/* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 200)));
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 1, /* duration= */ 200)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 10, /* duration= */ 100),
// 10ms ramp becomes 2 steps
new StepSegment(/* amplitude= */ 1, /* frequency= */ -4, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 2, /* duration= */ 5),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 10, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 150, /* duration= */ 5),
// 11ms ramp becomes 3 steps
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -3, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.6f, /* frequency= */ -2, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 0, /* duration= */ 1),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 30, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.6f, /* frequencyHz= */ 40, /* duration= */ 5),
new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 60, /* duration= */ 1),
// 200ms ramp with same amplitude becomes a single step
new StepSegment(/* amplitude= */ 0.65f, /* frequency= */ 0, /* duration= */ 200));
new StepSegment(/* amplitude= */ 0.65f, /* frequencyHz= */ 150,
/* duration= */ 200));
// Repeat index fixed after intermediate steps added
assertEquals(4, mAdapter.apply(segments, 3, createVibratorInfo()));
@@ -117,6 +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)
.build();
}
}

View File

@@ -44,6 +44,13 @@ import java.util.stream.IntStream;
*/
@Presubmit
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(
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
private StepToRampAdapter mAdapter;
@Before
@@ -55,7 +62,7 @@ public class StepToRampAdapterTest {
public void testRampAndPrebakedAndPrimitiveSegments_returnsOriginalSegments() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
/* startFrequencyHz= */ 40f, /* endFrequencyHz= */ 20f, /* duration= */ 10),
new PrebakedSegment(
VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT),
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
@@ -71,27 +78,28 @@ public class StepToRampAdapterTest {
public void testRampSegments_withPwleDurationLimit_splitsLongRamps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25),
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 50, /* duration= */ 25),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
/* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)));
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
/* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8),
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 118f, /* duration= */ 8),
new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
/* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f,
/* startFrequencyHz= */ 118f, /* endFrequencyHz= */ 86f,
/* duration= */ 8),
new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
/* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9),
/* startFrequencyHz= */ 86f, /* endFrequencyHz= */ 50f, /* duration= */ 9),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
/* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5));
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5));
VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
.setPwlePrimitiveDurationMax(10)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.build();
// Update repeat index to skip the ramp splits.
@@ -102,9 +110,9 @@ public class StepToRampAdapterTest {
@Test
public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
assertEquals(-1, mAdapter.apply(segments, -1, createVibratorInfo()));
@@ -116,13 +124,13 @@ public class StepToRampAdapterTest {
@Test
public void testStepAndRampSegments_withPwleCapabilityAndNoFrequency_keepsOriginalSteps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100),
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
/* startFrequencyHz= */ 40, /* endFrequencyHz= */ 200, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)));
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 1, /* duration= */ 20)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
@@ -135,25 +143,25 @@ public class StepToRampAdapterTest {
@Test
public void testStepAndRampSegments_withPwleCapabilityAndStepNextToRamp_convertsStepsToRamps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 60),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -1, /* duration= */ 60)));
/* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20),
new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 10, /* duration= */ 60)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 10),
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100),
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 60),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
/* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
/* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 50),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
/* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20),
/* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20),
new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 60));
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 60));
VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
@@ -165,13 +173,13 @@ public class StepToRampAdapterTest {
@Test
public void testStepSegments_withPwleCapabilityAndFrequency_convertsStepsToRamps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ -1, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 1, /* duration= */ 100)));
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 100, /* duration= */ 10),
new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 6)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
/* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 100));
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 6));
VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo));
@@ -183,6 +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)
.build();
}
}

View File

@@ -553,8 +553,8 @@ public class VibrationThreadTest {
VibrationEffect effect = VibrationEffect.startWaveform()
.addStep(1, 10)
.addRamp(0, 20)
.addStep(0.8f, 1, 30)
.addRamp(0.6f, -1, 40)
.addStep(0.8f, 100, 30)
.addRamp(0.6f, 200, 40)
.build();
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
@@ -565,12 +565,13 @@ public class VibrationThreadTest {
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10),
expectedRamp(/* StartAmplitude= */ 1, /* endAmplitude= */ 0,
/* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 20),
expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 20),
expectedRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 30),
expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f,
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200,
/* duration= */ 40)),
fakeVibrator.getEffectSegments());
assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking());
}
@@ -589,8 +590,8 @@ public class VibrationThreadTest {
VibrationEffect effect = VibrationEffect.startWaveform()
.addStep(1, 10)
.addRamp(0, 20)
.addStep(0.8f, 1, 30)
.addRamp(0.6f, -1, 40)
.addStep(0.8f, 10, 30)
.addRamp(0.6f, 100, 40)
.build();
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
waitForCompletion(thread);
@@ -1345,7 +1346,8 @@ public class VibrationThreadTest {
}
private VibrationEffectSegment expectedOneShot(long millis) {
return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequency= */ 0, (int) millis);
return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
/* frequencyHz= */ 0, (int) millis);
}
private VibrationEffectSegment expectedPrebaked(int effectId) {
@@ -1356,13 +1358,13 @@ public class VibrationThreadTest {
return new PrimitiveSegment(primitiveId, scale, delay);
}
private VibrationEffectSegment expectedRamp(float amplitude, float frequency, int duration) {
return expectedRamp(amplitude, amplitude, frequency, frequency, duration);
private VibrationEffectSegment expectedRamp(float amplitude, float frequencyHz, int duration) {
return expectedRamp(amplitude, amplitude, frequencyHz, frequencyHz, duration);
}
private VibrationEffectSegment expectedRamp(float startAmplitude, float endAmplitude,
float startFrequency, float endFrequency, int duration) {
return new RampSegment(startAmplitude, endAmplitude, startFrequency, endFrequency,
float startFrequencyHz, float endFrequencyHz, int duration) {
return new RampSegment(startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz,
duration);
}

View File

@@ -21,7 +21,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -236,7 +235,7 @@ public class VibratorControllerTest {
RampSegment[] primitives = new RampSegment[]{
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 10)
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 10)
};
assertEquals(15L, controller.on(primitives, 12));
assertTrue(controller.isVibrating());
@@ -312,10 +311,10 @@ public class VibratorControllerTest {
private void mockVibratorCapabilities(int capabilities) {
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class)))
Float.NaN, Float.NaN, Float.NaN, null);
when(mNativeWrapperMock.getInfo(any(VibratorInfo.Builder.class)))
.then(invocation -> {
((VibratorInfo.Builder) invocation.getArgument(1))
((VibratorInfo.Builder) invocation.getArgument(0))
.setCapabilities(capabilities)
.setFrequencyMapping(frequencyMapping);
return true;