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

Commit 5adb3167 authored by Matt Pietal's avatar Matt Pietal
Browse files

Ensure exit animations are canceled prior to user switch

On user switch, remove any pending requests and set a flag to indicate
a cancelation, and verify that flag just prior to handling the
exit animation callback from window manager.

Bug: 407562568
Test: atest KeyguardViewMediatorTest
Flag: EXEMPT bugfix
Change-Id: Ib262129422e6198f019482547a6b129cc60f0865
Merged-In: Ib262129422e6198f019482547a6b129cc60f0865
(cherry picked from commit f17b66e7)
parent 8400b3f5
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -2444,6 +2444,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
                mHandler.removeCallbacksAndMessages(mDismissToken);
                mHandler.removeMessages(DISMISS);
                mHandler.removeMessages(HIDE);
                mHandler.removeMessages(START_KEYGUARD_EXIT_ANIM);
                notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(newUserId));
                resetKeyguardDonePendingLocked();
                adjustStatusBarLocked();
@@ -3275,6 +3276,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
        }
    }

    // Allows the runnable to be controlled for tests by overriding this method
    @VisibleForTesting
    void postAfterTraversal(Runnable runnable) {
        DejankUtils.postAfterTraversal(runnable);
    }

    /**
     * Called when we're done running the keyguard exit animation, we should now end up unlocked.
     *
@@ -3306,13 +3313,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
        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.
        DejankUtils.postAfterTraversal(() -> {
            if (mIsKeyguardExitAnimationCanceled) {
                Log.d(TAG, "Ignoring dejank exitKeyguardAndFinishSurfaceBehindRemoteAnimation. "
                        + "mIsKeyguardExitAnimationCanceled==true");
                return;
            }

        postAfterTraversal(() -> {
            if (!mPM.isInteractive() && !mPendingLock) {
                Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal:"
                        + " mPM.isInteractive()=" + mPM.isInteractive()
@@ -3326,6 +3327,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,

                return;
            }
            if (mIsKeyguardExitAnimationCanceled) {
                Log.d(TAG, "Ignoring exitKeyguardAndFinishSurfaceBehindRemoteAnimation. "
                        + "mIsKeyguardExitAnimationCanceled==true");
                finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
                setShowingLocked(true /* showing */, true /* force */);
                return;
            }

            onKeyguardExitFinished();

+70 −3
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.startsWith;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -176,12 +177,16 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {

    private FakeFeatureFlags mFeatureFlags;
    private int mInitialUserId;
    private final int mDefaultUserId = 100;
    private boolean mUsePostAfterTraversalRunnable;
    private Runnable mPostAfterTraversalRunnable;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mFalsingCollector = new FalsingCollectorFake();

        mUsePostAfterTraversalRunnable = false;
        mPostAfterTraversalRunnable = null;
        when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
        when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
        when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
@@ -243,12 +248,64 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
                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());
        assertTrue(mViewMediator.isShowingAndNotOccluded());
    }

    @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() {
@@ -354,7 +411,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {

    @Test
    public void testRegisterDumpable() {
        verify(mDumpManager).registerDumpable(KeyguardViewMediator.class.getName(), mViewMediator);
        verify(mDumpManager).registerDumpable(startsWith(KeyguardViewMediator.class.getName()),
                eq(mViewMediator));
        verify(mStatusBarKeyguardViewManager, never()).setKeyguardGoingAwayState(anyBoolean());
    }

@@ -999,7 +1057,16 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
                mFeatureFlags,
                mDispatcher,
                () -> mDreamingToLockscreenTransitionViewModel,
                mSystemPropertiesHelper);
                mSystemPropertiesHelper) {
                    @Override
                    void postAfterTraversal(Runnable runnable) {
                        if (mUsePostAfterTraversalRunnable) {
                            mPostAfterTraversalRunnable = runnable;
                        } else {
                            super.postAfterTraversal(runnable);
                        }
                    }
            };
        mViewMediator.start();

        mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);