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

Commit 0c861746 authored by Ikram Gabiyev's avatar Ikram Gabiyev
Browse files

Update DisplayLayouts upon deferred updates

We wanna make sure we also update the Shell-side
cache for display layouts when deferred display updates come in,
similar to how this is done with Transition triggered onDisplayChange
calls.

Also refactored DisplayChangeController a little to have
DisplayController injected into it, as per the past TODO in
DisplayController::ctor().

Bug: 416553608
Flag: EXEMPT bugfix
Test: atest DisplayChangeControllerTest
Test: manually fold/unfold while in PiP2 until PiP bounds are wrong.

Change-Id: I16f2144e947864cdb1eddd11072a4ab00250c6a7
parent 6d937c27
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.common;

import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
@@ -28,6 +29,7 @@ import android.window.WindowContainerTransaction;

import androidx.annotation.BinderThread;

import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;

@@ -43,6 +45,7 @@ public class DisplayChangeController {
    private static final String TAG = DisplayChangeController.class.getSimpleName();
    private static final String HANDLE_DISPLAY_CHANGE_TRACE_TAG = "HandleRemoteDisplayChange";

    private final DisplayController mDisplayController;
    private final ShellExecutor mMainExecutor;
    private final IWindowManager mWmService;
    private final IDisplayChangeWindowController mControllerImpl;
@@ -50,8 +53,9 @@ public class DisplayChangeController {
    private final CopyOnWriteArrayList<OnDisplayChangingListener> mDisplayChangeListener =
            new CopyOnWriteArrayList<>();

    public DisplayChangeController(IWindowManager wmService, ShellInit shellInit,
            ShellExecutor mainExecutor) {
    public DisplayChangeController(DisplayController displayController, IWindowManager wmService,
            ShellInit shellInit, ShellExecutor mainExecutor) {
        mDisplayController = displayController;
        mMainExecutor = mainExecutor;
        mWmService = wmService;
        mControllerImpl = new DisplayChangeWindowControllerImpl();
@@ -94,8 +98,21 @@ public class DisplayChangeController {
        }
    }

    private void onDisplayChange(int displayId, int fromRotation, int toRotation,
    @VisibleForTesting
    void onDisplayChange(int displayId, int fromRotation, int toRotation,
            DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) {
        final DisplayLayout dl = mDisplayController.getDisplayLayout(displayId);
        if (dl != null && newDisplayAreaInfo != null) {
            // Note: there is a chance Transitions has triggered
            // DisplayController#onDisplayChangeRequested first, in which case layout was updated
            // and startBounds equals endBounds; then DisplayLayout size remains the same.
            // TODO(b/370721807): Remove DisplayChangeWindowControllerImpl and rely on transitions.
            final Rect startBounds = new Rect(0, 0, dl.width(), dl.height());
            final Rect endBounds = newDisplayAreaInfo.configuration.windowConfiguration.getBounds();
            mDisplayController.updateDisplayLayout(displayId, startBounds, endBounds,
                    fromRotation, toRotation);
        }

        WindowContainerTransaction t = new WindowContainerTransaction();
        dispatchOnDisplayChange(t, displayId, fromRotation, toRotation, newDisplayAreaInfo);
        try {
+24 −17
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.wm.shell.common;

import static android.app.WindowConfiguration.ROTATION_UNDEFINED;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -78,8 +81,8 @@ public class DisplayController {
        mWmService = wmService;
        mDisplayManager = displayManager;
        mDesktopState = desktopState;
        // TODO: Inject this instead
        mChangeController = new DisplayChangeController(mWmService, shellInit, mainExecutor);
        mChangeController = new DisplayChangeController(this, wmService, shellInit,
                mainExecutor);
        mDisplayContainerListener = new DisplayWindowListenerImpl();
        // Note, add this after DisplaceChangeController is constructed to ensure that is
        // initialized first
@@ -230,27 +233,31 @@ public class DisplayController {
    public void onDisplayChangeRequested(WindowContainerTransaction wct, int displayId,
            Rect startAbsBounds, Rect endAbsBounds, int fromRotation, int toRotation) {
        synchronized (mDisplays) {
            final DisplayRecord dr = mDisplays.get(displayId);
            if (dr == null) {
            final DisplayLayout dl = getDisplayLayout(displayId);
            if (dl == null) {
                Slog.w(TAG, "Skipping Display rotate on non-added display.");
                return;
            }
            updateDisplayLayout(displayId, startAbsBounds, endAbsBounds, fromRotation, toRotation);

            if (dr.mDisplayLayout != null) {
                if (endAbsBounds != null) {
                    // If there is a change in the display dimensions update the layout as well;
                    // note that endAbsBounds should ignore any potential rotation changes, so
                    // we still need to rotate the layout after if needed.
                    dr.mDisplayLayout.resizeTo(dr.mContext.getResources(),
                            new Size(endAbsBounds.width(), endAbsBounds.height()));
                }
                if (fromRotation != toRotation) {
                    dr.mDisplayLayout.rotateTo(dr.mContext.getResources(), toRotation);
            mChangeController.dispatchOnDisplayChange(
                    wct, displayId, fromRotation, toRotation, null /* newDisplayAreaInfo */);
        }
    }

            mChangeController.dispatchOnDisplayChange(
                    wct, displayId, fromRotation, toRotation, null /* newDisplayAreaInfo */);
    void updateDisplayLayout(int displayId,
            @NonNull Rect startBounds, @Nullable Rect endBounds, int fromRotation, int toRotation) {
        final DisplayLayout dl = getDisplayLayout(displayId);
        final Context ctx = getDisplayContext(displayId);
        if (dl == null || ctx == null) return;

        if (endBounds != null) {
            // Note that endAbsBounds should ignore any potential rotation changes, so
            // we still need to rotate the layout after if needed.
            dl.resizeTo(ctx.getResources(), new Size(endBounds.width(), endBounds.height()));
        }
        if (fromRotation != toRotation && toRotation != ROTATION_UNDEFINED) {
            dl.rotateTo(ctx.getResources(), toRotation);
        }
    }

+102 −1
Original line number Diff line number Diff line
@@ -19,10 +19,23 @@ package com.android.wm.shell.common;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Size;
import android.view.IDisplayChangeWindowCallback;
import android.view.IWindowManager;
import android.view.Surface;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -49,16 +62,104 @@ public class DisplayChangeControllerTests extends ShellTestCase {
    private @Mock IWindowManager mWM;
    private @Mock ShellInit mShellInit;
    private @Mock ShellExecutor mMainExecutor;
    private @Mock DisplayController mDisplayController;

    private @Mock DisplayLayout mMockDisplayLayout;
    private @Mock DisplayChangeController.OnDisplayChangingListener mMockOnDisplayChangingListener;
    private @Mock IDisplayChangeWindowCallback mMockDisplayChangeWindowCallback;
    private DisplayChangeController mController;

    private static final int DISPLAY_ID = 0;
    private static final int START_ROTATION = Surface.ROTATION_0;
    private static final int END_ROTATION = Surface.ROTATION_90;
    private static final Size DISPLAY_START_SIZE = new Size(100, 100);
    private static final Size DISPLAY_END_SIZE = new Size(200, 200);

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mController = spy(new DisplayChangeController(mWM, mShellInit, mMainExecutor));
        mController = spy(new DisplayChangeController(mDisplayController, mWM, mShellInit,
                mMainExecutor));
        mController.addDisplayChangeListener(mMockOnDisplayChangingListener);
    }

    @Test
    public void instantiate_addInitCallback() {
        verify(mShellInit, times(1)).addInitCallback(any(), any());
    }

    @Test
    public void onDisplayChange_sizeChange_updateDisplayLayout_thenContinueDisplayChange() throws
            RemoteException {
        // set up the init display layout
        when(mMockDisplayLayout.width()).thenReturn(DISPLAY_START_SIZE.getWidth());
        when(mMockDisplayLayout.height()).thenReturn(DISPLAY_START_SIZE.getHeight());
        when(mDisplayController.getDisplayLayout(eq(DISPLAY_ID))).thenReturn(mMockDisplayLayout);

        // set up the new display area info
        final Rect startBounds = new Rect(0, 0,
                DISPLAY_START_SIZE.getWidth(), DISPLAY_START_SIZE.getHeight());
        final Rect endBounds = new Rect(0, 0,
                DISPLAY_END_SIZE.getWidth(), DISPLAY_END_SIZE.getHeight());

        // create a new display area info for size change
        final DisplayAreaInfo displayAreaInfo = createDisplayAreaInfo(DISPLAY_ID, endBounds);

        mController.onDisplayChange(DISPLAY_ID, START_ROTATION, START_ROTATION, displayAreaInfo,
                mMockDisplayChangeWindowCallback);

        // verify that local display layouts are updated
        verify(mDisplayController, times(1)).updateDisplayLayout(eq(DISPLAY_ID),
                eq(startBounds), eq(endBounds), eq(START_ROTATION), eq(START_ROTATION));

        // verify that display changing callbacks are dispatched
        verify(mMockOnDisplayChangingListener, times(1)).onDisplayChange(
                eq(DISPLAY_ID), eq(START_ROTATION), eq(START_ROTATION), eq(displayAreaInfo),
                any(WindowContainerTransaction.class));

        verify(mMockDisplayChangeWindowCallback, times(1))
                .continueDisplayChange(any(WindowContainerTransaction.class));
    }

    @Test
    public void onDisplayChange_rotationChange_updateDisplayLayout_thenContinueDisplayChange()
            throws RemoteException {
        // set up the init display layout
        when(mMockDisplayLayout.width()).thenReturn(DISPLAY_START_SIZE.getWidth());
        when(mMockDisplayLayout.height()).thenReturn(DISPLAY_START_SIZE.getHeight());
        when(mDisplayController.getDisplayLayout(eq(DISPLAY_ID))).thenReturn(mMockDisplayLayout);

        // set up the new display area info
        final Rect startBounds = new Rect(0, 0,
                DISPLAY_START_SIZE.getWidth(), DISPLAY_START_SIZE.getHeight());

        // create a new display area info for size change
        final DisplayAreaInfo displayAreaInfo = createDisplayAreaInfo(DISPLAY_ID, startBounds);

        mController.onDisplayChange(DISPLAY_ID, START_ROTATION, END_ROTATION, displayAreaInfo,
                mMockDisplayChangeWindowCallback);

        // verify that local display layouts are updated
        verify(mDisplayController, times(1)).updateDisplayLayout(eq(DISPLAY_ID),
                eq(startBounds), eq(startBounds), eq(START_ROTATION), eq(END_ROTATION));

        // verify that display changing callbacks are dispatched
        verify(mMockOnDisplayChangingListener, times(1)).onDisplayChange(
                eq(DISPLAY_ID), eq(START_ROTATION), eq(END_ROTATION), eq(displayAreaInfo),
                any(WindowContainerTransaction.class));

        verify(mMockDisplayChangeWindowCallback, times(1))
                .continueDisplayChange(any(WindowContainerTransaction.class));
    }

    private DisplayAreaInfo createDisplayAreaInfo(int displayId, Rect endBounds) {
        final WindowContainerToken mMockToken = mock(WindowContainerToken.class);
        final WindowConfiguration windowConfiguration = new WindowConfiguration();
        windowConfiguration.setBounds(endBounds);

        final DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(mMockToken,
                displayId, DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER);
        displayAreaInfo.configuration.windowConfiguration.setTo(windowConfiguration);
        return displayAreaInfo;
    }
}