Loading services/core/java/com/android/server/wm/DisplayContent.java +12 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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(); } Loading services/core/java/com/android/server/wm/WindowContextListenerController.java +33 −10 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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}. Loading Loading @@ -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; } Loading @@ -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 Loading @@ -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); Loading @@ -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)) { Loading Loading @@ -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; Loading @@ -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 Loading @@ -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; } Loading services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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(); Loading services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java +79 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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 */); Loading @@ -89,6 +105,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase { assertEquals(container, listener.getWindowContainer()); } @UseTestDisplay @Test public void testRegisterWindowContextListenerClientConfigPropagation() { final TestWindowTokenClient clientToken = new TestWindowTokenClient(); Loading @@ -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); Loading Loading @@ -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 */); Loading @@ -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); Loading @@ -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; Loading Loading
services/core/java/com/android/server/wm/DisplayContent.java +12 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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(); } Loading
services/core/java/com/android/server/wm/WindowContextListenerController.java +33 −10 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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}. Loading Loading @@ -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; } Loading @@ -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 Loading @@ -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); Loading @@ -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)) { Loading Loading @@ -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; Loading @@ -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 Loading @@ -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; } Loading
services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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(); Loading
services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java +79 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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 */); Loading @@ -89,6 +105,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase { assertEquals(container, listener.getWindowContainer()); } @UseTestDisplay @Test public void testRegisterWindowContextListenerClientConfigPropagation() { final TestWindowTokenClient clientToken = new TestWindowTokenClient(); Loading @@ -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); Loading Loading @@ -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 */); Loading @@ -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); Loading @@ -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; Loading