Merge changes from topic "window_context_2.0" into sc-dev
* changes: Apply WindowContext 2.0 for InputMethodMenuController Migrate WindowContext to WindowContextListener mechanism
This commit is contained in:
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
import static android.view.WindowManagerGlobal.ADD_OKAY;
|
||||
import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
|
||||
import static android.view.WindowManagerImpl.createWindowContextWindowManager;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -28,8 +27,8 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.view.Display;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.view.WindowManagerImpl;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
@@ -46,10 +45,10 @@ import java.lang.ref.Reference;
|
||||
*/
|
||||
@UiContext
|
||||
public class WindowContext extends ContextWrapper {
|
||||
private final WindowManagerImpl mWindowManager;
|
||||
private final WindowManager mWindowManager;
|
||||
private final IWindowManager mWms;
|
||||
private final WindowTokenClient mToken;
|
||||
private boolean mOwnsToken;
|
||||
private boolean mListenerRegistered;
|
||||
|
||||
/**
|
||||
* Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
|
||||
@@ -86,25 +85,14 @@ public class WindowContext extends ContextWrapper {
|
||||
|
||||
mToken.attachContext(this);
|
||||
|
||||
mWindowManager = new WindowManagerImpl(this);
|
||||
mWindowManager.setDefaultToken(mToken);
|
||||
mWindowManager = createWindowContextWindowManager(this);
|
||||
|
||||
int result;
|
||||
try {
|
||||
// Register the token with WindowManager. This will also call back with the current
|
||||
// config back to the client.
|
||||
result = mWms.addWindowTokenWithOptions(
|
||||
mToken, type, getDisplayId(), options, getPackageName());
|
||||
mListenerRegistered = mWms.registerWindowContextListener(mToken, type, getDisplayId(),
|
||||
options);
|
||||
} catch (RemoteException e) {
|
||||
mOwnsToken = false;
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
if (result == ADD_TOO_MANY_TOKENS) {
|
||||
throw new UnsupportedOperationException("createWindowContext failed! Too many unused "
|
||||
+ "window contexts. Please see Context#createWindowContext documentation for "
|
||||
+ "detail.");
|
||||
}
|
||||
mOwnsToken = result == ADD_OKAY;
|
||||
Reference.reachabilityFence(this);
|
||||
}
|
||||
|
||||
@@ -131,10 +119,10 @@ public class WindowContext extends ContextWrapper {
|
||||
/** Used for test to invoke because we can't invoke finalize directly. */
|
||||
@VisibleForTesting
|
||||
public void release() {
|
||||
if (mOwnsToken) {
|
||||
if (mListenerRegistered) {
|
||||
mListenerRegistered = false;
|
||||
try {
|
||||
mWms.removeWindowToken(mToken, getDisplayId());
|
||||
mOwnsToken = false;
|
||||
mWms.unregisterWindowContextListener(mToken);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import android.view.Display;
|
||||
import android.view.DisplayAdjustments;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams.WindowType;
|
||||
import android.view.autofill.AutofillManager.AutofillClient;
|
||||
@@ -6110,18 +6111,19 @@ public abstract class Context {
|
||||
*
|
||||
* // WindowManager.LayoutParams initialization
|
||||
* ...
|
||||
* // The types used in addView and createWindowContext must match.
|
||||
* mParams.type = TYPE_APPLICATION_OVERLAY;
|
||||
* ...
|
||||
*
|
||||
* mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
|
||||
* windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This context's configuration and resources are adjusted to a display area where the windows
|
||||
* with provided type will be added. <b>Note that all windows associated with the same context
|
||||
* will have an affinity and can only be moved together between different displays or areas on a
|
||||
* display.</b> If there is a need to add different window types, or non-associated windows,
|
||||
* separate Contexts should be used.
|
||||
* This context's configuration and resources are adjusted to an area of the display where
|
||||
* the windows with provided type will be added. <b>Note that all windows associated with the
|
||||
* same context will have an affinity and can only be moved together between different displays
|
||||
* or areas on a display.</b> If there is a need to add different window types, or
|
||||
* non-associated windows, separate Contexts should be used.
|
||||
* </p>
|
||||
* <p>
|
||||
* Creating a window context is an expensive operation. Misuse of this API may lead to a huge
|
||||
@@ -6129,7 +6131,43 @@ public abstract class Context {
|
||||
* An approach is to create one window context with specific window type and display and
|
||||
* use it everywhere it's needed.
|
||||
* </p>
|
||||
* <p>
|
||||
* After {@link Build.VERSION_CODES#S}, window context provides the capability to receive
|
||||
* configuration changes for existing token by overriding the
|
||||
* {@link android.view.WindowManager.LayoutParams#token token} of the
|
||||
* {@link android.view.WindowManager.LayoutParams} passed in
|
||||
* {@link WindowManager#addView(View, LayoutParams)}. This is useful when an application needs
|
||||
* to attach its window to an existing activity for window token sharing use-case.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the window context in {@link Build.VERSION_CODES#R} didn't have this
|
||||
* capability. This is a no-op for the window context in {@link Build.VERSION_CODES#R}.
|
||||
* </p>
|
||||
* Below is sample code to <b>attach an existing token to a window context:</b>
|
||||
* <pre class="prettyprint">
|
||||
* final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
|
||||
* final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
|
||||
* final Context windowContext = anyContext.createWindowContext(primaryDisplay,
|
||||
* TYPE_APPLICATION, null);
|
||||
*
|
||||
* // Get an existing token.
|
||||
* final IBinder existingToken = activity.getWindow().getAttributes().token;
|
||||
*
|
||||
* // The types used in addView() and createWindowContext() must match.
|
||||
* final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_APPLICATION);
|
||||
* params.token = existingToken;
|
||||
*
|
||||
* // After WindowManager#addView(), the server side will extract the provided token from
|
||||
* // LayoutParams#token (existingToken in the sample code), and switch to propagate
|
||||
* // configuration changes from the node associated with the provided token.
|
||||
* windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
|
||||
* </pre>
|
||||
* <p>
|
||||
* Note that using {@link android.app.Application} or {@link android.app.Service} context for
|
||||
* UI-related queries may result in layout or continuity issues on devices with variable screen
|
||||
* sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect
|
||||
* the {@link Configuration} changes for the visual container.
|
||||
* </p>
|
||||
* @param type Window type in {@link WindowManager.LayoutParams}
|
||||
* @param options A bundle used to pass window-related options
|
||||
* @return A {@link Context} that can be used to create
|
||||
@@ -6141,9 +6179,7 @@ public abstract class Context {
|
||||
* @see #LAYOUT_INFLATER_SERVICE
|
||||
* @see #WALLPAPER_SERVICE
|
||||
* @throws UnsupportedOperationException if this {@link Context} does not attach to a display,
|
||||
* such as {@link android.app.Application Application} or {@link android.app.Service Service},
|
||||
* or the current number of window contexts without adding any view by
|
||||
* {@link WindowManager#addView} <b>exceeds five</b>.
|
||||
* such as {@link android.app.Application Application} or {@link android.app.Service Service}.
|
||||
*/
|
||||
@UiContext
|
||||
@NonNull
|
||||
|
||||
@@ -117,7 +117,7 @@ interface IWindowManager
|
||||
// These can only be called when holding the MANAGE_APP_TOKENS permission.
|
||||
void setEventDispatching(boolean enabled);
|
||||
|
||||
/** @return {@code true} if this binder is a registered window token. */
|
||||
/** Returns {@code true} if this binder is a registered window token. */
|
||||
boolean isWindowToken(in IBinder binder);
|
||||
/**
|
||||
* Adds window token for a given type.
|
||||
|
||||
@@ -147,6 +147,9 @@
|
||||
<!-- WindowMetricsTest permissions -->
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
|
||||
<!-- WindowContextTest permissions -->
|
||||
<uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
|
||||
|
||||
<!-- Allow use of PendingIntent.getIntent() -->
|
||||
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
|
||||
|
||||
|
||||
@@ -18,29 +18,38 @@ package android.app;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.Display;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams.WindowType;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.view.WindowManagerImpl;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Tests for {@link WindowContext}
|
||||
*
|
||||
@@ -54,20 +63,14 @@ import org.junit.runner.RunWith;
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
public class WindowContextTest {
|
||||
@Rule
|
||||
public ActivityTestRule<EmptyActivity> mActivityRule =
|
||||
new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */,
|
||||
false /* launchActivity */);
|
||||
|
||||
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
private final WindowContext mWindowContext = createWindowContext();
|
||||
|
||||
@Test
|
||||
public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
|
||||
final IBinder token = mWindowContext.getWindowContextToken();
|
||||
final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
|
||||
|
||||
assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
|
||||
|
||||
mWindowContext.release();
|
||||
|
||||
assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
|
||||
}
|
||||
private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
|
||||
|
||||
@Test
|
||||
public void testCreateWindowContextWindowManagerAttachClientToken() {
|
||||
@@ -83,12 +86,133 @@ public class WindowContextTest {
|
||||
assertEquals(mWindowContext.getWindowContextToken(), params.mWindowContextToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the {@link WindowContext} life cycle behavior to add a new window token:
|
||||
* <ul>
|
||||
* <li>The window token is created before adding the first view.</li>
|
||||
* <li>The window token is registered after adding the first view.</li>
|
||||
* <li>The window token is removed after {@link WindowContext}'s release.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWindowContextNewTokenFromClient() throws Throwable {
|
||||
final IBinder token = mWindowContext.getWindowContextToken();
|
||||
|
||||
// Test that the window token is not created yet.
|
||||
assertFalse("Token must not be registered until adding the first window",
|
||||
mWms.isWindowToken(token));
|
||||
|
||||
final WindowManager.LayoutParams params =
|
||||
new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
|
||||
final View testView = new View(mWindowContext);
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
testView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {}
|
||||
});
|
||||
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
mWindowContext.getSystemService(WindowManager.class).addView(testView, params);
|
||||
|
||||
assertEquals(token, params.mWindowContextToken);
|
||||
});
|
||||
|
||||
|
||||
assertTrue(latch.await(4, TimeUnit.SECONDS));
|
||||
|
||||
|
||||
// Verify that the window token of the window context is created after first addView().
|
||||
assertTrue("Token must exist after adding the first view.",
|
||||
mWms.isWindowToken(token));
|
||||
|
||||
mWindowContext.release();
|
||||
|
||||
// After the window context's release, the window token is also removed.
|
||||
assertFalse("Token must be removed after release.", mWms.isWindowToken(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the behavior when window context attaches an {@link Activity} by override
|
||||
* {@link WindowManager.LayoutParams#token}.
|
||||
*
|
||||
* The window context token should be overridden to
|
||||
* {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must
|
||||
* not be removed regardless of the release of window context.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWindowContext_AttachActivity_TokenNotRemovedAfterRelease()
|
||||
throws Throwable {
|
||||
mActivityRule.launchActivity(new Intent());
|
||||
final Activity activity = mActivityRule.getActivity();
|
||||
final WindowManager.LayoutParams params = activity.getWindow().getAttributes();
|
||||
|
||||
final WindowContext windowContext = createWindowContext(params.type);
|
||||
final IBinder token = windowContext.getWindowContextToken();
|
||||
|
||||
final View testView = new View(windowContext);
|
||||
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
windowContext.getSystemService(WindowManager.class).addView(testView, params);
|
||||
|
||||
assertEquals(token, params.mWindowContextToken);
|
||||
});
|
||||
windowContext.release();
|
||||
|
||||
// Even if the window context is released, the activity should still exist.
|
||||
assertTrue("Token must exist even if the window context is released.",
|
||||
mWms.isWindowToken(activity.getActivityToken()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the behavior when window context attaches an existing token by override
|
||||
* {@link WindowManager.LayoutParams#token}.
|
||||
*
|
||||
* The window context token should be overridden to
|
||||
* {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must not be
|
||||
* removed regardless of release of window context.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWindowContext_AttachWindowToken_TokenNotRemovedAfterRelease()
|
||||
throws Throwable {
|
||||
final WindowContext windowContext = createWindowContext(TYPE_INPUT_METHOD);
|
||||
final IBinder token = windowContext.getWindowContextToken();
|
||||
|
||||
final IBinder existingToken = new Binder();
|
||||
mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId());
|
||||
|
||||
final WindowManager.LayoutParams params =
|
||||
new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
|
||||
params.token = existingToken;
|
||||
final View testView = new View(windowContext);
|
||||
|
||||
mInstrumentation.runOnMainSync(() -> {
|
||||
windowContext.getSystemService(WindowManager.class).addView(testView, params);
|
||||
|
||||
assertEquals(token, params.mWindowContextToken);
|
||||
});
|
||||
windowContext.release();
|
||||
|
||||
// Even if the window context is released, the existing token should still exist.
|
||||
assertTrue("Token must exist even if the window context is released.",
|
||||
mWms.isWindowToken(existingToken));
|
||||
|
||||
mWms.removeWindowToken(existingToken, DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
private WindowContext createWindowContext() {
|
||||
return createWindowContext(TYPE_APPLICATION_OVERLAY);
|
||||
}
|
||||
|
||||
private WindowContext createWindowContext(@WindowType int type) {
|
||||
final Context instContext = mInstrumentation.getTargetContext();
|
||||
final Display display = instContext.getSystemService(DisplayManager.class)
|
||||
.getDisplay(DEFAULT_DISPLAY);
|
||||
final Context context = instContext.createDisplayContext(display);
|
||||
return new WindowContext(context, TYPE_APPLICATION_OVERLAY,
|
||||
null /* options */);
|
||||
return (WindowContext) instContext.createWindowContext(display, type, null /* options */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,8 @@ public class InputMethodMenuController {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public Context getSettingsContext(int displayId) {
|
||||
if (mSettingsContext == null) {
|
||||
// TODO(b/178462039): Cover the case when IME is moved to another ImeContainer.
|
||||
if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
|
||||
final Context systemUiContext = ActivityThread.currentActivityThread()
|
||||
.createSystemUiContext(displayId);
|
||||
final Context windowContext = systemUiContext.createWindowContext(
|
||||
@@ -229,11 +230,6 @@ public class InputMethodMenuController {
|
||||
windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
|
||||
mSwitchingDialogToken = mSettingsContext.getWindowContextToken();
|
||||
}
|
||||
// TODO(b/159767464): register the listener to another display again if window token is not
|
||||
// yet created.
|
||||
if (mSettingsContext.getDisplayId() != displayId) {
|
||||
mWindowManagerInternal.moveWindowTokenToDisplay(mSwitchingDialogToken, displayId);
|
||||
}
|
||||
return mSettingsContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -770,7 +770,8 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
final AnrController mAnrController;
|
||||
|
||||
private final ScreenshotHashController mScreenshotHashController;
|
||||
private final WindowContextListenerController mWindowContextListenerController =
|
||||
@VisibleForTesting
|
||||
final WindowContextListenerController mWindowContextListenerController =
|
||||
new WindowContextListenerController();
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -2794,6 +2795,17 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
return WindowManagerGlobal.ADD_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener for a {@link android.app.WindowContext} to subscribe to configuration
|
||||
* changes of a {@link DisplayArea}.
|
||||
*
|
||||
* @param clientToken the window context's token
|
||||
* @param type Window type of 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
|
||||
*
|
||||
* @return {@code true} if the listener was registered successfully.
|
||||
*/
|
||||
@Override
|
||||
public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
|
||||
Bundle options) {
|
||||
@@ -2849,6 +2861,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns {@code true} if this binder is a registered window token. */
|
||||
@Override
|
||||
public boolean isWindowToken(IBinder binder) {
|
||||
synchronized (mGlobalLock) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.os.Process.INVALID_UID;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
|
||||
@@ -26,7 +25,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
|
||||
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
|
||||
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
|
||||
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
|
||||
@@ -41,7 +39,6 @@ import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.IWindowToken;
|
||||
import android.app.servertransaction.FixedRotationAdjustmentsItem;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
@@ -58,7 +55,6 @@ import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.server.policy.WindowManagerPolicy;
|
||||
|
||||
@@ -112,17 +108,10 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
|
||||
private FixedRotationTransformState mFixedRotationTransformState;
|
||||
|
||||
private Configuration mLastReportedConfig;
|
||||
private int mLastReportedDisplay = INVALID_DISPLAY;
|
||||
|
||||
/**
|
||||
* When set to {@code true}, this window token is created from {@link android.app.WindowContext}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
final boolean mFromClientToken;
|
||||
|
||||
private DeathRecipient mDeathRecipient;
|
||||
private boolean mBinderDied = false;
|
||||
private final boolean mFromClientToken;
|
||||
|
||||
private final int mOwnerUid;
|
||||
|
||||
@@ -188,30 +177,6 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
}
|
||||
}
|
||||
|
||||
private class DeathRecipient implements IBinder.DeathRecipient {
|
||||
private boolean mHasUnlinkToDeath = false;
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
synchronized (mWmService.mGlobalLock) {
|
||||
mBinderDied = true;
|
||||
removeImmediately();
|
||||
}
|
||||
}
|
||||
|
||||
void linkToDeath() throws RemoteException {
|
||||
token.linkToDeath(DeathRecipient.this, 0);
|
||||
}
|
||||
|
||||
void unlinkToDeath() {
|
||||
if (mHasUnlinkToDeath) {
|
||||
return;
|
||||
}
|
||||
token.unlinkToDeath(DeathRecipient.this, 0);
|
||||
mHasUnlinkToDeath = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two child window of this token and returns -1 if the first is lesser than the
|
||||
* second in terms of z-order and 1 otherwise.
|
||||
@@ -266,17 +231,6 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
if (dc != null) {
|
||||
dc.addWindowToken(token, this);
|
||||
}
|
||||
if (shouldReportToClient()) {
|
||||
try {
|
||||
mDeathRecipient = new DeathRecipient();
|
||||
mDeathRecipient.linkToDeath();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
|
||||
+ "display " + dc.getDisplayId(), e);
|
||||
mDeathRecipient = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeAllWindowsIfPossible() {
|
||||
@@ -414,22 +368,6 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
// Needs to occur after the token is removed from the display above to avoid attempt at
|
||||
// duplicate removal of this window container from it's parent.
|
||||
super.removeImmediately();
|
||||
|
||||
reportWindowTokenRemovedToClient();
|
||||
}
|
||||
|
||||
// TODO(b/159767464): Remove after we migrate to listener approach.
|
||||
private void reportWindowTokenRemovedToClient() {
|
||||
if (!shouldReportToClient()) {
|
||||
return;
|
||||
}
|
||||
mDeathRecipient.unlinkToDeath();
|
||||
IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
|
||||
try {
|
||||
windowTokenClient.onWindowTokenRemoved();
|
||||
} catch (RemoteException e) {
|
||||
ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -441,51 +379,11 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
// to another display before the window behind
|
||||
// it is ready.
|
||||
super.onDisplayChanged(dc);
|
||||
reportConfigToWindowTokenClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newParentConfig) {
|
||||
super.onConfigurationChanged(newParentConfig);
|
||||
reportConfigToWindowTokenClient();
|
||||
}
|
||||
|
||||
void reportConfigToWindowTokenClient() {
|
||||
if (!shouldReportToClient()) {
|
||||
return;
|
||||
}
|
||||
if (mLastReportedConfig == null) {
|
||||
mLastReportedConfig = new Configuration();
|
||||
}
|
||||
final Configuration config = getConfiguration();
|
||||
final int displayId = getDisplayContent().getDisplayId();
|
||||
if (config.diff(mLastReportedConfig) == 0 && displayId == mLastReportedDisplay) {
|
||||
// No changes since last reported time.
|
||||
return;
|
||||
}
|
||||
|
||||
mLastReportedConfig.setTo(config);
|
||||
mLastReportedDisplay = displayId;
|
||||
|
||||
IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
|
||||
try {
|
||||
windowTokenClient.onConfigurationChanged(config, displayId);
|
||||
} catch (RemoteException e) {
|
||||
ProtoLog.w(WM_ERROR,
|
||||
"Could not report config changes to the window token client.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
|
||||
* registered from client side.
|
||||
*/
|
||||
private boolean shouldReportToClient() {
|
||||
// Only report to client for WindowToken because Activities are updated through ATM
|
||||
// callbacks.
|
||||
return asActivityRecord() == null
|
||||
// Report to {@link android.view.WindowTokenClient} if this token was registered from it.
|
||||
&& mFromClientToken && !mBinderDied;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
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.doReturn;
|
||||
@@ -26,7 +27,6 @@ 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.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@@ -70,13 +70,15 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
|
||||
spyOn(wms);
|
||||
doAnswer(invocation -> {
|
||||
Object[] args = invocation.getArguments();
|
||||
final IBinder token = (IBinder) args[0];
|
||||
final int windowType = (int) args[1];
|
||||
new WindowToken(mWm, token, windowType, true /* persistOnEmpty */,
|
||||
mDefaultDisplay, true /* ownerCanManageAppTokens */, 1000 /* ownerUid */,
|
||||
false /* roundedCornerOverlay */, true /* fromClientToken */);
|
||||
return WindowManagerGlobal.ADD_OKAY;
|
||||
}).when(wms).addWindowTokenWithOptions(any(), anyInt(), anyInt(), any(), anyString());
|
||||
IBinder clientToken = (IBinder) args[0];
|
||||
int displayId = (int) args[2];
|
||||
DisplayContent dc = mWm.mRoot.getDisplayContent(displayId);
|
||||
mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
|
||||
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
|
||||
null /* options */);
|
||||
return true;
|
||||
}).when(wms).registerWindowContextListener(any(), eq(TYPE_INPUT_METHOD_DIALOG),
|
||||
anyInt(), any());
|
||||
|
||||
mSecondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1000).build();
|
||||
|
||||
@@ -95,14 +97,12 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
|
||||
|
||||
assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
|
||||
|
||||
// Obtain the context again and check they are the same instance and match the display
|
||||
// metrics of the secondary display.
|
||||
// Obtain the context again and check if the window metrics match the IME container bounds
|
||||
// of the secondary display.
|
||||
final Context contextOnSecondaryDisplay = mController.getSettingsContext(
|
||||
mSecondaryDisplay.getDisplayId());
|
||||
|
||||
assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay);
|
||||
assertThat(contextOnDefaultDisplay.getWindowContextToken())
|
||||
.isEqualTo(contextOnSecondaryDisplay.getWindowContextToken());
|
||||
}
|
||||
|
||||
private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
|
||||
|
||||
Reference in New Issue
Block a user