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

Commit b630081d authored by Matt Pietal's avatar Matt Pietal Committed by Mohammed Althaf T
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
(cherry picked from commit f17b66e7)
Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:4a17a272b6eacdb5f62bc3510f6109524eeaab6e
Merged-In: Ib262129422e6198f019482547a6b129cc60f0865
Change-Id: Ib262129422e6198f019482547a6b129cc60f0865
parent 3daf40cc
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -2513,6 +2513,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();
@@ -3409,6 +3410,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.
     *
@@ -3440,13 +3447,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()
@@ -3460,6 +3461,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();

+68 −2
Original line number Diff line number Diff line
@@ -227,12 +227,17 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
    private FakeFeatureFlags mFeatureFlags;
    private final int mDefaultUserId = 100;
    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();
        mSystemClock = new FakeSystemClock();
        mUsePostAfterTraversalRunnable = false;
        mPostAfterTraversalRunnable = null;
        when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
        when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
        when(mPowerManager.isInteractive()).thenReturn(true);
@@ -324,12 +329,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() {
@@ -594,7 +651,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {

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

@@ -1428,7 +1485,16 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
                mSystemPropertiesHelper,
                () -> mock(WindowManagerLockscreenVisibilityManager.class),
                mSelectedUserInteractor,
                mKeyguardInteractor);
                mKeyguardInteractor) {
                    @Override
                    void postAfterTraversal(Runnable runnable) {
                        if (mUsePostAfterTraversalRunnable) {
                            mPostAfterTraversalRunnable = runnable;
                        } else {
                            super.postAfterTraversal(runnable);
                        }
                    }
            };
        mViewMediator.start();
    }