From 37430c5ba8bb6c41dbec908b7b592860335f2d03 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Mon, 21 Jun 2021 15:36:44 +0800 Subject: [PATCH] [RESTRICT AUTOMERGE] Send DA's config directly when attaching to DA WindowContext relies on WindowTokenClient#onConfigurationChanged after calling WMS#attachWindowContextToDisplayArea. However, it took some time to wait for onConfigurationChanged callback from the server side so that we may get a stale value right after creating WindowContext. This confuses developers especially when the foreground activity is in size compat mode or freeform because the process config is overridden by activity's config. This CL makes #attachWindowContextToDisplayArea return DA's configuration and applies to WindowContext direcly. It also benefits WindowProviderService because it can obtain DA's configuration before onCreate() based on [1] and this CL. Bug: 190019118 Bug: 190745506 Bug: 198298520 Test: manual - 1. launch an Activity in size compat mode 2. create a WindowContext and verify if WindowMetrics matches DA bounds. Test: atest WindowContextTest WindowContextTests Test: atest WindowContextControllerTest ContextGetDisplayTest [1]: dd4a748af05851356b5dbd90fb42f503905370bb Change-Id: I8dd3987b731662502bc01e9d2ed67e718ada5f46 --- core/java/android/app/ContextImpl.java | 5 ++++- core/java/android/view/IWindowManager.aidl | 5 +++-- core/java/android/window/WindowContext.java | 3 +-- .../window/WindowContextController.java | 20 ++++++++++++------- .../android/window/WindowTokenClient.java | 15 +++++++++++--- .../window/WindowContextControllerTest.java | 20 +++++++++++++------ .../server/wm/WindowManagerService.java | 10 ++++++---- .../wm/InputMethodMenuControllerTest.java | 2 +- 8 files changed, 54 insertions(+), 26 deletions(-) diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4db1f71e82561..1397f5ea10dc7 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2721,10 +2721,13 @@ class ContextImpl extends Context { // need to override their display in ResourcesManager. baseContext.mForceDisplayOverrideInResources = false; baseContext.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT; - baseContext.mDisplay = display; final Resources windowContextResources = createWindowContextResources(baseContext); baseContext.setResources(windowContextResources); + // Associate the display with window context resources so that configuration update from + // the server side will also apply to the display's metrics. + baseContext.mDisplay = ResourcesManager.getInstance() + .getAdjustedDisplay(display.getDisplayId(), windowContextResources); return baseContext; } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 802163617b3b9..a7ecf1f2a81dc 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -814,9 +814,10 @@ interface IWindowManager * @param displayId The display associated with the window context * @param options A bundle used to pass window-related options and choose the right DisplayArea * - * @return {@code true} if the WindowContext is attached to the DisplayArea successfully. + * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is + * attached to the DisplayArea successfully. {@code null}, otherwise. */ - boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId, + Configuration attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId, in Bundle options); /** diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java index 901625b0732c0..6d0a6bd559ae2 100644 --- a/core/java/android/window/WindowContext.java +++ b/core/java/android/window/WindowContext.java @@ -26,7 +26,6 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.res.Configuration; import android.os.Bundle; -import android.os.IBinder; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; @@ -67,7 +66,7 @@ public class WindowContext extends ContextWrapper { mType = type; mOptions = options; mWindowManager = createWindowContextWindowManager(this); - IBinder token = getWindowContextToken(); + WindowTokenClient token = (WindowTokenClient) getWindowContextToken(); mController = new WindowContextController(token); Reference.reachabilityFence(this); diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java index d84f571931fdd..505b450086632 100644 --- a/core/java/android/window/WindowContextController.java +++ b/core/java/android/window/WindowContextController.java @@ -19,6 +19,7 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -46,7 +47,7 @@ public class WindowContextController { @VisibleForTesting public boolean mAttachedToDisplayArea; @NonNull - private final IBinder mToken; + private final WindowTokenClient mToken; /** * Window Context Controller constructor @@ -54,14 +55,13 @@ public class WindowContextController { * @param token The token used to attach to a window manager node. It is usually from * {@link Context#getWindowContextToken()}. */ - public WindowContextController(@NonNull IBinder token) { - mToken = token; - mWms = WindowManagerGlobal.getWindowManagerService(); + public WindowContextController(@NonNull WindowTokenClient token) { + this(token, WindowManagerGlobal.getWindowManagerService()); } /** Used for test only. DO NOT USE it in production code. */ @VisibleForTesting - public WindowContextController(@NonNull IBinder token, IWindowManager mockWms) { + public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) { mToken = token; mWms = mockWms; } @@ -81,8 +81,14 @@ public class WindowContextController { + "a DisplayArea once."); } try { - mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId, - options); + final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type, + displayId, options); + if (configuration != null) { + mAttachedToDisplayArea = true; + // Send the DisplayArea's configuration to WindowContext directly instead of + // waiting for dispatching from WMS. + mToken.onConfigurationChanged(configuration, displayId); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index 6abf5575d3538..4dcd2e74a53fd 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -24,6 +24,8 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.ref.WeakReference; /** @@ -33,7 +35,7 @@ import java.lang.ref.WeakReference; * {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}. * * @see WindowContext - * @see android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, Bundle) + * @see android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, Bundle) * * @hide */ @@ -50,8 +52,8 @@ public class WindowTokenClient extends IWindowToken.Stub { * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} * can only attach one {@link Context}. *

This method must be called before invoking - * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, - * Bundle, boolean)}.

+ * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, + * Bundle)}.

* * @param context context to be attached * @throws IllegalStateException if attached context has already existed. @@ -63,6 +65,13 @@ public class WindowTokenClient extends IWindowToken.Stub { mContextRef = new WeakReference<>(context); } + /** + * Called when {@link Configuration} updates from the server side receive. + * + * @param newConfig the updated {@link Configuration} + * @param newDisplayId the updated {@link android.view.Display} ID + */ + @VisibleForTesting @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { final Context context = mContextRef.get(); diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java index 020f4a06b892d..073e46827bbb1 100644 --- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java +++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java @@ -23,11 +23,13 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import android.content.res.Configuration; import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.view.IWindowManager; @@ -38,6 +40,8 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; /** * Tests for {@link WindowContextController} @@ -53,15 +57,18 @@ import org.junit.runner.RunWith; @Presubmit public class WindowContextControllerTest { private WindowContextController mController; + @Mock private IWindowManager mMockWms; + @Mock + private WindowTokenClient mMockToken; @Before public void setUp() throws Exception { - mMockWms = mock(IWindowManager.class); - mController = new WindowContextController(new Binder(), mMockWms); - - doReturn(true).when(mMockWms).attachWindowContextToDisplayArea(any(), anyInt(), - anyInt(), any()); + MockitoAnnotations.initMocks(this); + mController = new WindowContextController(mMockToken, mMockWms); + doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt()); + doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(), + anyInt(), anyInt(), any()); } @Test(expected = IllegalStateException.class) @@ -85,6 +92,7 @@ public class WindowContextControllerTest { null /* options */); assertThat(mController.mAttachedToDisplayArea).isTrue(); + verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY)); mController.detachIfNeeded(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2913e1071ed6f..e3b25a5fda3e7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2717,8 +2717,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId, - Bundle options) { + public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int + type, int displayId, Bundle options) { final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, "attachWindowContextToDisplayArea", false /* printLog */); final int callingUid = Binder.getCallingUid(); @@ -2729,15 +2729,17 @@ public class WindowManagerService extends IWindowManager.Stub if (dc == null) { ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach" + " to a non-existing display:%d", displayId); - return false; + return null; } // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after // the feature b/155340867 is completed. final DisplayArea da = dc.findAreaForWindowType(type, options, callerCanManageAppTokens, false /* roundedCornerOverlay */); + // TODO(b/190019118): Avoid to send onConfigurationChanged because it has been done + // in return value of attachWindowContextToDisplayArea. mWindowContextListenerController.registerWindowContainerListener(clientToken, da, callingUid, type, options); - return true; + return da.getConfiguration(); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java index f2418c68358d0..a8ede13e5de64 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java @@ -82,7 +82,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase { mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken, dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG, null /* options */); - return true; + return dc.getImeContainer().getConfiguration(); }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());