Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e483051 authored by Matt Pietal's avatar Matt Pietal Committed by Android (Google) Code Review
Browse files

Merge "Update user switching processes" into udc-qpr-dev

parents 32c25dba 2efdc51b
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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}
+194 −36
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;

/**
@@ -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)
@@ -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)
     */
@@ -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;
@@ -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;

@@ -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;
@@ -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
@@ -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();
        }
@@ -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);
@@ -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;
        }
@@ -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 {
@@ -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;
        }

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

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

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

@@ -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);
        }
@@ -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)) {
@@ -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");
@@ -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;
@@ -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 {
@@ -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
@@ -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);
                            }

@@ -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 "
@@ -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 {
@@ -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;
@@ -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()
@@ -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);
@@ -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--) {
@@ -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;
        }
    }
}
+77 −4
Original line number Diff line number Diff line
@@ -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() {
@@ -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();

@@ -638,7 +695,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
        TestableLooper.get(this).processAllMessages();

        assertFalse(mViewMediator.isShowingAndNotOccluded());
        verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(false);
    }

    @Test
@@ -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);

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

    /**
@@ -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
@@ -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();
    }
}