Migrate IME switch dialog to WindowContext

Bug: 157888351
Test: manual - reproducible steps mentioned in the bug
Test: atest InputMethodMenuControllerTest
Test: atest WindowManagerServiceTests

Change-Id: I8adb0c569ea6c208aee659603ee1d69df9939267
This commit is contained in:
Charles Chen
2020-09-28 11:18:22 +08:00
parent c88452c3ee
commit 9329fbdd72
6 changed files with 191 additions and 4 deletions

View File

@@ -2467,6 +2467,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
"781471998": {
"message": "moveWindowTokenToDisplay: Cannot move to the original display for token: %s",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
"791468751": {
"message": "Pausing rotation during re-position",
"level": "DEBUG",
@@ -2671,6 +2677,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"1033274509": {
"message": "moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
"1040675582": {
"message": "Can't report activity configuration update - client not running, activityRecord=%s",
"level": "WARN",
@@ -3457,6 +3469,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
},
"2060978050": {
"message": "moveWindowTokenToDisplay: Attempted to move token: %s to non-exiting displayId=%d",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
"2081291430": {
"message": "Focus not requested for window=%s because it has no surface",
"level": "DEBUG",

View File

@@ -136,10 +136,7 @@ public class InputMethodMenuController {
}
}
final ActivityThread currentThread = ActivityThread.currentActivityThread();
final Context settingsContext = new ContextThemeWrapper(
currentThread.createSystemUiContext(displayId),
com.android.internal.R.style.Theme_DeviceDefault_Settings);
final Context settingsContext = getSettingsContext(displayId);
mDialogBuilder = new AlertDialog.Builder(settingsContext);
mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
@@ -214,6 +211,32 @@ public class InputMethodMenuController {
}
}
/**
* Returns the window context for IME switch dialogs to receive configuration changes.
*
* This method initializes the window context if it was not initialized. This method also moves
* the context to the targeted display if the current display of context is different than
* the display specified by {@code displayId}.
*/
@VisibleForTesting
public Context getSettingsContext(int displayId) {
if (mSettingsContext == null) {
final Context systemUiContext = ActivityThread.currentActivityThread()
.createSystemUiContext(displayId);
final Context windowContext = systemUiContext.createWindowContext(
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
mSettingsContext = new ContextThemeWrapper(
windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
mSwitchingDialogToken = mSettingsContext.getActivityToken();
}
// 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;
}
private boolean isScreenLocked() {
return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()
&& mKeyguardManager.isKeyguardSecure();

View File

@@ -597,4 +597,9 @@ public abstract class WindowManagerInternal {
* @return The corresponding {@link WindowState#getName()}
*/
public abstract @Nullable String getImeTargetNameForLogging(int displayId);
/**
* Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
*/
public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId);
}

View File

@@ -2723,6 +2723,32 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
/** @see WindowManagerInternal#moveWindowTokenToDisplay(IBinder, int) */
public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
if (dc == null) {
ProtoLog.w(WM_ERROR, "moveWindowTokenToDisplay: Attempted to move token: %s"
+ " to non-exiting displayId=%d", binder, displayId);
return;
}
final WindowToken token = mRoot.getWindowToken(binder);
if (token == null) {
ProtoLog.w(WM_ERROR,
"moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
binder);
return;
}
if (token.getDisplayContent() == dc) {
ProtoLog.w(WM_ERROR,
"moveWindowTokenToDisplay: Cannot move to the original display "
+ "for token: %s", binder);
return;
}
dc.reParentWindowToken(token);
}
}
void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
@NonNull DisplayContent dc) {
if (dc.mWaitingForConfig) {
@@ -7355,6 +7381,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
@Override
public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
WindowManagerService.this.moveWindowTokenToDisplay(binder, displayId);
}
// TODO(multi-display): currently only used by PWM to notify keyguard transitions as well
// forwarding it to SystemUI for synchronizing status and navigation bar animations.
@Override

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import android.content.Context;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
import com.android.server.inputmethod.InputMethodManagerService;
import com.android.server.inputmethod.InputMethodMenuController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
// TODO(b/157888351): Move the test to inputmethod package once we find the way to test the
// scenario there.
/**
* Build/Install/Run:
* atest WmTests:InputMethodMenuControllerTest
*/
@Presubmit
@RunWith(WindowTestRunner.class)
public class InputMethodMenuControllerTest extends WindowTestsBase {
private InputMethodMenuController mController;
@Before
public void setUp() {
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
}
@Test
public void testGetSettingsContext() {
final Context contextOnDefaultDisplay = mController.getSettingsContext(DEFAULT_DISPLAY);
assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
// Obtain the context again and check they are the same instance and match the display
// metrics of the secondary display.
final Context contextOnSecondaryDisplay = mController.getSettingsContext(
mDisplayContent.getDisplayId());
assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mDisplayContent);
assertThat(contextOnDefaultDisplay.getActivityToken())
.isEqualTo(contextOnSecondaryDisplay.getActivityToken());
}
private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
assertThat(context.getDisplayId()).isEqualTo(dc.getDisplayId());
final Rect contextBounds = context.getSystemService(WindowManager.class)
.getMaximumWindowMetrics().getBounds();
final Rect imeContainerBounds = dc.getImeContainer().getBounds();
assertThat(contextBounds).isEqualTo(imeContainerBounds);
}
}

View File

@@ -21,7 +21,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
@@ -32,8 +34,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -176,4 +181,31 @@ public class WindowManagerServiceTests extends WindowTestsBase {
mWm.dismissKeyguard(null, "test-dismiss-keyguard");
verify(mWm.mAtmService.mStackSupervisor).wakeUp(anyString());
}
@Test
public void testMoveWindowTokenToDisplay_NullToken_DoNothing() {
mWm.moveWindowTokenToDisplay(null, mDisplayContent.getDisplayId());
verify(mDisplayContent, never()).reParentWindowToken(any());
}
@Test
public void testMoveWindowTokenToDisplay_SameDisplay_DoNothing() {
final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
mDisplayContent);
mWm.moveWindowTokenToDisplay(windowToken.token, mDisplayContent.getDisplayId());
verify(mDisplayContent, never()).reParentWindowToken(any());
}
@Test
public void testMoveWindowTokenToDisplay_DifferentDisplay_DoMoveDisplay() {
final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
mDisplayContent);
mWm.moveWindowTokenToDisplay(windowToken.token, DEFAULT_DISPLAY);
assertThat(windowToken.getDisplayContent()).isEqualTo(mDefaultDisplay);
}
}