Loading core/java/android/app/KeyguardManager.java +9 −0 Original line number Diff line number Diff line Loading @@ -201,6 +201,15 @@ public class KeyguardManager { */ public static final String EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS = "check_dpm"; /** * When switching to a secure user, system server will expect a callback when the UI has * completed the switch. * * @hide */ public static final String LOCK_ON_USER_SWITCH_CALLBACK = "onSwitchCallback"; /** * * Password lock type, see {@link #setLock} Loading packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +194 −36 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard; import static android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK; import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT; import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED; Loading Loading @@ -44,6 +45,7 @@ import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.BroadcastOptions; Loading @@ -70,6 +72,7 @@ import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.PowerManager; Loading Loading @@ -176,11 +179,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; import dagger.Lazy; import kotlinx.coroutines.CoroutineDispatcher; /** Loading Loading @@ -258,6 +264,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17; private static final int SYSTEM_READY = 18; private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19; private static final int USER_SWITCH_COMPLETE = 23; /** Enum for reasons behind updating wakeAndUnlock state. */ @Retention(RetentionPolicy.SOURCE) Loading @@ -275,6 +282,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, int WAKE_AND_UNLOCK = 3; } private final List<LockNowCallback> mLockNowCallbacks = new ArrayList<>(); /** * The default amount of time we stay awake (used for all key input) */ Loading Loading @@ -328,6 +337,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final ScreenOffAnimationController mScreenOffAnimationController; private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthController; private final Lazy<ShadeController> mShadeController; /* * Records the user id on request to go away, for validation when WM calls back to start the * exit animation. */ private int mGoingAwayRequestedForUserId = -1; private boolean mSystemReady; private boolean mBootCompleted; Loading Loading @@ -431,6 +445,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private boolean mDeviceInteractive; private boolean mGoingToSleep; private final Object mDismissToken = new Object(); // last known state of the cellular connection private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE; Loading Loading @@ -465,6 +481,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private boolean mUnlockingAndWakingFromDream = false; private boolean mHideAnimationRun = false; private boolean mHideAnimationRunning = false; private boolean mIsKeyguardExitAnimationCanceled = false; private SoundPool mLockSounds; private int mLockSoundId; Loading Loading @@ -595,6 +612,32 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } }; @VisibleForTesting protected UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override public void onUserChanged(int newUser, Context userContext) { mHandler.sendMessage(mHandler.obtainMessage(USER_SWITCH_COMPLETE, newUser, 0)); } }; /** * Handle {@link #USER_SWITCH_COMPLETE} */ @VisibleForTesting void handleUserSwitchComplete(int userId) { Log.d(TAG, String.format("onUserSwitchComplete %d", userId)); if (userId != KeyguardUpdateMonitor.getCurrentUser()) { Log.i(TAG, "Ignoring user switch complete, new user change"); return; } if (!mLockPatternUtils.isSecure(userId)) { mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), mDismissToken, 500); } } KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @Override Loading @@ -610,25 +653,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } @Override public void onUserSwitching(int userId) { if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId)); synchronized (KeyguardViewMediator.this) { resetKeyguardDonePendingLocked(); dismiss(null /* callback */, null /* message */); adjustStatusBarLocked(); } } @Override public void onUserSwitchComplete(int userId) { if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId)); // We are calling dismiss again and with a delay as there are race conditions // in some scenarios caused by async layout listeners mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500); } @Override public void onDeviceProvisioned() { sendUserPresentBroadcast(); } Loading Loading @@ -1549,7 +1573,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, com.android.internal.R.anim.lock_screen_behind_enter); mWorkLockController = new WorkLockActivityController(mContext, mUserTracker); mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); mJavaAdapter.alwaysCollectFlow( mWallpaperRepository.getWallpaperSupportsAmbientMode(), this::setWallpaperSupportsAmbientMode); Loading Loading @@ -2188,11 +2212,22 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * Enable the keyguard if the settings are appropriate. */ private void doKeyguardLocked(Bundle options) { int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); if (options != null && options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) { LockNowCallback callback = new LockNowCallback(currentUserId, IRemoteCallback.Stub.asInterface( options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK))); synchronized (mLockNowCallbacks) { mLockNowCallbacks.add(callback); } Log.d(TAG, "LockNowCallback required for user: " + callback.mUserId); } // if another app is disabling us, don't show if (!mExternallyEnabled && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); notifyLockNowCallback(); mNeedToReshowWhenReenabled = true; return; } Loading @@ -2216,6 +2251,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, + "previously hiding. It should be safe to short-circuit " + "here."); resetStateLocked(/* hideBouncer= */ false); notifyLockNowCallback(); return; } } else { Loading @@ -2242,6 +2278,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Log.d(TAG, "doKeyguard: not showing because device isn't provisioned and the sim is" + " not locked or missing"); } notifyLockNowCallback(); return; } Loading @@ -2249,6 +2286,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser()) && !lockedOrMissing && !forceShow) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off"); notifyLockNowCallback(); return; } Loading Loading @@ -2282,6 +2320,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } public void dismiss(IKeyguardDismissCallback callback, CharSequence message) { if (mKeyguardStateController.isKeyguardGoingAway()) { Log.i(TAG, "Ignoring dismiss because we're already going away."); return; } mHandler.obtainMessage(DISMISS, new DismissMessage(callback, message)).sendToTarget(); } Loading @@ -2294,7 +2337,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void resetStateLocked(boolean hideBouncer) { if (DEBUG) Log.e(TAG, "resetStateLocked"); if (DEBUG) Log.d(TAG, "resetStateLocked"); Message msg = mHandler.obtainMessage(RESET, hideBouncer ? 1 : 0, 0); mHandler.sendMessage(msg); } Loading Loading @@ -2411,9 +2454,32 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * @param newUserId The id of the incoming user. */ public void setCurrentUser(int newUserId) { KeyguardUpdateMonitor.setCurrentUser(newUserId); synchronized (this) { if (newUserId != KeyguardUpdateMonitor.getCurrentUser()) { Log.d(TAG, String.format("setCurrentUser %d", newUserId)); KeyguardUpdateMonitor.setCurrentUser(newUserId); mHandler.removeCallbacksAndMessages(mDismissToken); mHandler.removeMessages(DISMISS); mHandler.removeMessages(HIDE); notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(newUserId)); resetKeyguardDonePendingLocked(); adjustStatusBarLocked(); mKeyguardStateController.notifyKeyguardGoingAway(false); if (mLockPatternUtils.isSecure(newUserId)) { mIsKeyguardExitAnimationCanceled = true; setPendingLock(true); // Immediately tell WM to show keyguard Log.i(TAG, "Calling setLockScreenShow(true, " + mAodShowing + " during user switch"); try { ActivityTaskManager.getService().setLockScreenShown(true, mAodShowing); } catch (RemoteException e) { } doKeyguardLocked(null); } else { resetStateLocked(); } } } } Loading Loading @@ -2567,6 +2633,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, message = "SYSTEM_READY"; handleSystemReady(); break; case USER_SWITCH_COMPLETE: message = "USER_SWITCH_COMPLETE"; handleUserSwitchComplete(msg.arg1); break; } Log.d(TAG, "KeyguardViewMediator queue processing message: " + message); } Loading Loading @@ -2703,8 +2773,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private void updateActivityLockScreenState(boolean showing, boolean aodShowing) { mUiBgExecutor.execute(() -> { if (DEBUG) { Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")"); Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing); if (showing) { notifyLockNowCallback(); } if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { Loading Loading @@ -2732,6 +2803,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, synchronized (KeyguardViewMediator.this) { if (!mSystemReady) { if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready."); notifyLockNowCallback(); return; } else { if (DEBUG) Log.d(TAG, "handleShow"); Loading Loading @@ -2793,11 +2865,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } private final Runnable mKeyguardGoingAwayRunnable = new Runnable() { final Runnable mKeyguardGoingAwayRunnable = new Runnable() { @SuppressLint("MissingPermission") @Override public void run() { Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable"); if (DEBUG) Log.d(TAG, "keyguardGoingAway"); mKeyguardViewControllerLazy.get().keyguardGoingAway(); int flags = 0; Loading Loading @@ -2833,10 +2905,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // Handled in WmLockscreenVisibilityManager if flag is enabled. if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { // Don't actually hide the Keyguard at the moment, wait for window manager until it // tells us it's safe to do so with startKeyguardExitAnimation. // Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager will be // in order. mGoingAwayRequestedForUserId = KeyguardUpdateMonitor.getCurrentUser(); Log.d(TAG, "keyguardGoingAway requested for userId: " + mGoingAwayRequestedForUserId); // Don't actually hide the Keyguard at the moment, wait for window manager // until it tells us it's safe to do so with startKeyguardExitAnimation. // Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager // will be in order. final int keyguardFlag = flags; mUiBgExecutor.execute(() -> { try { Loading Loading @@ -2962,7 +3038,33 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation"); Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime + " fadeoutDuration=" + fadeoutDuration); int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); if (mGoingAwayRequestedForUserId != currentUserId) { Log.e(TAG, "Not executing handleStartKeyguardExitAnimationInner() due to userId " + "mismatch. Requested: " + mGoingAwayRequestedForUserId + ", current: " + currentUserId); mIsKeyguardExitAnimationCanceled = true; if (finishedCallback != null) { // There will not execute animation, send a finish callback to ensure the remote // animation won't hang there. try { finishedCallback.onAnimationFinished(); } catch (RemoteException e) { Slog.w(TAG, "Failed to call onAnimationFinished", e); } } mHiding = false; if (mLockPatternUtils.isSecure(currentUserId)) { doKeyguardLocked(null); } else { resetStateLocked(); dismiss(null, null); } return; } synchronized (KeyguardViewMediator.this) { mIsKeyguardExitAnimationCanceled = false; // Tell ActivityManager that we canceled the keyguard animation if // handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard, // unless we're animating the surface behind the keyguard and will be hiding the Loading Loading @@ -3006,9 +3108,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Slog.w(TAG, "Failed to call onAnimationFinished", e); } } if (!mIsKeyguardExitAnimationCanceled) { onKeyguardExitFinished(); mKeyguardViewControllerLazy.get().hide(0 /* startTime */, 0 /* fadeoutDuration */); } mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION); } Loading Loading @@ -3183,6 +3287,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * app transition before finishing the current RemoteAnimation, or the keyguard being re-shown). */ private void handleCancelKeyguardExitAnimation() { if (mGoingAwayRequestedForUserId != KeyguardUpdateMonitor.getCurrentUser()) { Log.e(TAG, "Setting pendingLock = true due to userId mismatch. Requested: " + mGoingAwayRequestedForUserId + ", current: " + KeyguardUpdateMonitor.getCurrentUser()); setPendingLock(true); } if (mPendingLock) { Log.d(TAG, "#handleCancelKeyguardExitAnimation: keyguard exit animation cancelled. " + "There's a pending lock, so we were cancelled because the device was locked " Loading @@ -3191,6 +3301,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // A lock is pending, meaning the keyguard exit animation was cancelled because we're // re-locking. We should just end the surface-behind animation without exiting the // keyguard. The pending lock will be handled by onFinishedGoingToSleep(). mIsKeyguardExitAnimationCanceled = true; finishSurfaceBehindRemoteAnimation(true /* showKeyguard */); maybeHandlePendingLock(); } else { Loading Loading @@ -3223,6 +3334,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, return; } if (mIsKeyguardExitAnimationCanceled) { Log.d(TAG, "Ignoring exitKeyguardAndFinishSurfaceBehindRemoteAnimation. " + "mIsKeyguardExitAnimationCanceled==true"); return; } // Block the panel from expanding, in case we were doing a swipe to dismiss gesture. mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch(); final boolean wasShowing = mShowing; Loading @@ -3230,6 +3347,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // Post layout changes to the next frame, so we don't hang at the end of the animation. DejankUtils.postAfterTraversal(() -> { if (mIsKeyguardExitAnimationCanceled) { Log.d(TAG, "Ignoring dejank exitKeyguardAndFinishSurfaceBehindRemoteAnimation. " + "mIsKeyguardExitAnimationCanceled==true"); return; } if (!mPM.isInteractive() && !mPendingLock) { Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal:" + "mPM.isInteractive()=" + mPM.isInteractive() Loading Loading @@ -3285,7 +3408,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { // Handled in WmLockscreenVisibilityManager. mGoingAwayRequestedForUserId = KeyguardUpdateMonitor.getCurrentUser(); Log.d(TAG, "keyguardGoingAway requested for userId: " + mGoingAwayRequestedForUserId); ActivityTaskManager.getService().keyguardGoingAway(flags); } mKeyguardStateController.notifyKeyguardGoingAway(true); Loading Loading @@ -3733,6 +3858,29 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, }); } private void notifyLockNowCallback() { List<LockNowCallback> callbacks; synchronized (mLockNowCallbacks) { callbacks = new ArrayList<LockNowCallback>(mLockNowCallbacks); mLockNowCallbacks.clear(); } Iterator<LockNowCallback> iter = callbacks.listIterator(); while (iter.hasNext()) { LockNowCallback callback = iter.next(); iter.remove(); if (callback.mUserId != KeyguardUpdateMonitor.getCurrentUser()) { Log.i(TAG, "Not notifying lockNowCallback due to user mismatch"); continue; } Log.i(TAG, "Notifying lockNowCallback"); try { callback.mRemoteCallback.sendResult(null); } catch (RemoteException e) { Log.e(TAG, "Could not issue LockNowCallback sendResult", e); } } } private void notifyTrustedChangedLocked(boolean trusted) { int size = mKeyguardStateCallbacks.size(); for (int i = size - 1; i >= 0; i--) { Loading Loading @@ -3888,4 +4036,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } }; } private class LockNowCallback { final int mUserId; final IRemoteCallback mRemoteCallback; LockNowCallback(int userId, IRemoteCallback remoteCallback) { mUserId = userId; mRemoteCallback = remoteCallback; } } } packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +77 −4 Original line number Diff line number Diff line Loading @@ -238,6 +238,60 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { KeyguardUpdateMonitor.setCurrentUser(mInitialUserId); } @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void testGoingAwayFollowedByUserSwitchDoesNotHideKeyguard() { setCurrentUser(/* userId= */1099, /* isSecure= */false); // Setup keyguard mViewMediator.onSystemReady(); processAllMessagesAndBgExecutorMessages(); mViewMediator.setShowingLocked(true); // Request keyguard going away when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); mViewMediator.mKeyguardGoingAwayRunnable.run(); // After the request, begin a switch to a new secure user int nextUserId = 500; setCurrentUser(nextUserId, /* isSecure= */true); // After that request has begun, have WM tell us to exit keyguard RemoteAnimationTarget[] apps = new RemoteAnimationTarget[]{ mock(RemoteAnimationTarget.class) }; RemoteAnimationTarget[] wallpapers = new RemoteAnimationTarget[]{ mock(RemoteAnimationTarget.class) }; IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class); mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers, null, callback); processAllMessagesAndBgExecutorMessages(); // The call to exit should be rejected, and keyguard should still be visible verify(mKeyguardUnlockAnimationController, never()).notifyStartSurfaceBehindRemoteAnimation( any(), any(), anyLong(), anyBoolean()); assertTrue(mViewMediator.isShowingAndNotOccluded()); } @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void testUserSwitchToSecureUserWhileKeyguardNotVisibleShowsKeyguard() { setCurrentUser(/* userId= */1099, /* isSecure= */true); // Setup keyguard as not visible mViewMediator.onSystemReady(); processAllMessagesAndBgExecutorMessages(); mViewMediator.setShowingLocked(false); processAllMessagesAndBgExecutorMessages(); // Begin a switch to a new secure user int nextUserId = 500; setCurrentUser(nextUserId, /* isSecure= */true); assertTrue(mViewMediator.isShowingAndNotOccluded()); } @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally() { Loading Loading @@ -630,6 +684,9 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void testCancelKeyguardExitAnimation_noPendingLock_keyguardWillNotBeShowing() { when(mPowerManager.isInteractive()).thenReturn(true); setCurrentUser(0, false); startMockKeyguardExitAnimation(); cancelMockKeyguardExitAnimation(); Loading @@ -638,7 +695,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); assertFalse(mViewMediator.isShowingAndNotOccluded()); verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(false); } @Test Loading Loading @@ -692,12 +748,21 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(false); } /** * Interactions with the ActivityTaskManagerService and others are posted to an executor that * doesn't use the testable looper. Use this method to ensure those are run as well. */ private void processAllMessagesAndBgExecutorMessages() { TestableLooper.get(this).processAllMessages(); mUiBgExecutor.runAllReady(); } /** * Configures mocks appropriately, then starts the keyguard exit animation. */ private void startMockKeyguardExitAnimation() { mViewMediator.onSystemReady(); TestableLooper.get(this).processAllMessages(); processAllMessagesAndBgExecutorMessages(); mViewMediator.setShowingLocked(true); Loading @@ -710,9 +775,10 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class); when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); mViewMediator.mKeyguardGoingAwayRunnable.run(); mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers, null, callback); TestableLooper.get(this).processAllMessages(); processAllMessagesAndBgExecutorMessages(); } /** Loading @@ -721,7 +787,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private void cancelMockKeyguardExitAnimation() { when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); mViewMediator.cancelKeyguardExitAnimation(); TestableLooper.get(this).processAllMessages(); processAllMessagesAndBgExecutorMessages(); } @Test Loading Loading @@ -1027,4 +1093,11 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private void captureKeyguardUpdateMonitorCallback() { verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture()); } private void setCurrentUser(int userId, boolean isSecure) { when(mUserTracker.getUserId()).thenReturn(userId); when(mLockPatternUtils.isSecure(userId)).thenReturn(isSecure); mViewMediator.setCurrentUser(userId); processAllMessagesAndBgExecutorMessages(); } } Loading
core/java/android/app/KeyguardManager.java +9 −0 Original line number Diff line number Diff line Loading @@ -201,6 +201,15 @@ public class KeyguardManager { */ public static final String EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS = "check_dpm"; /** * When switching to a secure user, system server will expect a callback when the UI has * completed the switch. * * @hide */ public static final String LOCK_ON_USER_SWITCH_CALLBACK = "onSwitchCallback"; /** * * Password lock type, see {@link #setLock} Loading
packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +194 −36 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard; import static android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK; import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT; import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED; Loading Loading @@ -44,6 +45,7 @@ import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.BroadcastOptions; Loading @@ -70,6 +72,7 @@ import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.PowerManager; Loading Loading @@ -176,11 +179,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; import dagger.Lazy; import kotlinx.coroutines.CoroutineDispatcher; /** Loading Loading @@ -258,6 +264,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17; private static final int SYSTEM_READY = 18; private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19; private static final int USER_SWITCH_COMPLETE = 23; /** Enum for reasons behind updating wakeAndUnlock state. */ @Retention(RetentionPolicy.SOURCE) Loading @@ -275,6 +282,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, int WAKE_AND_UNLOCK = 3; } private final List<LockNowCallback> mLockNowCallbacks = new ArrayList<>(); /** * The default amount of time we stay awake (used for all key input) */ Loading Loading @@ -328,6 +337,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final ScreenOffAnimationController mScreenOffAnimationController; private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthController; private final Lazy<ShadeController> mShadeController; /* * Records the user id on request to go away, for validation when WM calls back to start the * exit animation. */ private int mGoingAwayRequestedForUserId = -1; private boolean mSystemReady; private boolean mBootCompleted; Loading Loading @@ -431,6 +445,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private boolean mDeviceInteractive; private boolean mGoingToSleep; private final Object mDismissToken = new Object(); // last known state of the cellular connection private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE; Loading Loading @@ -465,6 +481,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private boolean mUnlockingAndWakingFromDream = false; private boolean mHideAnimationRun = false; private boolean mHideAnimationRunning = false; private boolean mIsKeyguardExitAnimationCanceled = false; private SoundPool mLockSounds; private int mLockSoundId; Loading Loading @@ -595,6 +612,32 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } }; @VisibleForTesting protected UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override public void onUserChanged(int newUser, Context userContext) { mHandler.sendMessage(mHandler.obtainMessage(USER_SWITCH_COMPLETE, newUser, 0)); } }; /** * Handle {@link #USER_SWITCH_COMPLETE} */ @VisibleForTesting void handleUserSwitchComplete(int userId) { Log.d(TAG, String.format("onUserSwitchComplete %d", userId)); if (userId != KeyguardUpdateMonitor.getCurrentUser()) { Log.i(TAG, "Ignoring user switch complete, new user change"); return; } if (!mLockPatternUtils.isSecure(userId)) { mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), mDismissToken, 500); } } KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @Override Loading @@ -610,25 +653,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } @Override public void onUserSwitching(int userId) { if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId)); synchronized (KeyguardViewMediator.this) { resetKeyguardDonePendingLocked(); dismiss(null /* callback */, null /* message */); adjustStatusBarLocked(); } } @Override public void onUserSwitchComplete(int userId) { if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId)); // We are calling dismiss again and with a delay as there are race conditions // in some scenarios caused by async layout listeners mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500); } @Override public void onDeviceProvisioned() { sendUserPresentBroadcast(); } Loading Loading @@ -1549,7 +1573,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, com.android.internal.R.anim.lock_screen_behind_enter); mWorkLockController = new WorkLockActivityController(mContext, mUserTracker); mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); mJavaAdapter.alwaysCollectFlow( mWallpaperRepository.getWallpaperSupportsAmbientMode(), this::setWallpaperSupportsAmbientMode); Loading Loading @@ -2188,11 +2212,22 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * Enable the keyguard if the settings are appropriate. */ private void doKeyguardLocked(Bundle options) { int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); if (options != null && options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) { LockNowCallback callback = new LockNowCallback(currentUserId, IRemoteCallback.Stub.asInterface( options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK))); synchronized (mLockNowCallbacks) { mLockNowCallbacks.add(callback); } Log.d(TAG, "LockNowCallback required for user: " + callback.mUserId); } // if another app is disabling us, don't show if (!mExternallyEnabled && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); notifyLockNowCallback(); mNeedToReshowWhenReenabled = true; return; } Loading @@ -2216,6 +2251,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, + "previously hiding. It should be safe to short-circuit " + "here."); resetStateLocked(/* hideBouncer= */ false); notifyLockNowCallback(); return; } } else { Loading @@ -2242,6 +2278,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Log.d(TAG, "doKeyguard: not showing because device isn't provisioned and the sim is" + " not locked or missing"); } notifyLockNowCallback(); return; } Loading @@ -2249,6 +2286,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser()) && !lockedOrMissing && !forceShow) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off"); notifyLockNowCallback(); return; } Loading Loading @@ -2282,6 +2320,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } public void dismiss(IKeyguardDismissCallback callback, CharSequence message) { if (mKeyguardStateController.isKeyguardGoingAway()) { Log.i(TAG, "Ignoring dismiss because we're already going away."); return; } mHandler.obtainMessage(DISMISS, new DismissMessage(callback, message)).sendToTarget(); } Loading @@ -2294,7 +2337,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void resetStateLocked(boolean hideBouncer) { if (DEBUG) Log.e(TAG, "resetStateLocked"); if (DEBUG) Log.d(TAG, "resetStateLocked"); Message msg = mHandler.obtainMessage(RESET, hideBouncer ? 1 : 0, 0); mHandler.sendMessage(msg); } Loading Loading @@ -2411,9 +2454,32 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * @param newUserId The id of the incoming user. */ public void setCurrentUser(int newUserId) { KeyguardUpdateMonitor.setCurrentUser(newUserId); synchronized (this) { if (newUserId != KeyguardUpdateMonitor.getCurrentUser()) { Log.d(TAG, String.format("setCurrentUser %d", newUserId)); KeyguardUpdateMonitor.setCurrentUser(newUserId); mHandler.removeCallbacksAndMessages(mDismissToken); mHandler.removeMessages(DISMISS); mHandler.removeMessages(HIDE); notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(newUserId)); resetKeyguardDonePendingLocked(); adjustStatusBarLocked(); mKeyguardStateController.notifyKeyguardGoingAway(false); if (mLockPatternUtils.isSecure(newUserId)) { mIsKeyguardExitAnimationCanceled = true; setPendingLock(true); // Immediately tell WM to show keyguard Log.i(TAG, "Calling setLockScreenShow(true, " + mAodShowing + " during user switch"); try { ActivityTaskManager.getService().setLockScreenShown(true, mAodShowing); } catch (RemoteException e) { } doKeyguardLocked(null); } else { resetStateLocked(); } } } } Loading Loading @@ -2567,6 +2633,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, message = "SYSTEM_READY"; handleSystemReady(); break; case USER_SWITCH_COMPLETE: message = "USER_SWITCH_COMPLETE"; handleUserSwitchComplete(msg.arg1); break; } Log.d(TAG, "KeyguardViewMediator queue processing message: " + message); } Loading Loading @@ -2703,8 +2773,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private void updateActivityLockScreenState(boolean showing, boolean aodShowing) { mUiBgExecutor.execute(() -> { if (DEBUG) { Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")"); Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing); if (showing) { notifyLockNowCallback(); } if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { Loading Loading @@ -2732,6 +2803,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, synchronized (KeyguardViewMediator.this) { if (!mSystemReady) { if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready."); notifyLockNowCallback(); return; } else { if (DEBUG) Log.d(TAG, "handleShow"); Loading Loading @@ -2793,11 +2865,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } private final Runnable mKeyguardGoingAwayRunnable = new Runnable() { final Runnable mKeyguardGoingAwayRunnable = new Runnable() { @SuppressLint("MissingPermission") @Override public void run() { Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable"); if (DEBUG) Log.d(TAG, "keyguardGoingAway"); mKeyguardViewControllerLazy.get().keyguardGoingAway(); int flags = 0; Loading Loading @@ -2833,10 +2905,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // Handled in WmLockscreenVisibilityManager if flag is enabled. if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { // Don't actually hide the Keyguard at the moment, wait for window manager until it // tells us it's safe to do so with startKeyguardExitAnimation. // Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager will be // in order. mGoingAwayRequestedForUserId = KeyguardUpdateMonitor.getCurrentUser(); Log.d(TAG, "keyguardGoingAway requested for userId: " + mGoingAwayRequestedForUserId); // Don't actually hide the Keyguard at the moment, wait for window manager // until it tells us it's safe to do so with startKeyguardExitAnimation. // Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager // will be in order. final int keyguardFlag = flags; mUiBgExecutor.execute(() -> { try { Loading Loading @@ -2962,7 +3038,33 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation"); Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime + " fadeoutDuration=" + fadeoutDuration); int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); if (mGoingAwayRequestedForUserId != currentUserId) { Log.e(TAG, "Not executing handleStartKeyguardExitAnimationInner() due to userId " + "mismatch. Requested: " + mGoingAwayRequestedForUserId + ", current: " + currentUserId); mIsKeyguardExitAnimationCanceled = true; if (finishedCallback != null) { // There will not execute animation, send a finish callback to ensure the remote // animation won't hang there. try { finishedCallback.onAnimationFinished(); } catch (RemoteException e) { Slog.w(TAG, "Failed to call onAnimationFinished", e); } } mHiding = false; if (mLockPatternUtils.isSecure(currentUserId)) { doKeyguardLocked(null); } else { resetStateLocked(); dismiss(null, null); } return; } synchronized (KeyguardViewMediator.this) { mIsKeyguardExitAnimationCanceled = false; // Tell ActivityManager that we canceled the keyguard animation if // handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard, // unless we're animating the surface behind the keyguard and will be hiding the Loading Loading @@ -3006,9 +3108,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Slog.w(TAG, "Failed to call onAnimationFinished", e); } } if (!mIsKeyguardExitAnimationCanceled) { onKeyguardExitFinished(); mKeyguardViewControllerLazy.get().hide(0 /* startTime */, 0 /* fadeoutDuration */); } mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION); } Loading Loading @@ -3183,6 +3287,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * app transition before finishing the current RemoteAnimation, or the keyguard being re-shown). */ private void handleCancelKeyguardExitAnimation() { if (mGoingAwayRequestedForUserId != KeyguardUpdateMonitor.getCurrentUser()) { Log.e(TAG, "Setting pendingLock = true due to userId mismatch. Requested: " + mGoingAwayRequestedForUserId + ", current: " + KeyguardUpdateMonitor.getCurrentUser()); setPendingLock(true); } if (mPendingLock) { Log.d(TAG, "#handleCancelKeyguardExitAnimation: keyguard exit animation cancelled. " + "There's a pending lock, so we were cancelled because the device was locked " Loading @@ -3191,6 +3301,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // A lock is pending, meaning the keyguard exit animation was cancelled because we're // re-locking. We should just end the surface-behind animation without exiting the // keyguard. The pending lock will be handled by onFinishedGoingToSleep(). mIsKeyguardExitAnimationCanceled = true; finishSurfaceBehindRemoteAnimation(true /* showKeyguard */); maybeHandlePendingLock(); } else { Loading Loading @@ -3223,6 +3334,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, return; } if (mIsKeyguardExitAnimationCanceled) { Log.d(TAG, "Ignoring exitKeyguardAndFinishSurfaceBehindRemoteAnimation. " + "mIsKeyguardExitAnimationCanceled==true"); return; } // Block the panel from expanding, in case we were doing a swipe to dismiss gesture. mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch(); final boolean wasShowing = mShowing; Loading @@ -3230,6 +3347,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // Post layout changes to the next frame, so we don't hang at the end of the animation. DejankUtils.postAfterTraversal(() -> { if (mIsKeyguardExitAnimationCanceled) { Log.d(TAG, "Ignoring dejank exitKeyguardAndFinishSurfaceBehindRemoteAnimation. " + "mIsKeyguardExitAnimationCanceled==true"); return; } if (!mPM.isInteractive() && !mPendingLock) { Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal:" + "mPM.isInteractive()=" + mPM.isInteractive() Loading Loading @@ -3285,7 +3408,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { // Handled in WmLockscreenVisibilityManager. mGoingAwayRequestedForUserId = KeyguardUpdateMonitor.getCurrentUser(); Log.d(TAG, "keyguardGoingAway requested for userId: " + mGoingAwayRequestedForUserId); ActivityTaskManager.getService().keyguardGoingAway(flags); } mKeyguardStateController.notifyKeyguardGoingAway(true); Loading Loading @@ -3733,6 +3858,29 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, }); } private void notifyLockNowCallback() { List<LockNowCallback> callbacks; synchronized (mLockNowCallbacks) { callbacks = new ArrayList<LockNowCallback>(mLockNowCallbacks); mLockNowCallbacks.clear(); } Iterator<LockNowCallback> iter = callbacks.listIterator(); while (iter.hasNext()) { LockNowCallback callback = iter.next(); iter.remove(); if (callback.mUserId != KeyguardUpdateMonitor.getCurrentUser()) { Log.i(TAG, "Not notifying lockNowCallback due to user mismatch"); continue; } Log.i(TAG, "Notifying lockNowCallback"); try { callback.mRemoteCallback.sendResult(null); } catch (RemoteException e) { Log.e(TAG, "Could not issue LockNowCallback sendResult", e); } } } private void notifyTrustedChangedLocked(boolean trusted) { int size = mKeyguardStateCallbacks.size(); for (int i = size - 1; i >= 0; i--) { Loading Loading @@ -3888,4 +4036,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } }; } private class LockNowCallback { final int mUserId; final IRemoteCallback mRemoteCallback; LockNowCallback(int userId, IRemoteCallback remoteCallback) { mUserId = userId; mRemoteCallback = remoteCallback; } } }
packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +77 −4 Original line number Diff line number Diff line Loading @@ -238,6 +238,60 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { KeyguardUpdateMonitor.setCurrentUser(mInitialUserId); } @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void testGoingAwayFollowedByUserSwitchDoesNotHideKeyguard() { setCurrentUser(/* userId= */1099, /* isSecure= */false); // Setup keyguard mViewMediator.onSystemReady(); processAllMessagesAndBgExecutorMessages(); mViewMediator.setShowingLocked(true); // Request keyguard going away when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); mViewMediator.mKeyguardGoingAwayRunnable.run(); // After the request, begin a switch to a new secure user int nextUserId = 500; setCurrentUser(nextUserId, /* isSecure= */true); // After that request has begun, have WM tell us to exit keyguard RemoteAnimationTarget[] apps = new RemoteAnimationTarget[]{ mock(RemoteAnimationTarget.class) }; RemoteAnimationTarget[] wallpapers = new RemoteAnimationTarget[]{ mock(RemoteAnimationTarget.class) }; IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class); mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers, null, callback); processAllMessagesAndBgExecutorMessages(); // The call to exit should be rejected, and keyguard should still be visible verify(mKeyguardUnlockAnimationController, never()).notifyStartSurfaceBehindRemoteAnimation( any(), any(), anyLong(), anyBoolean()); assertTrue(mViewMediator.isShowingAndNotOccluded()); } @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void testUserSwitchToSecureUserWhileKeyguardNotVisibleShowsKeyguard() { setCurrentUser(/* userId= */1099, /* isSecure= */true); // Setup keyguard as not visible mViewMediator.onSystemReady(); processAllMessagesAndBgExecutorMessages(); mViewMediator.setShowingLocked(false); processAllMessagesAndBgExecutorMessages(); // Begin a switch to a new secure user int nextUserId = 500; setCurrentUser(nextUserId, /* isSecure= */true); assertTrue(mViewMediator.isShowingAndNotOccluded()); } @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally() { Loading Loading @@ -630,6 +684,9 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) public void testCancelKeyguardExitAnimation_noPendingLock_keyguardWillNotBeShowing() { when(mPowerManager.isInteractive()).thenReturn(true); setCurrentUser(0, false); startMockKeyguardExitAnimation(); cancelMockKeyguardExitAnimation(); Loading @@ -638,7 +695,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); assertFalse(mViewMediator.isShowingAndNotOccluded()); verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(false); } @Test Loading Loading @@ -692,12 +748,21 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(false); } /** * Interactions with the ActivityTaskManagerService and others are posted to an executor that * doesn't use the testable looper. Use this method to ensure those are run as well. */ private void processAllMessagesAndBgExecutorMessages() { TestableLooper.get(this).processAllMessages(); mUiBgExecutor.runAllReady(); } /** * Configures mocks appropriately, then starts the keyguard exit animation. */ private void startMockKeyguardExitAnimation() { mViewMediator.onSystemReady(); TestableLooper.get(this).processAllMessages(); processAllMessagesAndBgExecutorMessages(); mViewMediator.setShowingLocked(true); Loading @@ -710,9 +775,10 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class); when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); mViewMediator.mKeyguardGoingAwayRunnable.run(); mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers, null, callback); TestableLooper.get(this).processAllMessages(); processAllMessagesAndBgExecutorMessages(); } /** Loading @@ -721,7 +787,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private void cancelMockKeyguardExitAnimation() { when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); mViewMediator.cancelKeyguardExitAnimation(); TestableLooper.get(this).processAllMessages(); processAllMessagesAndBgExecutorMessages(); } @Test Loading Loading @@ -1027,4 +1093,11 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private void captureKeyguardUpdateMonitorCallback() { verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture()); } private void setCurrentUser(int userId, boolean isSecure) { when(mUserTracker.getUserId()).thenReturn(userId); when(mLockPatternUtils.isSecure(userId)).thenReturn(isSecure); mViewMediator.setCurrentUser(userId); processAllMessagesAndBgExecutorMessages(); } }