Merge changes I3de63438,I671517b9 into tm-dev
* changes: Don't expose vibrator controllers via VibrationThread. Move VibrationStepConductor locking out of VibrationThread.
This commit is contained in:
committed by
Android (Google) Code Review
commit
bfdb2defa9
@@ -55,13 +55,15 @@ final class StartSequentialEffectStep extends Step {
|
|||||||
|
|
||||||
private long mVibratorsOnMaxDuration;
|
private long mVibratorsOnMaxDuration;
|
||||||
|
|
||||||
|
/** Start a sequential effect at the beginning. */
|
||||||
StartSequentialEffectStep(VibrationStepConductor conductor,
|
StartSequentialEffectStep(VibrationStepConductor conductor,
|
||||||
CombinedVibration.Sequential effect) {
|
CombinedVibration.Sequential effect) {
|
||||||
this(conductor, SystemClock.uptimeMillis() + effect.getDelays().get(0), effect,
|
this(conductor, SystemClock.uptimeMillis() + effect.getDelays().get(0), effect,
|
||||||
/* index= */ 0);
|
/* index= */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
StartSequentialEffectStep(VibrationStepConductor conductor, long startTime,
|
/** Continue a SequentialEffect from the specified index. */
|
||||||
|
private StartSequentialEffectStep(VibrationStepConductor conductor, long startTime,
|
||||||
CombinedVibration.Sequential effect, int index) {
|
CombinedVibration.Sequential effect, int index) {
|
||||||
super(conductor, startTime);
|
super(conductor, startTime);
|
||||||
sequentialEffect = effect;
|
sequentialEffect = effect;
|
||||||
@@ -123,8 +125,7 @@ final class StartSequentialEffectStep extends Step {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the next {@link StartSequentialEffectStep} to play this sequential effect, starting at
|
* Create the next {@link StartSequentialEffectStep} to play this sequential effect, starting at
|
||||||
* the
|
* the time this method is called, or null if sequence is complete.
|
||||||
* time this method is called, or null if sequence is complete.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Step nextStep() {
|
Step nextStep() {
|
||||||
|
|||||||
@@ -16,11 +16,10 @@
|
|||||||
|
|
||||||
package com.android.server.vibrator;
|
package com.android.server.vibrator;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.CombinedVibration;
|
import android.os.CombinedVibration;
|
||||||
import android.os.VibrationEffect;
|
import android.os.VibrationEffect;
|
||||||
import android.os.WorkSource;
|
|
||||||
import android.os.vibrator.PrebakedSegment;
|
import android.os.vibrator.PrebakedSegment;
|
||||||
import android.os.vibrator.PrimitiveSegment;
|
import android.os.vibrator.PrimitiveSegment;
|
||||||
import android.os.vibrator.RampSegment;
|
import android.os.vibrator.RampSegment;
|
||||||
@@ -42,6 +41,9 @@ import java.util.Queue;
|
|||||||
* dispatch of callbacks.
|
* dispatch of callbacks.
|
||||||
*/
|
*/
|
||||||
final class VibrationStepConductor {
|
final class VibrationStepConductor {
|
||||||
|
private static final boolean DEBUG = VibrationThread.DEBUG;
|
||||||
|
private static final String TAG = VibrationThread.TAG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extra timeout added to the end of each vibration step to ensure it finishes even when
|
* Extra timeout added to the end of each vibration step to ensure it finishes even when
|
||||||
* vibrator callbacks are lost.
|
* vibrator callbacks are lost.
|
||||||
@@ -51,14 +53,13 @@ final class VibrationStepConductor {
|
|||||||
static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
|
static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
|
||||||
static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
|
static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
|
||||||
|
|
||||||
final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
// Used within steps.
|
// Used within steps.
|
||||||
public final VibrationSettings vibrationSettings;
|
public final VibrationSettings vibrationSettings;
|
||||||
public final DeviceVibrationEffectAdapter deviceEffectAdapter;
|
public final DeviceVibrationEffectAdapter deviceEffectAdapter;
|
||||||
public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
|
public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
|
||||||
|
|
||||||
private final WorkSource mWorkSource;
|
|
||||||
private final Vibration mVibration;
|
private final Vibration mVibration;
|
||||||
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
|
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ final class VibrationStepConductor {
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private int mPendingVibrateSteps;
|
private int mPendingVibrateSteps;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private int mConsumedStartVibrateSteps;
|
private int mRemainingStartSequentialEffectSteps;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private int mSuccessfulVibratorOnSteps;
|
private int mSuccessfulVibratorOnSteps;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -86,7 +87,6 @@ final class VibrationStepConductor {
|
|||||||
this.vibrationSettings = vibrationSettings;
|
this.vibrationSettings = vibrationSettings;
|
||||||
this.deviceEffectAdapter = effectAdapter;
|
this.deviceEffectAdapter = effectAdapter;
|
||||||
this.vibratorManagerHooks = vibratorManagerHooks;
|
this.vibratorManagerHooks = vibratorManagerHooks;
|
||||||
this.mWorkSource = new WorkSource(mVibration.uid);
|
|
||||||
|
|
||||||
CombinedVibration effect = vib.getEffect();
|
CombinedVibration effect = vib.getEffect();
|
||||||
for (int i = 0; i < availableVibrators.size(); i++) {
|
for (int i = 0; i < availableVibrators.size(); i++) {
|
||||||
@@ -100,6 +100,9 @@ final class VibrationStepConductor {
|
|||||||
AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
|
AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
|
||||||
VibrationEffect.Composed effect, int segmentIndex,
|
VibrationEffect.Composed effect, int segmentIndex,
|
||||||
long previousStepVibratorOffTimeout) {
|
long previousStepVibratorOffTimeout) {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
if (segmentIndex >= effect.getSegments().size()) {
|
if (segmentIndex >= effect.getSegments().size()) {
|
||||||
segmentIndex = effect.getRepeatIndex();
|
segmentIndex = effect.getRepeatIndex();
|
||||||
}
|
}
|
||||||
@@ -126,26 +129,35 @@ final class VibrationStepConductor {
|
|||||||
previousStepVibratorOffTimeout);
|
previousStepVibratorOffTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initializeForEffect(@NonNull CombinedVibration.Sequential vibration) {
|
/** Called when this conductor is going to be started running by the VibrationThread. */
|
||||||
|
public void prepareToStart() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
mPendingVibrateSteps++;
|
mPendingVibrateSteps++;
|
||||||
mNextSteps.offer(new StartSequentialEffectStep(this, vibration));
|
// This count is decremented at the completion of the step, so we don't subtract one.
|
||||||
|
mRemainingStartSequentialEffectSteps = sequentialEffect.getEffects().size();
|
||||||
|
mNextSteps.offer(new StartSequentialEffectStep(this, sequentialEffect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vibration getVibration() {
|
public Vibration getVibration() {
|
||||||
|
// No thread assertion: immutable
|
||||||
return mVibration;
|
return mVibration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorkSource getWorkSource() {
|
|
||||||
return mWorkSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
SparseArray<VibratorController> getVibrators() {
|
SparseArray<VibratorController> getVibrators() {
|
||||||
|
// No thread assertion: immutable
|
||||||
return mVibrators;
|
return mVibrators;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFinished() {
|
public boolean isFinished() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
|
return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
|
||||||
}
|
}
|
||||||
@@ -155,10 +167,14 @@ final class VibrationStepConductor {
|
|||||||
* Calculate the {@link Vibration.Status} based on the current queue state and the expected
|
* Calculate the {@link Vibration.Status} based on the current queue state and the expected
|
||||||
* number of {@link StartSequentialEffectStep} to be played.
|
* number of {@link StartSequentialEffectStep} to be played.
|
||||||
*/
|
*/
|
||||||
public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) {
|
public Vibration.Status calculateVibrationStatus() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mPendingVibrateSteps > 0
|
if (mPendingVibrateSteps > 0
|
||||||
|| mConsumedStartVibrateSteps < expectedStartVibrateSteps) {
|
|| mRemainingStartSequentialEffectSteps > 0) {
|
||||||
return Vibration.Status.RUNNING;
|
return Vibration.Status.RUNNING;
|
||||||
}
|
}
|
||||||
if (mSuccessfulVibratorOnSteps > 0) {
|
if (mSuccessfulVibratorOnSteps > 0) {
|
||||||
@@ -169,15 +185,39 @@ final class VibrationStepConductor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the time in millis to wait before calling {@link #runNextStep()}. */
|
/**
|
||||||
@GuardedBy("mLock")
|
* Blocks until the next step is due to run. The wait here may be interrupted by calling
|
||||||
public long getWaitMillisBeforeNextStepLocked() {
|
* {@link #notifyWakeUp} or other "notify" methods.
|
||||||
if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
|
*
|
||||||
// Steps resumed by vibrator complete callback should be played right away.
|
* <p>This method returns false if the next step is ready to run now. If the method returns
|
||||||
return 0;
|
* true, then some waiting was done, but may have been interrupted by a wakeUp.
|
||||||
|
*
|
||||||
|
* @return true if the method waited at all, or false if a step is ready to run now.
|
||||||
|
*/
|
||||||
|
public boolean waitUntilNextStepIsDue() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
|
||||||
|
// Steps resumed by vibrator complete callback should be played right away.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Step nextStep = mNextSteps.peek();
|
||||||
|
if (nextStep == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long waitMillis = nextStep.calculateWaitTime();
|
||||||
|
if (waitMillis <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mLock.wait(waitMillis);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
Step nextStep = mNextSteps.peek();
|
|
||||||
return nextStep == null ? 0 : nextStep.calculateWaitTime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,6 +225,10 @@ final class VibrationStepConductor {
|
|||||||
* to be played next.
|
* to be played next.
|
||||||
*/
|
*/
|
||||||
public void runNextStep() {
|
public void runNextStep() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Vibrator callbacks should wait until the polled step is played and the next steps are
|
// Vibrator callbacks should wait until the polled step is played and the next steps are
|
||||||
// added back to the queue, so they can handle the callback.
|
// added back to the queue, so they can handle the callback.
|
||||||
markWaitToProcessVibratorCallbacks();
|
markWaitToProcessVibratorCallbacks();
|
||||||
@@ -199,7 +243,7 @@ final class VibrationStepConductor {
|
|||||||
mSuccessfulVibratorOnSteps++;
|
mSuccessfulVibratorOnSteps++;
|
||||||
}
|
}
|
||||||
if (nextStep instanceof StartSequentialEffectStep) {
|
if (nextStep instanceof StartSequentialEffectStep) {
|
||||||
mConsumedStartVibrateSteps++;
|
mRemainingStartSequentialEffectSteps--;
|
||||||
}
|
}
|
||||||
if (!nextStep.isCleanUp()) {
|
if (!nextStep.isCleanUp()) {
|
||||||
mPendingVibrateSteps--;
|
mPendingVibrateSteps--;
|
||||||
@@ -218,39 +262,71 @@ final class VibrationStepConductor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify the vibrator completion.
|
* Wake up the execution thread, which may be waiting until the next step is due.
|
||||||
|
* The caller is responsible for diverting VibrationThread execution.
|
||||||
*
|
*
|
||||||
* <p>This is a lightweight method that do not trigger any operation from {@link
|
* <p>At the moment this is used after the signal is set that a cancellation needs to be
|
||||||
* VibratorController}, so it can be called directly from a native callback.
|
* processed. The actual cancellation will be invoked from the VibrationThread.
|
||||||
*/
|
*/
|
||||||
|
public void notifyWakeUp() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mLock) {
|
||||||
|
mLock.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void notifyVibratorCompleteLocked(int vibratorId) {
|
private void markVibratorCompleteLocked(int vibratorId) {
|
||||||
mCompletionNotifiedVibrators.offer(vibratorId);
|
mCompletionNotifiedVibrators.offer(vibratorId);
|
||||||
if (!mWaitToProcessVibratorCompleteCallbacks) {
|
if (!mWaitToProcessVibratorCompleteCallbacks) {
|
||||||
// No step is being played or cancelled now, process the callback right away.
|
// No step is being played or cancelled now, process the callback right away.
|
||||||
processVibratorCompleteCallbacksLocked();
|
processVibratorCompleteCallbacksLocked();
|
||||||
}
|
}
|
||||||
|
// mLock.notify() is done outside this method to ensure it's only done once when
|
||||||
|
// multiple vibrators are notified.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the conductor that a vibrator has completed its work.
|
||||||
|
*
|
||||||
|
* <p>This is a lightweight method intended to be called directly via native callbacks.
|
||||||
|
* The state update is recorded for processing on the main execution thread (VibrationThread).
|
||||||
|
*/
|
||||||
public void notifyVibratorComplete(int vibratorId) {
|
public void notifyVibratorComplete(int vibratorId) {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(false);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (VibrationThread.DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(VibrationThread.TAG,
|
Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
|
||||||
"Vibration complete reported by vibrator " + vibratorId);
|
|
||||||
}
|
}
|
||||||
notifyVibratorCompleteLocked(vibratorId);
|
markVibratorCompleteLocked(vibratorId);
|
||||||
mLock.notify();
|
mLock.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify that a VibratorManager sync operation has completed.
|
||||||
|
*
|
||||||
|
* <p>This is a lightweight method intended to be called directly via native callbacks.
|
||||||
|
* The state update is recorded for processing on the main execution thread
|
||||||
|
* (VibrationThread).
|
||||||
|
*/
|
||||||
public void notifySyncedVibrationComplete() {
|
public void notifySyncedVibrationComplete() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(false);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (VibrationThread.DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(VibrationThread.TAG,
|
Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
|
||||||
"Synced vibration complete reported by vibrator manager");
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < mVibrators.size(); i++) {
|
for (int i = 0; i < mVibrators.size(); i++) {
|
||||||
notifyVibratorCompleteLocked(mVibrators.keyAt(i));
|
markVibratorCompleteLocked(mVibrators.keyAt(i));
|
||||||
}
|
}
|
||||||
mLock.notify();
|
mLock.notify();
|
||||||
}
|
}
|
||||||
@@ -263,6 +339,10 @@ final class VibrationStepConductor {
|
|||||||
* {@link Step#cancel()}.
|
* {@link Step#cancel()}.
|
||||||
*/
|
*/
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Vibrator callbacks should wait until all steps from the queue are properly cancelled
|
// Vibrator callbacks should wait until all steps from the queue are properly cancelled
|
||||||
// and clean up steps are added back to the queue, so they can handle the callback.
|
// and clean up steps are added back to the queue, so they can handle the callback.
|
||||||
markWaitToProcessVibratorCallbacks();
|
markWaitToProcessVibratorCallbacks();
|
||||||
@@ -290,6 +370,10 @@ final class VibrationStepConductor {
|
|||||||
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
|
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
|
||||||
*/
|
*/
|
||||||
public void cancelImmediately() {
|
public void cancelImmediately() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Vibrator callbacks should wait until all steps from the queue are properly cancelled.
|
// Vibrator callbacks should wait until all steps from the queue are properly cancelled.
|
||||||
markWaitToProcessVibratorCallbacks();
|
markWaitToProcessVibratorCallbacks();
|
||||||
try {
|
try {
|
||||||
@@ -311,6 +395,10 @@ final class VibrationStepConductor {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Step pollNext() {
|
private Step pollNext() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
// Prioritize the steps resumed by a vibrator complete callback.
|
// Prioritize the steps resumed by a vibrator complete callback.
|
||||||
if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
|
if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
|
||||||
@@ -338,6 +426,12 @@ final class VibrationStepConductor {
|
|||||||
*/
|
*/
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void processVibratorCompleteCallbacksLocked() {
|
private void processVibratorCompleteCallbacksLocked() {
|
||||||
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
// TODO: ensure this method is only called on the vibration thread. Currently it
|
||||||
|
// can be invoked on the completion callback paths.
|
||||||
|
//expectIsVibrationThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
mWaitToProcessVibratorCompleteCallbacks = false;
|
mWaitToProcessVibratorCompleteCallbacks = false;
|
||||||
while (!mCompletionNotifiedVibrators.isEmpty()) {
|
while (!mCompletionNotifiedVibrators.isEmpty()) {
|
||||||
int vibratorId = mCompletionNotifiedVibrators.poll();
|
int vibratorId = mCompletionNotifiedVibrators.poll();
|
||||||
@@ -352,4 +446,27 @@ final class VibrationStepConductor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
|
||||||
|
if (effect instanceof CombinedVibration.Sequential) {
|
||||||
|
return (CombinedVibration.Sequential) effect;
|
||||||
|
}
|
||||||
|
return (CombinedVibration.Sequential) CombinedVibration.startSequential()
|
||||||
|
.addNext(effect)
|
||||||
|
.combine();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This check is used for debugging and documentation to indicate the thread that's expected
|
||||||
|
* to invoke a given public method on this class. Most methods are only invoked by
|
||||||
|
* VibrationThread, which is where all the steps and HAL calls should be made. Other threads
|
||||||
|
* should only signal to the execution flow being run by VibrationThread.
|
||||||
|
*/
|
||||||
|
private void expectIsVibrationThread(boolean isVibrationThread) {
|
||||||
|
if ((Thread.currentThread() instanceof VibrationThread) != isVibrationThread) {
|
||||||
|
Slog.wtfStack("VibrationStepConductor",
|
||||||
|
"Thread caller assertion failed, expected isVibrationThread="
|
||||||
|
+ isVibrationThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,17 +16,15 @@
|
|||||||
|
|
||||||
package com.android.server.vibrator;
|
package com.android.server.vibrator;
|
||||||
|
|
||||||
import android.os.CombinedVibration;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
|
import android.os.WorkSource;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
/** Plays a {@link Vibration} in dedicated thread. */
|
/** Plays a {@link Vibration} in dedicated thread. */
|
||||||
@@ -105,11 +103,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
return mStepConductor.getVibration();
|
return mStepConductor.getVibration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
SparseArray<VibratorController> getVibrators() {
|
|
||||||
return mStepConductor.getVibrators();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -136,12 +129,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
|
|
||||||
/** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
|
/** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
|
||||||
private void runWithWakeLock() {
|
private void runWithWakeLock() {
|
||||||
mWakeLock.setWorkSource(mStepConductor.getWorkSource());
|
WorkSource workSource = new WorkSource(mStepConductor.getVibration().uid);
|
||||||
|
mWakeLock.setWorkSource(workSource);
|
||||||
mWakeLock.acquire();
|
mWakeLock.acquire();
|
||||||
try {
|
try {
|
||||||
runWithWakeLockAndDeathLink();
|
runWithWakeLockAndDeathLink();
|
||||||
} finally {
|
} finally {
|
||||||
mWakeLock.release();
|
mWakeLock.release();
|
||||||
|
mWakeLock.setWorkSource(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,12 +173,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mStop = true;
|
mStop = true;
|
||||||
synchronized (mStepConductor.mLock) {
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Slog.d(TAG, "Vibration cancelled");
|
||||||
Slog.d(TAG, "Vibration cancelled");
|
|
||||||
}
|
|
||||||
mStepConductor.mLock.notify();
|
|
||||||
}
|
}
|
||||||
|
mStepConductor.notifyWakeUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancel current vibration and shuts off the vibrators immediately. */
|
/** Cancel current vibration and shuts off the vibrators immediately. */
|
||||||
@@ -192,13 +185,11 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
// Already forced the thread to stop, wait for it to finish.
|
// Already forced the thread to stop, wait for it to finish.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mStop = mForceStop = true;
|
if (DEBUG) {
|
||||||
synchronized (mStepConductor.mLock) {
|
Slog.d(TAG, "Vibration cancelled immediately");
|
||||||
if (DEBUG) {
|
|
||||||
Slog.d(TAG, "Vibration cancelled immediately");
|
|
||||||
}
|
|
||||||
mStepConductor.mLock.notify();
|
|
||||||
}
|
}
|
||||||
|
mStop = mForceStop = true;
|
||||||
|
mStepConductor.notifyWakeUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Notify current vibration that a synced step has completed. */
|
/** Notify current vibration that a synced step has completed. */
|
||||||
@@ -225,27 +216,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
private void playVibration() {
|
private void playVibration() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
|
||||||
try {
|
try {
|
||||||
CombinedVibration.Sequential sequentialEffect =
|
mStepConductor.prepareToStart();
|
||||||
toSequential(mStepConductor.getVibration().getEffect());
|
|
||||||
final int sequentialEffectSize = sequentialEffect.getEffects().size();
|
|
||||||
mStepConductor.initializeForEffect(sequentialEffect);
|
|
||||||
|
|
||||||
while (!mStepConductor.isFinished()) {
|
while (!mStepConductor.isFinished()) {
|
||||||
long waitMillisBeforeNextStep;
|
// Skip wait and next step if mForceStop already happened.
|
||||||
synchronized (mStepConductor.mLock) {
|
boolean waited = mForceStop || mStepConductor.waitUntilNextStepIsDue();
|
||||||
waitMillisBeforeNextStep = mStepConductor.getWaitMillisBeforeNextStepLocked();
|
// If we waited, don't run the next step, but instead re-evaluate cancellation
|
||||||
if (waitMillisBeforeNextStep > 0) {
|
// status
|
||||||
try {
|
if (!waited) {
|
||||||
mStepConductor.mLock.wait(waitMillisBeforeNextStep);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Only run the next vibration step if we didn't have to wait in this loop.
|
|
||||||
// If we waited then the queue may have changed or the wait could have been
|
|
||||||
// interrupted by a cancel call, so loop again to re-evaluate the scheduling of
|
|
||||||
// the queue top element.
|
|
||||||
if (waitMillisBeforeNextStep <= 0) {
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Play vibration consuming next step...");
|
Slog.d(TAG, "Play vibration consuming next step...");
|
||||||
}
|
}
|
||||||
@@ -253,8 +231,17 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
// blocking the thread.
|
// blocking the thread.
|
||||||
mStepConductor.runNextStep();
|
mStepConductor.runNextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mForceStop) {
|
||||||
|
// Cancel every step and stop playing them right away, even clean-up steps.
|
||||||
|
mStepConductor.cancelImmediately();
|
||||||
|
clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Vibration.Status status = mStop ? Vibration.Status.CANCELLED
|
Vibration.Status status = mStop ? Vibration.Status.CANCELLED
|
||||||
: mStepConductor.calculateVibrationStatus(sequentialEffectSize);
|
: mStepConductor.calculateVibrationStatus();
|
||||||
|
// This block can only run once due to mCalledVibrationCompleteCallback.
|
||||||
if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
|
if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
|
||||||
// First time vibration stopped running, start clean-up tasks and notify
|
// First time vibration stopped running, start clean-up tasks and notify
|
||||||
// callback immediately.
|
// callback immediately.
|
||||||
@@ -263,25 +250,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
mStepConductor.cancel();
|
mStepConductor.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mForceStop) {
|
|
||||||
// Cancel every step and stop playing them right away, even clean-up steps.
|
|
||||||
mStepConductor.cancelImmediately();
|
|
||||||
clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
|
|
||||||
if (effect instanceof CombinedVibration.Sequential) {
|
|
||||||
return (CombinedVibration.Sequential) effect;
|
|
||||||
}
|
|
||||||
return (CombinedVibration.Sequential) CombinedVibration.startSequential()
|
|
||||||
.addNext(effect)
|
|
||||||
.combine();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ public class VibrationThreadTest {
|
|||||||
private TestLooper mTestLooper;
|
private TestLooper mTestLooper;
|
||||||
private TestLooperAutoDispatcher mCustomTestLooperDispatcher;
|
private TestLooperAutoDispatcher mCustomTestLooperDispatcher;
|
||||||
|
|
||||||
|
// Setup from the providers when VibrationThread is initialized.
|
||||||
|
private SparseArray<VibratorController> mControllers;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mTestLooper = new TestLooper();
|
mTestLooper = new TestLooper();
|
||||||
@@ -178,7 +181,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
@@ -197,7 +200,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
@@ -219,7 +222,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(15)),
|
assertEquals(Arrays.asList(expectedOneShot(15)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
@@ -243,7 +246,7 @@ public class VibrationThreadTest {
|
|||||||
thread, TEST_TIMEOUT_MILLIS));
|
thread, TEST_TIMEOUT_MILLIS));
|
||||||
// Vibration still running after 2 cycles.
|
// Vibration still running after 2 cycles.
|
||||||
assertTrue(thread.isAlive());
|
assertTrue(thread.isAlive());
|
||||||
assertTrue(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
thread.cancel();
|
thread.cancel();
|
||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
@@ -251,7 +254,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
|
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
|
||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
|
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
|
||||||
assertFalse(fakeVibrator.getEffectSegments().isEmpty());
|
assertFalse(fakeVibrator.getEffectSegments().isEmpty());
|
||||||
@@ -280,7 +283,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(expectedOneShot(1000)), fakeVibrator.getEffectSegments());
|
assertEquals(Arrays.asList(expectedOneShot(1000)), fakeVibrator.getEffectSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +305,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(expectedOneShot(5550)), fakeVibrator.getEffectSegments());
|
assertEquals(Arrays.asList(expectedOneShot(5550)), fakeVibrator.getEffectSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +328,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(2, fakeVibrator.getEffectSegments().size());
|
assertEquals(2, fakeVibrator.getEffectSegments().size());
|
||||||
// First time turn vibrator ON for minimum of 1s.
|
// First time turn vibrator ON for minimum of 1s.
|
||||||
assertEquals(1000L, fakeVibrator.getEffectSegments().get(0).getDuration());
|
assertEquals(1000L, fakeVibrator.getEffectSegments().get(0).getDuration());
|
||||||
@@ -350,7 +353,7 @@ public class VibrationThreadTest {
|
|||||||
.compose();
|
.compose();
|
||||||
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
|
||||||
assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
|
assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread,
|
||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
assertTrue(vibrationThread.isAlive());
|
assertTrue(vibrationThread.isAlive());
|
||||||
|
|
||||||
@@ -363,7 +366,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -375,7 +378,7 @@ public class VibrationThreadTest {
|
|||||||
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
|
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
|
||||||
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
|
||||||
assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
|
assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread,
|
||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
assertTrue(vibrationThread.isAlive());
|
assertTrue(vibrationThread.isAlive());
|
||||||
|
|
||||||
@@ -388,7 +391,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -404,7 +407,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
|
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
@@ -427,7 +430,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
@@ -466,7 +469,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(
|
assertEquals(Arrays.asList(
|
||||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
||||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
|
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
|
||||||
@@ -536,7 +539,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(
|
assertEquals(Arrays.asList(
|
||||||
expectedOneShot(10),
|
expectedOneShot(10),
|
||||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
||||||
@@ -573,7 +576,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(
|
assertEquals(Arrays.asList(
|
||||||
expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
|
expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
|
||||||
expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
|
expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
|
||||||
@@ -626,11 +629,11 @@ public class VibrationThreadTest {
|
|||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
// Vibration still running after 2 cycles.
|
// Vibration still running after 2 cycles.
|
||||||
assertTrue(thread.isAlive());
|
assertTrue(thread.isAlive());
|
||||||
assertTrue(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
thread.binderDied();
|
thread.binderDied();
|
||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
}
|
}
|
||||||
@@ -667,7 +670,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
|
verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
|
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
|
||||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
@@ -692,9 +695,9 @@ public class VibrationThreadTest {
|
|||||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(mControllers.get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(mControllers.get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(mControllers.get(3).isVibrating());
|
||||||
|
|
||||||
VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK);
|
VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK);
|
||||||
assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments());
|
assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments());
|
||||||
@@ -731,10 +734,10 @@ public class VibrationThreadTest {
|
|||||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(mControllers.get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(mControllers.get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(mControllers.get(3).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(4).isVibrating());
|
assertFalse(mControllers.get(4).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
|
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
|
||||||
mVibratorProviders.get(1).getEffectSegments());
|
mVibratorProviders.get(1).getEffectSegments());
|
||||||
@@ -782,9 +785,9 @@ public class VibrationThreadTest {
|
|||||||
batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
|
batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(mControllers.get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(mControllers.get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(mControllers.get(3).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||||
mVibratorProviders.get(1).getEffectSegments());
|
mVibratorProviders.get(1).getEffectSegments());
|
||||||
@@ -941,9 +944,9 @@ public class VibrationThreadTest {
|
|||||||
|
|
||||||
// All vibrators are turned on in parallel.
|
// All vibrators are turned on in parallel.
|
||||||
assertTrue(waitUntil(
|
assertTrue(waitUntil(
|
||||||
t -> t.getVibrators().get(1).isVibrating()
|
t -> mControllers.get(1).isVibrating()
|
||||||
&& t.getVibrators().get(2).isVibrating()
|
&& mControllers.get(2).isVibrating()
|
||||||
&& t.getVibrators().get(3).isVibrating(),
|
&& mControllers.get(3).isVibrating(),
|
||||||
thread, TEST_TIMEOUT_MILLIS));
|
thread, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
@@ -954,9 +957,9 @@ public class VibrationThreadTest {
|
|||||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(mControllers.get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(mControllers.get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(mControllers.get(3).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(25)),
|
assertEquals(Arrays.asList(expectedOneShot(25)),
|
||||||
mVibratorProviders.get(1).getEffectSegments());
|
mVibratorProviders.get(1).getEffectSegments());
|
||||||
@@ -1031,7 +1034,7 @@ public class VibrationThreadTest {
|
|||||||
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
|
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
|
||||||
waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS);
|
waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS);
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1051,7 +1054,7 @@ public class VibrationThreadTest {
|
|||||||
.combine();
|
.combine();
|
||||||
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
|
||||||
assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread,
|
assertTrue(waitUntil(t -> mControllers.get(2).isVibrating(), vibrationThread,
|
||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
assertTrue(vibrationThread.isAlive());
|
assertTrue(vibrationThread.isAlive());
|
||||||
|
|
||||||
@@ -1064,8 +1067,8 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
assertFalse(mControllers.get(1).isVibrating());
|
||||||
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
assertFalse(mControllers.get(2).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1082,8 +1085,8 @@ public class VibrationThreadTest {
|
|||||||
.combine();
|
.combine();
|
||||||
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
|
||||||
assertTrue(waitUntil(t -> t.getVibrators().get(1).isVibrating()
|
assertTrue(waitUntil(t -> mControllers.get(1).isVibrating()
|
||||||
&& t.getVibrators().get(2).isVibrating(),
|
&& mControllers.get(2).isVibrating(),
|
||||||
vibrationThread, TEST_TIMEOUT_MILLIS));
|
vibrationThread, TEST_TIMEOUT_MILLIS));
|
||||||
assertTrue(vibrationThread.isAlive());
|
assertTrue(vibrationThread.isAlive());
|
||||||
|
|
||||||
@@ -1096,8 +1099,8 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
assertFalse(mControllers.get(1).isVibrating());
|
||||||
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
assertFalse(mControllers.get(2).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1106,7 +1109,7 @@ public class VibrationThreadTest {
|
|||||||
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
|
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
|
||||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
|
||||||
assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread,
|
assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread,
|
||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
assertTrue(thread.isAlive());
|
assertTrue(thread.isAlive());
|
||||||
|
|
||||||
@@ -1117,7 +1120,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mVibrationToken).unlinkToDeath(same(thread), eq(0));
|
verify(mVibrationToken).unlinkToDeath(same(thread), eq(0));
|
||||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1188,7 +1191,7 @@ public class VibrationThreadTest {
|
|||||||
long vibrationId = 1;
|
long vibrationId = 1;
|
||||||
VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
|
VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
|
||||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread,
|
assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread,
|
||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
thread.cancel();
|
thread.cancel();
|
||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
@@ -1295,8 +1298,9 @@ public class VibrationThreadTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private VibrationThread startThreadAndDispatcher(Vibration vib) {
|
private VibrationThread startThreadAndDispatcher(Vibration vib) {
|
||||||
|
mControllers = createVibratorControllers();
|
||||||
VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter,
|
VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter,
|
||||||
createVibratorControllers(), mWakeLock, mManagerHooks);
|
mControllers, mWakeLock, mManagerHooks);
|
||||||
doAnswer(answer -> {
|
doAnswer(answer -> {
|
||||||
thread.vibratorComplete(answer.getArgument(0));
|
thread.vibratorComplete(answer.getArgument(0));
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user