Merge cherrypicks of ['googleplex-android-review.googlesource.com/36774913', 'googleplex-android-review.googlesource.com/36774914', 'googleplex-android-review.googlesource.com/36774915'] into udc-platform-release. Change-Id: I4f91629156152f2e3cf8a8a87514215c5c767909
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 3f53a99..14883da 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -299,6 +299,8 @@ private static final int UNOCCLUDE_ANIMATION_DURATION = 250; + private static final int NO_KEYGUARD_DONE_PENDING = -10000; + /** * How far down to animate the unoccluding activity, in terms of percent of the activity's * height. @@ -446,6 +448,13 @@ private boolean mHiding; /** + * Tracks SHOW/HIDE requests, in order to determine if a HIDE request show be completed after a + * series of binder calls returns from WM. + */ + private int mLastShowRequest = 0; + private int mLastHideRequest = 0; + + /** * we send this intent when the keyguard is dismissed. */ private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT) @@ -466,7 +475,7 @@ private boolean mWaitingUntilKeyguardVisible = false; private final LockPatternUtils mLockPatternUtils; private final BroadcastDispatcher mBroadcastDispatcher; - private boolean mKeyguardDonePending = false; + private int mKeyguardDonePendingForUser = NO_KEYGUARD_DONE_PENDING; private boolean mUnlockingAndWakingFromDream = false; private boolean mHideAnimationRun = false; private boolean mHideAnimationRunning = false; @@ -763,6 +772,11 @@ @Override public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, boolean isStrongBiometric) { + if (userId != KeyguardUpdateMonitor.getCurrentUser()) { + Log.w(TAG, "onBiometricAuthenticated() invoked for userId: " + userId + ", current " + + "userId: " + KeyguardUpdateMonitor.getCurrentUser()); + return; + } if (mLockPatternUtils.isSecure(userId)) { mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt( userId); @@ -798,8 +812,8 @@ if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) { return; } - if (DEBUG) Log.d(TAG, "keyguardDone"); - tryKeyguardDone(); + Log.d(TAG, "keyguardDone: " + targetUserId); + tryKeyguardDone(targetUserId); } @Override @@ -816,18 +830,16 @@ @Override public void keyguardDonePending(boolean primaryAuth, int targetUserId) { - Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending"); - if (DEBUG) Log.d(TAG, "keyguardDonePending"); + Log.d(TAG, "keyguardDonePending: " + targetUserId); if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) { - Trace.endSection(); return; } - - mKeyguardDonePending = true; + Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending"); + mKeyguardDonePendingForUser = targetUserId; mHideAnimationRun = true; mHideAnimationRunning = true; mKeyguardViewControllerLazy.get() - .startPreHideAnimation(mHideAnimationFinishedRunnable); + .startPreHideAnimation(new OnHideAnimationFinished(targetUserId)); mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT, KEYGUARD_DONE_PENDING_TIMEOUT_MS); Trace.endSection(); @@ -865,9 +877,10 @@ @Override public void readyForKeyguardDone() { Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#readyForKeyguardDone"); - if (mKeyguardDonePending) { - mKeyguardDonePending = false; - tryKeyguardDone(); + if (mKeyguardDonePendingForUser != NO_KEYGUARD_DONE_PENDING) { + int user = mKeyguardDonePendingForUser; + mKeyguardDonePendingForUser = NO_KEYGUARD_DONE_PENDING; + tryKeyguardDone(user); } Trace.endSection(); } @@ -1966,6 +1979,8 @@ mNeedToReshowWhenReenabled = false; updateInputRestrictedLocked(); + mHandler.removeMessages(HIDE); + mHandler.removeMessages(START_KEYGUARD_EXIT_ANIM); showLocked(null); // block until we know the keyguard is done drawing (and post a message @@ -2186,7 +2201,7 @@ // if another app is disabling us, don't show if (!mExternallyEnabled - && !mUpdateMonitor.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); notifyLockNowCallback(); mNeedToReshowWhenReenabled = true; @@ -2336,6 +2351,7 @@ if (DEBUG) Log.d(TAG, "showLocked"); // ensure we stay awake until we are finished displaying the keyguard mShowKeyguardWakeLock.acquire(); + mLastShowRequest++; Message msg = mHandler.obtainMessage(SHOW, options); // Treat these messages with priority - This call can originate from #doKeyguardTimeout, // meaning the device should lock as soon as possible and not wait for other messages on @@ -2348,7 +2364,8 @@ * Send message to keyguard telling it to hide itself * @see #handleHide() */ - private void hideLocked() { + @VisibleForTesting + void hideLocked() { Trace.beginSection("KeyguardViewMediator#hideLocked"); if (DEBUG) Log.d(TAG, "hideLocked"); Message msg = mHandler.obtainMessage(HIDE); @@ -2364,7 +2381,7 @@ * expecting the keyguard to go away when called. */ public void hideWithAnimation(IRemoteAnimationRunner runner) { - if (!mKeyguardDonePending) { + if (mKeyguardDonePendingForUser == NO_KEYGUARD_DONE_PENDING) { return; } @@ -2422,7 +2439,6 @@ mHandler.removeCallbacksAndMessages(mDismissToken); mHandler.removeMessages(DISMISS); mHandler.removeMessages(HIDE); - mHandler.removeMessages(START_KEYGUARD_EXIT_ANIM); notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(newUserId)); resetKeyguardDonePendingLocked(); adjustStatusBarLocked(); @@ -2585,12 +2601,17 @@ } }; - private void tryKeyguardDone() { - if (DEBUG) { - Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - " - + mHideAnimationRun + " animRunning - " + mHideAnimationRunning); + private void tryKeyguardDone(int userId) { + String logUserId; + if (mKeyguardDonePendingForUser == NO_KEYGUARD_DONE_PENDING) { + logUserId = "NO_KEYGUARD_DONE_PENDING"; + } else { + logUserId = "" + mKeyguardDonePendingForUser; } - if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) { + Log.d(TAG, "tryKeyguardDone: pendingForUser - " + logUserId + + ", animRan - " + mHideAnimationRun + " animRunning - " + mHideAnimationRunning); + if (mKeyguardDonePendingForUser == NO_KEYGUARD_DONE_PENDING && mHideAnimationRun + && !mHideAnimationRunning) { handleKeyguardDone(); } else if (mSurfaceBehindRemoteAnimationRunning) { // We're already running the keyguard exit animation, likely due to an in-progress swipe @@ -2601,7 +2622,7 @@ mHideAnimationRun = true; mHideAnimationRunning = true; mKeyguardViewControllerLazy.get() - .startPreHideAnimation(mHideAnimationFinishedRunnable); + .startPreHideAnimation(new OnHideAnimationFinished(userId)); } } @@ -2762,7 +2783,17 @@ } mHiding = false; + // Any valid exit animation will set this to false before proceeding + mIsKeyguardExitAnimationCanceled = true; + // Make sure to remove any pending exit animation requests that would override a SHOW + mHandler.removeMessages(START_KEYGUARD_EXIT_ANIM); + mHandler.removeMessages(HIDE); + mShadeController.get().instantCollapseShade(); + mKeyguardStateController.notifyKeyguardGoingAway(false); + + // Handled directly in StatusBarKeyguardViewManager if enabled. mKeyguardViewControllerLazy.get().show(options); + resetKeyguardDonePendingLocked(); mHideAnimationRun = false; adjustStatusBarLocked(); @@ -2833,7 +2864,6 @@ mUpdateMonitor.setKeyguardGoingAway(true); mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(true); - // Handled in WmLockscreenVisibilityManager if flag is enabled. mGoingAwayRequestedForUserId = KeyguardUpdateMonitor.getCurrentUser(); Log.d(TAG, "keyguardGoingAway requested for userId: " + mGoingAwayRequestedForUserId); @@ -2844,6 +2874,15 @@ // will be in order. final int keyguardFlag = flags; mUiBgExecutor.execute(() -> { + int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); + if (mGoingAwayRequestedForUserId != currentUserId) { + Log.e(TAG, "Not executing goingAwayRunnable() due to userId mismatch. " + + "Requested: " + mGoingAwayRequestedForUserId + ", current: " + + currentUserId); + mUpdateMonitor.setKeyguardGoingAway(false); + mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false); + return; + } try { ActivityTaskManager.getService().keyguardGoingAway(keyguardFlag); } catch (RemoteException e) { @@ -2855,12 +2894,6 @@ } }; - private final Runnable mHideAnimationFinishedRunnable = () -> { - Log.e(TAG, "mHideAnimationFinishedRunnable#run"); - mHideAnimationRunning = false; - tryKeyguardDone(); - }; - private void setUnlockAndWakeFromDream(boolean updatedValue, @WakeAndUnlockUpdateReason int reason) { if (!mOrderUnlockAndWake) { @@ -2928,7 +2961,7 @@ if (DEBUG) Log.d(TAG, "handleHide"); mHiding = true; - + mLastHideRequest = mLastShowRequest; // If waking and unlocking, waking from dream has been set properly. if (!mWakeAndUnlocking) { setUnlockAndWakeFromDream(mStatusBarStateController.isDreaming() @@ -2941,6 +2974,7 @@ } mKeyguardGoingAwayRunnable.run(); } else { + mGoingAwayRequestedForUserId = KeyguardUpdateMonitor.getCurrentUser(); // TODO(bc-unlock): Fill parameters mNotificationShadeWindowControllerLazy.get().batchApplyWindowLayoutParams(() -> { handleStartKeyguardExitAnimation( @@ -2967,11 +3001,22 @@ Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime + " fadeoutDuration=" + fadeoutDuration); int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); + + // Requests to exit directly from WM are valid if the lockscreen can be dismissed + if (mKeyguardStateController.canDismissLockScreen()) { + mLastHideRequest = mLastShowRequest; + } + + String error = null; if (mGoingAwayRequestedForUserId != currentUserId) { - Log.e(TAG, "Not executing handleStartKeyguardExitAnimationInner() due to userId " + error = "Not executing handleStartKeyguardExitAnimationInner() due to userId " + "mismatch. Requested: " + mGoingAwayRequestedForUserId + ", current: " - + currentUserId); - mIsKeyguardExitAnimationCanceled = true; + + currentUserId; + } else if (mLastHideRequest != mLastShowRequest) { + error = "Show requested after hide, cancel animation"; + } + if (error != null) { + Log.e(TAG, error); if (finishedCallback != null) { // There will not execute animation, send a finish callback to ensure the remote // animation won't hang there. @@ -2984,6 +3029,8 @@ mHiding = false; if (mLockPatternUtils.isSecure(currentUserId)) { doKeyguardLocked(null); + mShadeController.get().instantCollapseShade(); + setShowingLocked(true /* showing */, true /* force */); } else { resetStateLocked(); dismiss(null, null); @@ -3260,7 +3307,13 @@ InteractionJankMonitor.getInstance().end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION); // Post layout changes to the next frame, so we don't hang at the end of the animation. - postAfterTraversal(() -> { + 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() @@ -3274,13 +3327,6 @@ return; } - if (mIsKeyguardExitAnimationCanceled) { - Log.d(TAG, "Ignoring exitKeyguardAndFinishSurfaceBehindRemoteAnimation. " - + "mIsKeyguardExitAnimationCanceled==true"); - finishSurfaceBehindRemoteAnimation(true /* showKeyguard */); - setShowingLocked(true /* showing */, true /* force */); - return; - } onKeyguardExitFinished(); @@ -3321,6 +3367,8 @@ if (KeyguardUnlockAnimationController.Companion.isNexusLauncherUnderneath()) { flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; } + mLastHideRequest = mLastShowRequest; + mIsKeyguardExitAnimationCanceled = false; mGoingAwayRequestedForUserId = KeyguardUpdateMonitor.getCurrentUser(); Log.d(TAG, "keyguardGoingAway requested for userId: " @@ -3494,7 +3542,7 @@ } private void resetKeyguardDonePendingLocked() { - mKeyguardDonePending = false; + mKeyguardDonePendingForUser = NO_KEYGUARD_DONE_PENDING; mHandler.removeMessages(KEYGUARD_DONE_PENDING_TIMEOUT); } @@ -3653,7 +3701,7 @@ pw.print(" mDozing: "); pw.println(mDozing); pw.print(" mAodShowing: "); pw.println(mAodShowing); pw.print(" mWaitingUntilKeyguardVisible: "); pw.println(mWaitingUntilKeyguardVisible); - pw.print(" mKeyguardDonePending: "); pw.println(mKeyguardDonePending); + pw.print(" mKeyguardDonePendingForUser: "); pw.println(mKeyguardDonePendingForUser); pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun); pw.print(" mPendingReset: "); pw.println(mPendingReset); pw.print(" mPendingLock: "); pw.println(mPendingLock); @@ -3951,4 +3999,26 @@ mRemoteCallback = remoteCallback; } } + + private class OnHideAnimationFinished implements Runnable { + private final int mUserId; + public OnHideAnimationFinished(int userId) { + mUserId = userId; + } + + public void run() { + Log.d(TAG, "OnHideAnimationFinished.run(" + mUserId + ")"); + mHideAnimationRunning = false; + int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); + if (mUserId != currentUserId) { + Log.e(TAG, "Not executing OnHideAnimationFinished.run() due to userId mismatch. " + + "Requested: " + mUserId + ", current: " + currentUserId); + mShadeController.get().instantCollapseShade(); + resetStateLocked(); + return; + } + + tryKeyguardDone(mUserId); + } + } }
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 d085067..2745464 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -177,9 +177,6 @@ private FakeFeatureFlags mFeatureFlags; private int mInitialUserId; - private final int mDefaultUserId = 100; - private boolean mUsePostAfterTraversalRunnable; - private Runnable mPostAfterTraversalRunnable; @Before public void setUp() throws Exception { @@ -248,10 +245,6 @@ null, callback); processAllMessagesAndBgExecutorMessages(); - // Followed by a request to dismiss the keyguard completely, which needs to be rejected - mViewMediator.mViewMediatorCallback.keyguardDonePending(true, mDefaultUserId); - mViewMediator.mViewMediatorCallback.readyForKeyguardDone(); - // The call to exit should be rejected, and keyguard should still be visible verify(mKeyguardUnlockAnimationController, never()).notifyStartSurfaceBehindRemoteAnimation( any(), any(), anyLong(), anyBoolean()); @@ -260,54 +253,6 @@ @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) - public void testGoingAwayFollowedByBeforeUserSwitchWithDelayedExitAnimationDoesNotHideKeyguard() { - mUsePostAfterTraversalRunnable = true; - - int insecureUserId = 1099; - setCurrentUser(/* userId= */insecureUserId, /* isSecure= */false); - - // Setup keyguard - mViewMediator.onSystemReady(); - processAllMessagesAndBgExecutorMessages(); - mViewMediator.setShowingLocked(true); - - // Request keyguard going away - when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); - mViewMediator.showSurfaceBehindKeyguard(); - - // WM will have started the exit animation... - 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(); - - // Followed by a request to dismiss the keyguard completely - mViewMediator.mViewMediatorCallback.keyguardDonePending(true, insecureUserId); - mViewMediator.mViewMediatorCallback.readyForKeyguardDone(); - - // ...but while the exit animation is running, a user switch comes in - int nextUserId = 500; - setCurrentUser(nextUserId, /* isSecure= */true); - - processAllMessagesAndBgExecutorMessages(); - - // This simulates the race condition in DejankUtils.postAfterTraversal() - mPostAfterTraversalRunnable.run(); - - // At this point, the exit animation should have been canceled, with a true value - // indicating that keyguard will be showing - verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(eq(true)); - assertTrue(mViewMediator.isShowingAndNotOccluded()); - } - - @Test - @TestableLooper.RunWithLooper(setAsMainLooper = true) public void testUserSwitchToSecureUserWhileKeyguardNotVisibleShowsKeyguard() { setCurrentUser(/* userId= */1099, /* isSecure= */true); @@ -335,7 +280,7 @@ assertFalse(mViewMediator.isShowingAndNotOccluded()); // WHEN lockdown occurs - when(mUpdateMonitor.isUserInLockdown(anyInt())).thenReturn(true); + when(mLockPatternUtils.isUserInLockdown(anyInt())).thenReturn(true); mKeyguardUpdateMonitorCallbackCaptor.getValue().onStrongAuthStateChanged(0); // THEN keyguard is shown @@ -351,7 +296,7 @@ mViewMediator.setKeyguardEnabled(true); TestableLooper.get(this).processAllMessages(); captureKeyguardUpdateMonitorCallback(); - when(mUpdateMonitor.isUserInLockdown(anyInt())).thenReturn(true); + when(mLockPatternUtils.isUserInLockdown(anyInt())).thenReturn(true); mKeyguardUpdateMonitorCallbackCaptor.getValue().onStrongAuthStateChanged(0); assertTrue(mViewMediator.isShowingAndNotOccluded()); @@ -693,7 +638,8 @@ IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class); when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); - mViewMediator.mKeyguardGoingAwayRunnable.run(); + mViewMediator.hideLocked(); + processAllMessagesAndBgExecutorMessages(); mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers, null, callback); processAllMessagesAndBgExecutorMessages(); @@ -945,6 +891,71 @@ assertTrue(mViewMediator.isShowingAndNotOccluded()); } + @Test + public void biometricAuthSuccessForCurrentUserDoesProceed() { + final int currentUser = 25; + setCurrentUser(currentUser, true); + + mViewMediator.mUpdateCallback.onBiometricAuthenticated(currentUser, null, false); + + verify(mDevicePolicyManager).reportSuccessfulBiometricAttempt(currentUser); + } + + @Test + public void biometricAuthSuccessForDifferentUserDoesNotProceed() { + final int newUser = 40; + setCurrentUser(newUser, true); + + final int oldUser = 25; + mViewMediator.mUpdateCallback.onBiometricAuthenticated(oldUser, null, false); + + verify(mDevicePolicyManager, never()).reportSuccessfulBiometricAttempt(anyInt()); + } + + @Test + public void testDonePendingExecutesOnHideAnimationFinished() { + final int currentUser = 25; + setCurrentUser(currentUser, true); + mViewMediator.mViewMediatorCallback.keyguardDonePending(true, currentUser); + + final ArgumentCaptor<Runnable> onHideAnimationFinished = + ArgumentCaptor.forClass(Runnable.class); + verify(mStatusBarKeyguardViewManager).startPreHideAnimation( + onHideAnimationFinished.capture()); + + onHideAnimationFinished.getValue().run(); + + // This is executed when the user is incorrect. Should not be called + verify(mShadeController, never()).instantCollapseShade(); + } + + @Test + public void testDonePendingExecutesOnHideAnimationFinishedButTerminatesWhenUserHasChanged() { + final int currentUser = 25; + setCurrentUser(currentUser, true); + mViewMediator.mViewMediatorCallback.keyguardDonePending(true, currentUser); + + final ArgumentCaptor<Runnable> onHideAnimationFinished = + ArgumentCaptor.forClass(Runnable.class); + verify(mStatusBarKeyguardViewManager).startPreHideAnimation( + onHideAnimationFinished.capture()); + + // User switched before runnable could execute + final int newUser = 40; + setCurrentUser(newUser, true); + + onHideAnimationFinished.getValue().run(); + + // This is executed when the user is incorrect + verify(mShadeController).instantCollapseShade(); + } + + @Test + public void testBouncerSwipeDown() { + mViewMediator.getViewMediatorCallback().onBouncerSwipeDown(); + verify(mStatusBarKeyguardViewManager).reset(true); + } + private void createAndStartViewMediator() { createAndStartViewMediator(false); }