[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]: dd4a748af0

Change-Id: I8dd3987b731662502bc01e9d2ed67e718ada5f46
This commit is contained in:
Charles Chen
2021-06-21 15:36:44 +08:00
parent 2f7e887a15
commit 37430c5ba8
8 changed files with 54 additions and 26 deletions

View File

@@ -2721,10 +2721,13 @@ class ContextImpl extends Context {
// need to override their display in ResourcesManager. // need to override their display in ResourcesManager.
baseContext.mForceDisplayOverrideInResources = false; baseContext.mForceDisplayOverrideInResources = false;
baseContext.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT; baseContext.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
baseContext.mDisplay = display;
final Resources windowContextResources = createWindowContextResources(baseContext); final Resources windowContextResources = createWindowContextResources(baseContext);
baseContext.setResources(windowContextResources); 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; return baseContext;
} }

View File

@@ -814,9 +814,10 @@ interface IWindowManager
* @param displayId The display associated with the window context * @param displayId The display associated with the window context
* @param options A bundle used to pass window-related options and choose the right DisplayArea * @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); in Bundle options);
/** /**

View File

@@ -26,7 +26,6 @@ import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.view.WindowManager; import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -67,7 +66,7 @@ public class WindowContext extends ContextWrapper {
mType = type; mType = type;
mOptions = options; mOptions = options;
mWindowManager = createWindowContextWindowManager(this); mWindowManager = createWindowContextWindowManager(this);
IBinder token = getWindowContextToken(); WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
mController = new WindowContextController(token); mController = new WindowContextController(token);
Reference.reachabilityFence(this); Reference.reachabilityFence(this);

View File

@@ -19,6 +19,7 @@ package android.window;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
@@ -46,7 +47,7 @@ public class WindowContextController {
@VisibleForTesting @VisibleForTesting
public boolean mAttachedToDisplayArea; public boolean mAttachedToDisplayArea;
@NonNull @NonNull
private final IBinder mToken; private final WindowTokenClient mToken;
/** /**
* Window Context Controller constructor * 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 * @param token The token used to attach to a window manager node. It is usually from
* {@link Context#getWindowContextToken()}. * {@link Context#getWindowContextToken()}.
*/ */
public WindowContextController(@NonNull IBinder token) { public WindowContextController(@NonNull WindowTokenClient token) {
mToken = token; this(token, WindowManagerGlobal.getWindowManagerService());
mWms = WindowManagerGlobal.getWindowManagerService();
} }
/** Used for test only. DO NOT USE it in production code. */ /** Used for test only. DO NOT USE it in production code. */
@VisibleForTesting @VisibleForTesting
public WindowContextController(@NonNull IBinder token, IWindowManager mockWms) { public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token; mToken = token;
mWms = mockWms; mWms = mockWms;
} }
@@ -81,8 +81,14 @@ public class WindowContextController {
+ "a DisplayArea once."); + "a DisplayArea once.");
} }
try { try {
mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId, final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
options); 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) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }

View File

@@ -24,6 +24,8 @@ import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
/** /**
@@ -33,7 +35,7 @@ import java.lang.ref.WeakReference;
* {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}. * {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}.
* *
* @see WindowContext * @see WindowContext
* @see android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, Bundle) * @see android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
* *
* @hide * @hide
*/ */
@@ -50,8 +52,8 @@ public class WindowTokenClient extends IWindowToken.Stub {
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}. * can only attach one {@link Context}.
* <p>This method must be called before invoking * <p>This method must be called before invoking
* {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int,
* Bundle, boolean)}.<p/> * Bundle)}.<p/>
* *
* @param context context to be attached * @param context context to be attached
* @throws IllegalStateException if attached context has already existed. * @throws IllegalStateException if attached context has already existed.
@@ -63,6 +65,13 @@ public class WindowTokenClient extends IWindowToken.Stub {
mContextRef = new WeakReference<>(context); 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 @Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
final Context context = mContextRef.get(); final Context context = mContextRef.get();

View File

@@ -23,11 +23,13 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; 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.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.os.Binder; import android.os.Binder;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.view.IWindowManager; import android.view.IWindowManager;
@@ -38,6 +40,8 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/** /**
* Tests for {@link WindowContextController} * Tests for {@link WindowContextController}
@@ -53,15 +57,18 @@ import org.junit.runner.RunWith;
@Presubmit @Presubmit
public class WindowContextControllerTest { public class WindowContextControllerTest {
private WindowContextController mController; private WindowContextController mController;
@Mock
private IWindowManager mMockWms; private IWindowManager mMockWms;
@Mock
private WindowTokenClient mMockToken;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
mMockWms = mock(IWindowManager.class); MockitoAnnotations.initMocks(this);
mController = new WindowContextController(new Binder(), mMockWms); mController = new WindowContextController(mMockToken, mMockWms);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt());
doReturn(true).when(mMockWms).attachWindowContextToDisplayArea(any(), anyInt(), doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
anyInt(), any()); anyInt(), anyInt(), any());
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
@@ -85,6 +92,7 @@ public class WindowContextControllerTest {
null /* options */); null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue(); assertThat(mController.mAttachedToDisplayArea).isTrue();
verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY));
mController.detachIfNeeded(); mController.detachIfNeeded();

View File

@@ -2717,8 +2717,8 @@ public class WindowManagerService extends IWindowManager.Stub
} }
@Override @Override
public boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId, public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
Bundle options) { type, int displayId, Bundle options) {
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */); "attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid();
@@ -2729,15 +2729,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (dc == null) { if (dc == null) {
ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach" ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach"
+ " to a non-existing display:%d", displayId); + " to a non-existing display:%d", displayId);
return false; return null;
} }
// TODO(b/155340867): Investigate if we still need roundedCornerOverlay after // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
// the feature b/155340867 is completed. // the feature b/155340867 is completed.
final DisplayArea da = dc.findAreaForWindowType(type, options, final DisplayArea da = dc.findAreaForWindowType(type, options,
callerCanManageAppTokens, false /* roundedCornerOverlay */); callerCanManageAppTokens, false /* roundedCornerOverlay */);
// TODO(b/190019118): Avoid to send onConfigurationChanged because it has been done
// in return value of attachWindowContextToDisplayArea.
mWindowContextListenerController.registerWindowContainerListener(clientToken, da, mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
callingUid, type, options); callingUid, type, options);
return true; return da.getConfiguration();
} }
} finally { } finally {
Binder.restoreCallingIdentity(origId); Binder.restoreCallingIdentity(origId);

View File

@@ -82,7 +82,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken, mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG, dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */); null /* options */);
return true; return dc.getImeContainer().getConfiguration();
}).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG), }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
anyInt(), any()); anyInt(), any());