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

This commit is contained in:
Charles Chen
2021-11-24 09:52:47 +00:00
committed by Android (Google) Code Review
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.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();
}

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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;