Merge "Add support to slowly bring the vibrator down in VibrationThread" into sc-dev
This commit is contained in:
@@ -3370,8 +3370,7 @@
|
||||
<integer name="config_vibrationWaveformRampStepDuration">5</integer>
|
||||
|
||||
<!-- The duration (in milliseconds) that should be applied to waveform vibrations that ends in
|
||||
non-zero amplitudes, . The waveform will
|
||||
be played as a PWLE instead of on/off calls if this value is set. -->
|
||||
non-zero amplitudes, to bring the vibrator amplitude down to zero using this timing. -->
|
||||
<integer name="config_vibrationWaveformRampDownDuration">0</integer>
|
||||
|
||||
<!-- Number of retries Cell Data should attempt for a given error code before
|
||||
|
||||
@@ -99,16 +99,24 @@ final class VibrationSettings {
|
||||
private boolean mLowPowerMode;
|
||||
|
||||
VibrationSettings(Context context, Handler handler) {
|
||||
this(context, handler,
|
||||
context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_vibrationWaveformRampDownDuration),
|
||||
context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_vibrationWaveformRampStepDuration));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
VibrationSettings(Context context, Handler handler, int rampDownDuration,
|
||||
int rampStepDuration) {
|
||||
mContext = context;
|
||||
mSettingObserver = new SettingsObserver(handler);
|
||||
mUidObserver = new UidObserver();
|
||||
mUserReceiver = new UserObserver();
|
||||
|
||||
// TODO(b/191150049): move these to vibrator static config file
|
||||
mRampStepDuration = context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_vibrationWaveformRampStepDuration);
|
||||
mRampDownDuration = context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_vibrationWaveformRampDownDuration);
|
||||
mRampDownDuration = rampDownDuration;
|
||||
mRampStepDuration = rampStepDuration;
|
||||
|
||||
VibrationEffect clickEffect = createEffectFromResource(
|
||||
com.android.internal.R.array.config_virtualKeyVibePattern);
|
||||
|
||||
@@ -61,6 +61,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
*/
|
||||
private static final long CALLBACKS_EXTRA_TIMEOUT = 100;
|
||||
|
||||
/** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
|
||||
private static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
|
||||
|
||||
/** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
|
||||
private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
|
||||
|
||||
@@ -87,26 +90,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
/** Callback triggered to cancel a prepared synced vibration. */
|
||||
void cancelSyncedVibration();
|
||||
|
||||
/** Callback triggered when vibration thread is complete. */
|
||||
void onVibrationEnded(long vibrationId, Vibration.Status status);
|
||||
/** Callback triggered when the vibration is complete. */
|
||||
void onVibrationCompleted(long vibrationId, Vibration.Status status);
|
||||
|
||||
/** Callback triggered when the vibrators are released after the thread is complete. */
|
||||
void onVibratorsReleased();
|
||||
}
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final WorkSource mWorkSource = new WorkSource();
|
||||
private final PowerManager.WakeLock mWakeLock;
|
||||
private final IBatteryStats mBatteryStatsService;
|
||||
private final VibrationSettings mVibrationSettings;
|
||||
private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
|
||||
private final Vibration mVibration;
|
||||
private final VibrationCallbacks mCallbacks;
|
||||
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
|
||||
private final StepQueue mStepQueue = new StepQueue();
|
||||
|
||||
private volatile boolean mStop;
|
||||
private volatile boolean mForceStop;
|
||||
|
||||
VibrationThread(Vibration vib, DeviceVibrationEffectAdapter effectAdapter,
|
||||
VibrationThread(Vibration vib, VibrationSettings vibrationSettings,
|
||||
DeviceVibrationEffectAdapter effectAdapter,
|
||||
SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
|
||||
IBatteryStats batteryStatsService, VibrationCallbacks callbacks) {
|
||||
mVibration = vib;
|
||||
mVibrationSettings = vibrationSettings;
|
||||
mDeviceEffectAdapter = effectAdapter;
|
||||
mCallbacks = callbacks;
|
||||
mWakeLock = wakeLock;
|
||||
@@ -145,8 +155,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
mWakeLock.acquire();
|
||||
try {
|
||||
mVibration.token.linkToDeath(this, 0);
|
||||
Vibration.Status status = playVibration();
|
||||
mCallbacks.onVibrationEnded(mVibration.id, status);
|
||||
playVibration();
|
||||
mCallbacks.onVibratorsReleased();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error linking vibration to token death", e);
|
||||
} finally {
|
||||
@@ -155,9 +165,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
}
|
||||
|
||||
/** Cancel current vibration and shuts down the thread gracefully. */
|
||||
/** Cancel current vibration and ramp down the vibrators gracefully. */
|
||||
public void cancel() {
|
||||
mForceStop = true;
|
||||
if (mStop) {
|
||||
// Already cancelled, running clean-up steps.
|
||||
return;
|
||||
}
|
||||
mStop = true;
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Vibration cancelled");
|
||||
@@ -166,6 +180,21 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
}
|
||||
|
||||
/** Cancel current vibration and shuts off the vibrators immediately. */
|
||||
public void cancelImmediately() {
|
||||
if (mForceStop) {
|
||||
// Already forced the thread to stop, wait for it to finish.
|
||||
return;
|
||||
}
|
||||
mStop = mForceStop = true;
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Vibration cancelled immediately");
|
||||
}
|
||||
mLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/** Notify current vibration that a synced step has completed. */
|
||||
public void syncedVibrationComplete() {
|
||||
synchronized (mLock) {
|
||||
@@ -190,17 +219,18 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
}
|
||||
|
||||
private Vibration.Status playVibration() {
|
||||
private void playVibration() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
|
||||
try {
|
||||
CombinedVibration.Sequential effect = toSequential(mVibration.getEffect());
|
||||
mStepQueue.offer(new StartVibrateStep(effect));
|
||||
CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
|
||||
final int sequentialEffectSize = sequentialEffect.getEffects().size();
|
||||
mStepQueue.offer(new StartVibrateStep(sequentialEffect));
|
||||
|
||||
int stepsPlayed = 0;
|
||||
Vibration.Status status = null;
|
||||
while (!mStepQueue.isEmpty()) {
|
||||
long waitTime = mStepQueue.calculateWaitTime();
|
||||
if (waitTime <= 0) {
|
||||
stepsPlayed += mStepQueue.consumeNext();
|
||||
mStepQueue.consumeNext();
|
||||
} else {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
@@ -209,17 +239,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
}
|
||||
}
|
||||
Vibration.Status currentStatus = mStop ? Vibration.Status.CANCELLED
|
||||
: mStepQueue.calculateVibrationStatus(sequentialEffectSize);
|
||||
if (status == null && currentStatus != Vibration.Status.RUNNING) {
|
||||
// First time vibration stopped running, start clean-up tasks and notify
|
||||
// callback immediately.
|
||||
status = currentStatus;
|
||||
mCallbacks.onVibrationCompleted(mVibration.id, status);
|
||||
if (status == Vibration.Status.CANCELLED) {
|
||||
mStepQueue.cancel();
|
||||
}
|
||||
}
|
||||
if (mForceStop) {
|
||||
mStepQueue.cancel();
|
||||
return Vibration.Status.CANCELLED;
|
||||
// Cancel every step and stop playing them right away, even clean-up steps.
|
||||
mStepQueue.cancelImmediately();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Some effects might be ignored because the specified vibrator don't exist or doesn't
|
||||
// support the effect. We only report ignored here if nothing was played besides the
|
||||
// StartVibrateStep (which means every attempt to turn on the vibrator was ignored).
|
||||
return stepsPlayed > effect.getEffects().size()
|
||||
? Vibration.Status.FINISHED : Vibration.Status.IGNORED_UNSUPPORTED;
|
||||
if (status == null) {
|
||||
status = mStepQueue.calculateVibrationStatus(sequentialEffectSize);
|
||||
if (status == Vibration.Status.RUNNING) {
|
||||
Slog.w(TAG, "Something went wrong, step queue completed but vibration status"
|
||||
+ " is still RUNNING for vibration " + mVibration.id);
|
||||
status = Vibration.Status.FINISHED;
|
||||
}
|
||||
mCallbacks.onVibrationCompleted(mVibration.id, status);
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
@@ -260,12 +306,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
segmentIndex = effect.getRepeatIndex();
|
||||
}
|
||||
if (segmentIndex < 0) {
|
||||
if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
|
||||
// No more segments to play, last step is to wait for the vibrator to complete
|
||||
return new OffStep(vibratorOffTimeout, controller);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
// No more segments to play, last step is to complete the vibration on this vibrator.
|
||||
return new CompleteStep(startTime, /* cancelled= */ false, controller,
|
||||
vibratorOffTimeout);
|
||||
}
|
||||
|
||||
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
|
||||
@@ -299,8 +342,18 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
@GuardedBy("mLock")
|
||||
private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private int mPendingVibrateSteps;
|
||||
@GuardedBy("mLock")
|
||||
private int mConsumedStartVibrateSteps;
|
||||
@GuardedBy("mLock")
|
||||
private int mSuccessfulVibratorOnSteps;
|
||||
|
||||
public void offer(@NonNull Step step) {
|
||||
synchronized (mLock) {
|
||||
if (!step.isCleanUp()) {
|
||||
mPendingVibrateSteps++;
|
||||
}
|
||||
mNextSteps.offer(step);
|
||||
}
|
||||
}
|
||||
@@ -311,6 +364,24 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the {@link Vibration.Status} based on the current queue state and the expected
|
||||
* number of {@link StartVibrateStep} to be played.
|
||||
*/
|
||||
public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) {
|
||||
synchronized (mLock) {
|
||||
if (mPendingVibrateSteps > 0
|
||||
|| mConsumedStartVibrateSteps < expectedStartVibrateSteps) {
|
||||
return Vibration.Status.RUNNING;
|
||||
}
|
||||
if (mSuccessfulVibratorOnSteps > 0) {
|
||||
return Vibration.Status.FINISHED;
|
||||
}
|
||||
// If no step was able to turn the vibrator ON successfully.
|
||||
return Vibration.Status.IGNORED_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the time in millis to wait before calling {@link #consumeNext()}. */
|
||||
public long calculateWaitTime() {
|
||||
Step nextStep;
|
||||
@@ -330,18 +401,28 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
*
|
||||
* @return the number of steps played
|
||||
*/
|
||||
public int consumeNext() {
|
||||
public void consumeNext() {
|
||||
Step nextStep = pollNext();
|
||||
if (nextStep != null) {
|
||||
// This might turn on the vibrator and have a HAL latency. Execute this outside any
|
||||
// lock to avoid blocking other interactions with the thread.
|
||||
List<Step> nextSteps = nextStep.play();
|
||||
synchronized (mLock) {
|
||||
if (nextStep.getVibratorOnDuration() > 0) {
|
||||
mSuccessfulVibratorOnSteps++;
|
||||
}
|
||||
if (nextStep instanceof StartVibrateStep) {
|
||||
mConsumedStartVibrateSteps++;
|
||||
}
|
||||
if (!nextStep.isCleanUp()) {
|
||||
mPendingVibrateSteps--;
|
||||
}
|
||||
for (int i = 0; i < nextSteps.size(); i++) {
|
||||
mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
|
||||
}
|
||||
mNextSteps.addAll(nextSteps);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,16 +449,38 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the current queue, clearing all remaining steps.
|
||||
* Cancel the current queue, replacing all remaining steps with respective clean-up steps.
|
||||
*
|
||||
* <p>This will remove and trigger {@link Step#cancel()} in all steps, in order.
|
||||
* <p>This will remove all steps and replace them with respective
|
||||
* {@link Step#cancel()}.
|
||||
*/
|
||||
public void cancel() {
|
||||
List<Step> cleanUpSteps = new ArrayList<>();
|
||||
Step step;
|
||||
while ((step = pollNext()) != null) {
|
||||
cleanUpSteps.addAll(step.cancel());
|
||||
}
|
||||
synchronized (mLock) {
|
||||
// All steps generated by Step.cancel() should be clean-up steps.
|
||||
mPendingVibrateSteps = 0;
|
||||
mNextSteps.addAll(cleanUpSteps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
|
||||
*
|
||||
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
|
||||
*/
|
||||
public void cancelImmediately() {
|
||||
Step step;
|
||||
while ((step = pollNext()) != null) {
|
||||
// This might turn off the vibrator and have a HAL latency. Execute this outside
|
||||
// any lock to avoid blocking other interactions with the thread.
|
||||
step.cancel();
|
||||
step.cancelImmediately();
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mPendingVibrateSteps = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,12 +509,37 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or
|
||||
* {@link CombinedVibration}.
|
||||
*/
|
||||
public boolean isCleanUp() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Play this step, returning a (possibly empty) list of next steps. */
|
||||
@NonNull
|
||||
public abstract List<Step> play();
|
||||
|
||||
/** Cancel this pending step. */
|
||||
public void cancel() {
|
||||
/**
|
||||
* Cancel this pending step and return a (possibly empty) list of clean-up steps that should
|
||||
* be played to gracefully cancel this step.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract List<Step> cancel();
|
||||
|
||||
/** Cancel this pending step immediately, skipping any clean-up. */
|
||||
public abstract void cancelImmediately();
|
||||
|
||||
/**
|
||||
* Return the duration the vibrator was turned on when this step was played.
|
||||
*
|
||||
* @return A positive duration that the vibrator was turned on for by this step;
|
||||
* Zero if the segment is not supported, the step was not played yet or vibrator was never
|
||||
* turned on by this step; A negative value if the vibrator call has failed.
|
||||
*/
|
||||
public long getVibratorOnDuration() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,6 +580,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
public final CombinedVibration.Sequential sequentialEffect;
|
||||
public final int currentIndex;
|
||||
|
||||
private long mVibratorsOnMaxDuration;
|
||||
|
||||
StartVibrateStep(CombinedVibration.Sequential effect) {
|
||||
this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
|
||||
}
|
||||
@@ -462,11 +592,16 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
currentIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVibratorOnDuration() {
|
||||
return mVibratorsOnMaxDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> play() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
|
||||
List<Step> nextSteps = new ArrayList<>();
|
||||
long duration = -1;
|
||||
mVibratorsOnMaxDuration = -1;
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
|
||||
@@ -478,25 +613,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
return nextSteps;
|
||||
}
|
||||
|
||||
duration = startVibrating(effectMapping, nextSteps);
|
||||
noteVibratorOn(duration);
|
||||
mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
|
||||
noteVibratorOn(mVibratorsOnMaxDuration);
|
||||
} finally {
|
||||
if (duration < 0) {
|
||||
// Something failed while playing this step so stop playing this sequence.
|
||||
return EMPTY_STEP_LIST;
|
||||
}
|
||||
// It least one vibrator was started then add a finish step to wait for all
|
||||
// active vibrators to finish their individual steps before going to the next.
|
||||
// Otherwise this step was ignored so just go to the next one.
|
||||
Step nextStep = duration > 0 ? new FinishVibrateStep(this) : nextStep();
|
||||
if (nextStep != null) {
|
||||
nextSteps.add(nextStep);
|
||||
if (mVibratorsOnMaxDuration >= 0) {
|
||||
// It least one vibrator was started then add a finish step to wait for all
|
||||
// active vibrators to finish their individual steps before going to the next.
|
||||
// Otherwise this step was ignored so just go to the next one.
|
||||
Step nextStep =
|
||||
mVibratorsOnMaxDuration > 0 ? new FinishVibrateStep(this) : nextStep();
|
||||
if (nextStep != null) {
|
||||
nextSteps.add(nextStep);
|
||||
}
|
||||
}
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
return nextSteps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> cancel() {
|
||||
return EMPTY_STEP_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelImmediately() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
|
||||
* time this method is called, or null if sequence is complete.
|
||||
@@ -593,7 +736,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
// Some vibrator failed without being prepared so other vibrators might be
|
||||
// active. Cancel and remove every pending step from output list.
|
||||
for (int i = nextSteps.size() - 1; i >= 0; i--) {
|
||||
nextSteps.remove(i).cancel();
|
||||
nextSteps.remove(i).cancelImmediately();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -626,6 +769,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
this.startedStep = startedStep;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCleanUp() {
|
||||
// This step only notes that all the vibrators has been turned off.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> play() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
|
||||
@@ -642,7 +791,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
public List<Step> cancel() {
|
||||
cancelImmediately();
|
||||
return EMPTY_STEP_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelImmediately() {
|
||||
noteVibratorOff();
|
||||
}
|
||||
}
|
||||
@@ -658,6 +813,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
public final long vibratorOffTimeout;
|
||||
|
||||
long mVibratorOnResult;
|
||||
boolean mVibratorCallbackReceived;
|
||||
|
||||
/**
|
||||
* @param startTime The time to schedule this step in the {@link StepQueue}.
|
||||
@@ -678,27 +834,28 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
this.vibratorOffTimeout = vibratorOffTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the duration the vibrator was turned on when this step was played.
|
||||
*
|
||||
* @return A positive duration that the vibrator was turned on for by this step;
|
||||
* Zero if the segment is not supported, the step was not played yet or vibrator was never
|
||||
* turned on by this step; A negative value if the vibrator call has failed.
|
||||
*/
|
||||
@Override
|
||||
public long getVibratorOnDuration() {
|
||||
return mVibratorOnResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
|
||||
boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
|
||||
mVibratorCallbackReceived |= isSameVibrator;
|
||||
// Only anticipate 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 (controller.getVibratorInfo().getId() == vibratorId)
|
||||
&& (vibratorOffTimeout > SystemClock.uptimeMillis());
|
||||
return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
public List<Step> cancel() {
|
||||
return Arrays.asList(new CompleteStep(SystemClock.uptimeMillis(),
|
||||
/* cancelled= */ true, controller, vibratorOffTimeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelImmediately() {
|
||||
if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
|
||||
// Vibrator might be running from previous steps, so turn it off while canceling.
|
||||
stopVibrating();
|
||||
@@ -712,6 +869,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
controller.off();
|
||||
}
|
||||
|
||||
void changeAmplitude(float amplitude) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
|
||||
+ " to " + amplitude);
|
||||
}
|
||||
controller.setAmplitude(amplitude);
|
||||
}
|
||||
|
||||
/** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */
|
||||
public List<Step> skipToNextSteps(int segmentsSkipped) {
|
||||
return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped);
|
||||
@@ -937,6 +1102,140 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a step to complete a {@link VibrationEffect}.
|
||||
*
|
||||
* <p>This runs right at the time the vibration is considered to end and will update the pending
|
||||
* vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
|
||||
*/
|
||||
private final class CompleteStep extends SingleVibratorStep {
|
||||
private final boolean mCancelled;
|
||||
|
||||
CompleteStep(long startTime, boolean cancelled, VibratorController controller,
|
||||
long vibratorOffTimeout) {
|
||||
super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
|
||||
mCancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCleanUp() {
|
||||
// If the vibration was cancelled then this is just a clean up to ramp off the vibrator.
|
||||
// Otherwise this step is part of the vibration.
|
||||
return mCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> cancel() {
|
||||
if (mCancelled) {
|
||||
// Double cancelling will just turn off the vibrator right away.
|
||||
return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
|
||||
}
|
||||
return super.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> play() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteStep");
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
|
||||
+ " step on vibrator " + controller.getVibratorInfo().getId());
|
||||
}
|
||||
if (mVibratorCallbackReceived) {
|
||||
// Vibration completion callback was received by this step, just turn if off
|
||||
// and skip any clean-up.
|
||||
stopVibrating();
|
||||
return EMPTY_STEP_LIST;
|
||||
}
|
||||
|
||||
float currentAmplitude = controller.getCurrentAmplitude();
|
||||
long remainingOnDuration =
|
||||
vibratorOffTimeout - CALLBACKS_EXTRA_TIMEOUT - SystemClock.uptimeMillis();
|
||||
long rampDownDuration =
|
||||
Math.min(remainingOnDuration, mVibrationSettings.getRampDownDuration());
|
||||
long stepDownDuration = mVibrationSettings.getRampStepDuration();
|
||||
if (currentAmplitude < RAMP_OFF_AMPLITUDE_MIN
|
||||
|| rampDownDuration <= stepDownDuration) {
|
||||
// No need to ramp down the amplitude, just wait to turn it off.
|
||||
if (mCancelled) {
|
||||
// Vibration is completing because it was cancelled, turn off right away.
|
||||
stopVibrating();
|
||||
return EMPTY_STEP_LIST;
|
||||
} else {
|
||||
return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Ramping down vibrator " + controller.getVibratorInfo().getId()
|
||||
+ " from amplitude " + currentAmplitude
|
||||
+ " for " + rampDownDuration + "ms");
|
||||
}
|
||||
float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
|
||||
float amplitudeTarget = currentAmplitude - amplitudeDelta;
|
||||
long newVibratorOffTimeout = mCancelled ? rampDownDuration : vibratorOffTimeout;
|
||||
return Arrays.asList(new RampOffStep(startTime, amplitudeTarget, amplitudeDelta,
|
||||
controller, newVibratorOffTimeout));
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents a step to ramp down the vibrator amplitude before turning it off. */
|
||||
private final class RampOffStep extends SingleVibratorStep {
|
||||
private final float mAmplitudeTarget;
|
||||
private final float mAmplitudeDelta;
|
||||
|
||||
RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta,
|
||||
VibratorController controller, long vibratorOffTimeout) {
|
||||
super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
|
||||
mAmplitudeTarget = amplitudeTarget;
|
||||
mAmplitudeDelta = amplitudeDelta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCleanUp() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> cancel() {
|
||||
return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> play() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffStep");
|
||||
try {
|
||||
if (DEBUG) {
|
||||
long latency = SystemClock.uptimeMillis() - startTime;
|
||||
Slog.d(TAG, "Ramp down the vibrator amplitude, step with "
|
||||
+ latency + "ms latency.");
|
||||
}
|
||||
if (mVibratorCallbackReceived) {
|
||||
// Vibration completion callback was received by this step, just turn if off
|
||||
// and skip the rest of the steps to ramp down the vibrator amplitude.
|
||||
stopVibrating();
|
||||
return EMPTY_STEP_LIST;
|
||||
}
|
||||
|
||||
changeAmplitude(mAmplitudeTarget);
|
||||
|
||||
float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
|
||||
if (newAmplitudeTarget < RAMP_OFF_AMPLITUDE_MIN) {
|
||||
// Vibrator amplitude cannot go further down, just turn it off.
|
||||
return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
|
||||
}
|
||||
return Arrays.asList(new RampOffStep(
|
||||
startTime + mVibrationSettings.getRampStepDuration(), newAmplitudeTarget,
|
||||
mAmplitudeDelta, controller, vibratorOffTimeout));
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a step to turn the vibrator off.
|
||||
*
|
||||
@@ -949,6 +1248,21 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCleanUp() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> cancel() {
|
||||
return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelImmediately() {
|
||||
stopVibrating();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Step> play() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep");
|
||||
@@ -1044,14 +1358,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
return controller.on(duration, mVibration.id);
|
||||
}
|
||||
|
||||
private void changeAmplitude(float amplitude) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
|
||||
+ " to " + amplitude);
|
||||
}
|
||||
controller.setAmplitude(amplitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
|
||||
* until the next time it's vibrating amplitude is zero or a different type of segment is
|
||||
@@ -1080,6 +1386,11 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
if (i == segmentCount && effect.getRepeatIndex() < 0) {
|
||||
// Vibration ending at non-zero amplitude, add extra timings to ramp down after
|
||||
// vibration is complete.
|
||||
timing += mVibrationSettings.getRampDownDuration();
|
||||
}
|
||||
return timing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ final class VibratorController {
|
||||
private boolean mIsVibrating;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsUnderExternalControl;
|
||||
@GuardedBy("mLock")
|
||||
private float mCurrentAmplitude;
|
||||
|
||||
/** Listener for vibration completion callbacks from native. */
|
||||
public interface OnVibrationCompleteListener {
|
||||
@@ -131,6 +133,23 @@ final class VibratorController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current amplitude the device is vibrating.
|
||||
*
|
||||
* <p>This value is set to 1 by the method {@link #on(long, long)}, and can be updated via
|
||||
* {@link #setAmplitude(float)} if called while the device is vibrating.
|
||||
*
|
||||
* <p>If the device is vibrating via any other {@link #on} method then the current amplitude is
|
||||
* unknown and this will return -1.
|
||||
*
|
||||
* <p>If {@link #isVibrating()} is false then this will be zero.
|
||||
*/
|
||||
public float getCurrentAmplitude() {
|
||||
synchronized (mLock) {
|
||||
return mCurrentAmplitude;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return {@code true} if this vibrator is under external control, false otherwise. */
|
||||
public boolean isUnderExternalControl() {
|
||||
synchronized (mLock) {
|
||||
@@ -192,6 +211,9 @@ final class VibratorController {
|
||||
if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
|
||||
mNativeWrapper.setAmplitude(amplitude);
|
||||
}
|
||||
if (mIsVibrating) {
|
||||
mCurrentAmplitude = amplitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +230,7 @@ final class VibratorController {
|
||||
synchronized (mLock) {
|
||||
long duration = mNativeWrapper.on(milliseconds, vibrationId);
|
||||
if (duration > 0) {
|
||||
mCurrentAmplitude = -1;
|
||||
notifyVibratorOnLocked();
|
||||
}
|
||||
return duration;
|
||||
@@ -228,6 +251,7 @@ final class VibratorController {
|
||||
long duration = mNativeWrapper.perform(prebaked.getEffectId(),
|
||||
prebaked.getEffectStrength(), vibrationId);
|
||||
if (duration > 0) {
|
||||
mCurrentAmplitude = -1;
|
||||
notifyVibratorOnLocked();
|
||||
}
|
||||
return duration;
|
||||
@@ -250,6 +274,7 @@ final class VibratorController {
|
||||
synchronized (mLock) {
|
||||
long duration = mNativeWrapper.compose(primitives, vibrationId);
|
||||
if (duration > 0) {
|
||||
mCurrentAmplitude = -1;
|
||||
notifyVibratorOnLocked();
|
||||
}
|
||||
return duration;
|
||||
@@ -272,6 +297,7 @@ final class VibratorController {
|
||||
int braking = mVibratorInfo.getDefaultBraking();
|
||||
long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
|
||||
if (duration > 0) {
|
||||
mCurrentAmplitude = -1;
|
||||
notifyVibratorOnLocked();
|
||||
}
|
||||
return duration;
|
||||
@@ -282,19 +308,23 @@ final class VibratorController {
|
||||
public void off() {
|
||||
synchronized (mLock) {
|
||||
mNativeWrapper.off();
|
||||
mCurrentAmplitude = 0;
|
||||
notifyVibratorOffLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VibratorController{"
|
||||
+ "mVibratorInfo=" + mVibratorInfo
|
||||
+ ", mIsVibrating=" + mIsVibrating
|
||||
+ ", mIsUnderExternalControl=" + mIsUnderExternalControl
|
||||
+ ", mVibratorStateListeners count="
|
||||
+ mVibratorStateListeners.getRegisteredCallbackCount()
|
||||
+ '}';
|
||||
synchronized (mLock) {
|
||||
return "VibratorController{"
|
||||
+ "mVibratorInfo=" + mVibratorInfo
|
||||
+ ", mIsVibrating=" + mIsVibrating
|
||||
+ ", mCurrentAmplitude=" + mCurrentAmplitude
|
||||
+ ", mIsUnderExternalControl=" + mIsUnderExternalControl
|
||||
+ ", mVibratorStateListeners count="
|
||||
+ mVibratorStateListeners.getRegisteredCallbackCount()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
|
||||
@@ -514,8 +514,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
|
||||
}
|
||||
|
||||
VibrationThread vibThread = new VibrationThread(vib, mDeviceVibrationEffectAdapter,
|
||||
mVibrators, mWakeLock, mBatteryStatsService, mVibrationCallbacks);
|
||||
VibrationThread vibThread = new VibrationThread(vib, mVibrationSettings,
|
||||
mDeviceVibrationEffectAdapter, mVibrators, mWakeLock, mBatteryStatsService,
|
||||
mVibrationCallbacks);
|
||||
|
||||
if (mCurrentVibration == null) {
|
||||
return startVibrationThreadLocked(vibThread);
|
||||
@@ -569,7 +570,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
try {
|
||||
Vibration vib = mCurrentVibration.getVibration();
|
||||
mCurrentVibration = null;
|
||||
endVibrationLocked(vib, status);
|
||||
finishAppOpModeLocked(vib.uid, vib.opPkg);
|
||||
} finally {
|
||||
@@ -613,7 +613,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
// Repeating vibrations always take precedence.
|
||||
return null;
|
||||
}
|
||||
if (mCurrentVibration != null) {
|
||||
if (mCurrentVibration != null && !mCurrentVibration.getVibration().hasEnded()) {
|
||||
if (mCurrentVibration.getVibration().attrs.getUsage()
|
||||
== VibrationAttributes.USAGE_ALARM) {
|
||||
if (DEBUG) {
|
||||
@@ -1003,20 +1003,29 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVibrationEnded(long vibrationId, Vibration.Status status) {
|
||||
public void onVibrationCompleted(long vibrationId, Vibration.Status status) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Vibration " + vibrationId + " thread finished with status " + status);
|
||||
Slog.d(TAG, "Vibration " + vibrationId + " finished with status " + status);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (mCurrentVibration != null
|
||||
&& mCurrentVibration.getVibration().id == vibrationId) {
|
||||
reportFinishedVibrationLocked(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mNextVibration != null) {
|
||||
VibrationThread vibThread = mNextVibration;
|
||||
mNextVibration = null;
|
||||
startVibrationThreadLocked(vibThread);
|
||||
}
|
||||
@Override
|
||||
public void onVibratorsReleased() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Vibrators released after finished vibration");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mCurrentVibration = null;
|
||||
if (mNextVibration != null) {
|
||||
VibrationThread vibThread = mNextVibration;
|
||||
mNextVibration = null;
|
||||
startVibrationThreadLocked(vibThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1337,7 +1346,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
||||
// vibration that may be playing and ready the vibrator for external control.
|
||||
if (mCurrentVibration != null) {
|
||||
mNextVibration = null;
|
||||
mCurrentVibration.cancel();
|
||||
mCurrentVibration.cancelImmediately();
|
||||
cancelingVibration = mCurrentVibration;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -86,6 +87,7 @@ public class VibrationThreadTest {
|
||||
private static final int VIBRATOR_ID = 1;
|
||||
private static final String PACKAGE_NAME = "package";
|
||||
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
|
||||
private static final int TEST_RAMP_STEP_DURATION = 5;
|
||||
|
||||
@Rule
|
||||
public MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
@@ -100,6 +102,7 @@ public class VibrationThreadTest {
|
||||
private IBatteryStats mIBatteryStatsMock;
|
||||
|
||||
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
|
||||
private VibrationSettings mVibrationSettings;
|
||||
private DeviceVibrationEffectAdapter mEffectAdapter;
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
private TestLooper mTestLooper;
|
||||
@@ -109,9 +112,9 @@ public class VibrationThreadTest {
|
||||
mTestLooper = new TestLooper();
|
||||
|
||||
Context context = InstrumentationRegistry.getContext();
|
||||
VibrationSettings vibrationSettings = new VibrationSettings(context,
|
||||
new Handler(mTestLooper.getLooper()));
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(vibrationSettings);
|
||||
mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
|
||||
/* rampDownDuration= */ 0, TEST_RAMP_STEP_DURATION);
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||
mWakeLock = context.getSystemService(
|
||||
PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
|
||||
|
||||
@@ -128,8 +131,7 @@ public class VibrationThreadTest {
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,8 +145,7 @@ public class VibrationThreadTest {
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -159,7 +160,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||
@@ -178,7 +179,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||
@@ -200,7 +201,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(15L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
assertEquals(Arrays.asList(expectedOneShot(15)),
|
||||
@@ -232,7 +233,7 @@ public class VibrationThreadTest {
|
||||
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
|
||||
@@ -269,7 +270,7 @@ public class VibrationThreadTest {
|
||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||
waitForCompletion(cancellingThread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
}
|
||||
|
||||
@@ -294,7 +295,7 @@ public class VibrationThreadTest {
|
||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||
waitForCompletion(cancellingThread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
}
|
||||
|
||||
@@ -310,7 +311,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
|
||||
@@ -333,7 +334,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||
@@ -352,8 +353,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
|
||||
verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(40L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(Arrays.asList(
|
||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
||||
@@ -393,8 +393,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
|
||||
verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||
}
|
||||
|
||||
@@ -413,7 +412,7 @@ public class VibrationThreadTest {
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
// Vibrator compose called twice.
|
||||
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
assertEquals(3, fakeVibrator.getEffectSegments().size());
|
||||
@@ -443,7 +442,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(Arrays.asList(
|
||||
expectedOneShot(10),
|
||||
@@ -479,7 +478,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(100L));
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(Arrays.asList(
|
||||
expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10),
|
||||
@@ -512,7 +511,7 @@ public class VibrationThreadTest {
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
// Vibrator compose called twice.
|
||||
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
assertEquals(4, fakeVibrator.getEffectSegments().size());
|
||||
@@ -537,7 +536,7 @@ public class VibrationThreadTest {
|
||||
waitForCompletion(thread);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -545,10 +544,10 @@ public class VibrationThreadTest {
|
||||
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
|
||||
long vibrationId = 1;
|
||||
waitForCompletion(startThreadAndDispatcher(vibrationId++,
|
||||
waitForCompletion(startThreadAndDispatcher(vibrationId,
|
||||
VibrationEffect.createOneShot(10, 100)));
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(anyLong(), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any());
|
||||
verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong());
|
||||
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
||||
@@ -571,7 +570,7 @@ public class VibrationThreadTest {
|
||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
|
||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
|
||||
@@ -596,7 +595,7 @@ public class VibrationThreadTest {
|
||||
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
|
||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||
@@ -635,7 +634,7 @@ public class VibrationThreadTest {
|
||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||
verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||
@@ -686,7 +685,7 @@ public class VibrationThreadTest {
|
||||
batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
|
||||
batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||
@@ -727,7 +726,7 @@ public class VibrationThreadTest {
|
||||
verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
|
||||
verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
|
||||
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
|
||||
VibrationEffectSegment expected = expectedPrimitive(
|
||||
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
|
||||
@@ -767,7 +766,7 @@ public class VibrationThreadTest {
|
||||
verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
|
||||
verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
|
||||
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -858,7 +857,7 @@ public class VibrationThreadTest {
|
||||
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
|
||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||
@@ -935,7 +934,7 @@ public class VibrationThreadTest {
|
||||
|
||||
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
|
||||
waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS);
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
}
|
||||
|
||||
@@ -968,7 +967,7 @@ public class VibrationThreadTest {
|
||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||
waitForCompletion(cancellingThread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
||||
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
||||
}
|
||||
@@ -1000,7 +999,7 @@ public class VibrationThreadTest {
|
||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||
waitForCompletion(cancellingThread);
|
||||
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
||||
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
||||
}
|
||||
@@ -1020,11 +1019,179 @@ public class VibrationThreadTest {
|
||||
|
||||
verify(mVibrationToken).linkToDeath(same(thread), eq(0));
|
||||
verify(mVibrationToken).unlinkToDeath(same(thread), eq(0));
|
||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_waveformWithRampDown_addsRampDownAfterVibrationCompleted() {
|
||||
int rampDownDuration = 15;
|
||||
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.createWaveform(
|
||||
new long[]{5, 5, 5}, new int[]{60, 120, 240}, -1);
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
|
||||
// Duration extended for 5 + 5 + 5 + 15.
|
||||
assertEquals(Arrays.asList(expectedOneShot(30)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||
List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
|
||||
assertTrue(amplitudes.size() > 3);
|
||||
assertEquals(expectedAmplitudes(60, 120, 240), amplitudes.subList(0, 3));
|
||||
for (int i = 3; i < amplitudes.size(); i++) {
|
||||
assertTrue(amplitudes.get(i) < amplitudes.get(i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds() {
|
||||
int rampDownDuration = 10_000;
|
||||
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.createOneShot(10, 200);
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
|
||||
// Vibration completed but vibrator not yet released.
|
||||
verify(mThreadCallbacks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
|
||||
eq(Vibration.Status.FINISHED));
|
||||
verify(mThreadCallbacks, never()).onVibratorsReleased();
|
||||
|
||||
// Thread still running ramp down.
|
||||
assertTrue(thread.isAlive());
|
||||
|
||||
// Duration extended for 10 + 10000.
|
||||
assertEquals(Arrays.asList(expectedOneShot(10_010)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||
|
||||
// Will stop the ramp down right away.
|
||||
thread.cancelImmediately();
|
||||
waitForCompletion(thread);
|
||||
|
||||
// Does not cancel already finished vibration, but releases vibrator.
|
||||
verify(mThreadCallbacks, never()).onVibrationCompleted(eq(vibrationId),
|
||||
eq(Vibration.Status.CANCELLED));
|
||||
verify(mThreadCallbacks).onVibratorsReleased();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_waveformCancelledWithRampDown_addsRampDownAfterVibrationCancelled()
|
||||
throws Exception {
|
||||
int rampDownDuration = 15;
|
||||
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread,
|
||||
TEST_TIMEOUT_MILLIS));
|
||||
thread.cancel();
|
||||
waitForCompletion(thread);
|
||||
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||
|
||||
// Duration extended for 10000 + 15.
|
||||
assertEquals(Arrays.asList(expectedOneShot(10_015)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||
List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
|
||||
assertTrue(amplitudes.size() > 1);
|
||||
for (int i = 1; i < amplitudes.size(); i++) {
|
||||
assertTrue(amplitudes.get(i) < amplitudes.get(i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_predefinedWithRampDown_doesNotAddRampDown() {
|
||||
int rampDownDuration = 15;
|
||||
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
|
||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_composedWithRampDown_doesNotAddRampDown() {
|
||||
int rampDownDuration = 15;
|
||||
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
|
||||
IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
|
||||
VibrationEffect.Composition.PRIMITIVE_CLICK);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.compose();
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
|
||||
assertEquals(
|
||||
Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_pwleWithRampDown_doesNotAddRampDown() {
|
||||
int rampDownDuration = 15;
|
||||
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
|
||||
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
fakeVibrator.setMinFrequency(100);
|
||||
fakeVibrator.setResonantFrequency(150);
|
||||
fakeVibrator.setFrequencyResolution(50);
|
||||
fakeVibrator.setMaxAmplitudes(1, 1, 1);
|
||||
fakeVibrator.setPwleSizeMax(2);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startWaveform().addRamp(1, 1).build();
|
||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion(thread);
|
||||
|
||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
|
||||
assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)),
|
||||
fakeVibrator.getEffectSegments());
|
||||
assertTrue(fakeVibrator.getAmplitudes().isEmpty());
|
||||
}
|
||||
|
||||
private void mockVibrators(int... vibratorIds) {
|
||||
for (int vibratorId : vibratorIds) {
|
||||
mVibratorProviders.put(vibratorId,
|
||||
@@ -1042,7 +1209,7 @@ public class VibrationThreadTest {
|
||||
}
|
||||
|
||||
private VibrationThread startThreadAndDispatcher(Vibration vib) {
|
||||
VibrationThread thread = new VibrationThread(vib, mEffectAdapter,
|
||||
VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter,
|
||||
createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks);
|
||||
doAnswer(answer -> {
|
||||
thread.vibratorComplete(answer.getArgument(0));
|
||||
@@ -1117,4 +1284,9 @@ public class VibrationThreadTest {
|
||||
.mapToObj(amplitude -> amplitude / 255f)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) {
|
||||
verify(mThreadCallbacks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus));
|
||||
verify(mThreadCallbacks).onVibratorsReleased();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ import android.os.VibratorInfo;
|
||||
import android.os.test.TestLooper;
|
||||
import android.os.vibrator.PrebakedSegment;
|
||||
import android.os.vibrator.PrimitiveSegment;
|
||||
import android.os.vibrator.VibrationEffectSegment;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.provider.Settings;
|
||||
import android.view.InputDevice;
|
||||
@@ -470,13 +471,14 @@ public class VibratorManagerServiceTest {
|
||||
mockVibrators(1);
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
|
||||
mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK,
|
||||
VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
|
||||
|
||||
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
|
||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
||||
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
vibrate(service, VibrationEffect.createOneShot(1, 1), RINGTONE_ATTRS);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||
// Wait before checking it never played.
|
||||
assertFalse(waitUntil(s -> !fakeVibrator.getEffectSegments().isEmpty(),
|
||||
service, /* timeout= */ 50));
|
||||
@@ -484,43 +486,52 @@ public class VibratorManagerServiceTest {
|
||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
||||
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
|
||||
service = createSystemReadyService();
|
||||
vibrate(service, VibrationEffect.createOneShot(1, 10), RINGTONE_ATTRS);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
|
||||
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
|
||||
service = createSystemReadyService();
|
||||
vibrate(service, VibrationEffect.createOneShot(1, 100), RINGTONE_ATTRS);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
assertEquals(Arrays.asList(10 / 255f, 100 / 255f),
|
||||
mVibratorProviders.get(1).getAmplitudes());
|
||||
assertEquals(
|
||||
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
||||
mVibratorProviders.get(1).getEffectSegments());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withPowerMode_usesPowerModeState() throws Exception {
|
||||
mockVibrators(1);
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_CLICK,
|
||||
VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
|
||||
VibratorManagerService service = createSystemReadyService();
|
||||
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
||||
vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS);
|
||||
vibrate(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
|
||||
vibrate(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||
/* attrs= */ null);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS);
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
|
||||
NOTIFICATION_ATTRS);
|
||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3,
|
||||
service, TEST_TIMEOUT_MILLIS));
|
||||
|
||||
assertEquals(Arrays.asList(2 / 255f, 3 / 255f, 4 / 255f), fakeVibrator.getAmplitudes());
|
||||
assertEquals(
|
||||
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK),
|
||||
expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
||||
mVibratorProviders.get(1).getEffectSegments());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -836,7 +847,6 @@ public class VibratorManagerServiceTest {
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||
|
||||
assertEquals(4, fakeVibrator.getEffectSegments().size());
|
||||
assertEquals(1, fakeVibrator.getAmplitudes().size());
|
||||
|
||||
// Notification vibrations will be scaled with SCALE_VERY_HIGH.
|
||||
assertTrue(0.6 < fakeVibrator.getAmplitudes().get(0));
|
||||
@@ -957,6 +967,10 @@ public class VibratorManagerServiceTest {
|
||||
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||
}
|
||||
|
||||
private VibrationEffectSegment expectedPrebaked(int effectId) {
|
||||
return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
|
||||
}
|
||||
|
||||
private void mockCapabilities(long... capabilities) {
|
||||
when(mNativeWrapperMock.getCapabilities()).thenReturn(
|
||||
Arrays.stream(capabilities).reduce(0, (a, b) -> a | b));
|
||||
|
||||
Reference in New Issue
Block a user