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

Commit 5d3eb299 authored by Josh Tsuji's avatar Josh Tsuji
Browse files

After each test, verify that SysUI and WM states match.

Also, adds a test condition for b/266668894 (testing that bug
required the addition of this infrastructure).

Test: hard-code true and/or false values for setLockScreenShown in KeyguardViewMediator and watch half the tests fail now (yeah that's right, a test clause for the test itself)
Test: atest KeyguardViewMediatorTest
Bug: 266668894
Change-Id: Ia867d7f7a5bc6e30f9c8b28546ad8c0ee7b21f15
parent 21896f7e
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -39,9 +39,9 @@ import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTra
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.BroadcastOptions;
import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.WindowConfiguration;
@@ -1177,6 +1177,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,

    private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
    private Lazy<ScrimController> mScrimControllerLazy;
    private IActivityTaskManager mActivityTaskManagerService;

    /**
     * Injected constructor. See {@link KeyguardModule}.
@@ -1208,7 +1209,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
            Lazy<ShadeController> shadeControllerLazy,
            Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
            Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
            Lazy<ScrimController> scrimControllerLazy) {
            Lazy<ScrimController> scrimControllerLazy,
            IActivityTaskManager activityTaskManagerService) {
        mContext = context;
        mUserTracker = userTracker;
        mFalsingCollector = falsingCollector;
@@ -1256,6 +1258,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,

        mActivityLaunchAnimator = activityLaunchAnimator;
        mScrimControllerLazy = scrimControllerLazy;
        mActivityTaskManagerService = activityTaskManagerService;

        mPowerButtonY = context.getResources().getDimensionPixelSize(
                R.dimen.physical_power_button_center_screen_location_y);
@@ -1799,12 +1802,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
    }

    /**
     * Is the keyguard currently showing and not being force hidden?
     * Is the keyguard currently showing, and not occluded (no activity is drawing over the
     * lockscreen).
     */
    public boolean isShowingAndNotOccluded() {
        return mShowing && !mOccluded;
    }

    public boolean isShowing() {
        return mShowing;
    }

    public boolean isOccludeAnimationPlaying() {
        return mOccludeAnimationPlaying;
    }
@@ -2416,7 +2424,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
                Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
            }
            try {
                ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing);
                mActivityTaskManagerService.setLockScreenShown(showing, aodShowing);
            } catch (RemoteException e) {
            }
        });
@@ -2532,7 +2540,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
            final int keyguardFlag = flags;
            mUiBgExecutor.execute(() -> {
                try {
                    ActivityTaskManager.getService().keyguardGoingAway(keyguardFlag);
                    mActivityTaskManagerService.keyguardGoingAway(keyguardFlag);
                } catch (RemoteException e) {
                    Log.e(TAG, "Error while calling WindowManager", e);
                }
@@ -2867,7 +2875,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
                flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
            }

            ActivityTaskManager.getService().keyguardGoingAway(flags);
            mActivityTaskManagerService.keyguardGoingAway(flags);
            mKeyguardStateController.notifyKeyguardGoingAway(true);
        } catch (RemoteException e) {
            mSurfaceBehindRemoteAnimationRequested = false;
+5 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.keyguard.dagger;

import android.app.IActivityTaskManager;
import android.app.trust.TrustManager;
import android.content.Context;
import android.os.PowerManager;
@@ -119,7 +120,8 @@ public class KeyguardModule {
            Lazy<ShadeController> shadeController,
            Lazy<NotificationShadeWindowController> notificationShadeWindowController,
            Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
            Lazy<ScrimController> scrimControllerLazy) {
            Lazy<ScrimController> scrimControllerLazy,
            IActivityTaskManager activityTaskManagerService) {
        return new KeyguardViewMediator(
                context,
                userTracker,
@@ -149,7 +151,8 @@ public class KeyguardModule {
                shadeController,
                notificationShadeWindowController,
                activityLaunchAnimator,
                scrimControllerLazy);
                scrimControllerLazy,
                activityTaskManagerService);
    }

    /** */
+146 −32
Original line number Diff line number Diff line
@@ -25,21 +25,28 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -89,9 +96,12 @@ import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -131,6 +141,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
    private @Mock DreamOverlayStateController mDreamOverlayStateController;
    private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
    private @Mock ScrimController mScrimController;
    private @Mock IActivityTaskManager mActivityTaskManagerService;
    private @Mock SysuiColorExtractor mColorExtractor;
    private @Mock AuthController mAuthController;
    private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
@@ -142,6 +153,9 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {

    private @Mock CentralSurfaces mCentralSurfaces;

    /** Most recent value passed to {@link KeyguardStateController#notifyKeyguardGoingAway}. */
    private boolean mKeyguardGoingAway = false;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
@@ -163,9 +177,38 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {

        DejankUtils.setImmediate(true);

        // Keep track of what we told KeyguardStateController about whether we're going away or
        // not.
        mKeyguardGoingAway = false;
        doAnswer(invocation -> {
            mKeyguardGoingAway = invocation.getArgument(0);
            return null;
        }).when(mKeyguardStateController).notifyKeyguardGoingAway(anyBoolean());

        createAndStartViewMediator();
    }

    /**
     * After each test, verify that System UI's going away/showing state matches the most recent
     * calls we made to ATMS.
     *
     * This will help us catch showing and going away state mismatch issues.
     */
    @After
    public void assertATMSAndKeyguardViewMediatorStatesMatch() {
        try {
            if (mKeyguardGoingAway) {
                assertATMSKeyguardGoingAway();
            } else {
                assertATMSLockScreenShowing(mViewMediator.isShowing());
            }

        } catch (Exception e) {
            // Just so we don't have to add the exception signature to every test.
            fail();
        }
    }

    @Test
    public void testOnGoingToSleep_UpdatesKeyguardGoingAway() {
        mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -410,38 +453,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
    public void testStartKeyguardExitAnimation_expectSurfaceBehindRemoteAnimation() {
        startMockKeyguardExitAnimation();
        assertTrue(mViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind());
    }

    /**
     * Configures mocks appropriately, then starts the keyguard exit animation.
     */
    private void startMockKeyguardExitAnimation() {
        mViewMediator.onSystemReady();
        TestableLooper.get(this).processAllMessages();

        mViewMediator.setShowingLocked(true);

        RemoteAnimationTarget[] apps = new RemoteAnimationTarget[]{
                mock(RemoteAnimationTarget.class)
        };
        RemoteAnimationTarget[] wallpapers = new RemoteAnimationTarget[]{
                mock(RemoteAnimationTarget.class)
        };
        IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class);

        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
        mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers,
                null, callback);
        TestableLooper.get(this).processAllMessages();
    }

    /**
     * Configures mocks appropriately, then cancels the keyguard exit animation.
     */
    private void cancelMockKeyguardExitAnimation() {
        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
        mViewMediator.cancelKeyguardExitAnimation();
        TestableLooper.get(this).processAllMessages();
    }

    @Test
@@ -515,6 +527,107 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
        verify(mStatusBarKeyguardViewManager, never()).reset(anyBoolean());
    }

    @Test
    @TestableLooper.RunWithLooper(setAsMainLooper = true)
    public void testStartKeyguardExitAnimation_thenCancelImmediately_doesNotResetAndUpdatesWM() {
        startMockKeyguardExitAnimation();
        cancelMockKeyguardExitAnimation();

        // This will trigger doKeyguardLocked and we can verify that we ask ATMS to show the
        // keyguard explicitly, even though we're already showing, because we cancelled immediately.
        mViewMediator.onSystemReady();
        reset(mActivityTaskManagerService);
        processAllMessagesAndBgExecutorMessages();

        verify(mStatusBarKeyguardViewManager, never()).reset(anyBoolean());
        assertATMSAndKeyguardViewMediatorStatesMatch();
    }

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

        mViewMediator.setShowingLocked(true);

        RemoteAnimationTarget[] apps = new RemoteAnimationTarget[]{
                mock(RemoteAnimationTarget.class)
        };
        RemoteAnimationTarget[] wallpapers = new RemoteAnimationTarget[]{
                mock(RemoteAnimationTarget.class)
        };
        IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class);

        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
        mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers,
                null, callback);
        processAllMessagesAndBgExecutorMessages();
    }

    /**
     * Configures mocks appropriately, then cancels the keyguard exit animation.
     */
    private void cancelMockKeyguardExitAnimation() {
        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
        mViewMediator.cancelKeyguardExitAnimation();
        processAllMessagesAndBgExecutorMessages();
    }
    /**
     * Asserts the last value passed to ATMS#setLockScreenShown. This should be confirmed alongside
     * {@link KeyguardViewMediator#isShowingAndNotOccluded()} to verify that state is not mismatched
     * between SysUI and WM.
     */
    private void assertATMSLockScreenShowing(boolean showing)
            throws RemoteException {
        // ATMS is called via bgExecutor, so make sure to run all of those calls first.
        processAllMessagesAndBgExecutorMessages();

        final InOrder orderedSetLockScreenShownCalls = inOrder(mActivityTaskManagerService);
        final ArgumentCaptor<Boolean> showingCaptor = ArgumentCaptor.forClass(Boolean.class);
        orderedSetLockScreenShownCalls
                .verify(mActivityTaskManagerService, atLeastOnce())
                .setLockScreenShown(showingCaptor.capture(), anyBoolean());

        // The captor will have the most recent setLockScreenShown call's value.
        assertEquals(showing, showingCaptor.getValue());

        // We're now just after the last setLockScreenShown call. If we expect the lockscreen to be
        // showing, ensure that we didn't subsequently ask for it to go away.
        if (showing) {
            orderedSetLockScreenShownCalls.verify(mActivityTaskManagerService, never())
                    .keyguardGoingAway(anyInt());
        }
    }

    /**
     * Asserts that we eventually called ATMS#keyguardGoingAway and did not subsequently call
     * ATMS#setLockScreenShown(true) which would cancel the going away.
     */
    private void assertATMSKeyguardGoingAway() throws RemoteException {
        // ATMS is called via bgExecutor, so make sure to run all of those calls first.
        processAllMessagesAndBgExecutorMessages();

        final InOrder orderedGoingAwayCalls = inOrder(mActivityTaskManagerService);
        orderedGoingAwayCalls.verify(mActivityTaskManagerService, atLeastOnce())
                .keyguardGoingAway(anyInt());

        // Advance the inOrder to just past the last goingAway call. Let's make sure we didn't
        // re-show the lockscreen, which would cancel going away.
        orderedGoingAwayCalls.verify(mActivityTaskManagerService, never())
                .setLockScreenShown(eq(true), anyBoolean());
    }

    private void createAndStartViewMediator() {
        mViewMediator = new KeyguardViewMediator(
                mContext,
@@ -545,7 +658,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
                () -> mShadeController,
                () -> mNotificationShadeWindowController,
                () -> mActivityLaunchAnimator,
                () -> mScrimController);
                () -> mScrimController,
                mActivityTaskManagerService);
        mViewMediator.start();

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