Merge "Fix composed VibrationEffect timing out"
This commit is contained in:
@@ -31,9 +31,9 @@ abstract class AbstractVibratorStep extends Step {
|
||||
public final VibratorController controller;
|
||||
public final VibrationEffect.Composed effect;
|
||||
public final int segmentIndex;
|
||||
public final long previousStepVibratorOffTimeout;
|
||||
|
||||
long mVibratorOnResult;
|
||||
long mPendingVibratorOffDeadline;
|
||||
boolean mVibratorCompleteCallbackReceived;
|
||||
|
||||
/**
|
||||
@@ -43,19 +43,19 @@ abstract class AbstractVibratorStep extends Step {
|
||||
* @param controller The vibrator that is playing the effect.
|
||||
* @param effect The effect being played in this step.
|
||||
* @param index The index of the next segment to be played by this step
|
||||
* @param previousStepVibratorOffTimeout The time the vibrator is expected to complete any
|
||||
* @param pendingVibratorOffDeadline The time the vibrator is expected to complete any
|
||||
* previous vibration and turn off. This is used to allow this step to
|
||||
* be triggered when the completion callback is received, and can
|
||||
* be used to play effects back-to-back.
|
||||
*/
|
||||
AbstractVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
VibratorController controller, VibrationEffect.Composed effect, int index,
|
||||
long previousStepVibratorOffTimeout) {
|
||||
long pendingVibratorOffDeadline) {
|
||||
super(conductor, startTime);
|
||||
this.controller = controller;
|
||||
this.effect = effect;
|
||||
this.segmentIndex = index;
|
||||
this.previousStepVibratorOffTimeout = previousStepVibratorOffTimeout;
|
||||
mPendingVibratorOffDeadline = pendingVibratorOffDeadline;
|
||||
}
|
||||
|
||||
public int getVibratorId() {
|
||||
@@ -69,27 +69,57 @@ abstract class AbstractVibratorStep extends Step {
|
||||
|
||||
@Override
|
||||
public boolean acceptVibratorCompleteCallback(int vibratorId) {
|
||||
boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
|
||||
mVibratorCompleteCallbackReceived |= isSameVibrator;
|
||||
if (getVibratorId() != vibratorId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only activate this step if a timeout was set to wait for the vibration to complete,
|
||||
// otherwise we are waiting for the correct time to play the next step.
|
||||
return isSameVibrator && (previousStepVibratorOffTimeout > SystemClock.uptimeMillis());
|
||||
boolean shouldAcceptCallback = mPendingVibratorOffDeadline > SystemClock.uptimeMillis();
|
||||
if (VibrationThread.DEBUG) {
|
||||
Slog.d(VibrationThread.TAG,
|
||||
"Received completion callback from " + vibratorId
|
||||
+ ", accepted = " + shouldAcceptCallback);
|
||||
}
|
||||
|
||||
// The callback indicates this vibrator has stopped, reset the timeout.
|
||||
mPendingVibratorOffDeadline = 0;
|
||||
mVibratorCompleteCallbackReceived = true;
|
||||
return shouldAcceptCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> cancel() {
|
||||
return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
|
||||
/* cancelled= */ true, controller, previousStepVibratorOffTimeout));
|
||||
/* cancelled= */ true, controller, mPendingVibratorOffDeadline));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelImmediately() {
|
||||
if (previousStepVibratorOffTimeout > SystemClock.uptimeMillis()) {
|
||||
if (mPendingVibratorOffDeadline > SystemClock.uptimeMillis()) {
|
||||
// Vibrator might be running from previous steps, so turn it off while canceling.
|
||||
stopVibrating();
|
||||
}
|
||||
}
|
||||
|
||||
protected long handleVibratorOnResult(long vibratorOnResult) {
|
||||
mVibratorOnResult = vibratorOnResult;
|
||||
if (VibrationThread.DEBUG) {
|
||||
Slog.d(VibrationThread.TAG,
|
||||
"Turned on vibrator " + getVibratorId() + ", result = " + mVibratorOnResult);
|
||||
}
|
||||
if (mVibratorOnResult > 0) {
|
||||
// Vibrator was turned on by this step, with vibratorOnResult as the duration.
|
||||
// Set an extra timeout to wait for the vibrator completion callback.
|
||||
mPendingVibratorOffDeadline = SystemClock.uptimeMillis() + mVibratorOnResult
|
||||
+ VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
|
||||
} else {
|
||||
// Vibrator does not support the request or failed to turn on, reset callback deadline.
|
||||
mPendingVibratorOffDeadline = 0;
|
||||
}
|
||||
return mVibratorOnResult;
|
||||
}
|
||||
|
||||
protected void stopVibrating() {
|
||||
if (VibrationThread.DEBUG) {
|
||||
Slog.d(VibrationThread.TAG,
|
||||
@@ -97,6 +127,7 @@ abstract class AbstractVibratorStep extends Step {
|
||||
}
|
||||
controller.off();
|
||||
getVibration().stats().reportVibratorOff();
|
||||
mPendingVibratorOffDeadline = 0;
|
||||
}
|
||||
|
||||
protected void changeAmplitude(float amplitude) {
|
||||
@@ -109,40 +140,29 @@ abstract class AbstractVibratorStep extends Step {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link VibrationStepConductor#nextVibrateStep} with same timings, only jumping
|
||||
* the segments.
|
||||
*/
|
||||
protected List<Step> skipToNextSteps(int segmentsSkipped) {
|
||||
return nextSteps(startTime, previousStepVibratorOffTimeout, segmentsSkipped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link VibrationStepConductor#nextVibrateStep} with same start and off timings
|
||||
* calculated from {@link #getVibratorOnDuration()}, jumping all played segments.
|
||||
*
|
||||
* <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
|
||||
* result is non-positive, meaning the vibrator has either ignored or failed to turn on.
|
||||
* Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings
|
||||
* calculated from {@link #getVibratorOnDuration()} based on the current
|
||||
* {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect.
|
||||
*/
|
||||
protected List<Step> nextSteps(int segmentsPlayed) {
|
||||
if (mVibratorOnResult <= 0) {
|
||||
// Vibration was not started, so just skip the played segments and keep timings.
|
||||
return skipToNextSteps(segmentsPlayed);
|
||||
// Schedule next steps to run right away.
|
||||
long nextStartTime = SystemClock.uptimeMillis();
|
||||
if (mVibratorOnResult > 0) {
|
||||
// Vibrator was turned on by this step, with mVibratorOnResult as the duration.
|
||||
// Schedule next steps for right after the vibration finishes.
|
||||
nextStartTime += mVibratorOnResult;
|
||||
}
|
||||
long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
|
||||
long nextVibratorOffTimeout =
|
||||
nextStartTime + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
|
||||
return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
|
||||
return nextSteps(nextStartTime, segmentsPlayed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link VibrationStepConductor#nextVibrateStep} with given start and off timings,
|
||||
* which might be calculated independently, jumping all played segments.
|
||||
* Return the {@link VibrationStepConductor#nextVibrateStep} with given start time,
|
||||
* which might be calculated independently, and jumping all played segments from the effect.
|
||||
*
|
||||
* <p>This should be used when the vibrator on/off state is not responsible for the steps
|
||||
* execution timings, e.g. while playing the vibrator amplitudes.
|
||||
* <p>This should be used when the vibrator on/off state is not responsible for the step
|
||||
* execution timing, e.g. while playing the vibrator amplitudes.
|
||||
*/
|
||||
protected List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
|
||||
int segmentsPlayed) {
|
||||
protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) {
|
||||
int nextSegmentIndex = segmentIndex + segmentsPlayed;
|
||||
int effectSize = effect.getSegments().size();
|
||||
int repeatIndex = effect.getRepeatIndex();
|
||||
@@ -154,7 +174,7 @@ abstract class AbstractVibratorStep extends Step {
|
||||
nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
|
||||
}
|
||||
Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
|
||||
nextSegmentIndex, vibratorOffTimeout);
|
||||
nextSegmentIndex, mPendingVibratorOffDeadline);
|
||||
return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
|
||||
private final boolean mCancelled;
|
||||
|
||||
CompleteEffectVibratorStep(VibrationStepConductor conductor, long startTime, boolean cancelled,
|
||||
VibratorController controller, long previousStepVibratorOffTimeout) {
|
||||
VibratorController controller, long pendingVibratorOffDeadline) {
|
||||
super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
|
||||
previousStepVibratorOffTimeout);
|
||||
pendingVibratorOffDeadline);
|
||||
mCancelled = cancelled;
|
||||
}
|
||||
|
||||
@@ -73,10 +73,11 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
|
||||
return VibrationStepConductor.EMPTY_STEP_LIST;
|
||||
}
|
||||
|
||||
long now = SystemClock.uptimeMillis();
|
||||
float currentAmplitude = controller.getCurrentAmplitude();
|
||||
long remainingOnDuration =
|
||||
previousStepVibratorOffTimeout - VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT
|
||||
- SystemClock.uptimeMillis();
|
||||
mPendingVibratorOffDeadline - now
|
||||
- VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
|
||||
long rampDownDuration =
|
||||
Math.min(remainingOnDuration,
|
||||
conductor.vibrationSettings.getRampDownDuration());
|
||||
@@ -89,8 +90,10 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
|
||||
stopVibrating();
|
||||
return VibrationStepConductor.EMPTY_STEP_LIST;
|
||||
} else {
|
||||
// Vibration is completing normally, turn off after the deadline in case we
|
||||
// don't receive the callback in time (callback also triggers it right away).
|
||||
return Arrays.asList(new TurnOffVibratorStep(
|
||||
conductor, previousStepVibratorOffTimeout, controller));
|
||||
conductor, mPendingVibratorOffDeadline, controller));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,13 +103,18 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
|
||||
+ " from amplitude " + currentAmplitude
|
||||
+ " for " + rampDownDuration + "ms");
|
||||
}
|
||||
|
||||
// If we are cancelling this vibration then make sure the vibrator will be turned off
|
||||
// immediately after the ramp off duration. Otherwise, this is a planned ramp off for
|
||||
// the remaining ON duration, then just propagate the mPendingVibratorOffDeadline so the
|
||||
// turn off step will wait for the vibration completion callback and end gracefully.
|
||||
long rampOffVibratorOffDeadline =
|
||||
mCancelled ? (now + rampDownDuration) : mPendingVibratorOffDeadline;
|
||||
float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
|
||||
float amplitudeTarget = currentAmplitude - amplitudeDelta;
|
||||
long newVibratorOffTimeout =
|
||||
mCancelled ? rampDownDuration : previousStepVibratorOffTimeout;
|
||||
return Arrays.asList(
|
||||
new RampOffVibratorStep(conductor, startTime, amplitudeTarget, amplitudeDelta,
|
||||
controller, newVibratorOffTimeout));
|
||||
controller, rampOffVibratorOffDeadline));
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
|
||||
|
||||
ComposePrimitivesVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
VibratorController controller, VibrationEffect.Composed effect, int index,
|
||||
long previousStepVibratorOffTimeout) {
|
||||
long pendingVibratorOffDeadline) {
|
||||
// This step should wait for the last vibration to finish (with the timeout) and for the
|
||||
// intended step start time (to respect the effect delays).
|
||||
super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
|
||||
index, previousStepVibratorOffTimeout);
|
||||
super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
|
||||
index, pendingVibratorOffDeadline);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,18 +60,22 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
|
||||
if (primitives.isEmpty()) {
|
||||
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
|
||||
+ effect.getSegments().get(segmentIndex));
|
||||
return skipToNextSteps(/* segmentsSkipped= */ 1);
|
||||
// Skip this step and play the next one right away.
|
||||
return nextSteps(/* segmentsPlayed= */ 1);
|
||||
}
|
||||
|
||||
if (VibrationThread.DEBUG) {
|
||||
Slog.d(VibrationThread.TAG, "Compose " + primitives + " primitives on vibrator "
|
||||
+ controller.getVibratorInfo().getId());
|
||||
+ getVibratorId());
|
||||
}
|
||||
|
||||
PrimitiveSegment[] primitivesArray =
|
||||
primitives.toArray(new PrimitiveSegment[primitives.size()]);
|
||||
mVibratorOnResult = controller.on(primitivesArray, getVibration().id);
|
||||
getVibration().stats().reportComposePrimitives(mVibratorOnResult, primitivesArray);
|
||||
long vibratorOnResult = controller.on(primitivesArray, getVibration().id);
|
||||
handleVibratorOnResult(vibratorOnResult);
|
||||
getVibration().stats().reportComposePrimitives(vibratorOnResult, primitivesArray);
|
||||
|
||||
// The next start and off times will be calculated from mVibratorOnResult.
|
||||
return nextSteps(/* segmentsPlayed= */ primitives.size());
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
|
||||
@@ -41,11 +41,11 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
|
||||
|
||||
ComposePwleVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
VibratorController controller, VibrationEffect.Composed effect, int index,
|
||||
long previousStepVibratorOffTimeout) {
|
||||
long pendingVibratorOffDeadline) {
|
||||
// This step should wait for the last vibration to finish (with the timeout) and for the
|
||||
// intended step start time (to respect the effect delays).
|
||||
super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
|
||||
index, previousStepVibratorOffTimeout);
|
||||
super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
|
||||
index, pendingVibratorOffDeadline);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +61,8 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
|
||||
if (pwles.isEmpty()) {
|
||||
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePwleStep: "
|
||||
+ effect.getSegments().get(segmentIndex));
|
||||
return skipToNextSteps(/* segmentsSkipped= */ 1);
|
||||
// Skip this step and play the next one right away.
|
||||
return nextSteps(/* segmentsPlayed= */ 1);
|
||||
}
|
||||
|
||||
if (VibrationThread.DEBUG) {
|
||||
@@ -69,9 +70,11 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
|
||||
+ controller.getVibratorInfo().getId());
|
||||
}
|
||||
RampSegment[] pwlesArray = pwles.toArray(new RampSegment[pwles.size()]);
|
||||
mVibratorOnResult = controller.on(pwlesArray, getVibration().id);
|
||||
getVibration().stats().reportComposePwle(mVibratorOnResult, pwlesArray);
|
||||
long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
|
||||
handleVibratorOnResult(vibratorOnResult);
|
||||
getVibration().stats().reportComposePwle(vibratorOnResult, pwlesArray);
|
||||
|
||||
// The next start and off times will be calculated from mVibratorOnResult.
|
||||
return nextSteps(/* segmentsPlayed= */ pwles.size());
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
|
||||
@@ -35,11 +35,11 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
|
||||
|
||||
PerformPrebakedVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
VibratorController controller, VibrationEffect.Composed effect, int index,
|
||||
long previousStepVibratorOffTimeout) {
|
||||
long pendingVibratorOffDeadline) {
|
||||
// This step should wait for the last vibration to finish (with the timeout) and for the
|
||||
// intended step start time (to respect the effect delays).
|
||||
super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
|
||||
index, previousStepVibratorOffTimeout);
|
||||
super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
|
||||
index, pendingVibratorOffDeadline);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,7 +50,8 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
|
||||
if (!(segment instanceof PrebakedSegment)) {
|
||||
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a "
|
||||
+ "PerformPrebakedVibratorStep: " + segment);
|
||||
return skipToNextSteps(/* segmentsSkipped= */ 1);
|
||||
// Skip this step and play the next one right away.
|
||||
return nextSteps(/* segmentsPlayed= */ 1);
|
||||
}
|
||||
|
||||
PrebakedSegment prebaked = (PrebakedSegment) segment;
|
||||
@@ -61,10 +62,11 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
|
||||
}
|
||||
|
||||
VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
|
||||
mVibratorOnResult = controller.on(prebaked, getVibration().id);
|
||||
getVibration().stats().reportPerformEffect(mVibratorOnResult, prebaked);
|
||||
long vibratorOnResult = controller.on(prebaked, getVibration().id);
|
||||
handleVibratorOnResult(vibratorOnResult);
|
||||
getVibration().stats().reportPerformEffect(vibratorOnResult, prebaked);
|
||||
|
||||
if (mVibratorOnResult == 0 && prebaked.shouldFallback()
|
||||
if (vibratorOnResult == 0 && prebaked.shouldFallback()
|
||||
&& (fallback instanceof VibrationEffect.Composed)) {
|
||||
if (VibrationThread.DEBUG) {
|
||||
Slog.d(VibrationThread.TAG, "Playing fallback for effect "
|
||||
@@ -72,14 +74,15 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
|
||||
}
|
||||
AbstractVibratorStep fallbackStep = conductor.nextVibrateStep(startTime, controller,
|
||||
replaceCurrentSegment((VibrationEffect.Composed) fallback),
|
||||
segmentIndex, previousStepVibratorOffTimeout);
|
||||
segmentIndex, mPendingVibratorOffDeadline);
|
||||
List<Step> fallbackResult = fallbackStep.play();
|
||||
// Update the result with the fallback result so this step is seamlessly
|
||||
// replaced by the fallback to any outer application of this.
|
||||
mVibratorOnResult = fallbackStep.getVibratorOnDuration();
|
||||
handleVibratorOnResult(fallbackStep.getVibratorOnDuration());
|
||||
return fallbackResult;
|
||||
}
|
||||
|
||||
// The next start and off times will be calculated from mVibratorOnResult.
|
||||
return nextSteps(/* segmentsPlayed= */ 1);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
|
||||
@@ -30,9 +30,9 @@ final class RampOffVibratorStep extends AbstractVibratorStep {
|
||||
|
||||
RampOffVibratorStep(VibrationStepConductor conductor, long startTime, float amplitudeTarget,
|
||||
float amplitudeDelta, VibratorController controller,
|
||||
long previousStepVibratorOffTimeout) {
|
||||
long pendingVibratorOffDeadline) {
|
||||
super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
|
||||
previousStepVibratorOffTimeout);
|
||||
pendingVibratorOffDeadline);
|
||||
mAmplitudeTarget = amplitudeTarget;
|
||||
mAmplitudeDelta = amplitudeDelta;
|
||||
}
|
||||
@@ -68,15 +68,17 @@ final class RampOffVibratorStep extends AbstractVibratorStep {
|
||||
|
||||
float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
|
||||
if (newAmplitudeTarget < VibrationStepConductor.RAMP_OFF_AMPLITUDE_MIN) {
|
||||
// Vibrator amplitude cannot go further down, just turn it off.
|
||||
// Vibrator amplitude cannot go further down, just turn it off with the configured
|
||||
// deadline that has been adjusted for the scenario when this was triggered by a
|
||||
// cancelled vibration.
|
||||
return Arrays.asList(new TurnOffVibratorStep(
|
||||
conductor, previousStepVibratorOffTimeout, controller));
|
||||
conductor, mPendingVibratorOffDeadline, controller));
|
||||
}
|
||||
return Arrays.asList(new RampOffVibratorStep(
|
||||
conductor,
|
||||
startTime + conductor.vibrationSettings.getRampStepDuration(),
|
||||
newAmplitudeTarget, mAmplitudeDelta, controller,
|
||||
previousStepVibratorOffTimeout));
|
||||
mPendingVibratorOffDeadline));
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
|
||||
@@ -39,26 +39,34 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
|
||||
*/
|
||||
private static final int REPEATING_EFFECT_ON_DURATION = 5000; // 5s
|
||||
|
||||
private long mNextOffTime;
|
||||
|
||||
SetAmplitudeVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
VibratorController controller, VibrationEffect.Composed effect, int index,
|
||||
long previousStepVibratorOffTimeout) {
|
||||
long pendingVibratorOffDeadline) {
|
||||
// This step has a fixed startTime coming from the timings of the waveform it's playing.
|
||||
super(conductor, startTime, controller, effect, index, previousStepVibratorOffTimeout);
|
||||
mNextOffTime = previousStepVibratorOffTimeout;
|
||||
super(conductor, startTime, controller, effect, index, pendingVibratorOffDeadline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptVibratorCompleteCallback(int vibratorId) {
|
||||
if (controller.getVibratorInfo().getId() == vibratorId) {
|
||||
mVibratorCompleteCallbackReceived = true;
|
||||
mNextOffTime = SystemClock.uptimeMillis();
|
||||
// Ensure the super method is called and will reset the off timeout and boolean flag.
|
||||
// This is true if the vibrator was ON and this callback has the same vibratorId.
|
||||
if (!super.acceptVibratorCompleteCallback(vibratorId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Timings are tightly controlled here, so only trigger this step if the vibrator was
|
||||
// supposed to be ON but has completed prematurely, to turn it back on as soon as
|
||||
// possible.
|
||||
return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
|
||||
// possible. If the vibrator turned off during a zero-amplitude step, just wait for
|
||||
// the correct start time of this step before playing it.
|
||||
boolean shouldAcceptCallback =
|
||||
(SystemClock.uptimeMillis() < startTime) && (controller.getCurrentAmplitude() > 0);
|
||||
|
||||
if (VibrationThread.DEBUG) {
|
||||
Slog.d(VibrationThread.TAG,
|
||||
"Amplitude step received completion callback from " + vibratorId
|
||||
+ ", accepted = " + shouldAcceptCallback);
|
||||
}
|
||||
return shouldAcceptCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,40 +86,38 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
|
||||
if (mVibratorCompleteCallbackReceived && latency < 0) {
|
||||
// This step was run early because the vibrator turned off prematurely.
|
||||
// Turn it back on and return this same step to run at the exact right time.
|
||||
mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
|
||||
turnVibratorBackOn(/* remainingDuration= */ -latency);
|
||||
return Arrays.asList(new SetAmplitudeVibratorStep(conductor, startTime, controller,
|
||||
effect, segmentIndex, mNextOffTime));
|
||||
effect, segmentIndex, mPendingVibratorOffDeadline));
|
||||
}
|
||||
|
||||
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
|
||||
if (!(segment instanceof StepSegment)) {
|
||||
Slog.w(VibrationThread.TAG,
|
||||
"Ignoring wrong segment for a SetAmplitudeVibratorStep: " + segment);
|
||||
return skipToNextSteps(/* segmentsSkipped= */ 1);
|
||||
// Use original startTime to avoid propagating latencies to the waveform.
|
||||
return nextSteps(startTime, /* segmentsPlayed= */ 1);
|
||||
}
|
||||
|
||||
StepSegment stepSegment = (StepSegment) segment;
|
||||
if (stepSegment.getDuration() == 0) {
|
||||
// Skip waveform entries with zero timing.
|
||||
return skipToNextSteps(/* segmentsSkipped= */ 1);
|
||||
// Use original startTime to avoid propagating latencies to the waveform.
|
||||
return nextSteps(startTime, /* segmentsPlayed= */ 1);
|
||||
}
|
||||
|
||||
float amplitude = stepSegment.getAmplitude();
|
||||
if (amplitude == 0) {
|
||||
if (previousStepVibratorOffTimeout > now) {
|
||||
if (mPendingVibratorOffDeadline > now) {
|
||||
// Amplitude cannot be set to zero, so stop the vibrator.
|
||||
stopVibrating();
|
||||
mNextOffTime = now;
|
||||
}
|
||||
} else {
|
||||
if (startTime >= mNextOffTime) {
|
||||
if (startTime >= mPendingVibratorOffDeadline) {
|
||||
// Vibrator is OFF. Turn vibrator back on for the duration of another
|
||||
// cycle before setting the amplitude.
|
||||
long onDuration = getVibratorOnDuration(effect, segmentIndex);
|
||||
if (onDuration > 0) {
|
||||
mVibratorOnResult = startVibrating(onDuration);
|
||||
mNextOffTime = now + onDuration
|
||||
+ VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
|
||||
startVibrating(onDuration);
|
||||
}
|
||||
}
|
||||
changeAmplitude(amplitude);
|
||||
@@ -119,27 +125,32 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
|
||||
|
||||
// Use original startTime to avoid propagating latencies to the waveform.
|
||||
long nextStartTime = startTime + segment.getDuration();
|
||||
return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
|
||||
return nextSteps(nextStartTime, /* segmentsPlayed= */ 1);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
}
|
||||
|
||||
private long turnVibratorBackOn(long remainingDuration) {
|
||||
private void turnVibratorBackOn(long remainingDuration) {
|
||||
long onDuration = getVibratorOnDuration(effect, segmentIndex);
|
||||
if (onDuration <= 0) {
|
||||
// Vibrator is supposed to go back off when this step starts, so just leave it off.
|
||||
return previousStepVibratorOffTimeout;
|
||||
return;
|
||||
}
|
||||
onDuration += remainingDuration;
|
||||
|
||||
if (VibrationThread.DEBUG) {
|
||||
Slog.d(VibrationThread.TAG,
|
||||
"Turning the vibrator back ON using the remaining duration of "
|
||||
+ remainingDuration + "ms, for a total of " + onDuration + "ms");
|
||||
}
|
||||
|
||||
float expectedAmplitude = controller.getCurrentAmplitude();
|
||||
mVibratorOnResult = startVibrating(onDuration);
|
||||
if (mVibratorOnResult > 0) {
|
||||
long vibratorOnResult = startVibrating(onDuration);
|
||||
if (vibratorOnResult > 0) {
|
||||
// Set the amplitude back to the value it was supposed to be playing at.
|
||||
changeAmplitude(expectedAmplitude);
|
||||
}
|
||||
return SystemClock.uptimeMillis() + onDuration
|
||||
+ VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
|
||||
}
|
||||
|
||||
private long startVibrating(long duration) {
|
||||
@@ -149,6 +160,7 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
|
||||
+ duration + "ms");
|
||||
}
|
||||
long vibratorOnResult = controller.on(duration, getVibration().id);
|
||||
handleVibratorOnResult(vibratorOnResult);
|
||||
getVibration().stats().reportVibratorOn(vibratorOnResult);
|
||||
return vibratorOnResult;
|
||||
}
|
||||
|
||||
@@ -112,8 +112,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
|
||||
|
||||
@Nullable
|
||||
AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
|
||||
VibrationEffect.Composed effect, int segmentIndex,
|
||||
long previousStepVibratorOffTimeout) {
|
||||
VibrationEffect.Composed effect, int segmentIndex, long pendingVibratorOffDeadline) {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
expectIsVibrationThread(true);
|
||||
}
|
||||
@@ -123,24 +122,24 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
|
||||
if (segmentIndex < 0) {
|
||||
// No more segments to play, last step is to complete the vibration on this vibrator.
|
||||
return new CompleteEffectVibratorStep(this, startTime, /* cancelled= */ false,
|
||||
controller, previousStepVibratorOffTimeout);
|
||||
controller, pendingVibratorOffDeadline);
|
||||
}
|
||||
|
||||
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
|
||||
if (segment instanceof PrebakedSegment) {
|
||||
return new PerformPrebakedVibratorStep(this, startTime, controller, effect,
|
||||
segmentIndex, previousStepVibratorOffTimeout);
|
||||
segmentIndex, pendingVibratorOffDeadline);
|
||||
}
|
||||
if (segment instanceof PrimitiveSegment) {
|
||||
return new ComposePrimitivesVibratorStep(this, startTime, controller, effect,
|
||||
segmentIndex, previousStepVibratorOffTimeout);
|
||||
segmentIndex, pendingVibratorOffDeadline);
|
||||
}
|
||||
if (segment instanceof RampSegment) {
|
||||
return new ComposePwleVibratorStep(this, startTime, controller, effect, segmentIndex,
|
||||
previousStepVibratorOffTimeout);
|
||||
pendingVibratorOffDeadline);
|
||||
}
|
||||
return new SetAmplitudeVibratorStep(this, startTime, controller, effect, segmentIndex,
|
||||
previousStepVibratorOffTimeout);
|
||||
pendingVibratorOffDeadline);
|
||||
}
|
||||
|
||||
/** Called when this conductor is going to be started running by the VibrationThread. */
|
||||
|
||||
@@ -387,8 +387,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
* An internal-only version of vibrate that allows the caller access to the {@link Vibration}.
|
||||
* The Vibration is only returned if it is ongoing after this method returns.
|
||||
*/
|
||||
@Nullable
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
Vibration vibrateInternal(int uid, String opPkg, @NonNull CombinedVibration effect,
|
||||
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
|
||||
@@ -1844,6 +1844,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
attrs, commonOptions.description, deathBinder);
|
||||
if (vib != null && !commonOptions.background) {
|
||||
try {
|
||||
// Waits for the client vibration to finish, but the VibrationThread may still
|
||||
// do cleanup after this.
|
||||
vib.waitForEnd();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
@@ -574,7 +574,7 @@ public class VibrationThreadTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() throws Exception {
|
||||
public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() {
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
||||
@@ -665,6 +665,47 @@ public class VibrationThreadTest {
|
||||
assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorComposedWithFallback_replacedInTheMiddleOfComposition() {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||
fakeVibrator.setSupportedPrimitives(
|
||||
VibrationEffect.Composition.PRIMITIVE_CLICK,
|
||||
VibrationEffect.Composition.PRIMITIVE_TICK);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect fallback = VibrationEffect.createOneShot(10, 100);
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
||||
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_TICK))
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
|
||||
.compose();
|
||||
Vibration vib = createVibration(vibrationId, CombinedVibration.createParallel(effect));
|
||||
vib.addFallback(VibrationEffect.EFFECT_TICK, fallback);
|
||||
startThreadAndDispatcher(vib);
|
||||
waitForCompletion();
|
||||
|
||||
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
|
||||
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
|
||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
List<VibrationEffectSegment> segments =
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId);
|
||||
assertTrue("Wrong segments: " + segments, segments.size() >= 4);
|
||||
assertTrue(segments.get(0) instanceof PrebakedSegment);
|
||||
assertTrue(segments.get(1) instanceof PrimitiveSegment);
|
||||
for (int i = 2; i < segments.size() - 1; i++) {
|
||||
// One or more step segments as fallback for the EFFECT_TICK.
|
||||
assertTrue(segments.get(i) instanceof StepSegment);
|
||||
}
|
||||
assertTrue(segments.get(segments.size() - 1) instanceof PrimitiveSegment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
|
||||
@@ -72,6 +72,7 @@ import android.os.VibratorInfo;
|
||||
import android.os.test.TestLooper;
|
||||
import android.os.vibrator.PrebakedSegment;
|
||||
import android.os.vibrator.PrimitiveSegment;
|
||||
import android.os.vibrator.StepSegment;
|
||||
import android.os.vibrator.VibrationConfig;
|
||||
import android.os.vibrator.VibrationEffectSegment;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
@@ -99,6 +100,7 @@ import org.mockito.junit.MockitoRule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -391,22 +393,22 @@ public class VibratorManagerServiceTest {
|
||||
IVibratorStateListener listenerMock = mockVibratorStateListener();
|
||||
service.registerVibratorStateListener(1, listenerMock);
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(40, 100), ALARM_ATTRS);
|
||||
// Wait until service knows vibrator is on.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
// Wait until effect ends.
|
||||
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
long oneShotDuration = 20;
|
||||
vibrateAndWaitUntilFinished(service,
|
||||
VibrationEffect.createOneShot(oneShotDuration, VibrationEffect.DEFAULT_AMPLITUDE),
|
||||
ALARM_ATTRS);
|
||||
|
||||
InOrder inOrderVerifier = inOrder(listenerMock);
|
||||
// First notification done when listener is registered.
|
||||
inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
|
||||
inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
|
||||
inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
|
||||
// The last notification is after the vibration has completed.
|
||||
inOrderVerifier.verify(listenerMock, timeout(TEST_TIMEOUT_MILLIS)).onVibrating(eq(false));
|
||||
inOrderVerifier.verifyNoMoreInteractions();
|
||||
|
||||
InOrder batteryVerifier = inOrder(mBatteryStatsMock);
|
||||
batteryVerifier.verify(mBatteryStatsMock)
|
||||
.noteVibratorOn(UID, 40 + mVibrationConfig.getRampDownDurationMs());
|
||||
.noteVibratorOn(UID, oneShotDuration + mVibrationConfig.getRampDownDurationMs());
|
||||
batteryVerifier.verify(mBatteryStatsMock).noteVibratorOff(UID);
|
||||
}
|
||||
|
||||
@@ -577,22 +579,18 @@ public class VibratorManagerServiceTest {
|
||||
|
||||
setRingerMode(AudioManager.RINGER_MODE_SILENT);
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||
// Wait before checking it never played.
|
||||
assertFalse(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(),
|
||||
service, /* timeout= */ 50));
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||
RINGTONE_ATTRS);
|
||||
|
||||
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
|
||||
service = createSystemReadyService();
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(
|
||||
service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
|
||||
|
||||
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
|
||||
service = createSystemReadyService();
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(
|
||||
service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
|
||||
|
||||
assertEquals(
|
||||
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||
@@ -607,27 +605,18 @@ public class VibratorManagerServiceTest {
|
||||
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_CLICK,
|
||||
VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
||||
|
||||
// The haptic feedback should be ignored in low power, but not the ringtone. The end
|
||||
// of the test asserts which actual effects ended up playing.
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
// Allow the ringtone to complete, as the other vibrations won't cancel it.
|
||||
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
|
||||
HAPTIC_FEEDBACK_ATTRS);
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||
RINGTONE_ATTRS);
|
||||
|
||||
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||
/* attrs= */ null);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
|
||||
NOTIFICATION_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(service,
|
||||
VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), /* attrs= */ null);
|
||||
vibrateAndWaitUntilFinished(service,
|
||||
VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), NOTIFICATION_ATTRS);
|
||||
|
||||
assertEquals(
|
||||
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK),
|
||||
@@ -693,22 +682,17 @@ public class VibratorManagerServiceTest {
|
||||
Vibrator.VIBRATION_INTENSITY_HIGH);
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
|
||||
VibrationAttributes enforceFreshAttrs = new VibrationAttributes.Builder()
|
||||
VibrationAttributes notificationWithFreshAttrs = new VibrationAttributes.Builder()
|
||||
.setUsage(VibrationAttributes.USAGE_NOTIFICATION)
|
||||
.setFlags(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)
|
||||
.build();
|
||||
|
||||
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
|
||||
Vibrator.VIBRATION_INTENSITY_LOW);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), NOTIFICATION_ATTRS);
|
||||
// VibrationThread will start this vibration async, so wait before vibrating a second time.
|
||||
assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 0,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), enforceFreshAttrs);
|
||||
// VibrationThread will start this vibration async, so wait before checking.
|
||||
assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 1,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||
NOTIFICATION_ATTRS);
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
|
||||
notificationWithFreshAttrs);
|
||||
|
||||
assertEquals(
|
||||
Arrays.asList(
|
||||
@@ -784,21 +768,22 @@ public class VibratorManagerServiceTest {
|
||||
vibrate(service, repeatingEffect, new VibrationAttributes.Builder().setUsage(
|
||||
VibrationAttributes.USAGE_UNKNOWN).build());
|
||||
|
||||
// VibrationThread will start this vibration async, so wait before checking it started.
|
||||
// VibrationThread will start this vibration async, wait until it has started.
|
||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), HAPTIC_FEEDBACK_ATTRS);
|
||||
|
||||
// Wait before checking it never played a second effect.
|
||||
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
|
||||
service, /* timeout= */ 50));
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||
HAPTIC_FEEDBACK_ATTRS);
|
||||
|
||||
// The time estimate is recorded when the vibration starts, repeating vibrations
|
||||
// are capped at BATTERY_STATS_REPEATING_VIBRATION_DURATION (=5000).
|
||||
verify(mBatteryStatsMock).noteVibratorOn(UID, 5000);
|
||||
// The second vibration shouldn't have recorded that the vibrators were turned on.
|
||||
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
|
||||
// No segment played is the prebaked CLICK from the second vibration.
|
||||
assertFalse(
|
||||
mVibratorProviders.get(1).getAllEffectSegments().stream()
|
||||
.anyMatch(segment -> segment instanceof PrebakedSegment));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -811,7 +796,7 @@ public class VibratorManagerServiceTest {
|
||||
new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
|
||||
vibrate(service, alarmEffect, ALARM_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait before checking it started.
|
||||
// VibrationThread will start this vibration async, wait until it has started.
|
||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
@@ -841,14 +826,15 @@ public class VibratorManagerServiceTest {
|
||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
|
||||
|
||||
// Wait before checking it never played a second effect.
|
||||
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
|
||||
service, /* timeout= */ 50));
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||
HAPTIC_FEEDBACK_ATTRS);
|
||||
|
||||
// The second vibration shouldn't have recorded that the vibrators were turned on.
|
||||
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
|
||||
// The second vibration shouldn't have played any prebaked segment.
|
||||
assertFalse(
|
||||
mVibratorProviders.get(1).getAllEffectSegments().stream()
|
||||
.anyMatch(segment -> segment instanceof PrebakedSegment));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -856,6 +842,7 @@ public class VibratorManagerServiceTest {
|
||||
throws Exception {
|
||||
mockVibrators(1);
|
||||
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
|
||||
VibrationEffect effect = VibrationEffect.createWaveform(
|
||||
@@ -866,14 +853,16 @@ public class VibratorManagerServiceTest {
|
||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, effect, RINGTONE_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait before checking it started.
|
||||
assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||
RINGTONE_ATTRS);
|
||||
|
||||
// The second vibration should have recorded that the vibrators were turned on.
|
||||
verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
|
||||
// One segment played is the prebaked CLICK from the second vibration.
|
||||
assertEquals(1,
|
||||
mVibratorProviders.get(1).getAllEffectSegments().stream()
|
||||
.filter(PrebakedSegment.class::isInstance)
|
||||
.count());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -892,12 +881,10 @@ public class VibratorManagerServiceTest {
|
||||
|
||||
CombinedVibration effect = CombinedVibration.createParallel(
|
||||
VibrationEffect.createOneShot(10, 10));
|
||||
vibrate(service, effect, ALARM_ATTRS);
|
||||
verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
|
||||
vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait before checking it never played.
|
||||
assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||
service, /* timeout= */ 50));
|
||||
verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
|
||||
assertTrue(mVibratorProviders.get(1).getAllEffectSegments().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -992,9 +979,7 @@ public class VibratorManagerServiceTest {
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.compose())
|
||||
.combine();
|
||||
vibrate(service, effect, ALARM_ATTRS);
|
||||
assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service,
|
||||
TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
|
||||
|
||||
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
||||
verify(mNativeWrapperMock).triggerSynced(anyLong());
|
||||
@@ -1016,9 +1001,7 @@ public class VibratorManagerServiceTest {
|
||||
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
|
||||
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
||||
.combine();
|
||||
vibrate(service, effect, ALARM_ATTRS);
|
||||
assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service,
|
||||
TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
|
||||
|
||||
verify(mNativeWrapperMock, never()).prepareSynced(any());
|
||||
verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
|
||||
@@ -1036,9 +1019,7 @@ public class VibratorManagerServiceTest {
|
||||
.addVibrator(1, VibrationEffect.createOneShot(10, 50))
|
||||
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
||||
.combine();
|
||||
vibrate(service, effect, ALARM_ATTRS);
|
||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
|
||||
|
||||
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
||||
verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
|
||||
@@ -1057,9 +1038,7 @@ public class VibratorManagerServiceTest {
|
||||
.addVibrator(1, VibrationEffect.createOneShot(10, 50))
|
||||
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
||||
.combine();
|
||||
vibrate(service, effect, ALARM_ATTRS);
|
||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
|
||||
|
||||
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
||||
verify(mNativeWrapperMock).triggerSynced(anyLong());
|
||||
@@ -1096,28 +1075,21 @@ public class VibratorManagerServiceTest {
|
||||
VibrationEffect.Composition.PRIMITIVE_TICK);
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
|
||||
vibrate(service, VibrationEffect.startComposition()
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
|
||||
.compose(), HAPTIC_FEEDBACK_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, CombinedVibration.startSequential()
|
||||
vibrateAndWaitUntilFinished(service, CombinedVibration.startSequential()
|
||||
.addNext(1, VibrationEffect.createOneShot(100, 125))
|
||||
.combine(), NOTIFICATION_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, VibrationEffect.startComposition()
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
||||
.compose(), ALARM_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
// Ring vibrations have intensity OFF and are not played.
|
||||
vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
|
||||
assertFalse(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() > 3,
|
||||
service, /* timeout= */ 50));
|
||||
vibrateAndWaitUntilFinished(service, VibrationEffect.createOneShot(100, 125),
|
||||
RINGTONE_ATTRS);
|
||||
|
||||
// Only 3 effects played successfully.
|
||||
assertEquals(3, fakeVibrator.getAllEffectSegments().size());
|
||||
@@ -1145,6 +1117,7 @@ public class VibratorManagerServiceTest {
|
||||
.combine(),
|
||||
HAPTIC_FEEDBACK_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait until vibration is triggered.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
||||
@@ -1159,13 +1132,49 @@ public class VibratorManagerServiceTest {
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait until vibration is triggered.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
service.updateServiceState();
|
||||
|
||||
// Vibration is not stopped nearly after updating service.
|
||||
assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_prebakedAndComposedVibrationsWithFallbacks_playsFallbackOnlyForPredefined()
|
||||
throws Exception {
|
||||
mockVibrators(1);
|
||||
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||
mVibratorProviders.get(1).setSupportedPrimitives(
|
||||
VibrationEffect.Composition.PRIMITIVE_CLICK);
|
||||
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
vibrateAndWaitUntilFinished(service,
|
||||
VibrationEffect.startComposition()
|
||||
.addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
|
||||
.addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.compose(),
|
||||
ALARM_ATTRS);
|
||||
|
||||
List<VibrationEffectSegment> segments = mVibratorProviders.get(1).getAllEffectSegments();
|
||||
// At least one step segment played as fallback for unusupported vibration effect
|
||||
assertTrue(segments.size() > 2);
|
||||
// 0: Supported effect played
|
||||
assertTrue(segments.get(0) instanceof PrebakedSegment);
|
||||
// 1: No segment for unsupported primitive
|
||||
// 2: One or more intermediate step segments as fallback for unsupported effect
|
||||
for (int i = 1; i < segments.size() - 1; i++) {
|
||||
assertTrue(segments.get(i) instanceof StepSegment);
|
||||
}
|
||||
// 3: Supported primitive played
|
||||
assertTrue(segments.get(segments.size() - 1) instanceof PrimitiveSegment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelVibrate_withoutUsageFilter_stopsVibrating() throws Exception {
|
||||
mockVibrators(1);
|
||||
@@ -1175,9 +1184,13 @@ public class VibratorManagerServiceTest {
|
||||
assertFalse(service.isVibrating(1));
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait until vibration is triggered.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
|
||||
|
||||
// Alarm cancelled on filter match all.
|
||||
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
}
|
||||
|
||||
@@ -1187,6 +1200,8 @@ public class VibratorManagerServiceTest {
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait until vibration is triggered.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
// Vibration is not cancelled with a different usage.
|
||||
@@ -1216,6 +1231,8 @@ public class VibratorManagerServiceTest {
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait until vibration is triggered.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
// Do not cancel UNKNOWN vibration when filter is being applied for other usages.
|
||||
@@ -1232,6 +1249,8 @@ public class VibratorManagerServiceTest {
|
||||
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait until vibration is triggered.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
// Cancel UNKNOWN vibration when all vibrations are being cancelled.
|
||||
@@ -1312,6 +1331,8 @@ public class VibratorManagerServiceTest {
|
||||
|
||||
VibrationEffect effect = VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100);
|
||||
vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
|
||||
|
||||
// VibrationThread will start this vibration async, so wait until vibration is triggered.
|
||||
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
|
||||
|
||||
Reference in New Issue
Block a user