diff options
| author | 2025-02-12 09:05:48 -0800 | |
|---|---|---|
| committer | 2025-02-12 09:05:48 -0800 | |
| commit | 7394d01c2035e378a21a247dd18af9a9138f9de4 (patch) | |
| tree | d7e949b7d7df3894a1688fec5a62a0c2b890a7e9 | |
| parent | 51fa01175b5e50c76af6a6361a154ca6e50c2cb1 (diff) | |
| parent | d8c3d450f77f77232a89ac37c9b9b266e28c0202 (diff) | |
Merge "Update user switching processes" into main
5 files changed, 383 insertions, 87 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index caf0fd4450fc..efa9c21f96b4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -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; @@ -75,6 +76,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; @@ -192,6 +194,8 @@ 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; @@ -282,6 +286,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private static final int SYSTEM_READY = 18; private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19; private static final int BOOT_INTERACTOR = 20; + private static final int BEFORE_USER_SWITCHING = 21; + private static final int USER_SWITCHING = 22; + private static final int USER_SWITCH_COMPLETE = 23; /** Enum for reasons behind updating wakeAndUnlock state. */ @Retention(RetentionPolicy.SOURCE) @@ -299,6 +306,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) */ @@ -357,13 +366,18 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthController; private final Lazy<ShadeController> mShadeController; private final Lazy<CommunalSceneInteractor> mCommunalSceneInteractor; + /* + * 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; private boolean mBootSendUserPresent; private boolean mShuttingDown; private boolean mDozing; private boolean mAnimatingScreenOff; - private boolean mIgnoreDismiss; private final Context mContext; private final FalsingCollector mFalsingCollector; @@ -626,41 +640,92 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } }; - KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { + @VisibleForTesting + protected UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override - public void onKeyguardVisibilityChanged(boolean visible) { - synchronized (KeyguardViewMediator.this) { - if (!visible && mPendingPinLock) { - Log.i(TAG, "PIN lock requested, starting keyguard"); + public void onBeforeUserSwitching(int newUser, @NonNull Runnable resultCallback) { + mHandler.sendMessage(mHandler.obtainMessage(BEFORE_USER_SWITCHING, + newUser, 0, resultCallback)); + } - // Bring the keyguard back in order to show the PIN lock - mPendingPinLock = false; - doKeyguardLocked(null); - } - } + @Override + public void onUserChanging(int newUser, @NonNull Context userContext, + @NonNull Runnable resultCallback) { + mHandler.sendMessage(mHandler.obtainMessage(USER_SWITCHING, + newUser, 0, resultCallback)); } @Override - public void onUserSwitching(int userId) { - Log.d(TAG, String.format("onUserSwitching %d", userId)); - synchronized (KeyguardViewMediator.this) { - mIgnoreDismiss = true; - notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId)); - resetKeyguardDonePendingLocked(); + public void onUserChanged(int newUser, Context userContext) { + mHandler.sendMessage(mHandler.obtainMessage(USER_SWITCH_COMPLETE, + newUser, 0)); + } + }; + + /** + * Handle {@link #BEFORE_USER_SWITCHING} + */ + @VisibleForTesting + void handleBeforeUserSwitching(int userId, Runnable resultCallback) { + Log.d(TAG, String.format("onBeforeUserSwitching %d", userId)); + synchronized (KeyguardViewMediator.this) { + mHandler.removeMessages(DISMISS); + notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId)); + resetKeyguardDonePendingLocked(); + adjustStatusBarLocked(); + mKeyguardStateController.notifyKeyguardGoingAway(false); + if (mLockPatternUtils.isSecure(userId) && !mShowing) { + doKeyguardLocked(null); + } else { resetStateLocked(); - adjustStatusBarLocked(); } + resultCallback.run(); } + } - @Override - public void onUserSwitchComplete(int userId) { - mIgnoreDismiss = false; - Log.d(TAG, String.format("onUserSwitchComplete %d", userId)); + /** + * Handle {@link #USER_SWITCHING} + */ + @VisibleForTesting + void handleUserSwitching(int userId, Runnable resultCallback) { + Log.d(TAG, String.format("onUserSwitching %d", userId)); + synchronized (KeyguardViewMediator.this) { + if (!mLockPatternUtils.isSecure(userId)) { + dismiss(null, null); + } + resultCallback.run(); + } + } + + /** + * Handle {@link #USER_SWITCH_COMPLETE} + */ + @VisibleForTesting + void handleUserSwitchComplete(int userId) { + Log.d(TAG, String.format("onUserSwitchComplete %d", userId)); + // Calling dismiss on a secure user will show the bouncer + if (mLockPatternUtils.isSecure(userId)) { // We are calling dismiss with a delay as there are race conditions in some scenarios // caused by async layout listeners mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500); } + } + + KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { + + @Override + public void onKeyguardVisibilityChanged(boolean visible) { + synchronized (KeyguardViewMediator.this) { + if (!visible && mPendingPinLock) { + Log.i(TAG, "PIN lock requested, starting keyguard"); + + // Bring the keyguard back in order to show the PIN lock + mPendingPinLock = false; + doKeyguardLocked(null); + } + } + } @Override public void onDeviceProvisioned() { @@ -1671,7 +1736,13 @@ 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()); + // start() can be invoked in the middle of user switching, so check for this state and issue + // the call manually as that important event was missed. + if (mUserTracker.isUserSwitching()) { + handleBeforeUserSwitching(mUserTracker.getUserId(), () -> {}); + handleUserSwitching(mUserTracker.getUserId(), () -> {}); + } mJavaAdapter.alwaysCollectFlow( mWallpaperRepository.getWallpaperSupportsAmbientMode(), this::setWallpaperSupportsAmbientMode); @@ -1720,7 +1791,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // System ready can be invoked in the middle of user switching, so check for this state // and issue the call manually as that important event was missed. if (mUserTracker.isUserSwitching()) { - mUpdateCallback.onUserSwitching(mUserTracker.getUserId()); + mUserChangedCallback.onUserChanging(mUserTracker.getUserId(), mContext, () -> {}); } } // Most services aren't available until the system reaches the ready state, so we @@ -2361,12 +2432,23 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mCommunalSceneInteractor.get().showHubFromPowerButton(); } + int currentUserId = mSelectedUserInteractor.getSelectedUserId(); + 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( mSelectedUserInteractor.getSelectedUserId())) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); - + notifyLockNowCallback(); mNeedToReshowWhenReenabled = true; return; } @@ -2384,6 +2466,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // We're removing "reset" in the refactor - "resetting" the views will happen // as a reaction to the root cause of the "reset" signal. if (KeyguardWmStateRefactor.isEnabled()) { + notifyLockNowCallback(); return; } @@ -2396,6 +2479,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, + "previously hiding. It should be safe to short-circuit " + "here."); resetStateLocked(/* hideBouncer= */ false); + notifyLockNowCallback(); return; } } else { @@ -2422,6 +2506,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; } @@ -2429,6 +2514,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (mLockPatternUtils.isLockScreenDisabled(mSelectedUserInteractor.getSelectedUserId()) && !lockedOrMissing && !forceShow) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off"); + notifyLockNowCallback(); return; } @@ -2476,11 +2562,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } public void dismiss(IKeyguardDismissCallback callback, CharSequence message) { - if (mIgnoreDismiss) { - android.util.Log.i(TAG, "Ignoring request to dismiss (user switch in progress?)"); - return; - } - if (mKeyguardStateController.isKeyguardGoingAway()) { Log.i(TAG, "Ignoring dismiss because we're already going away."); return; @@ -2498,7 +2579,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); } @@ -2746,6 +2827,18 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, message = "BOOT_INTERACTOR"; handleBootInteractor(); break; + case BEFORE_USER_SWITCHING: + message = "BEFORE_USER_SWITCHING"; + handleBeforeUserSwitching(msg.arg1, (Runnable) msg.obj); + break; + case USER_SWITCHING: + message = "USER_SWITCHING"; + handleUserSwitching(msg.arg1, (Runnable) msg.obj); + break; + case USER_SWITCH_COMPLETE: + message = "USER_SWITCH_COMPLETE"; + handleUserSwitchComplete(msg.arg1); + break; } Log.d(TAG, "KeyguardViewMediator queue processing message: " + message); } @@ -2887,6 +2980,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mUiBgExecutor.execute(() -> { Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ", " + reason + ")"); + if (showing) { + notifyLockNowCallback(); + } if (KeyguardWmStateRefactor.isEnabled()) { // Handled in WmLockscreenVisibilityManager if flag is enabled. @@ -2931,6 +3027,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; } if (DEBUG) Log.d(TAG, "handleShow"); @@ -2989,12 +3086,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"); - Log.d(TAG, "keyguardGoingAwayRunnable"); mKeyguardViewControllerLazy.get().keyguardGoingAway(); int flags = 0; @@ -3031,6 +3127,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // Handled in WmLockscreenVisibilityManager if flag is enabled. if (!KeyguardWmStateRefactor.isEnabled()) { + mGoingAwayRequestedForUserId = mSelectedUserInteractor.getSelectedUserId(); + 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 @@ -3169,6 +3269,30 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) { Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime + " fadeoutDuration=" + fadeoutDuration); + int currentUserId = mSelectedUserInteractor.getSelectedUserId(); + if (!KeyguardWmStateRefactor.isEnabled() && mGoingAwayRequestedForUserId != currentUserId) { + Log.e(TAG, "Not executing handleStartKeyguardExitAnimationInner() due to userId " + + "mismatch. Requested: " + mGoingAwayRequestedForUserId + ", current: " + + currentUserId); + 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 @@ -3413,6 +3537,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * app transition before finishing the current RemoteAnimation, or the keyguard being re-shown). */ private void handleCancelKeyguardExitAnimation() { + if (!KeyguardWmStateRefactor.isEnabled() + && mGoingAwayRequestedForUserId != mSelectedUserInteractor.getSelectedUserId()) { + Log.e(TAG, "Setting pendingLock = true due to userId mismatch. Requested: " + + mGoingAwayRequestedForUserId + ", current: " + + mSelectedUserInteractor.getSelectedUserId()); + 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 " @@ -3513,6 +3644,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mSurfaceBehindRemoteAnimationRequested = true; if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS && !KeyguardWmStateRefactor.isEnabled()) { + mGoingAwayRequestedForUserId = mSelectedUserInteractor.getSelectedUserId(); startKeyguardTransition(false /* keyguardShowing */, false /* aodShowing */); return; } @@ -3533,6 +3665,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (!KeyguardWmStateRefactor.isEnabled()) { // Handled in WmLockscreenVisibilityManager. + mGoingAwayRequestedForUserId = mSelectedUserInteractor.getSelectedUserId(); + Log.d(TAG, "keyguardGoingAway requested for userId: " + + mGoingAwayRequestedForUserId); mActivityTaskManagerService.keyguardGoingAway(flags); } } catch (RemoteException e) { @@ -3988,6 +4123,29 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mUiBgExecutor.execute(mTrustManager::reportKeyguardShowingChanged); } + 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 != mSelectedUserInteractor.getSelectedUserId()) { + 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--) { @@ -4152,4 +4310,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; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 4ccfa29d4ba0..e8054c07eac8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -206,6 +206,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ShadeInteractor mShadeInteractor; private @Mock ShadeWindowLogger mShadeWindowLogger; private @Mock SelectedUserInteractor mSelectedUserInteractor; + private @Mock UserTracker.Callback mUserTrackerCallback; private @Mock KeyguardInteractor mKeyguardInteractor; private @Mock KeyguardTransitionBootInteractor mKeyguardTransitionBootInteractor; private @Captor ArgumentCaptor<KeyguardStateController.Callback> @@ -280,7 +281,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mShadeInteractor, mShadeWindowLogger, () -> mSelectedUserInteractor, - mUserTracker, + mock(UserTracker.class), mKosmos.getNotificationShadeWindowModel(), mSecureSettings, mKosmos::getCommunalInteractor, @@ -318,7 +319,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { } catch (Exception e) { // Just so we don't have to add the exception signature to every test. - fail(); + fail(e.getMessage()); } } @@ -330,18 +331,156 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { /* First test the default behavior: handleUserSwitching() is not invoked */ when(mUserTracker.isUserSwitching()).thenReturn(false); - mViewMediator.mUpdateCallback = mock(KeyguardUpdateMonitorCallback.class); mViewMediator.onSystemReady(); TestableLooper.get(this).processAllMessages(); - verify(mViewMediator.mUpdateCallback, never()).onUserSwitching(userId); + verify(mUserTrackerCallback, never()).onUserChanging(eq(userId), eq(mContext), + any(Runnable.class)); /* Next test user switching is already in progress when started */ when(mUserTracker.isUserSwitching()).thenReturn(true); mViewMediator.onSystemReady(); TestableLooper.get(this).processAllMessages(); - verify(mViewMediator.mUpdateCallback).onUserSwitching(userId); + verify(mUserTrackerCallback).onUserChanging(eq(userId), eq(mContext), + any(Runnable.class)); + } + + @Test + @TestableLooper.RunWithLooper(setAsMainLooper = true) + public void testGoingAwayFollowedByBeforeUserSwitchDoesNotHideKeyguard() { + 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); + Runnable result = mock(Runnable.class); + mViewMediator.handleBeforeUserSwitching(nextUserId, result); + processAllMessagesAndBgExecutorMessages(); + verify(result).run(); + + // 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(), any(), anyLong(), anyBoolean()); + try { + assertATMSLockScreenShowing(true); + } catch (Exception e) { + fail(e.getMessage()); + } + assertTrue(mViewMediator.isShowingAndNotOccluded()); + } + + @Test + @TestableLooper.RunWithLooper(setAsMainLooper = true) + public void testUserSwitchToSecureUserShowsBouncer() { + setCurrentUser(/* userId= */1099, /* isSecure= */true); + + // Setup keyguard + mViewMediator.onSystemReady(); + processAllMessagesAndBgExecutorMessages(); + mViewMediator.setShowingLocked(true, ""); + + // After the request, begin a switch to a new secure user + int nextUserId = 500; + setCurrentUser(nextUserId, /* isSecure= */true); + + Runnable beforeResult = mock(Runnable.class); + mViewMediator.handleBeforeUserSwitching(nextUserId, beforeResult); + processAllMessagesAndBgExecutorMessages(); + verify(beforeResult).run(); + + // Dismiss should not be called while user switch is in progress + Runnable onSwitchResult = mock(Runnable.class); + mViewMediator.handleUserSwitching(nextUserId, onSwitchResult); + processAllMessagesAndBgExecutorMessages(); + verify(onSwitchResult).run(); + verify(mStatusBarKeyguardViewManager, never()).dismissAndCollapse(); + + // The attempt to dismiss only comes on user switch complete, which will trigger a call to + // show the bouncer in StatusBarKeyguardViewManager + mViewMediator.handleUserSwitchComplete(nextUserId); + TestableLooper.get(this).moveTimeForward(600); + processAllMessagesAndBgExecutorMessages(); + + verify(mStatusBarKeyguardViewManager).dismissAndCollapse(); + } + + @Test + @TestableLooper.RunWithLooper(setAsMainLooper = true) + public void testUserSwitchToInsecureUserDismissesKeyguard() { + int userId = 1099; + when(mUserTracker.getUserId()).thenReturn(userId); + + // Setup keyguard + mViewMediator.onSystemReady(); + processAllMessagesAndBgExecutorMessages(); + mViewMediator.setShowingLocked(true, ""); + + // After the request, begin a switch to an insecure user + int nextUserId = 500; + when(mLockPatternUtils.isSecure(nextUserId)).thenReturn(false); + + Runnable beforeResult = mock(Runnable.class); + mViewMediator.handleBeforeUserSwitching(nextUserId, beforeResult); + processAllMessagesAndBgExecutorMessages(); + verify(beforeResult).run(); + + // The call to dismiss comes during the user switch + Runnable onSwitchResult = mock(Runnable.class); + mViewMediator.handleUserSwitching(nextUserId, onSwitchResult); + processAllMessagesAndBgExecutorMessages(); + verify(onSwitchResult).run(); + + verify(mStatusBarKeyguardViewManager).dismissAndCollapse(); + } + + @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); + + Runnable beforeResult = mock(Runnable.class); + mViewMediator.handleBeforeUserSwitching(nextUserId, beforeResult); + processAllMessagesAndBgExecutorMessages(); + verify(beforeResult).run(); + + try { + assertATMSLockScreenShowing(true); + } catch (Exception e) { + fail(); + } + assertTrue(mViewMediator.isShowingAndNotOccluded()); } @Test @@ -1105,7 +1244,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { processAllMessagesAndBgExecutorMessages(); verify(mStatusBarKeyguardViewManager, never()).reset(anyBoolean()); - assertATMSAndKeyguardViewMediatorStatesMatch(); + } @Test @@ -1149,6 +1288,7 @@ 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); processAllMessagesAndBgExecutorMessages(); @@ -1203,13 +1343,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { // The captor will have the most recent setLockScreenShown call's value. assertEquals(showing, showingCaptor.getValue()); - - // We're now just after the last setLockScreenShown call. If we expect the lockscreen to be - // showing, ensure that we didn't subsequently ask for it to go away. - if (showing) { - orderedSetLockScreenShownCalls.verify(mActivityTaskManagerService, never()) - .keyguardGoingAway(anyInt()); - } } /** @@ -1371,6 +1504,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mKeyguardTransitionBootInteractor, mKosmos::getCommunalSceneInteractor, mock(WindowManagerOcclusionManager.class)); + mViewMediator.mUserChangedCallback = mUserTrackerCallback; mViewMediator.start(); mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null); @@ -1384,4 +1518,10 @@ 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(mSelectedUserInteractor.getSelectedUserId()).thenReturn(userId); + when(mLockPatternUtils.isSecure(userId)).thenReturn(isSecure); + } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 27e9e44f1090..e0fbaf43ea43 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -31,6 +31,7 @@ import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL; +import static android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK; import static android.os.PowerWhitelistManager.REASON_BOOT_COMPLETED; import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED; import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; @@ -3904,10 +3905,6 @@ class UserController implements Handler.Callback { return mService.mWindowManager; } - ActivityTaskManagerInternal getActivityTaskManagerInternal() { - return mService.mAtmInternal; - } - void activityManagerOnUserStopped(@UserIdInt int userId) { LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId); } @@ -4122,40 +4119,25 @@ class UserController implements Handler.Callback { } void lockDeviceNowAndWaitForKeyguardShown() { - if (getWindowManager().isKeyguardLocked()) { - Slogf.w(TAG, "Not locking the device since the keyguard is already locked"); - return; - } - final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("lockDeviceNowAndWaitForKeyguardShown"); final CountDownLatch latch = new CountDownLatch(1); - ActivityTaskManagerInternal.ScreenObserver screenObserver = - new ActivityTaskManagerInternal.ScreenObserver() { - @Override - public void onAwakeStateChanged(boolean isAwake) { - - } - - @Override - public void onKeyguardStateChanged(boolean isShowing) { - if (isShowing) { - latch.countDown(); - } - } - }; - - getActivityTaskManagerInternal().registerScreenObserver(screenObserver); - getWindowManager().lockDeviceNow(); + Bundle bundle = new Bundle(); + bundle.putBinder(LOCK_ON_USER_SWITCH_CALLBACK, new IRemoteCallback.Stub() { + public void sendResult(Bundle data) { + latch.countDown(); + } + }); + getWindowManager().lockNow(bundle); try { if (!latch.await(20, TimeUnit.SECONDS)) { - throw new RuntimeException("Keyguard is not shown in 20 seconds"); + throw new RuntimeException("User controller expected a callback while waiting " + + "to show the keyguard. Timed out after 20 seconds."); } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { - getActivityTaskManagerInternal().unregisterScreenObserver(screenObserver); t.traceEnd(); } } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index 587447b8af26..9d7e9f53a1aa 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -92,6 +92,8 @@ public class KeyguardServiceDelegate { public boolean bootCompleted; public int screenState; public int interactiveState; + boolean doKeyguardTimeoutRequested; + Bundle doKeyguardTimeoutRequestedOptions; private void reset() { // Assume keyguard is showing and secure until we know for sure. This is here in @@ -225,6 +227,12 @@ public class KeyguardServiceDelegate { if (mKeyguardState.dreaming) { mKeyguardService.onDreamingStarted(); } + if (mKeyguardState.doKeyguardTimeoutRequested) { + mKeyguardService.doKeyguardTimeout( + mKeyguardState.doKeyguardTimeoutRequestedOptions); + mKeyguardState.doKeyguardTimeoutRequested = false; + mKeyguardState.doKeyguardTimeoutRequestedOptions = null; + } } @Override @@ -410,6 +418,11 @@ public class KeyguardServiceDelegate { public void doKeyguardTimeout(Bundle options) { if (mKeyguardService != null) { mKeyguardService.doKeyguardTimeout(options); + } else { + mKeyguardState.doKeyguardTimeoutRequested = true; + if (options != null) { + mKeyguardState.doKeyguardTimeoutRequestedOptions = options; + } } } diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 06958b81d846..1627f683cd3e 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -25,6 +25,7 @@ import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL; +import static android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; @@ -115,7 +116,6 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.pm.UserTypeDetails; import com.android.server.pm.UserTypeFactory; -import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerService; import com.google.common.collect.Range; @@ -1563,11 +1563,11 @@ public class UserControllerTest { // and the thread is still alive assertTrue(threadStartUser.isAlive()); - // mock send the keyguard shown event - ArgumentCaptor<ActivityTaskManagerInternal.ScreenObserver> captor = ArgumentCaptor.forClass( - ActivityTaskManagerInternal.ScreenObserver.class); - verify(mInjector.mActivityTaskManagerInternal).registerScreenObserver(captor.capture()); - captor.getValue().onKeyguardStateChanged(true); + // mock the binder response for the user switch completion + ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class); + verify(mInjector.mWindowManagerMock).lockNow(captor.capture()); + IRemoteCallback.Stub.asInterface(captor.getValue().getBinder( + LOCK_ON_USER_SWITCH_CALLBACK)).sendResult(null); // verify the switch now moves on... Thread.sleep(1000); @@ -1757,7 +1757,6 @@ public class UserControllerTest { private final IStorageManager mStorageManagerMock; private final UserManagerInternal mUserManagerInternalMock; private final WindowManagerService mWindowManagerMock; - private final ActivityTaskManagerInternal mActivityTaskManagerInternal; private final PowerManagerInternal mPowerManagerInternal; private final AlarmManagerInternal mAlarmManagerInternal; private final KeyguardManager mKeyguardManagerMock; @@ -1779,7 +1778,6 @@ public class UserControllerTest { mUserManagerMock = mock(UserManagerService.class); mUserManagerInternalMock = mock(UserManagerInternal.class); mWindowManagerMock = mock(WindowManagerService.class); - mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class); mStorageManagerMock = mock(IStorageManager.class); mPowerManagerInternal = mock(PowerManagerInternal.class); mAlarmManagerInternal = mock(AlarmManagerInternal.class); @@ -1843,11 +1841,6 @@ public class UserControllerTest { } @Override - ActivityTaskManagerInternal getActivityTaskManagerInternal() { - return mActivityTaskManagerInternal; - } - - @Override PowerManagerInternal getPowerManagerInternal() { return mPowerManagerInternal; } |