diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index f02f9f98d9332..8ecc51b3087cd 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -47,12 +47,16 @@ final class Vibration { FINISHED, FINISHED_UNEXPECTED, // Didn't terminate in the usual way. FORWARDED_TO_INPUT_DEVICES, - CANCELLED, + CANCELLED_BINDER_DIED, + CANCELLED_BY_SCREEN_OFF, + CANCELLED_BY_SETTINGS_UPDATE, + CANCELLED_BY_USER, + CANCELLED_BY_UNKNOWN_REASON, + CANCELLED_SUPERSEDED, IGNORED_ERROR_APP_OPS, IGNORED_ERROR_CANCELLING, IGNORED_ERROR_SCHEDULING, IGNORED_ERROR_TOKEN, - IGNORED, IGNORED_APP_OPS, IGNORED_BACKGROUND, IGNORED_UNKNOWN_VIBRATION, diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java index e12426b2b02c6..e3d806755c6e3 100644 --- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java +++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java @@ -79,12 +79,14 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { private final Object mLock = new Object(); @GuardedBy("mLock") private final IntArray mSignalVibratorsComplete; + @Nullable @GuardedBy("mLock") - private boolean mSignalCancel = false; + private Vibration.Status mSignalCancelStatus = null; @GuardedBy("mLock") private boolean mSignalCancelImmediate = false; - private boolean mCancelled = false; + @Nullable + private Vibration.Status mCancelStatus = null; private boolean mCancelledImmediately = false; // hard stop private int mPendingVibrateSteps; private int mRemainingStartSequentialEffectSteps; @@ -185,8 +187,8 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { expectIsVibrationThread(true); } - if (mCancelled) { - return Vibration.Status.CANCELLED; + if (mCancelStatus != null) { + return mCancelStatus; } if (mPendingVibrateSteps > 0 || mRemainingStartSequentialEffectSteps > 0) { @@ -303,7 +305,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { if (DEBUG) { Slog.d(TAG, "Binder died, cancelling vibration..."); } - notifyCancelled(/* immediate= */ false); + notifyCancelled(Vibration.Status.CANCELLED_BINDER_DIED, /* immediate= */ false); } /** @@ -312,22 +314,41 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { * * @param immediate indicates whether cancellation should abort urgently and skip cleanup steps. */ - public void notifyCancelled(boolean immediate) { + public void notifyCancelled(@NonNull Vibration.Status cancelStatus, boolean immediate) { if (Build.IS_DEBUGGABLE) { expectIsVibrationThread(false); } + if (DEBUG) { + Slog.d(TAG, "Vibration cancel requested with status=" + cancelStatus + + ", immediate=" + immediate); + } + if ((cancelStatus == null) || !cancelStatus.name().startsWith("CANCEL")) { + Slog.w(TAG, "Vibration cancel requested with bad status=" + cancelStatus + + ", using CANCELLED_UNKNOWN_REASON to ensure cancellation."); + cancelStatus = Vibration.Status.CANCELLED_BY_UNKNOWN_REASON; + } synchronized (mLock) { - if (immediate && mSignalCancelImmediate || mSignalCancel) { - // Nothing to update: already cancelled previously. + if (immediate && mSignalCancelImmediate || (mSignalCancelStatus != null)) { + if (DEBUG) { + Slog.d(TAG, "Vibration cancel request ignored as the vibration " + + mVibration.id + "is already being cancelled with status=" + + mSignalCancelStatus + ", immediate=" + mSignalCancelImmediate); + } return; } mSignalCancelImmediate |= immediate; - mSignalCancel = true; + if (mSignalCancelStatus == null) { + mSignalCancelStatus = cancelStatus; + } else { + if (DEBUG) { + Slog.d(TAG, "Vibration cancel request new status=" + cancelStatus + + " ignored as the vibration was already cancelled with status=" + + mSignalCancelStatus + ", but immediate flag was updated to " + + mSignalCancelImmediate); + } + } mLock.notify(); } - if (DEBUG) { - Slog.d(TAG, "Vibration cancel requested, immediate=" + immediate); - } } /** @@ -380,7 +401,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { if (Build.IS_DEBUGGABLE) { expectIsVibrationThread(true); // Reads VibrationThread variables as well as signals. } - return (mSignalCancel && !mCancelled) + return (mSignalCancelStatus != mCancelStatus) || (mSignalCancelImmediate && !mCancelledImmediately) || (mSignalVibratorsComplete.size() > 0); } @@ -395,7 +416,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { } int[] vibratorsToProcess = null; - boolean doCancel = false; + Vibration.Status doCancelStatus = null; boolean doCancelImmediate = false; // Collect signals to process, but don't keep the lock while processing them. synchronized (mLock) { @@ -405,9 +426,10 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { } // This should only happen once. doCancelImmediate = true; + doCancelStatus = mSignalCancelStatus; } - if (mSignalCancel && !mCancelled) { - doCancel = true; + if (mSignalCancelStatus != mCancelStatus) { + doCancelStatus = mSignalCancelStatus; } if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) { // Swap out the queue of completions to process. @@ -421,11 +443,11 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { // completion signals that were collected in this call, but we won't process them // anyway as all steps are cancelled. if (doCancelImmediate) { - processCancelImmediately(); + processCancelImmediately(doCancelStatus); return; } - if (doCancel) { - processCancel(); + if (doCancelStatus != null) { + processCancel(doCancelStatus); } if (vibratorsToProcess != null) { processVibratorsComplete(vibratorsToProcess); @@ -438,12 +460,12 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { *
This will remove all steps and replace them with respective results of
* {@link Step#cancel()}.
*/
- public void processCancel() {
+ public void processCancel(Vibration.Status cancelStatus) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- mCancelled = true;
+ mCancelStatus = cancelStatus;
// 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.
List This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
*/
- public void processCancelImmediately() {
+ public void processCancelImmediately(Vibration.Status cancelStatus) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
mCancelledImmediately = true;
- mCancelled = true;
+ mCancelStatus = cancelStatus;
Step step;
while ((step = pollNext()) != null) {
step.cancelImmediately();
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 1f08b35d5540c..f749773d14a04 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -162,10 +162,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// When the system is entering a non-interactive state, we want to cancel
// vibrations in case a misbehaving app has abandoned them.
if (shouldCancelOnScreenOffLocked(mNextVibration)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED);
+ clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_SCREEN_OFF);
}
if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SCREEN_OFF,
+ /* immediate= */ false);
}
}
}
@@ -426,7 +427,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
final long ident = Binder.clearCallingIdentity();
try {
if (mCurrentVibration != null) {
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
+ /* immediate= */ false);
}
Vibration.Status status = startVibrationLocked(vib);
if (status != Vibration.Status.RUNNING) {
@@ -459,19 +461,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mNextVibration != null
&& shouldCancelVibration(mNextVibration.getVibration(),
usageFilter, token)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED);
+ clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_USER);
}
if (mCurrentVibration != null
&& shouldCancelVibration(mCurrentVibration.getVibration(),
usageFilter, token)) {
- mCurrentVibration.notifyCancelled(/* immediate= */false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_USER,
+ /* immediate= */false);
}
if (mCurrentExternalVibration != null
&& shouldCancelVibration(
mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
usageFilter)) {
mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_BY_USER,
/* continueExternalControl= */ false);
}
} finally {
@@ -600,7 +603,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.d(TAG, "Canceling vibration because settings changed: "
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
}
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE,
+ /* immediate= */ false);
}
}
}
@@ -1319,7 +1323,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (DEBUG) {
Slog.d(TAG, "External vibration finished because binder died");
}
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_BINDER_DIED,
/* continueExternalControl= */ false);
}
}
@@ -1543,7 +1547,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.notifyCancelled(/* immediate= */ true);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
+ /* immediate= */ true);
waitForCompletion = true;
}
} else {
@@ -1557,7 +1562,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// would need to mute the old one still if it came from a different controller.
alreadyUnderExternalControl = true;
mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_SUPERSEDED,
/* continueExternalControl= */ true);
}
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
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 704729ea22c4d..9f135918daa2f 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -257,13 +257,13 @@ public class VibrationThreadTest {
assertTrue(mThread.isRunningVibrationId(vibrationId));
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false);
waitForCompletion();
assertFalse(mThread.isRunningVibrationId(vibrationId));
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
List