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);
     }