[RESTRICT AUTOMERGE] Send WindowContext config only when Display is active am: 20fddbc609

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16047754

Change-Id: I37478c87e95dccea5c10712f6b631a41d61af20e
This commit is contained in:
Charles Chen
2021-11-24 10:07:20 +00:00
committed by Automerger Merge Worker
4 changed files with 127 additions and 25 deletions

View File

@@ -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.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT; 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_IME;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -322,11 +324,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/ */
private Rect mLastMirroredDisplayAreaBounds = null; 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 // 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 // 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 // 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() { void onDisplayChanged() {
mDisplay.getRealSize(mTmpDisplaySize); mDisplay.getRealSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y); setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
final int lastDisplayState = mDisplayInfo.state;
updateDisplayInfo(); updateDisplayInfo();
// The window policy is responsible for stopping activities on the default display. // The window policy is responsible for stopping activities on the default display.
final int displayId = mDisplay.getDisplayId(); final int displayId = mDisplay.getDisplayId();
final int displayState = mDisplayInfo.state;
if (displayId != DEFAULT_DISPLAY) { if (displayId != DEFAULT_DISPLAY) {
final int displayState = mDisplay.getState();
if (displayState == Display.STATE_OFF) { if (displayState == Display.STATE_OFF) {
mOffTokenAcquirer.acquire(mDisplayId); mOffTokenAcquirer.acquire(mDisplayId);
} else if (displayState == Display.STATE_ON) { } else if (displayState == Display.STATE_ON) {
@@ -5637,12 +5635,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING, ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
"Display %d state is now (%d), so update layer mirroring?", "Display %d state is now (%d), so update layer mirroring?",
mDisplayId, displayState); mDisplayId, displayState);
if (mLastDisplayState != displayState) { if (lastDisplayState != displayState) {
// If state is on due to surface being added, then start layer mirroring. // 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. // If state is off due to surface being removed, then stop layer mirroring.
updateMirroring(); 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(); mWmService.requestTraversal();
} }

View File

@@ -17,7 +17,9 @@
package com.android.server.wm; package com.android.server.wm;
import static android.view.Display.INVALID_DISPLAY; 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.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_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR; 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. * @param options a bundle used to pass window-related options.
*/ */
void registerWindowContainerListener(@NonNull IBinder clientToken, void registerWindowContainerListener(@NonNull IBinder clientToken,
@NonNull WindowContainer container, int ownerUid, @WindowType int type, @NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
@Nullable Bundle options) { @Nullable Bundle options) {
WindowContextListenerImpl listener = mListeners.get(clientToken); WindowContextListenerImpl listener = mListeners.get(clientToken);
if (listener == null) { if (listener == null) {
@@ -103,6 +105,16 @@ class WindowContextListenerController {
listener.unregister(); 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 * Verifies if the caller is allowed to do the operation to the listener specified by
* {@code clientToken}. * {@code clientToken}.
@@ -138,7 +150,7 @@ class WindowContextListenerController {
return listener != null ? listener.mOptions : null; return listener != null ? listener.mOptions : null;
} }
@Nullable WindowContainer getContainer(IBinder clientToken) { @Nullable WindowContainer<?> getContainer(IBinder clientToken) {
final WindowContextListenerImpl listener = mListeners.get(clientToken); final WindowContextListenerImpl listener = mListeners.get(clientToken);
return listener != null ? listener.mContainer : null; return listener != null ? listener.mContainer : null;
} }
@@ -163,7 +175,7 @@ class WindowContextListenerController {
class WindowContextListenerImpl implements WindowContainerListener { class WindowContextListenerImpl implements WindowContainerListener {
@NonNull private final IBinder mClientToken; @NonNull private final IBinder mClientToken;
private final int mOwnerUid; private final int mOwnerUid;
@NonNull private WindowContainer mContainer; @NonNull private WindowContainer<?> mContainer;
/** /**
* The options from {@link Context#createWindowContext(int, Bundle)}. * The options from {@link Context#createWindowContext(int, Bundle)}.
* <p>It can be used for choosing the {@link DisplayArea} where the window context * <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 int mLastReportedDisplay = INVALID_DISPLAY;
private Configuration mLastReportedConfig; 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) { int ownerUid, @WindowType int type, @Nullable Bundle options) {
mClientToken = clientToken; mClientToken = clientToken;
mContainer = Objects.requireNonNull(container); mContainer = Objects.requireNonNull(container);
@@ -197,11 +211,11 @@ class WindowContextListenerController {
/** TEST ONLY: returns the {@link WindowContainer} of the listener */ /** TEST ONLY: returns the {@link WindowContainer} of the listener */
@VisibleForTesting @VisibleForTesting
WindowContainer getWindowContainer() { WindowContainer<?> getWindowContainer() {
return mContainer; return mContainer;
} }
private void updateContainer(@NonNull WindowContainer newContainer) { private void updateContainer(@NonNull WindowContainer<?> newContainer) {
Objects.requireNonNull(newContainer); Objects.requireNonNull(newContainer);
if (mContainer.equals(newContainer)) { if (mContainer.equals(newContainer)) {
@@ -246,12 +260,20 @@ class WindowContextListenerController {
if (mDeathRecipient == null) { if (mDeathRecipient == null) {
throw new IllegalStateException("Invalid client token: " + mClientToken); throw new IllegalStateException("Invalid client token: " + mClientToken);
} }
// If the display of window context associated window container is suspended, don't
if (mLastReportedConfig == null) { // report the configuration update. Note that we still dispatch the configuration update
mLastReportedConfig = new Configuration(); // 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 Configuration config = mContainer.getConfiguration();
final int displayId = mContainer.getDisplayContent().getDisplayId(); final int displayId = mContainer.getDisplayContent().getDisplayId();
if (mLastReportedConfig == null) {
mLastReportedConfig = new Configuration();
}
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) { if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
// No changes since last reported time. // No changes since last reported time.
return; return;
@@ -266,6 +288,7 @@ class WindowContextListenerController {
} catch (RemoteException e) { } catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client."); ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
} }
mHasPendingConfiguration = false;
} }
@Override @Override
@@ -283,7 +306,7 @@ class WindowContextListenerController {
// If we cannot obtain the DisplayContent, the DisplayContent may also be removed. // If we cannot obtain the DisplayContent, the DisplayContent may also be removed.
// We should proceed the removal process. // We should proceed the removal process.
if (dc != null) { if (dc != null) {
final DisplayArea da = dc.findAreaForToken(windowToken); final DisplayArea<?> da = dc.findAreaForToken(windowToken);
updateContainer(da); updateContainer(da);
return; return;
} }

View File

@@ -17,6 +17,7 @@
package com.android.server.wm; package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY; 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 android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -70,7 +71,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
@Before @Before
public void setUp() throws Exception { 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 = final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider(); new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider(); Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
@@ -78,6 +79,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
mController = new InputMethodMenuController(mock(InputMethodManagerService.class)); mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
.Builder(mAtm, 1000, 1000).build(); .Builder(mAtm, 1000, 1000).build();
mSecondaryDisplay.getDisplayInfo().state = STATE_ON;
// Mock addWindowTokenWithOptions to create a test window token. // Mock addWindowTokenWithOptions to create a test window token.
mIWindowManager = WindowManagerGlobal.getWindowManagerService(); mIWindowManager = WindowManagerGlobal.getWindowManagerService();

View File

@@ -17,22 +17,35 @@
package com.android.server.wm; package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY; 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_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 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 com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; 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.app.IWindowToken;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@@ -55,12 +68,15 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
private static final int ANOTHER_UID = 1000; private static final int ANOTHER_UID = 1000;
private final IBinder mClientToken = new Binder(); private final IBinder mClientToken = new Binder();
private WindowContainer mContainer; private WindowContainer<?> mContainer;
@Before @Before
public void setUp() { public void setUp() {
mController = new WindowContextListenerController(); mController = new WindowContextListenerController();
mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
// Make display on to verify configuration propagation.
mDefaultDisplay.getDisplayInfo().state = STATE_ON;
mDisplayContent.getDisplayInfo().state = STATE_ON;
} }
@Test @Test
@@ -76,7 +92,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertEquals(2, mController.mListeners.size()); assertEquals(2, mController.mListeners.size());
final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY, final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay); mDefaultDisplay);
mController.registerWindowContainerListener(mClientToken, container, -1, mController.registerWindowContainerListener(mClientToken, container, -1,
TYPE_APPLICATION_OVERLAY, null /* options */); TYPE_APPLICATION_OVERLAY, null /* options */);
@@ -89,6 +105,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertEquals(container, listener.getWindowContainer()); assertEquals(container, listener.getWindowContainer());
} }
@UseTestDisplay
@Test @Test
public void testRegisterWindowContextListenerClientConfigPropagation() { public void testRegisterWindowContextListenerClientConfigPropagation() {
final TestWindowTokenClient clientToken = new TestWindowTokenClient(); final TestWindowTokenClient clientToken = new TestWindowTokenClient();
@@ -107,7 +124,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId); assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);
// Update the WindowContainer. // Update the WindowContainer.
final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY, final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay); mDefaultDisplay);
final Configuration config2 = container.getConfiguration(); final Configuration config2 = container.getConfiguration();
final Rect bounds2 = new Rect(0, 0, 20, 20); final Rect bounds2 = new Rect(0, 0, 20, 20);
@@ -174,7 +191,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
.setDisplayContent(mDefaultDisplay) .setDisplayContent(mDefaultDisplay)
.setFromClientToken(true) .setFromClientToken(true)
.build(); .build();
final DisplayArea da = windowContextCreatedToken.getDisplayArea(); final DisplayArea<?> da = windowContextCreatedToken.getDisplayArea();
mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken, mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken,
TEST_UID, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */); 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. // Let the Display to be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider = final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider(); new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider(); doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
// Create a DisplayContent with dual RootDisplayArea // Create a DisplayContent with dual RootDisplayArea
DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent = DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
new DualDisplayAreaGroupPolicyTest.DualDisplayContent new DualDisplayAreaGroupPolicyTest.DualDisplayContent
.Builder(mAtm, 1000, 1000).build(); .Builder(mAtm, 1000, 1000).build();
dualDisplayContent.getDisplayInfo().state = STATE_ON;
final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer(); final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer();
// Put the ImeContainer to the first sub-RootDisplayArea // Put the ImeContainer to the first sub-RootDisplayArea
dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer); dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer);
@@ -222,7 +240,62 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertThat(mController.getContainer(mClientToken)).isEqualTo(imeContainer); 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 Configuration mConfiguration;
private int mDisplayId; private int mDisplayId;
private boolean mRemoved; private boolean mRemoved;