Merge "Fix IME switch dialog crash on Dual rootDA display" into sc-dev

This commit is contained in:
Charles Chen
2021-05-11 04:08:49 +00:00
committed by Android (Google) Code Review
10 changed files with 97 additions and 37 deletions

View File

@@ -37,7 +37,8 @@ import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType; import android.view.WindowManager.LayoutParams.WindowType;
import com.android.internal.util.Preconditions; import java.util.Objects;
/** /**
* Base class for presentations. * Base class for presentations.
* <p> * <p>
@@ -153,7 +154,7 @@ public class Presentation extends Dialog {
private final Display mDisplay; private final Display mDisplay;
private final DisplayManager mDisplayManager; private final DisplayManager mDisplayManager;
private final Handler mHandler = new Handler(Preconditions.checkNotNull(Looper.myLooper(), private final Handler mHandler = new Handler(Objects.requireNonNull(Looper.myLooper(),
"Presentation must be constructed on a looper thread.")); "Presentation must be constructed on a looper thread."));
/** /**

View File

@@ -158,7 +158,7 @@ public class Preconditions {
* be converted to a string using {@link String#valueOf(Object)} * be converted to a string using {@link String#valueOf(Object)}
* @return the non-null reference that was validated * @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null * @throws NullPointerException if {@code reference} is null
* @deprecated - use {@link java.util.Objects.requireNonNull} instead. * @deprecated - use {@link java.util.Objects#requireNonNull} instead.
*/ */
@Deprecated @Deprecated
@UnsupportedAppUsage @UnsupportedAppUsage

View File

@@ -74,8 +74,8 @@ public abstract class DisplayAreaPolicy {
*/ */
public abstract void addWindow(WindowToken token); public abstract void addWindow(WindowToken token);
/** Gets the {@link DisplayArea} which a {@link WindowToken} is about to be attached to. */ /** Gets the {@link DisplayArea} with given window type and launched options */
public abstract DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options, public abstract DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,
boolean ownerCanManageAppTokens, boolean roundedCornerOverlay); boolean ownerCanManageAppTokens, boolean roundedCornerOverlay);
/** /**

View File

@@ -756,7 +756,14 @@ class DisplayAreaPolicyBuilder {
@VisibleForTesting @VisibleForTesting
DisplayArea.Tokens findAreaForToken(WindowToken token) { DisplayArea.Tokens findAreaForToken(WindowToken token) {
return mSelectRootForWindowFunc.apply(token.windowType, token.mOptions) return mSelectRootForWindowFunc.apply(token.windowType, token.mOptions)
.findAreaForToken(token); .findAreaForTokenInLayer(token);
}
@Override
public DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,
boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
return mSelectRootForWindowFunc.apply(type, options).findAreaForWindowTypeInLayer(type,
ownerCanManageAppTokens, roundedCornerOverlay);
} }
@VisibleForTesting @VisibleForTesting
@@ -794,13 +801,6 @@ class DisplayAreaPolicyBuilder {
public TaskDisplayArea getDefaultTaskDisplayArea() { public TaskDisplayArea getDefaultTaskDisplayArea() {
return mDefaultTaskDisplayArea; return mDefaultTaskDisplayArea;
} }
@Override
public DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options,
boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
return mSelectRootForWindowFunc.apply(type, options).findAreaForToken(type,
ownerCanManageAppTokens, roundedCornerOverlay);
}
} }
static class PendingArea { static class PendingArea {

View File

@@ -1123,15 +1123,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
token.mDisplayContent = this; token.mDisplayContent = this;
// Add non-app token to container hierarchy on the display. App tokens are added through // Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks). // the parent container managing them (e.g. Tasks).
switch (token.windowType) { final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
case TYPE_INPUT_METHOD: da.addChild(token);
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainer.addChild(token);
break;
default:
mDisplayAreaPolicy.addWindow(token);
break;
}
} }
} }
@@ -5982,7 +5975,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return mMagnificationSpec; return mMagnificationSpec;
} }
DisplayArea getAreaForWindowToken(int windowType, Bundle options, DisplayArea findAreaForWindowType(int windowType, Bundle options,
boolean ownerCanManageAppToken, boolean roundedCornerOverlay) { boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
// TODO(b/159767464): figure out how to find an appropriate TDA. // TODO(b/159767464): figure out how to find an appropriate TDA.
if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) { if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
@@ -5994,10 +5987,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) { if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
return getImeContainer(); return getImeContainer();
} }
return mDisplayAreaPolicy.getDisplayAreaForWindowToken(windowType, options, return mDisplayAreaPolicy.findAreaForWindowType(windowType, options,
ownerCanManageAppToken, roundedCornerOverlay); ownerCanManageAppToken, roundedCornerOverlay);
} }
/**
* Finds the {@link DisplayArea} for the {@link WindowToken} to attach to.
* <p>
* Note that the differences between this API and
* {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} is that this API finds a
* {@link DisplayArea} in {@link DisplayContent} level, which may find a {@link DisplayArea}
* from multiple {@link RootDisplayArea RootDisplayAreas} under this {@link DisplayContent}'s
* hierarchy, while {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} finds a
* {@link DisplayArea.Tokens} from a {@link DisplayArea.Tokens} list mapped to window layers.
* </p>
*
* @see DisplayContent#findAreaForTokenInLayer(WindowToken)
*/
DisplayArea findAreaForToken(WindowToken windowToken) {
return findAreaForWindowType(windowToken.getWindowType(), windowToken.mOptions,
windowToken.mOwnerCanManageAppTokens, windowToken.mRoundedCornerOverlay);
}
@Override @Override
DisplayContent asDisplayContent() { DisplayContent asDisplayContent() {
return this; return this;

View File

@@ -101,15 +101,24 @@ class RootDisplayArea extends DisplayArea.Dimmable {
"There is no FEATURE_IME_PLACEHOLDER in this root to place the IME container"); "There is no FEATURE_IME_PLACEHOLDER in this root to place the IME container");
} }
/** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */ /**
* Finds the {@link DisplayArea.Tokens} in {@code mAreaForLayer} that this type of window
* should be attached to.
* <p>
* Note that in most cases, users are expected to call
* {@link DisplayContent#findAreaForToken(WindowToken)} to find a {@link DisplayArea} in
* {@link DisplayContent} level instead of calling this inner method.
* </p>
*/
@Nullable @Nullable
DisplayArea.Tokens findAreaForToken(WindowToken token) { DisplayArea.Tokens findAreaForTokenInLayer(WindowToken token) {
return findAreaForToken(token.windowType, token.mOwnerCanManageAppTokens, return findAreaForWindowTypeInLayer(token.windowType, token.mOwnerCanManageAppTokens,
token.mRoundedCornerOverlay); token.mRoundedCornerOverlay);
} }
/** @see #findAreaForTokenInLayer(WindowToken) */
@Nullable @Nullable
DisplayArea.Tokens findAreaForToken(int windowType, boolean ownerCanManageAppTokens, DisplayArea.Tokens findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens,
boolean roundedCornerOverlay) { boolean roundedCornerOverlay) {
int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
ownerCanManageAppTokens, roundedCornerOverlay); ownerCanManageAppTokens, roundedCornerOverlay);

View File

@@ -201,7 +201,9 @@ class WindowContextListenerController {
return mContainer; return mContainer;
} }
private void updateContainer(WindowContainer newContainer) { private void updateContainer(@NonNull WindowContainer newContainer) {
Objects.requireNonNull(newContainer);
if (mContainer.equals(newContainer)) { if (mContainer.equals(newContainer)) {
return; return;
} }

View File

@@ -2688,7 +2688,7 @@ public class WindowManagerService extends IWindowManager.Stub
} }
// 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.getAreaForWindowToken(type, options, final DisplayArea da = dc.findAreaForWindowType(type, options,
callerCanManageAppTokens, false /* roundedCornerOverlay */); callerCanManageAppTokens, false /* roundedCornerOverlay */);
mWindowContextListenerController.registerWindowContainerListener(clientToken, da, mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
callingUid, type, options); callingUid, type, options);

View File

@@ -268,7 +268,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
// By default, the ime container is attached to DC as defined in DAPolicy. // By default, the ime container is attached to DC as defined in DAPolicy.
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay); assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
assertThat(mDisplay.findAreaForToken(imeToken)).isEqualTo(imeContainer); assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
final WindowState firstActivityWin = final WindowState firstActivityWin =
createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
@@ -290,9 +290,9 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mFirstRoot); assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mFirstRoot);
assertThat(imeContainer.getParent().asDisplayArea().mFeatureId) assertThat(imeContainer.getParent().asDisplayArea().mFeatureId)
.isEqualTo(FEATURE_IME_PLACEHOLDER); .isEqualTo(FEATURE_IME_PLACEHOLDER);
assertThat(mDisplay.findAreaForToken(imeToken)).isNull(); assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isNull();
assertThat(mFirstRoot.findAreaForToken(imeToken)).isEqualTo(imeContainer); assertThat(mFirstRoot.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
assertThat(mSecondRoot.findAreaForToken(imeToken)).isNull(); assertThat(mSecondRoot.findAreaForTokenInLayer(imeToken)).isNull();
// secondActivityWin should be the target // secondActivityWin should be the target
doReturn(false).when(firstActivityWin).canBeImeTarget(); doReturn(false).when(firstActivityWin).canBeImeTarget();
@@ -305,9 +305,9 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondRoot); assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondRoot);
assertThat(imeContainer.getParent().asDisplayArea().mFeatureId) assertThat(imeContainer.getParent().asDisplayArea().mFeatureId)
.isEqualTo(FEATURE_IME_PLACEHOLDER); .isEqualTo(FEATURE_IME_PLACEHOLDER);
assertThat(mDisplay.findAreaForToken(imeToken)).isNull(); assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isNull();
assertThat(mFirstRoot.findAreaForToken(imeToken)).isNull(); assertThat(mFirstRoot.findAreaForTokenInLayer(imeToken)).isNull();
assertThat(mSecondRoot.findAreaForToken(imeToken)).isEqualTo(imeContainer); assertThat(mSecondRoot.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
} }
@Test @Test

View File

@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
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 com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -38,6 +39,7 @@ import androidx.test.filters.SmallTest;
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.Mockito;
/** /**
* Build/Install/Run: * Build/Install/Run:
@@ -185,6 +187,41 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertThat(mController.getContainer(mClientToken)).isEqualTo(da); assertThat(mController.getContainer(mClientToken)).isEqualTo(da);
} }
@Test
public void testImeSwitchDialogWindowTokenRemovedOnDualDisplayContent_ListenToImeContainer() {
// Let the Display to be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
// Create a DisplayContent with dual RootDisplayArea
DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
new DualDisplayAreaGroupPolicyTest.DualDisplayContent
.Builder(mAtm, 1000, 1000).build();
final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer();
// Put the ImeContainer to the first sub-RootDisplayArea
dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer);
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(dualDisplayContent.mFirstRoot);
// Simulate the behavior to show IME switch dialog: its context switches to register to
// context created WindowToken.
WindowToken windowContextCreatedToken = new WindowToken.Builder(mWm, mClientToken,
TYPE_INPUT_METHOD_DIALOG)
.setDisplayContent(dualDisplayContent)
.setFromClientToken(true)
.build();
mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken,
TEST_UID, TYPE_INPUT_METHOD_DIALOG, null /* options */);
assertThat(mController.getContainer(mClientToken)).isEqualTo(windowContextCreatedToken);
// Remove WindowToken
windowContextCreatedToken.removeImmediately();
// Now context should listen to ImeContainer.
assertThat(mController.getContainer(mClientToken)).isEqualTo(imeContainer);
}
private class TestWindowTokenClient extends IWindowToken.Stub { private class TestWindowTokenClient extends IWindowToken.Stub {
private Configuration mConfiguration; private Configuration mConfiguration;
private int mDisplayId; private int mDisplayId;