diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index 0c15ee723dd31..f02f9f98d9332 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -49,6 +49,8 @@ final class Vibration { FORWARDED_TO_INPUT_DEVICES, CANCELLED, IGNORED_ERROR_APP_OPS, + IGNORED_ERROR_CANCELLING, + IGNORED_ERROR_SCHEDULING, IGNORED_ERROR_TOKEN, IGNORED, IGNORED_APP_OPS, diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index 26f7e7dee7577..4a169f92fbf44 100644 --- a/services/core/java/com/android/server/vibrator/VibrationThread.java +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -16,6 +16,8 @@ package com.android.server.vibrator; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.IBinder; import android.os.PowerManager; import android.os.Process; @@ -23,9 +25,12 @@ import android.os.RemoteException; import android.os.Trace; import android.os.WorkSource; import android.util.Slog; -import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import java.util.NoSuchElementException; +import java.util.Objects; /** Plays a {@link Vibration} in dedicated thread. */ final class VibrationThread extends Thread implements IBinder.DeathRecipient { @@ -76,31 +81,41 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { * Tells the manager that the VibrationThread is finished with the previous vibration and * all of its cleanup tasks, and the vibrators can now be used for another vibration. */ - void onVibrationThreadReleased(); + void onVibrationThreadReleased(long vibrationId); } private final PowerManager.WakeLock mWakeLock; private final VibrationThread.VibratorManagerHooks mVibratorManagerHooks; - private final VibrationStepConductor mStepConductor; + // mLock is used here to communicate that the thread's work status has changed. The + // VibrationThread is expected to wait until work arrives, and other threads may wait until + // work has finished. Therefore, any changes to the conductor must be followed by a notifyAll + // so that threads check if their desired state is achieved. + private final Object mLock = new Object(); - private volatile boolean mStop; - private volatile boolean mForceStop; - // Variable only set and read in main thread. + /** + * The conductor that is intended to be active. Null value means that a new conductor can + * be set to run. Note that this field is only reset to null when mExecutingConductor has + * completed, so the two fields should be in sync. + */ + @GuardedBy("mLock") + @Nullable + private VibrationStepConductor mRequestedActiveConductor; + + /** + * The conductor being executed by this thread, should only be accessed within this thread's + * execution. i.e. not thread-safe. {@link #mRequestedActiveConductor} is for cross-thread + * signalling. + */ + @Nullable + private VibrationStepConductor mExecutingConductor; + + // Variable only set and read in main thread, no need to lock. private boolean mCalledVibrationCompleteCallback = false; - VibrationThread(Vibration vib, VibrationSettings vibrationSettings, - DeviceVibrationEffectAdapter effectAdapter, - SparseArray availableVibrators, PowerManager.WakeLock wakeLock, - VibratorManagerHooks vibratorManagerHooks) { - mVibratorManagerHooks = vibratorManagerHooks; + VibrationThread(PowerManager.WakeLock wakeLock, VibratorManagerHooks vibratorManagerHooks) { mWakeLock = wakeLock; - mStepConductor = new VibrationStepConductor(vib, vibrationSettings, effectAdapter, - availableVibrators, vibratorManagerHooks); - } - - Vibration getVibration() { - return mStepConductor.getVibration(); + mVibratorManagerHooks = vibratorManagerHooks; } @Override @@ -108,32 +123,138 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { if (DEBUG) { Slog.d(TAG, "Binder died, cancelling vibration..."); } - mStepConductor.notifyCancelled(/* immediate= */ false); + // The binder death link only exists while the conductor is set. + // TODO: move the death linking to be associated with the conductor directly, this + // awkwardness will go away. + VibrationStepConductor conductor; + synchronized (mLock) { + conductor = mRequestedActiveConductor; + } + if (conductor != null) { + conductor.notifyCancelled(/* immediate= */ false); + } + } + + /** + * Sets/activates the current vibration. Must only be called after receiving + * onVibratorsReleased from the previous vibration. + * + * @return false if VibrationThread couldn't accept it, which shouldn't happen unless called + * before the release callback. + */ + boolean runVibrationOnVibrationThread(VibrationStepConductor conductor) { + synchronized (mLock) { + if (mRequestedActiveConductor != null) { + Slog.wtf(TAG, "Attempt to start vibration when one already running"); + return false; + } + mRequestedActiveConductor = conductor; + mLock.notifyAll(); + } + return true; } @Override public void run() { - // Structured to guarantee the vibrators completed and released callbacks at the end of - // thread execution. Both of these callbacks are exclusively called from this thread. - try { - try { - Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); - runWithWakeLock(); - } finally { - clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED); + Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); + while (true) { + // mExecutingConductor is only modified in this loop. + mExecutingConductor = Objects.requireNonNull(waitForVibrationRequest()); + + mCalledVibrationCompleteCallback = false; + runCurrentVibrationWithWakeLock(); + if (!mExecutingConductor.isFinished()) { + Slog.wtf(TAG, "VibrationThread terminated with unfinished vibration"); } - } finally { - mVibratorManagerHooks.onVibrationThreadReleased(); + synchronized (mLock) { + // Allow another vibration to be requested. + mRequestedActiveConductor = null; + } + // The callback is run without holding the lock, as it may initiate another vibration. + // It's safe to notify even if mVibratorConductor has been re-written, as the "wait" + // methods all verify their waited state before returning. In reality though, if the + // manager is waiting for the thread to finish, then there is no pending vibration + // for this thread. + // No point doing this in finally, as if there's an exception, this thread will die + // and be unusable anyway. + mVibratorManagerHooks.onVibrationThreadReleased(mExecutingConductor.getVibration().id); + synchronized (mLock) { + mLock.notifyAll(); + } + mExecutingConductor = null; + } + } + + /** + * Waits until the VibrationThread has finished processing, timing out after the given + * number of milliseconds. In general, external locking will manage the ordering of this + * with calls to {@link #runVibrationOnVibrationThread}. + * + * @return true if the vibration completed, or false if waiting timed out. + */ + public boolean waitForThreadIdle(long maxWaitMillis) { + long now = System.currentTimeMillis(); + long deadline = now + maxWaitMillis; + synchronized (mLock) { + while (true) { + if (mRequestedActiveConductor == null) { + return true; // Done + } + if (now >= deadline) { // Note that thread.wait(0) waits indefinitely. + return false; // Timed out. + } + try { + mLock.wait(deadline - now); + } catch (InterruptedException e) { + Slog.w(TAG, "VibrationThread interrupted waiting to stop, continuing"); + } + now = System.currentTimeMillis(); + } + } + } + + /** Waits for a signal indicating a vibration is ready to run, then returns its conductor. */ + @NonNull + private VibrationStepConductor waitForVibrationRequest() { + while (true) { + synchronized (mLock) { + if (mRequestedActiveConductor != null) { + return mRequestedActiveConductor; + } + try { + mLock.wait(); + } catch (InterruptedException e) { + Slog.w(TAG, "VibrationThread interrupted waiting to start, continuing"); + } + } + } + } + + /** + * Only for testing: this method relies on the requested-active conductor, rather than + * the executing conductor that's not intended for other threads. + * + * @return true if the vibration that's currently desired to be active has the given id. + */ + @VisibleForTesting + boolean isRunningVibrationId(long id) { + synchronized (mLock) { + return (mRequestedActiveConductor != null + && mRequestedActiveConductor.getVibration().id == id); } } /** Runs the VibrationThread ensuring that the wake lock is acquired and released. */ - private void runWithWakeLock() { - WorkSource workSource = new WorkSource(mStepConductor.getVibration().uid); + private void runCurrentVibrationWithWakeLock() { + WorkSource workSource = new WorkSource(mExecutingConductor.getVibration().uid); mWakeLock.setWorkSource(workSource); mWakeLock.acquire(); try { - runWithWakeLockAndDeathLink(); + try { + runCurrentVibrationWithWakeLockAndDeathLink(); + } finally { + clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED); + } } finally { mWakeLock.release(); mWakeLock.setWorkSource(null); @@ -144,8 +265,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { * Runs the VibrationThread with the binder death link, handling link/unlink failures. * Called from within runWithWakeLock. */ - private void runWithWakeLockAndDeathLink() { - IBinder vibrationBinderToken = mStepConductor.getVibration().token; + private void runCurrentVibrationWithWakeLockAndDeathLink() { + IBinder vibrationBinderToken = mExecutingConductor.getVibration().token; try { vibrationBinderToken.linkToDeath(this, 0); } catch (RemoteException e) { @@ -166,26 +287,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } } - /** Cancel current vibration and ramp down the vibrators gracefully. */ - public void cancel() { - mStepConductor.notifyCancelled(/* immediate= */ false); - } - - /** Cancel current vibration and shuts off the vibrators immediately. */ - public void cancelImmediately() { - mStepConductor.notifyCancelled(/* immediate= */ true); - } - - /** Notify current vibration that a synced step has completed. */ - public void syncedVibrationComplete() { - mStepConductor.notifySyncedVibrationComplete(); - } - - /** Notify current vibration that a step has completed on given vibrator. */ - public void vibratorComplete(int vibratorId) { - mStepConductor.notifyVibratorComplete(vibratorId); - } - // Indicate that the vibration is complete. This can be called multiple times only for // convenience of handling error conditions - an error after the client is complete won't // affect the status. @@ -193,16 +294,16 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { if (!mCalledVibrationCompleteCallback) { mCalledVibrationCompleteCallback = true; mVibratorManagerHooks.onVibrationCompleted( - mStepConductor.getVibration().id, completedStatus); + mExecutingConductor.getVibration().id, completedStatus); } } private void playVibration() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration"); try { - mStepConductor.prepareToStart(); - while (!mStepConductor.isFinished()) { - boolean readyToRun = mStepConductor.waitUntilNextStepIsDue(); + mExecutingConductor.prepareToStart(); + while (!mExecutingConductor.isFinished()) { + boolean readyToRun = mExecutingConductor.waitUntilNextStepIsDue(); // If we waited, don't run the next step, but instead re-evaluate status. if (readyToRun) { if (DEBUG) { @@ -210,10 +311,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } // Run the step without holding the main lock, to avoid HAL interactions from // blocking the thread. - mStepConductor.runNextStep(); + mExecutingConductor.runNextStep(); } - Vibration.Status status = mStepConductor.calculateVibrationStatus(); + Vibration.Status status = mExecutingConductor.calculateVibrationStatus(); // This block can only run once due to mCalledVibrationCompleteCallback. if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) { // First time vibration stopped running, start clean-up tasks and notify diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 94d0a7b22cdc0..9ec1079e46cc5 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -31,6 +31,7 @@ import android.content.pm.PackageManager; import android.hardware.vibrator.IVibrator; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.CombinedVibration; import android.os.ExternalVibration; import android.os.Handler; @@ -52,6 +53,7 @@ import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.VibrationEffectSegment; +import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; @@ -91,6 +93,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */ private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000; + /** + * Maximum millis to wait for a vibration thread cancellation to "clean up" and finish, when + * blocking for an external vibration. In practice, this should be plenty. + */ + private static final long VIBRATION_CANCEL_WAIT_MILLIS = 5000; + /** Lifecycle responsible for initializing this class at the right system server phases. */ public static class Lifecycle extends SystemService { private VibratorManagerService mService; @@ -121,6 +129,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private final PowerManager.WakeLock mWakeLock; private final IBatteryStats mBatteryStatsService; private final Handler mHandler; + private final VibrationThread mVibrationThread; private final AppOpsManager mAppOps; private final NativeWrapper mNativeWrapper; private final VibratorManagerRecords mVibratorManagerRecords; @@ -132,9 +141,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @GuardedBy("mLock") private final SparseArray mAlwaysOnEffects = new SparseArray<>(); @GuardedBy("mLock") - private VibrationThread mCurrentVibration; + private VibrationStepConductor mCurrentVibration; @GuardedBy("mLock") - private VibrationThread mNextVibration; + private VibrationStepConductor mNextVibration; @GuardedBy("mLock") private ExternalVibrationHolder mCurrentExternalVibration; @GuardedBy("mLock") @@ -156,7 +165,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { clearNextVibrationLocked(Vibration.Status.CANCELLED); } if (shouldCancelOnScreenOffLocked(mCurrentVibration)) { - mCurrentVibration.cancel(); + mCurrentVibration.notifyCancelled(/* immediate= */ false); } } } @@ -202,6 +211,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { PowerManager pm = context.getSystemService(PowerManager.class); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); mWakeLock.setReferenceCounted(true); + mVibrationThread = new VibrationThread(mWakeLock, mVibrationThreadCallbacks); + mVibrationThread.start(); // Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only // once and assumed unchanged for the lifecycle of this service. Each individual vibrator @@ -409,7 +420,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { final long ident = Binder.clearCallingIdentity(); try { if (mCurrentVibration != null) { - mCurrentVibration.cancel(); + mCurrentVibration.notifyCancelled(/* immediate= */ false); } Vibration.Status status = startVibrationLocked(vib); if (status != Vibration.Status.RUNNING) { @@ -447,7 +458,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (mCurrentVibration != null && shouldCancelVibration(mCurrentVibration.getVibration(), usageFilter, token)) { - mCurrentVibration.cancel(); + mCurrentVibration.notifyCancelled(/* immediate= */false); } if (mCurrentExternalVibration != null && shouldCancelVibration( @@ -584,7 +595,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.d(TAG, "Canceling vibration because settings changed: " + (inputDevicesChanged ? "input devices changed" : ignoreStatus)); } - mCurrentVibration.cancel(); + mCurrentVibration.notifyCancelled(/* immediate= */ false); } } } @@ -626,17 +637,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return Vibration.Status.FORWARDED_TO_INPUT_DEVICES; } - VibrationThread vibThread = new VibrationThread(vib, mVibrationSettings, - mDeviceVibrationEffectAdapter, mVibrators, mWakeLock, - mVibrationThreadCallbacks); - + VibrationStepConductor conductor = new VibrationStepConductor(vib, mVibrationSettings, + mDeviceVibrationEffectAdapter, mVibrators, mVibrationThreadCallbacks); if (mCurrentVibration == null) { - return startVibrationThreadLocked(vibThread); + return startVibrationOnThreadLocked(conductor); } // If there's already a vibration queued (waiting for the previous one to finish // cancelling), end it cleanly and replace it with the new one. clearNextVibrationLocked(Vibration.Status.IGNORED_SUPERSEDED); - mNextVibration = vibThread; + mNextVibration = conductor; return Vibration.Status.RUNNING; } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); @@ -644,16 +653,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @GuardedBy("mLock") - private Vibration.Status startVibrationThreadLocked(VibrationThread vibThread) { + private Vibration.Status startVibrationOnThreadLocked(VibrationStepConductor conductor) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked"); try { - Vibration vib = vibThread.getVibration(); + Vibration vib = conductor.getVibration(); int mode = startAppOpModeLocked(vib.uid, vib.opPkg, vib.attrs); switch (mode) { case AppOpsManager.MODE_ALLOWED: Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); - mCurrentVibration = vibThread; - mCurrentVibration.start(); + mCurrentVibration = conductor; + if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) { + // Shouldn't happen. The method call already logs a wtf. + mCurrentVibration = null; // Aborted. + return Vibration.Status.IGNORED_ERROR_SCHEDULING; + } return Vibration.Status.RUNNING; case AppOpsManager.MODE_ERRORED: Slog.w(TAG, "Start AppOpsManager operation errored for uid " + vib.uid); @@ -741,7 +754,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (DEBUG) { Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread"); } - mCurrentVibration.syncedVibrationComplete(); + mCurrentVibration.notifySyncedVibrationComplete(); } } } @@ -753,7 +766,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId + " complete, notifying thread"); } - mCurrentVibration.vibratorComplete(vibratorId); + mCurrentVibration.notifyVibratorComplete(vibratorId); } } } @@ -1064,11 +1077,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @GuardedBy("mLock") - private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationThread vibrationThread) { - if (vibrationThread == null) { + private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationStepConductor conductor) { + if (conductor == null) { return false; } - Vibration vib = vibrationThread.getVibration(); + Vibration vib = conductor.getVibration(); return mVibrationSettings.shouldCancelVibrationOnScreenOff( vib.uid, vib.opPkg, vib.attrs.getUsage()); } @@ -1185,21 +1198,27 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @Override - public void onVibrationThreadReleased() { + public void onVibrationThreadReleased(long vibrationId) { if (DEBUG) { - Slog.d(TAG, "Vibrators released after finished vibration"); + Slog.d(TAG, "VibrationThread released after finished vibration"); } synchronized (mLock) { if (DEBUG) { - Slog.d(TAG, "Processing vibrators released callback"); + Slog.d(TAG, "Processing VibrationThread released callback"); + } + if (Build.IS_DEBUGGABLE && mCurrentVibration != null + && mCurrentVibration.getVibration().id != vibrationId) { + Slog.wtf(TAG, TextUtils.formatSimple( + "VibrationId mismatch on release. expected=%d, released=%d", + mCurrentVibration.getVibration().id, vibrationId)); } mCurrentVibration = null; if (mNextVibration != null) { - VibrationThread vibThread = mNextVibration; + VibrationStepConductor nextConductor = mNextVibration; mNextVibration = null; - Vibration.Status status = startVibrationThreadLocked(vibThread); + Vibration.Status status = startVibrationOnThreadLocked(nextConductor); if (status != Vibration.Status.RUNNING) { - endVibrationLocked(vibThread.getVibration(), status); + endVibrationLocked(nextConductor.getVibration(), status); } } } @@ -1451,7 +1470,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } ExternalVibrationHolder cancelingExternalVibration = null; - VibrationThread cancelingVibration = null; + boolean waitForCompletion = false; int scale; synchronized (mLock) { Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked( @@ -1473,8 +1492,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // vibration that may be playing and ready the vibrator for external control. if (mCurrentVibration != null) { clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL); - mCurrentVibration.cancelImmediately(); - cancelingVibration = mCurrentVibration; + mCurrentVibration.notifyCancelled(/* immediate= */ true); + waitForCompletion = true; } } else { // At this point we have an externally controlled vibration playing already. @@ -1497,12 +1516,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { scale = mCurrentExternalVibration.scale; } - if (cancelingVibration != null) { - try { - cancelingVibration.join(); - } catch (InterruptedException e) { - Slog.w("Interrupted while waiting for vibration to finish before starting " - + "external control", e); + if (waitForCompletion) { + if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) { + Slog.e(TAG, "Timed out waiting for vibration to cancel"); + synchronized (mLock) { + stopExternalVibrateLocked(Vibration.Status.IGNORED_ERROR_CANCELLING); + } + return IExternalVibratorService.SCALE_MUTE; } } if (cancelingExternalVibration == null) { diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java index e88e9881181c5..fa3fcd9e94756 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -34,6 +34,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; /** * Provides {@link VibratorController} with controlled vibrator hardware capabilities and @@ -43,8 +44,8 @@ final class FakeVibratorControllerProvider { private static final int EFFECT_DURATION = 20; private final Map mEnabledAlwaysOnEffects = new HashMap<>(); - private final List mEffectSegments = new ArrayList<>(); - private final List mBraking = new ArrayList<>(); + private final Map> mEffectSegments = new TreeMap<>(); + private final Map> mBraking = new HashMap<>(); private final List mAmplitudes = new ArrayList<>(); private final List mExternalControlStates = new ArrayList<>(); private final Handler mHandler; @@ -67,6 +68,14 @@ final class FakeVibratorControllerProvider { private float mQFactor = Float.NaN; private float[] mMaxAmplitudes; + void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) { + mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment); + } + + void recordBraking(long vibrationId, int braking) { + mBraking.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(braking); + } + private final class FakeNativeWrapper extends VibratorController.NativeWrapper { public int vibratorId; public OnVibrationCompleteListener listener; @@ -86,7 +95,7 @@ final class FakeVibratorControllerProvider { @Override public long on(long milliseconds, long vibrationId) { - mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, + recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequencyHz= */ 0, (int) milliseconds)); applyLatency(); scheduleListener(milliseconds, vibrationId); @@ -110,7 +119,8 @@ final class FakeVibratorControllerProvider { || Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) { return 0; } - mEffectSegments.add(new PrebakedSegment((int) effect, false, (int) strength)); + recordEffectSegment(vibrationId, + new PrebakedSegment((int) effect, false, (int) strength)); applyLatency(); scheduleListener(EFFECT_DURATION, vibrationId); return EFFECT_DURATION; @@ -121,7 +131,7 @@ final class FakeVibratorControllerProvider { long duration = 0; for (PrimitiveSegment primitive : effects) { duration += EFFECT_DURATION + primitive.getDelay(); - mEffectSegments.add(primitive); + recordEffectSegment(vibrationId, primitive); } applyLatency(); scheduleListener(duration, vibrationId); @@ -133,9 +143,9 @@ final class FakeVibratorControllerProvider { long duration = 0; for (RampSegment primitive : primitives) { duration += primitive.getDuration(); - mEffectSegments.add(primitive); + recordEffectSegment(vibrationId, primitive); } - mBraking.add(braking); + recordBraking(vibrationId, braking); applyLatency(); scheduleListener(duration, vibrationId); return duration; @@ -304,15 +314,35 @@ final class FakeVibratorControllerProvider { } /** Return the braking values passed to the compose PWLE method. */ - public List getBraking() { - return mBraking; + public List getBraking(long vibrationId) { + if (mBraking.containsKey(vibrationId)) { + return new ArrayList<>(mBraking.get(vibrationId)); + } else { + return new ArrayList<>(); + } } /** Return list of {@link VibrationEffectSegment} played by this controller, in order. */ - public List getEffectSegments() { - return new ArrayList<>(mEffectSegments); + public List getEffectSegments(long vibrationId) { + if (mEffectSegments.containsKey(vibrationId)) { + return new ArrayList<>(mEffectSegments.get(vibrationId)); + } else { + return new ArrayList<>(); + } } + /** + * Returns a list of all vibrations' effect segments, for external-use where vibration IDs + * aren't exposed. + */ + public List getAllEffectSegments() { + // Returns segments in order of vibrationId, which increases over time. TreeMap gives order. + ArrayList result = new ArrayList<>(); + for (List subList : mEffectSegments.values()) { + result.addAll(subList); + } + return result; + } /** Return list of states set for external control to the fake vibrator hardware. */ public List getExternalControlStates() { return mExternalControlStates; diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 0590d7df5ee3f..6e2d099bd8c6f 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; @@ -79,7 +80,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Predicate; +import java.util.function.BooleanSupplier; import java.util.stream.Collectors; /** @@ -109,9 +110,9 @@ public class VibrationThreadTest { private final Map mVibratorProviders = new HashMap<>(); private VibrationSettings mVibrationSettings; private DeviceVibrationEffectAdapter mEffectAdapter; - private PowerManager.WakeLock mWakeLock; private TestLooper mTestLooper; private TestLooperAutoDispatcher mCustomTestLooperDispatcher; + private VibrationThread mThread; // Setup from the providers when VibrationThread is initialized. private SparseArray mControllers; @@ -132,11 +133,14 @@ public class VibrationThreadTest { Context context = InstrumentationRegistry.getContext(); mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()), mVibrationConfigMock); - mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); - mWakeLock = context.getSystemService( - PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); mockVibrators(VIBRATOR_ID); + + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); + PowerManager.WakeLock wakeLock = context.getSystemService( + PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); + mThread = new VibrationThread(wakeLock, mManagerHooks); + mThread.start(); } @After @@ -152,8 +156,8 @@ public class VibrationThreadTest { long vibrationId = 1; CombinedVibration effect = CombinedVibration.createParallel( VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); @@ -166,8 +170,8 @@ public class VibrationThreadTest { .addNext(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); @@ -179,8 +183,8 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createOneShot(10, 100); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -189,7 +193,7 @@ public class VibrationThreadTest { assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); } @@ -198,8 +202,8 @@ public class VibrationThreadTest { throws Exception { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createOneShot(10, 100); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -208,7 +212,7 @@ public class VibrationThreadTest { assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); } @@ -220,8 +224,8 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createWaveform( new long[]{5, 5, 5}, new int[]{1, 2, 3}, -1); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -230,7 +234,7 @@ public class VibrationThreadTest { assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(15)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(1, 2, 3), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); } @@ -244,17 +248,18 @@ public class VibrationThreadTest { long vibrationId = 1; int[] amplitudes = new int[]{1, 2, 3}; VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5, 5, 5}, amplitudes, 0); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); assertTrue( - waitUntil(t -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length, - thread, TEST_TIMEOUT_MILLIS)); + waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length, + TEST_TIMEOUT_MILLIS)); // Vibration still running after 2 cycles. - assertTrue(thread.isAlive()); + assertTrue(mThread.isRunningVibrationId(vibrationId)); assertTrue(mControllers.get(VIBRATOR_ID).isVibrating()); - thread.cancel(); - waitForCompletion(thread); + conductor.notifyCancelled(/* immediate= */ false); + waitForCompletion(); + assertFalse(mThread.isRunningVibrationId(vibrationId)); verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong()); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -262,7 +267,7 @@ public class VibrationThreadTest { assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); List playedAmplitudes = fakeVibrator.getAmplitudes(); - assertFalse(fakeVibrator.getEffectSegments().isEmpty()); + assertFalse(fakeVibrator.getEffectSegments(vibrationId).isEmpty()); assertFalse(playedAmplitudes.isEmpty()); for (int i = 0; i < playedAmplitudes.size(); i++) { @@ -280,16 +285,16 @@ public class VibrationThreadTest { int[] amplitudes = new int[]{1, 2, 3}; VibrationEffect effect = VibrationEffect.createWaveform( new long[]{1, 10, 100}, amplitudes, 0); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> !fakeVibrator.getAmplitudes().isEmpty(), thread, - TEST_TIMEOUT_MILLIS)); - thread.cancel(); - waitForCompletion(thread); + assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)); + conductor.notifyCancelled(/* immediate= */ false); + waitForCompletion(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList(expectedOneShot(1000)), fakeVibrator.getEffectSegments()); + assertEquals(Arrays.asList(expectedOneShot(1000)), + fakeVibrator.getEffectSegments(vibrationId)); } @Test @@ -302,16 +307,16 @@ public class VibrationThreadTest { int[] amplitudes = new int[]{1, 2, 3}; VibrationEffect effect = VibrationEffect.createWaveform( new long[]{5000, 500, 50}, amplitudes, 0); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> !fakeVibrator.getAmplitudes().isEmpty(), thread, - TEST_TIMEOUT_MILLIS)); - thread.cancel(); - waitForCompletion(thread); + assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)); + conductor.notifyCancelled(/* immediate= */ false); + waitForCompletion(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(Arrays.asList(expectedOneShot(5550)), fakeVibrator.getEffectSegments()); + assertEquals(Arrays.asList(expectedOneShot(5550)), + fakeVibrator.getEffectSegments(vibrationId)); } @@ -325,21 +330,22 @@ public class VibrationThreadTest { int[] amplitudes = new int[]{1, 2}; VibrationEffect effect = VibrationEffect.createWaveform( new long[]{900, 50}, amplitudes, 0); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length, - thread, 1000 + TEST_TIMEOUT_MILLIS)); - thread.cancel(); - waitForCompletion(thread); + assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length, + 1000 + TEST_TIMEOUT_MILLIS)); + conductor.notifyCancelled(/* immediate= */ false); + waitForCompletion(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - assertEquals(2, fakeVibrator.getEffectSegments().size()); + assertEquals(2, fakeVibrator.getEffectSegments(vibrationId).size()); // First time turn vibrator ON for minimum of 1s. - assertEquals(1000L, fakeVibrator.getEffectSegments().get(0).getDuration()); + assertEquals(1000L, fakeVibrator.getEffectSegments(vibrationId).get(0).getDuration()); // Vibrator turns off in the middle of the second execution of first step, turn it back ON // for another 1s + remaining of 850ms. - assertEquals(1850, fakeVibrator.getEffectSegments().get(1).getDuration(), /* delta= */ 20); + assertEquals(1850, + fakeVibrator.getEffectSegments(vibrationId).get(1).getDuration(), /* delta= */ 20); // Set amplitudes for a cycle {1, 2}, start second loop then turn it back on to same value. assertEquals(expectedAmplitudes(1, 2, 1, 1), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().subList(0, 4)); @@ -356,19 +362,20 @@ public class VibrationThreadTest { .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100) .compose(); - VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread, + assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), TEST_TIMEOUT_MILLIS)); - assertTrue(vibrationThread.isAlive()); + assertTrue(mThread.isRunningVibrationId(vibrationId)); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. - Thread cancellingThread = new Thread(() -> vibrationThread.cancel()); + Thread cancellingThread = + new Thread(() -> conductor.notifyCancelled(/* immediate= */ false)); cancellingThread.start(); - waitForCompletion(vibrationThread, /* timeout= */ 50); - waitForCompletion(cancellingThread); + waitForCompletion(/* timeout= */ 50); + cancellingThread.join(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); @@ -381,19 +388,20 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0); - VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread, + assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), TEST_TIMEOUT_MILLIS)); - assertTrue(vibrationThread.isAlive()); + assertTrue(mThread.isRunningVibrationId(vibrationId)); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. - Thread cancellingThread = new Thread(() -> vibrationThread.cancel()); + Thread cancellingThread = + new Thread(() -> conductor.notifyCancelled(/* immediate= */ false)); cancellingThread.start(); - waitForCompletion(vibrationThread, /* timeout= */ 50); - waitForCompletion(cancellingThread); + waitForCompletion(/* timeout= */ 50); + cancellingThread.join(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); @@ -405,8 +413,8 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_THUD); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -415,7 +423,7 @@ public class VibrationThreadTest { assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); } @Test @@ -428,8 +436,8 @@ public class VibrationThreadTest { Vibration vibration = createVibration(vibrationId, CombinedVibration.createParallel( VibrationEffect.get(VibrationEffect.EFFECT_CLICK))); vibration.addFallback(VibrationEffect.EFFECT_CLICK, fallback); - VibrationThread thread = startThreadAndDispatcher(vibration); - waitForCompletion(thread); + startThreadAndDispatcher(vibration); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -438,7 +446,7 @@ public class VibrationThreadTest { assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); } @@ -447,14 +455,14 @@ public class VibrationThreadTest { throws Exception { long vibrationId = 1; VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong()); verify(mManagerHooks, never()).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty()); + assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty()); } @Test @@ -467,8 +475,8 @@ public class VibrationThreadTest { .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) .compose(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -478,7 +486,7 @@ public class VibrationThreadTest { assertEquals(Arrays.asList( expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0), expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)), - fakeVibrator.getEffectSegments()); + fakeVibrator.getEffectSegments(vibrationId)); } @Test @@ -487,14 +495,14 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) .compose(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong()); verify(mManagerHooks, never()).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); - assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty()); + assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty()); } @Test @@ -509,13 +517,13 @@ public class VibrationThreadTest { .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f) .compose(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); // Vibrator compose called twice. verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - assertEquals(3, fakeVibrator.getEffectSegments().size()); + assertEquals(3, fakeVibrator.getEffectSegments(vibrationId).size()); } @Test @@ -536,8 +544,8 @@ public class VibrationThreadTest { .addOffDuration(Duration.ofMillis(100)) .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) .compose(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); // Use first duration the vibrator is turned on since we cannot estimate the clicks. verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); @@ -551,7 +559,7 @@ public class VibrationThreadTest { expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0), expectedPrebaked(VibrationEffect.EFFECT_CLICK), expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()); } @@ -574,8 +582,8 @@ public class VibrationThreadTest { .addSustain(Duration.ofMillis(30)) .addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200)) .build(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -590,8 +598,8 @@ public class VibrationThreadTest { expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f, /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 40)), - fakeVibrator.getEffectSegments()); - assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking()); + fakeVibrator.getEffectSegments(vibrationId)); + assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking(vibrationId)); } @Test @@ -612,13 +620,13 @@ public class VibrationThreadTest { .addSustain(Duration.ofMillis(30)) .addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200)) .build(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); // Vibrator compose called twice. verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - assertEquals(4, fakeVibrator.getEffectSegments().size()); + assertEquals(4, fakeVibrator.getEffectSegments(vibrationId).size()); } @Test @@ -628,16 +636,15 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> fakeVibrator.getAmplitudes().size() > 2, thread, - TEST_TIMEOUT_MILLIS)); + assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2, TEST_TIMEOUT_MILLIS)); // Vibration still running after 2 cycles. - assertTrue(thread.isAlive()); + assertTrue(mThread.isRunningVibrationId(vibrationId)); assertTrue(mControllers.get(VIBRATOR_ID).isVibrating()); - thread.binderDied(); - waitForCompletion(thread); + mThread.binderDied(); + waitForCompletion(); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); @@ -648,8 +655,9 @@ public class VibrationThreadTest { mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); long vibrationId = 1; - waitForCompletion(startThreadAndDispatcher(vibrationId, - VibrationEffect.createOneShot(10, 100))); + startThreadAndDispatcher(vibrationId, + VibrationEffect.createOneShot(10, 100)); + waitForCompletion(); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); verify(mManagerHooks, never()).prepareSyncedVibration(anyLong(), any()); @@ -667,8 +675,8 @@ public class VibrationThreadTest { .addVibrator(VIBRATOR_ID, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK)) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -678,7 +686,7 @@ public class VibrationThreadTest { assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); } @Test @@ -691,8 +699,8 @@ public class VibrationThreadTest { long vibrationId = 1; CombinedVibration effect = CombinedVibration.createParallel( VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -705,9 +713,12 @@ public class VibrationThreadTest { assertFalse(mControllers.get(3).isVibrating()); VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK); - assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments()); - assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getEffectSegments()); - assertEquals(Arrays.asList(expected), mVibratorProviders.get(3).getEffectSegments()); + assertEquals(Arrays.asList(expected), + mVibratorProviders.get(1).getEffectSegments(vibrationId)); + assertEquals(Arrays.asList(expected), + mVibratorProviders.get(2).getEffectSegments(vibrationId)); + assertEquals(Arrays.asList(expected), + mVibratorProviders.get(3).getEffectSegments(vibrationId)); } @Test @@ -729,8 +740,8 @@ public class VibrationThreadTest { new long[]{10, 10}, new int[]{1, 2}, -1)) .addVibrator(4, composed) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -745,16 +756,16 @@ public class VibrationThreadTest { assertFalse(mControllers.get(4).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(1).getEffectSegments()); + mVibratorProviders.get(1).getEffectSegments(vibrationId)); assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(2).getEffectSegments()); + mVibratorProviders.get(2).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(100), mVibratorProviders.get(2).getAmplitudes()); assertEquals(Arrays.asList(expectedOneShot(20)), - mVibratorProviders.get(3).getEffectSegments()); + mVibratorProviders.get(3).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(1, 2), mVibratorProviders.get(3).getAmplitudes()); assertEquals(Arrays.asList( expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)), - mVibratorProviders.get(4).getEffectSegments()); + mVibratorProviders.get(4).getEffectSegments(vibrationId)); } @Test @@ -773,9 +784,9 @@ public class VibrationThreadTest { .addNext(1, VibrationEffect.createOneShot(10, 100), /* delay= */ 50) .addNext(2, composed, /* delay= */ 50) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + waitForCompletion(); InOrder controllerVerifier = inOrder(mControllerCallbacks); controllerVerifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); @@ -795,13 +806,13 @@ public class VibrationThreadTest { assertFalse(mControllers.get(3).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(1).getEffectSegments()); + mVibratorProviders.get(1).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes()); assertEquals(Arrays.asList( expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)), - mVibratorProviders.get(2).getEffectSegments()); + mVibratorProviders.get(2).getEffectSegments(vibrationId)); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), - mVibratorProviders.get(3).getEffectSegments()); + mVibratorProviders.get(3).getEffectSegments(vibrationId)); } @Test @@ -818,13 +829,14 @@ public class VibrationThreadTest { .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100) .compose(); CombinedVibration effect = CombinedVibration.createParallel(composed); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> !mVibratorProviders.get(1).getEffectSegments().isEmpty() - && !mVibratorProviders.get(2).getEffectSegments().isEmpty(), thread, + assertTrue(waitUntil( + () -> !mVibratorProviders.get(1).getEffectSegments(vibrationId).isEmpty() + && !mVibratorProviders.get(2).getEffectSegments(vibrationId).isEmpty(), TEST_TIMEOUT_MILLIS)); - thread.syncedVibrationComplete(); - waitForCompletion(thread); + conductor.notifySyncedVibrationComplete(); + waitForCompletion(); long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_COMPOSE; verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); @@ -834,8 +846,10 @@ public class VibrationThreadTest { VibrationEffectSegment expected = expectedPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100); - assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments()); - assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getEffectSegments()); + assertEquals(Arrays.asList(expected), + mVibratorProviders.get(1).getEffectSegments(vibrationId)); + assertEquals(Arrays.asList(expected), + mVibratorProviders.get(2).getEffectSegments(vibrationId)); } @Test @@ -857,8 +871,8 @@ public class VibrationThreadTest { .addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1)) .addVibrator(4, composed) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON @@ -886,8 +900,8 @@ public class VibrationThreadTest { .addVibrator(1, VibrationEffect.createOneShot(10, 100)) .addVibrator(2, VibrationEffect.createWaveform(new long[]{5}, new int[]{200}, -1)) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON; verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); @@ -895,10 +909,10 @@ public class VibrationThreadTest { verify(mManagerHooks, never()).cancelSyncedVibration(); assertEquals(Arrays.asList(expectedOneShot(10)), - mVibratorProviders.get(1).getEffectSegments()); + mVibratorProviders.get(1).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes()); assertEquals(Arrays.asList(expectedOneShot(5)), - mVibratorProviders.get(2).getEffectSegments()); + mVibratorProviders.get(2).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(200), mVibratorProviders.get(2).getAmplitudes()); } @@ -915,8 +929,8 @@ public class VibrationThreadTest { .addVibrator(1, VibrationEffect.createOneShot(10, 100)) .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON @@ -945,16 +959,16 @@ public class VibrationThreadTest { .addVibrator(3, VibrationEffect.createWaveform( new long[]{60}, new int[]{6}, -1)) .combine(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + startThreadAndDispatcher(vibrationId, effect); // All vibrators are turned on in parallel. assertTrue(waitUntil( - t -> mControllers.get(1).isVibrating() + () -> mControllers.get(1).isVibrating() && mControllers.get(2).isVibrating() && mControllers.get(3).isVibrating(), - thread, TEST_TIMEOUT_MILLIS)); + TEST_TIMEOUT_MILLIS)); - waitForCompletion(thread); + waitForCompletion(); verify(mManagerHooks).noteVibratorOn(eq(UID), eq(80L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); @@ -967,11 +981,11 @@ public class VibrationThreadTest { assertFalse(mControllers.get(3).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(25)), - mVibratorProviders.get(1).getEffectSegments()); + mVibratorProviders.get(1).getEffectSegments(vibrationId)); assertEquals(Arrays.asList(expectedOneShot(80)), - mVibratorProviders.get(2).getEffectSegments()); + mVibratorProviders.get(2).getEffectSegments(vibrationId)); assertEquals(Arrays.asList(expectedOneShot(60)), - mVibratorProviders.get(3).getEffectSegments()); + mVibratorProviders.get(3).getEffectSegments(vibrationId)); assertEquals(expectedAmplitudes(1, 2, 3), mVibratorProviders.get(1).getAmplitudes()); assertEquals(expectedAmplitudes(4, 5), mVibratorProviders.get(2).getAmplitudes()); assertEquals(expectedAmplitudes(6), mVibratorProviders.get(3).getAmplitudes()); @@ -996,10 +1010,10 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(timings, amplitudes, -1); long vibrationId = 1; - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + startThreadAndDispatcher(vibrationId, effect); long startTime = SystemClock.elapsedRealtime(); - waitForCompletion(thread, totalDuration + TEST_TIMEOUT_MILLIS); + waitForCompletion(totalDuration + TEST_TIMEOUT_MILLIS); long delay = Math.abs(SystemClock.elapsedRealtime() - startTime - totalDuration); // Allow some delay for thread scheduling and callback triggering. @@ -1020,24 +1034,24 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil( - t -> !fakeVibrator.getEffectSegments().isEmpty(), - vibrationThread, TEST_TIMEOUT_MILLIS)); - assertTrue(vibrationThread.isAlive()); + assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(), + TEST_TIMEOUT_MILLIS)); + assertTrue(mThread.isRunningVibrationId(vibrationId)); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(cancellingThread). - Thread cancellingThread = new Thread(() -> vibrationThread.cancel()); + Thread cancellingThread = new Thread( + () -> conductor.notifyCancelled(/* immediate= */ false)); cancellingThread.start(); // Cancelling the vibration should be fast and return right away, even if the thread is // stuck at the slow call to the vibrator. - waitForCompletion(cancellingThread, /* timeout= */ 50); + waitForCompletion(/* timeout= */ 50); // After the vibrator call ends the vibration is cancelled and the vibrator is turned off. - waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS); + waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @@ -1057,19 +1071,20 @@ public class VibrationThreadTest { .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100) .compose()) .combine(); - VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> mControllers.get(2).isVibrating(), vibrationThread, + assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS)); - assertTrue(vibrationThread.isAlive()); + assertTrue(mThread.isRunningVibrationId(vibrationId)); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. - Thread cancellingThread = new Thread(() -> vibrationThread.cancel()); + Thread cancellingThread = new Thread( + () -> conductor.notifyCancelled(/* immediate= */ false)); cancellingThread.start(); - waitForCompletion(vibrationThread, /* timeout= */ 50); - waitForCompletion(cancellingThread); + waitForCompletion(/* timeout= */ 50); + cancellingThread.join(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(1).isVibrating()); @@ -1088,20 +1103,21 @@ public class VibrationThreadTest { new long[]{100, 100}, new int[]{1, 2}, 0)) .addVibrator(2, VibrationEffect.createOneShot(100, 100)) .combine(); - VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> mControllers.get(1).isVibrating() + assertTrue(waitUntil(() -> mControllers.get(1).isVibrating() && mControllers.get(2).isVibrating(), - vibrationThread, TEST_TIMEOUT_MILLIS)); - assertTrue(vibrationThread.isAlive()); + TEST_TIMEOUT_MILLIS)); + assertTrue(mThread.isRunningVibrationId(vibrationId)); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. - Thread cancellingThread = new Thread(() -> vibrationThread.cancel()); + Thread cancellingThread = + new Thread(() -> conductor.notifyCancelled(/* immediate= */ false)); cancellingThread.start(); - waitForCompletion(vibrationThread, /* timeout= */ 50); - waitForCompletion(cancellingThread); + waitForCompletion(/* timeout= */ 50); + cancellingThread.join(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mControllers.get(1).isVibrating()); @@ -1112,19 +1128,19 @@ public class VibrationThreadTest { public void vibrate_binderDied_cancelsVibration() throws Exception { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread, + assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), TEST_TIMEOUT_MILLIS)); - assertTrue(thread.isAlive()); + assertTrue(mThread.isRunningVibrationId(vibrationId)); - thread.binderDied(); - waitForCompletion(thread); + mThread.binderDied(); + waitForCompletion(); - verify(mVibrationToken).linkToDeath(same(thread), eq(0)); - verify(mVibrationToken).unlinkToDeath(same(thread), eq(0)); + verify(mVibrationToken).linkToDeath(same(mThread), eq(0)); + verify(mVibrationToken).unlinkToDeath(same(mThread), eq(0)); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty()); + assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty()); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @@ -1137,15 +1153,15 @@ public class VibrationThreadTest { 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); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); 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()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); List amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); assertTrue(amplitudes.size() > 3); assertEquals(expectedAmplitudes(60, 120, 240), amplitudes.subList(0, 3)); @@ -1162,28 +1178,28 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createOneShot(10, 200); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); // Vibration completed but vibrator not yet released. verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId), eq(Vibration.Status.FINISHED)); - verify(mManagerHooks, never()).onVibrationThreadReleased(); + verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong()); // Thread still running ramp down. - assertTrue(thread.isAlive()); + assertTrue(mThread.isRunningVibrationId(vibrationId)); // Duration extended for 10 + 10000. assertEquals(Arrays.asList(expectedOneShot(10_010)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); // Will stop the ramp down right away. - thread.cancelImmediately(); - waitForCompletion(thread); + conductor.notifyCancelled(/* immediate= */ true); + waitForCompletion(); // Does not cancel already finished vibration, but releases vibrator. verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId), eq(Vibration.Status.CANCELLED)); - verify(mManagerHooks).onVibrationThreadReleased(); + verify(mManagerHooks).onVibrationThreadReleased(vibrationId); } @Test @@ -1195,17 +1211,17 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread, + VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect); + assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), TEST_TIMEOUT_MILLIS)); - thread.cancel(); - waitForCompletion(thread); + conductor.notifyCancelled(/* immediate= */ false); + waitForCompletion(); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); // Duration extended for 10000 + 15. assertEquals(Arrays.asList(expectedOneShot(10_015)), - mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); List amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); assertTrue(amplitudes.size() > 1); for (int i = 1; i < amplitudes.size(); i++) { @@ -1222,14 +1238,14 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); 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()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); } @@ -1246,15 +1262,15 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) .compose(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); 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()); + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); } @@ -1275,17 +1291,112 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.startWaveform() .addTransition(Duration.ofMillis(1), targetAmplitude(1)) .build(); - VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - waitForCompletion(thread); + startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)), - fakeVibrator.getEffectSegments()); + fakeVibrator.getEffectSegments(vibrationId)); assertTrue(fakeVibrator.getAmplitudes().isEmpty()); } + @Test + public void vibrate_multipleVibrations_withCancel() throws Exception { + mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects( + VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK); + mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives( + VibrationEffect.Composition.PRIMITIVE_CLICK); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, + IVibrator.CAP_COMPOSE_EFFECTS); + + long vibrationId1 = 1; + long vibrationId2 = 2; + long vibrationId3 = 3; + long vibrationId4 = 4; + long vibrationId5 = 5; + + // A simple effect, followed by a repeating effect that gets cancelled, followed by another + // simple effect. + VibrationEffect effect1 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationEffect effect2 = VibrationEffect.startComposition() + .repeatEffectIndefinitely(VibrationEffect.get(VibrationEffect.EFFECT_TICK)) + .compose(); + VibrationEffect effect3 = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100); + VibrationEffect effect5 = VibrationEffect.createOneShot(20, 222); + + startThreadAndDispatcher(vibrationId1, effect1); + waitForCompletion(); + verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1); + verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED); + + VibrationStepConductor conductor2 = startThreadAndDispatcher(vibrationId2, effect2); + // Effect2 won't complete on its own. Cancel it after a couple of repeats. + Thread.sleep(150); // More than two TICKs. + conductor2.notifyCancelled(/* immediate= */ false); + waitForCompletion(); + + startThreadAndDispatcher(vibrationId3, effect3); + waitForCompletion(); + + // Effect4 is a long oneshot, but it gets cancelled as fast as possible. + long start4 = System.currentTimeMillis(); + VibrationStepConductor conductor4 = startThreadAndDispatcher(vibrationId4, effect4); + conductor4.notifyCancelled(/* immediate= */ true); + waitForCompletion(); + long duration4 = System.currentTimeMillis() - start4; + + // Effect5 is to show that things keep going after the immediate cancel. + startThreadAndDispatcher(vibrationId5, effect5); + waitForCompletion(); + + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + + // Effect1 + verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1); + verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED); + + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), + fakeVibrator.getEffectSegments(vibrationId1)); + + // Effect2: repeating, cancelled. + verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibrationId2); + verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED); + + // The exact count of segments might vary, so just check that there's more than 2 and + // all elements are the same segment. + List actualSegments2 = fakeVibrator.getEffectSegments(vibrationId2); + assertTrue(actualSegments2.size() + " > 2", actualSegments2.size() > 2); + for (VibrationEffectSegment segment : actualSegments2) { + assertEquals(expectedPrebaked(VibrationEffect.EFFECT_TICK), segment); + } + + // Effect3 + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId3)); + verifyCallbacksTriggered(vibrationId3, Vibration.Status.FINISHED); + assertEquals(Arrays.asList( + expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)), + fakeVibrator.getEffectSegments(vibrationId3)); + + // Effect4: cancelled quickly. + verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED); + assertTrue("Tested duration=" + duration4, duration4 < 2000); + + // Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have + // started. + + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5)); + verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED); + + assertEquals(Arrays.asList(expectedOneShot(20)), + fakeVibrator.getEffectSegments(vibrationId5)); + } + private void mockVibrators(int... vibratorIds) { for (int vibratorId : vibratorIds) { mVibratorProviders.put(vibratorId, @@ -1293,53 +1404,46 @@ public class VibrationThreadTest { } } - private VibrationThread startThreadAndDispatcher(long vibrationId, VibrationEffect effect) { + private VibrationStepConductor startThreadAndDispatcher( + long vibrationId, VibrationEffect effect) { return startThreadAndDispatcher(vibrationId, CombinedVibration.createParallel(effect)); } - private VibrationThread startThreadAndDispatcher(long vibrationId, + private VibrationStepConductor startThreadAndDispatcher(long vibrationId, CombinedVibration effect) { return startThreadAndDispatcher(createVibration(vibrationId, effect)); } - private VibrationThread startThreadAndDispatcher(Vibration vib) { + private VibrationStepConductor startThreadAndDispatcher(Vibration vib) { mControllers = createVibratorControllers(); - VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter, - mControllers, mWakeLock, mManagerHooks); + VibrationStepConductor conductor = new VibrationStepConductor(vib, mVibrationSettings, + mEffectAdapter, mControllers, mManagerHooks); doAnswer(answer -> { - thread.vibratorComplete(answer.getArgument(0)); + conductor.notifyVibratorComplete(answer.getArgument(0)); return null; }).when(mControllerCallbacks).onComplete(anyInt(), eq(vib.id)); - // TestLooper.AutoDispatchThread has a fixed 1s duration. Use a custom auto-dispatcher. - mCustomTestLooperDispatcher = new TestLooperAutoDispatcher(mTestLooper); - mCustomTestLooperDispatcher.start(); - thread.start(); - return thread; + assertTrue(mThread.runVibrationOnVibrationThread(conductor)); + return conductor; } - private boolean waitUntil(Predicate predicate, VibrationThread thread, - long timeout) throws InterruptedException { + private boolean waitUntil(BooleanSupplier predicate, long timeout) + throws InterruptedException { long timeoutTimestamp = SystemClock.uptimeMillis() + timeout; boolean predicateResult = false; while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) { Thread.sleep(10); - predicateResult = predicate.test(thread); + predicateResult = predicate.getAsBoolean(); } return predicateResult; } - private void waitForCompletion(Thread thread) { - waitForCompletion(thread, TEST_TIMEOUT_MILLIS); + private void waitForCompletion() { + waitForCompletion(TEST_TIMEOUT_MILLIS); } - private void waitForCompletion(Thread thread, long timeout) { - try { - thread.join(timeout); - } catch (InterruptedException e) { - } - assertFalse(thread.isAlive()); - mCustomTestLooperDispatcher.cancel(); - mTestLooper.dispatchAll(); + private void waitForCompletion(long timeout) { + mThread.waitForThreadIdle(timeout); + mTestLooper.dispatchAll(); // Flush callbacks } private Vibration createVibration(long id, CombinedVibration effect) { @@ -1352,6 +1456,12 @@ public class VibrationThreadTest { int id = e.getKey(); array.put(id, e.getValue().newVibratorController(id, mControllerCallbacks)); } + // Start a looper for the vibrationcontrollers if it's not already running. + // TestLooper.AutoDispatchThread has a fixed 1s duration. Use a custom auto-dispatcher. + if (mCustomTestLooperDispatcher == null) { + mCustomTestLooperDispatcher = new TestLooperAutoDispatcher(mTestLooper); + mCustomTestLooperDispatcher.start(); + } return array; } @@ -1386,10 +1496,10 @@ public class VibrationThreadTest { private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) { verify(mManagerHooks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus)); - verify(mManagerHooks).onVibrationThreadReleased(); + verify(mManagerHooks).onVibrationThreadReleased(vibrationId); } - private final class TestLooperAutoDispatcher extends Thread { + private static final class TestLooperAutoDispatcher extends Thread { private final TestLooper mTestLooper; private boolean mCancelled; diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 19111e5d16e9a..e46e28199d5a4 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -571,27 +571,27 @@ public class VibratorManagerServiceTest { VibratorManagerService service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); // Wait before checking it never played. - assertFalse(waitUntil(s -> !fakeVibrator.getEffectSegments().isEmpty(), + assertFalse(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(), service, /* timeout= */ 50)); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2, service, TEST_TIMEOUT_MILLIS)); assertEquals( Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK), expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)), - mVibratorProviders.get(1).getEffectSegments()); + mVibratorProviders.get(1).getAllEffectSegments()); } @Test @@ -604,25 +604,25 @@ public class VibratorManagerServiceTest { mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); 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, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), /* attrs= */ null); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2, service, TEST_TIMEOUT_MILLIS)); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), NOTIFICATION_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3, service, TEST_TIMEOUT_MILLIS)); assertEquals( Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK), expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK), expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)), - mVibratorProviders.get(1).getEffectSegments()); + mVibratorProviders.get(1).getAllEffectSegments()); } @Test @@ -738,14 +738,14 @@ public class VibratorManagerServiceTest { VibrationAttributes.USAGE_UNKNOWN).build()); // VibrationThread will start this vibration async, so wait before checking it started. - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(), + assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), service, TEST_TIMEOUT_MILLIS)); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), HAPTIC_FEEDBACK_ATTRS); // Wait before checking it never played a second effect. - assertFalse(waitUntil(s -> mVibratorProviders.get(1).getEffectSegments().size() > 1, + assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1, service, /* timeout= */ 50)); // The time estimate is recorded when the vibration starts, repeating vibrations @@ -767,14 +767,14 @@ public class VibratorManagerServiceTest { VibrationAttributes.USAGE_ALARM).build()); // VibrationThread will start this vibration async, so wait before checking it started. - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(), + assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), service, TEST_TIMEOUT_MILLIS)); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), HAPTIC_FEEDBACK_ATTRS); // Wait before checking it never played a second effect. - assertFalse(waitUntil(s -> mVibratorProviders.get(1).getEffectSegments().size() > 1, + assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1, service, /* timeout= */ 50)); } @@ -798,7 +798,7 @@ public class VibratorManagerServiceTest { verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any()); // VibrationThread will start this vibration async, so wait before checking it never played. - assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(), + assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), service, /* timeout= */ 50)); } @@ -863,8 +863,8 @@ public class VibratorManagerServiceTest { verify(mNativeWrapperMock).triggerSynced(anyLong()); PrimitiveSegment expected = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100); - assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments()); - assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getEffectSegments()); + assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getAllEffectSegments()); + assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getAllEffectSegments()); // VibrationThread needs some time to react to native callbacks and stop the vibrator. assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); @@ -891,7 +891,7 @@ public class VibratorManagerServiceTest { .compose()) .combine(); vibrate(service, effect, ALARM_ATTRS); - assertTrue(waitUntil(s -> !fakeVibrator1.getEffectSegments().isEmpty(), service, + assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service, TEST_TIMEOUT_MILLIS)); verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2})); @@ -915,7 +915,7 @@ public class VibratorManagerServiceTest { .addVibrator(2, VibrationEffect.createOneShot(10, 100)) .combine(); vibrate(service, effect, ALARM_ATTRS); - assertTrue(waitUntil(s -> !fakeVibrator1.getEffectSegments().isEmpty(), service, + assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service, TEST_TIMEOUT_MILLIS)); verify(mNativeWrapperMock, never()).prepareSynced(any()); @@ -935,8 +935,8 @@ public class VibratorManagerServiceTest { .addVibrator(2, VibrationEffect.createOneShot(10, 100)) .combine(); vibrate(service, effect, ALARM_ATTRS); - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(), service, - TEST_TIMEOUT_MILLIS)); + assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), + service, TEST_TIMEOUT_MILLIS)); verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2})); verify(mNativeWrapperMock, never()).triggerSynced(anyLong()); @@ -956,8 +956,8 @@ public class VibratorManagerServiceTest { .addVibrator(2, VibrationEffect.createOneShot(10, 100)) .combine(); vibrate(service, effect, ALARM_ATTRS); - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(), service, - TEST_TIMEOUT_MILLIS)); + assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), + service, TEST_TIMEOUT_MILLIS)); verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2})); verify(mNativeWrapperMock).triggerSynced(anyLong()); @@ -995,26 +995,26 @@ public class VibratorManagerServiceTest { vibrate(service, CombinedVibration.startSequential() .addNext(1, VibrationEffect.createOneShot(100, 125)) .combine(), NOTIFICATION_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); vibrate(service, VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) .compose(), HAPTIC_FEEDBACK_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2, service, TEST_TIMEOUT_MILLIS)); vibrate(service, VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) .compose(), ALARM_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3, + assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3, service, TEST_TIMEOUT_MILLIS)); vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS); - assertFalse(waitUntil(s -> fakeVibrator.getEffectSegments().size() > 3, + assertFalse(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() > 3, service, TEST_TIMEOUT_MILLIS)); - assertEquals(3, fakeVibrator.getEffectSegments().size()); + assertEquals(3, fakeVibrator.getAllEffectSegments().size()); // Notification vibrations will be scaled with SCALE_HIGH or none if default is high. assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH, @@ -1022,11 +1022,11 @@ public class VibratorManagerServiceTest { // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low. assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW, - 0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale()); + 0.5 > ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(1)).getScale()); // Alarm vibration will be scaled with SCALE_NONE. assertEquals(1f, - ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale(), 1e-5); + ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(2)).getScale(), 1e-5); // Ring vibrations have intensity OFF and are not played. }