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

Commit 0dcd4ddc authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "[RESTRICT AUTOMERGE] Send WindowContext config only when Display is active" into sc-v2-dev

parents 9e4c6144 20fddbc6
Loading
Loading
Loading
Loading
+12 −8
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
import static android.view.Display.STATE_UNKNOWN;
import static android.view.Display.isSuspendedState;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -322,11 +324,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     */
    private Rect mLastMirroredDisplayAreaBounds = null;

    /**
     * The last state of the display.
     */
    private int mLastDisplayState;

    // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
    // on the IME target. We mainly have this container grouping so we can keep track of all the IME
    // window containers together and move them in-sync if/when needed. We use a subclass of
@@ -5623,12 +5620,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    void onDisplayChanged() {
        mDisplay.getRealSize(mTmpDisplaySize);
        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
        final int lastDisplayState = mDisplayInfo.state;
        updateDisplayInfo();

        // The window policy is responsible for stopping activities on the default display.
        final int displayId = mDisplay.getDisplayId();
        final int displayState = mDisplayInfo.state;
        if (displayId != DEFAULT_DISPLAY) {
            final int displayState = mDisplay.getState();
            if (displayState == Display.STATE_OFF) {
                mOffTokenAcquirer.acquire(mDisplayId);
            } else if (displayState == Display.STATE_ON) {
@@ -5637,12 +5635,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
                    "Display %d state is now (%d), so update layer mirroring?",
                    mDisplayId, displayState);
            if (mLastDisplayState != displayState) {
            if (lastDisplayState != displayState) {
                // If state is on due to surface being added, then start layer mirroring.
                // If state is off due to surface being removed, then stop layer mirroring.
                updateMirroring();
            }
            mLastDisplayState = displayState;
        }
        // Dispatch pending Configuration to WindowContext if the associated display changes to
        // un-suspended state from suspended.
        if (isSuspendedState(lastDisplayState)
                && !isSuspendedState(displayState) && displayState != STATE_UNKNOWN) {
            mWmService.mWindowContextListenerController
                    .dispatchPendingConfigurationIfNeeded(mDisplayId);
        }
        mWmService.requestTraversal();
    }
+33 −10
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package com.android.server.wm;

import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.isSuspendedState;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.window.WindowProviderService.isWindowProviderService;

import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
@@ -80,7 +82,7 @@ class WindowContextListenerController {
     * @param options a bundle used to pass window-related options.
     */
    void registerWindowContainerListener(@NonNull IBinder clientToken,
            @NonNull WindowContainer container, int ownerUid, @WindowType int type,
            @NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
            @Nullable Bundle options) {
        WindowContextListenerImpl listener = mListeners.get(clientToken);
        if (listener == null) {
@@ -103,6 +105,16 @@ class WindowContextListenerController {
        listener.unregister();
    }

    void dispatchPendingConfigurationIfNeeded(int displayId) {
        for (int i = mListeners.size() - 1; i >= 0; --i) {
            final WindowContextListenerImpl listener = mListeners.valueAt(i);
            if (listener.getWindowContainer().getDisplayContent().getDisplayId() == displayId
                    && listener.mHasPendingConfiguration) {
                listener.reportConfigToWindowTokenClient();
            }
        }
    }

    /**
     * Verifies if the caller is allowed to do the operation to the listener specified by
     * {@code clientToken}.
@@ -138,7 +150,7 @@ class WindowContextListenerController {
        return listener != null ? listener.mOptions : null;
    }

    @Nullable WindowContainer getContainer(IBinder clientToken) {
    @Nullable WindowContainer<?> getContainer(IBinder clientToken) {
        final WindowContextListenerImpl listener = mListeners.get(clientToken);
        return listener != null ? listener.mContainer : null;
    }
@@ -163,7 +175,7 @@ class WindowContextListenerController {
    class WindowContextListenerImpl implements WindowContainerListener {
        @NonNull private final IBinder mClientToken;
        private final int mOwnerUid;
        @NonNull private WindowContainer mContainer;
        @NonNull private WindowContainer<?> mContainer;
        /**
         * The options from {@link Context#createWindowContext(int, Bundle)}.
         * <p>It can be used for choosing the {@link DisplayArea} where the window context
@@ -177,7 +189,9 @@ class WindowContextListenerController {
        private int mLastReportedDisplay = INVALID_DISPLAY;
        private Configuration mLastReportedConfig;

        private WindowContextListenerImpl(IBinder clientToken, WindowContainer container,
        private boolean mHasPendingConfiguration;

        private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
                int ownerUid, @WindowType int type, @Nullable Bundle options) {
            mClientToken = clientToken;
            mContainer = Objects.requireNonNull(container);
@@ -197,11 +211,11 @@ class WindowContextListenerController {

        /** TEST ONLY: returns the {@link WindowContainer} of the listener */
        @VisibleForTesting
        WindowContainer getWindowContainer() {
        WindowContainer<?> getWindowContainer() {
            return mContainer;
        }

        private void updateContainer(@NonNull WindowContainer newContainer) {
        private void updateContainer(@NonNull WindowContainer<?> newContainer) {
            Objects.requireNonNull(newContainer);

            if (mContainer.equals(newContainer)) {
@@ -246,12 +260,20 @@ class WindowContextListenerController {
            if (mDeathRecipient == null) {
                throw new IllegalStateException("Invalid client token: " + mClientToken);
            }

            if (mLastReportedConfig == null) {
                mLastReportedConfig = new Configuration();
            // If the display of window context associated window container is suspended, don't
            // report the configuration update. Note that we still dispatch the configuration update
            // to WindowProviderService to make it compatible with Service#onConfigurationChanged.
            // Service always receives #onConfigurationChanged callback regardless of display state.
            if (!isWindowProviderService(mOptions)
                    && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) {
                mHasPendingConfiguration = true;
                return;
            }
            final Configuration config = mContainer.getConfiguration();
            final int displayId = mContainer.getDisplayContent().getDisplayId();
            if (mLastReportedConfig == null) {
                mLastReportedConfig = new Configuration();
            }
            if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
                // No changes since last reported time.
                return;
@@ -266,6 +288,7 @@ class WindowContextListenerController {
            } catch (RemoteException e) {
                ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
            }
            mHasPendingConfiguration = false;
        }

        @Override
@@ -283,7 +306,7 @@ class WindowContextListenerController {
                // If we cannot obtain the DisplayContent, the DisplayContent may also be removed.
                // We should proceed the removal process.
                if (dc != null) {
                    final DisplayArea da = dc.findAreaForToken(windowToken);
                    final DisplayArea<?> da = dc.findAreaForToken(windowToken);
                    updateContainer(da);
                    return;
                }
+3 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.wm;

import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -70,7 +71,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {

    @Before
    public void setUp() throws Exception {
        // Let the Display to be created with the DualDisplay policy.
        // Let the Display be created with the DualDisplay policy.
        final DisplayAreaPolicy.Provider policyProvider =
                new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
        Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
@@ -78,6 +79,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
        mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
        mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
                .Builder(mAtm, 1000, 1000).build();
        mSecondaryDisplay.getDisplayInfo().state = STATE_ON;

        // Mock addWindowTokenWithOptions to create a test window token.
        mIWindowManager = WindowManagerGlobal.getWindowManagerService();
+79 −6
Original line number Diff line number Diff line
@@ -17,22 +17,35 @@
package com.android.server.wm;

import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.STATE_OFF;
import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.window.WindowProvider.KEY_IS_WINDOW_PROVIDER_SERVICE;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.app.IWindowToken;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayInfo;

import androidx.test.filters.SmallTest;

@@ -55,12 +68,15 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
    private static final int ANOTHER_UID = 1000;

    private final IBinder mClientToken = new Binder();
    private WindowContainer mContainer;
    private WindowContainer<?> mContainer;

    @Before
    public void setUp() {
        mController = new WindowContextListenerController();
        mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
        // Make display on to verify configuration propagation.
        mDefaultDisplay.getDisplayInfo().state = STATE_ON;
        mDisplayContent.getDisplayInfo().state = STATE_ON;
    }

    @Test
@@ -76,7 +92,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {

        assertEquals(2, mController.mListeners.size());

        final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
        final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
                mDefaultDisplay);
        mController.registerWindowContainerListener(mClientToken, container, -1,
                TYPE_APPLICATION_OVERLAY, null /* options */);
@@ -89,6 +105,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
        assertEquals(container, listener.getWindowContainer());
    }

    @UseTestDisplay
    @Test
    public void testRegisterWindowContextListenerClientConfigPropagation() {
        final TestWindowTokenClient clientToken = new TestWindowTokenClient();
@@ -107,7 +124,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
        assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);

        // Update the WindowContainer.
        final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
        final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
                mDefaultDisplay);
        final Configuration config2 = container.getConfiguration();
        final Rect bounds2 = new Rect(0, 0, 20, 20);
@@ -174,7 +191,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
                .setDisplayContent(mDefaultDisplay)
                .setFromClientToken(true)
                .build();
        final DisplayArea da = windowContextCreatedToken.getDisplayArea();
        final DisplayArea<?> da = windowContextCreatedToken.getDisplayArea();

        mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken,
                TEST_UID, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
@@ -192,11 +209,12 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
        // Let the Display to be created with the DualDisplay policy.
        final DisplayAreaPolicy.Provider policyProvider =
                new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
        Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
        doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
        // Create a DisplayContent with dual RootDisplayArea
        DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
                new DualDisplayAreaGroupPolicyTest.DualDisplayContent
                 .Builder(mAtm, 1000, 1000).build();
        dualDisplayContent.getDisplayInfo().state = STATE_ON;
        final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer();
        // Put the ImeContainer to the first sub-RootDisplayArea
        dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer);
@@ -222,7 +240,62 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
        assertThat(mController.getContainer(mClientToken)).isEqualTo(imeContainer);
    }

    private class TestWindowTokenClient extends IWindowToken.Stub {
    @Test
    public void testConfigUpdateForSuspendedWindowContext() {
        final TestWindowTokenClient mockToken = new TestWindowTokenClient();
        spyOn(mockToken);

        mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;

        final Configuration config1 = mContainer.getConfiguration();
        final Rect bounds1 = new Rect(0, 0, 10, 10);
        config1.windowConfiguration.setBounds(bounds1);
        config1.densityDpi = 100;
        mContainer.onRequestedOverrideConfigurationChanged(config1);

        mController.registerWindowContainerListener(mockToken, mContainer, -1,
                TYPE_APPLICATION_OVERLAY, null /* options */);

        verify(mockToken, never()).onConfigurationChanged(any(), anyInt());

        // Turn on the display and verify if the client receive the callback
        Display display = mContainer.getDisplayContent().getDisplay();
        spyOn(display);
        Mockito.doAnswer(invocation -> {
            final DisplayInfo info = mContainer.getDisplayContent().getDisplayInfo();
            info.state = STATE_ON;
            ((DisplayInfo) invocation.getArgument(0)).copyFrom(info);
            return null;
        }).when(display).getDisplayInfo(any(DisplayInfo.class));

        mContainer.getDisplayContent().onDisplayChanged();

        assertThat(mockToken.mConfiguration).isEqualTo(config1);
        assertThat(mockToken.mDisplayId).isEqualTo(mContainer.getDisplayContent().getDisplayId());
    }

    @Test
    public void testReportConfigUpdateForSuspendedWindowProviderService() {
        final TestWindowTokenClient clientToken = new TestWindowTokenClient();
        final Bundle options = new Bundle();
        options.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);

        mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;

        final Configuration config1 = mContainer.getConfiguration();
        final Rect bounds1 = new Rect(0, 0, 10, 10);
        config1.windowConfiguration.setBounds(bounds1);
        config1.densityDpi = 100;
        mContainer.onRequestedOverrideConfigurationChanged(config1);

        mController.registerWindowContainerListener(clientToken, mContainer, -1,
                TYPE_APPLICATION_OVERLAY, options);

        assertThat(clientToken.mConfiguration).isEqualTo(config1);
        assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId);
    }

    private static class TestWindowTokenClient extends IWindowToken.Stub {
        private Configuration mConfiguration;
        private int mDisplayId;
        private boolean mRemoved;