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

Commit 48d2463d authored by Nick Chameyev's avatar Nick Chameyev Committed by Android (Google) Code Review
Browse files

Merge "Wait for KeyguardService#onScreenTurningOn when switching displays" into main

parents 01a10553 60e93933
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRA
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS;
import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
import static com.android.window.flags.Flags.ensureWallpaperDrawnOnDisplaySwitch;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,9 +39,12 @@ import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.protolog.ProtoLog;
import com.android.server.wm.Transition.ReadyCondition;
import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
@@ -76,6 +80,8 @@ class DeferredDisplayUpdater {
            "Screen unblock: wait for transition";
    private static final int WAIT_FOR_TRANSITION_TIMEOUT = 1000;

    private static final String READY_CONDITION_KEYGUARD_DRAWN = "keyguard_drawn";

    private final DisplayContent mDisplayContent;

    @NonNull
@@ -104,6 +110,12 @@ class DeferredDisplayUpdater {

    private boolean mInPhysicalDisplayChangeTransition;

    /** True if we are waiting for the IKeyguardDrawnCallback which will eventually invoke
     *  {@link DeferredDisplayUpdater#waitForTransition(Message)}}
     */
    private boolean mPendingKeyguardDrawing;
    private final List<ReadyCondition> mWaitingForKeyguardDrawnConditions = new ArrayList<>();

    /** The message to notify PhoneWindowManager#finishWindowsDrawn. */
    @Nullable
    private Message mScreenUnblocker;
@@ -298,6 +310,14 @@ class DeferredDisplayUpdater {
        mDisplayContent.mTransitionController.requestStartTransition(transition,
                /* startTask= */ null, /* remoteTransition= */ null, displayChange);

        if (mPendingKeyguardDrawing && ensureWallpaperDrawnOnDisplaySwitch()) {
            // Keyguard hasn't reported that it has drawn yet, defer readiness until it draws
            final ReadyCondition condition = new ReadyCondition(READY_CONDITION_KEYGUARD_DRAWN,
                    /* newTrackerOnly= */ false);
            transition.mReadyTracker.add(condition);
            mWaitingForKeyguardDrawnConditions.add(condition);
        }

        final DisplayAreaInfo newDisplayAreaInfo = mDisplayContent.getDisplayAreaInfo();

        final boolean startedRemoteChange = mDisplayContent.mRemoteDisplayChangeController
@@ -315,8 +335,12 @@ class DeferredDisplayUpdater {
            mDisplayContent.mAtmService.mWindowOrganizerController.applyTransaction(
                    wct);
        }
        if (ensureWallpaperDrawnOnDisplaySwitch()) {
            transition.setReady(mDisplayContent, /* ready= */ true);
        } else {
            transition.setAllReady();
        }
    }

    private boolean isPhysicalDisplayUpdated(@Nullable DisplayInfo first,
            @Nullable DisplayInfo second) {
@@ -346,6 +370,13 @@ class DeferredDisplayUpdater {
     */
    void onDisplaySwitching(boolean switching) {
        mShouldWaitForTransitionWhenScreenOn = switching;
        mPendingKeyguardDrawing = switching;

        if (!switching) {
            // Reset keyguard drawn in case for some reason we haven't received the callback
            // and the screen is already fully switched on here
            onKeyguardDrawn();
        }
    }

    /**
@@ -359,6 +390,10 @@ class DeferredDisplayUpdater {

    /** Returns {@code true} if the transition will control when to turn on the screen. */
    boolean waitForTransition(@NonNull Message screenUnblocker) {
        // waitForTransition() is called by PhoneWindowManager after receiving keyguard
        // drawn callback, so mark keyguard as drawn
        onKeyguardDrawn();

        if (!mShouldWaitForTransitionWhenScreenOn) {
            return false;
        }
@@ -373,6 +408,14 @@ class DeferredDisplayUpdater {
        return true;
    }

    private void onKeyguardDrawn() {
        mPendingKeyguardDrawing = false;
        for (int i = 0; i < mWaitingForKeyguardDrawnConditions.size(); i++) {
            mWaitingForKeyguardDrawnConditions.get(i).meet();
        }
        mWaitingForKeyguardDrawnConditions.clear();
    }

    /**
     * Continues the screen unblocking flow, could be called either on a binder thread as
     * a result of surface transaction presented listener or from {@link WindowManagerService#mH}
+176 −6
Original line number Diff line number Diff line
@@ -37,12 +37,16 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;

import android.os.Message;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.window.ITransitionPlayer;

import androidx.test.filters.SmallTest;

import com.android.server.LocalServices;
import com.android.server.wm.RemoteDisplayChangeController.ContinueRemoteDisplayChangeCallback;
import com.android.server.wm.TransitionController.OnStartCollect;

import org.junit.Before;
@@ -69,6 +73,8 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {

    private DisplayContent mSecondaryDisplayContent;

    private final BLASTSyncEngine mSyncEngine = mock(BLASTSyncEngine.class);

    private final Message mScreenUnblocker = mock(Message.class);
    private final Message mSecondaryScreenUnblocker = mock(Message.class);

@@ -81,7 +87,7 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {

        mockTransitionsController();

        mockRemoteDisplayChangeController(mDisplayContent);
        mockRemoteDisplayChangeController(mDisplayContent, /* finishImmediately= */ true);
        performInitialDisplayUpdate(mDisplayContent);

        mWmInternal = LocalServices.getService(WindowManagerInternal.class);
@@ -184,6 +190,129 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
        assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("old");
    }

    @Test
    public void testDisplaySwitching_remoteDisplayChangePending_transitionIsNotReady() {
        performPhysicalDisplaySwitch();
        startCollectingTheLastTransition();

        // Transition should not be ready as there is a pending remote display change
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isFalse();
    }

    @Test
    @DisableFlags(com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_DRAWN_ON_DISPLAY_SWITCH)
    public void testDisplaySwitching_wallpaperFlagDisabled_remoteDisplayChangeFinished_transitionIsReady() {
        performPhysicalDisplaySwitch();
        startCollectingTheLastTransition();

        finishRemoteDisplayChange(mDisplayContent);

        // Transition should be ready as remote display change is completed
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isTrue();
    }

    @Test
    @EnableFlags(com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_DRAWN_ON_DISPLAY_SWITCH)
    public void testDisplaySwitching_remoteDisplayChangeFinished_transitionIsNotReady() {
        performPhysicalDisplaySwitch();
        startCollectingTheLastTransition();

        finishRemoteDisplayChange(mDisplayContent);

        // Transition should not be ready as keyguard is not drawn yet
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isFalse();
    }

    @Test
    @EnableFlags(com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_DRAWN_ON_DISPLAY_SWITCH)
    public void testDisplaySwitching_keyguardDrawn_transitionIsNotReady() {
        performPhysicalDisplaySwitch();
        startCollectingTheLastTransition();

        signalKeyguardIsDrawn();

        // Transition should not be ready as remote display change is not finished
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isFalse();
    }

    @Test
    @EnableFlags(com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_DRAWN_ON_DISPLAY_SWITCH)
    public void testDisplaySwitching_remoteDisplayChangeFinishedThenKeyguardDrawn_transitionIsReady() {
        performPhysicalDisplaySwitch();
        startCollectingTheLastTransition();

        finishRemoteDisplayChange(mDisplayContent);
        signalKeyguardIsDrawn();

        // Transition should be ready as both remote display change
        // and keyguard drawing are completed
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isTrue();
    }

    @Test
    @EnableFlags(com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_DRAWN_ON_DISPLAY_SWITCH)
    public void testDisplaySwitching_keyguardDrawnThenRemoteDisplayChangeFinished_transitionIsReady() {
        performPhysicalDisplaySwitch();
        startCollectingTheLastTransition();

        signalKeyguardIsDrawn();
        finishRemoteDisplayChange(mDisplayContent);

        // Transition should be ready as both remote display change
        // and keyguard drawing are completed
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isTrue();
    }

    @Test
    @EnableFlags(com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_DRAWN_ON_DISPLAY_SWITCH)
    public void testDisplaySwitching_keyguardDrawnBeforeTransitionThenRemoteDisplayChangeFinished_transitionIsReady() {
        performPhysicalDisplaySwitch();

        signalKeyguardIsDrawn();

        startCollectingTheLastTransition();
        finishRemoteDisplayChange(mDisplayContent);

        // Transition should be ready as both remote display change
        // and keyguard drawing are completed
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isTrue();
    }

    @Test
    @EnableFlags(com.android.window.flags.Flags.FLAG_ENSURE_WALLPAPER_DRAWN_ON_DISPLAY_SWITCH)
    public void testDisplaySwitching_keyguardWasNotDrawn_secondSwitchingIsNormal_transitionIsReady() {
        performPhysicalDisplaySwitch();

        startCollectingTheLastTransition();
        finishRemoteDisplayChange(mDisplayContent);
        mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ false);
        // Here we finished remote display change & display switching, but keyguard drawn callback
        // was not invoked. Let's verify that we still handle the next display change correctly.

        clearInvocations(mDisplayContent.mRemoteDisplayChangeController);
        mockRemoteDisplayChangeController(mDisplayContent, /* finishImmediately= */ false);
        mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
        mUniqueId = "new2";
        mDisplayContent.requestDisplayUpdate(mock(Runnable.class));

        startCollectingTheLastTransition();

        signalKeyguardIsDrawn();
        finishRemoteDisplayChange(mDisplayContent);

        // Transition should be ready as both remote display change
        // and keyguard drawing are completed
        final Transition transition = captureRequestedTransition().getValue();
        assertThat(transition.allReady()).isTrue();
    }

    @Test
    public void testTwoDisplayUpdates_transitionStarted_displayUpdated() {
        mUniqueId = "old";
@@ -319,26 +448,44 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
        mSecondaryDisplayContent = createNewDisplay();
        when(mSecondaryScreenUnblocker.getTarget()).thenReturn(mWm.mH);
        doReturn(true).when(mSecondaryDisplayContent).getLastHasContent();
        mockRemoteDisplayChangeController(mSecondaryDisplayContent);
        mockRemoteDisplayChangeController(mSecondaryDisplayContent, /* finishImmediately= */ true);
        performInitialDisplayUpdate(mSecondaryDisplayContent);
    }

    private void mockTransitionsController() {
        spyOn(mDisplayContent.mTransitionController);
        mDisplayContent.mTransitionController.setSyncEngine(mSyncEngine);
        when(mDisplayContent.mTransitionController.isShellTransitionsEnabled())
                .thenReturn(true);
        doReturn(mock(Transition.class)).when(mDisplayContent.mTransitionController)
                .createTransition(anyInt(), anyInt());
        final ITransitionPlayer player = new ITransitionPlayer.Default();
        mDisplayContent.mTransitionController.registerTransitionPlayer(player,
                /* playerProc= */ null);
        doReturn(true).when(mDisplayContent.mTransitionController)
                .startCollectOrQueue(any(), any());
    }

    private void mockRemoteDisplayChangeController(DisplayContent displayContent) {
    private void mockRemoteDisplayChangeController(DisplayContent displayContent,
            boolean finishImmediately) {
        spyOn(displayContent.mRemoteDisplayChangeController);
        doReturn(true).when(displayContent.mRemoteDisplayChangeController)
        doAnswer(invocation -> {
            if (finishImmediately) {
                final ContinueRemoteDisplayChangeCallback callback =
                        invocation.getArgument(invocation.getArguments().length - 1);
                callback.onContinueRemoteDisplayChange(null);
            }
            return true;
        }).when(displayContent.mRemoteDisplayChangeController)
                .performRemoteDisplayChange(anyInt(), anyInt(), any(), any());
    }

    private void finishRemoteDisplayChange(DisplayContent displayContent) {
        ArgumentCaptor<ContinueRemoteDisplayChangeCallback> callbackCaptor =
                ArgumentCaptor.forClass(ContinueRemoteDisplayChangeCallback.class);
        verify(displayContent.mRemoteDisplayChangeController)
                .performRemoteDisplayChange(anyInt(), anyInt(), any(), callbackCaptor.capture());
        callbackCaptor.getValue().onContinueRemoteDisplayChange(null);
    }

    private ArgumentCaptor<OnStartCollect> captureStartTransitionCollection() {
        ArgumentCaptor<OnStartCollect> callbackCaptor =
                ArgumentCaptor.forClass(OnStartCollect.class);
@@ -364,6 +511,29 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
        }
    }

    private void performPhysicalDisplaySwitch() {
        mockRemoteDisplayChangeController(mDisplayContent, /* finishImmediately= */ false);
        mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
        mUniqueId = "new";
        mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
    }

    private void signalKeyguardIsDrawn() {
        // waitForTransition call signals that keyguard is drawn
        mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
    }

    private void startCollectingTheLastTransition() {
        ArgumentCaptor<OnStartCollect> callbackCaptor =
                ArgumentCaptor.forClass(OnStartCollect.class);
        ArgumentCaptor<Transition> transitionCaptor =
                ArgumentCaptor.forClass(Transition.class);
        verify(mDisplayContent.mTransitionController, atLeast(1))
                .startCollectOrQueue(transitionCaptor.capture(), callbackCaptor.capture());
        transitionCaptor.getValue().startCollecting(/* timeoutMs= */ 0);
        captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
    }

    private void performInitialDisplayUpdate(DisplayContent displayContent) {
        mColorMode = 0;
        mLogicalDensityDpi = 400;