diff options
| author | 2018-11-05 20:32:30 +0800 | |
|---|---|---|
| committer | 2018-11-14 21:41:24 +0800 | |
| commit | ff0ab697f559b3fabe08306ecc422d2127bae92c (patch) | |
| tree | b0d33644af09960036510264efe2af6fa9241ed9 | |
| parent | b2117bbc3aa853cd7c3db3878be52e2137f7f3df (diff) | |
Let IME target window per display.
Since currently in WMS only recorded one IME target window that
the IME window on top of.
For multi-sessions IME on multi-displays scenerio, we need
move WMS.mInputMethodTarget to DisplayContent.
Added unit test testInputMethodTargetUpdateWhenSwitchingOnDisplays
for verifying per IME target window update when switching on displays.
Bug: 117962777
Test: atest ActivityManagerMultiDisplayTests
Test: atest FrameworksServicesTests:ZOrderingTests
Test: atest FrameworksServicesTests:WindowContainerTraversalTests
Test: atest FrameworksServicesTests:DisplayContentTests
Change-Id: I9aa934961fb3975bd2af18599b5a2884387b5007
8 files changed, 85 insertions, 44 deletions
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 9baafcb73279..7600399be93e 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -2010,9 +2010,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree clearThumbnail(); setClientHidden(isHidden() && hiddenRequested); - if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) { - getDisplayContent().computeImeTarget(true /* updateImeTarget */); - } + getDisplayContent().computeImeTargetIfNeeded(this); if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + this + ": reportedVisible=" + reportedVisible diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 348b2afe5bd5..28168550bff3 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -157,8 +157,8 @@ import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; -import android.view.WindowManagerPolicyConstants.PointerEventListener; import android.view.WindowManager; +import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; @@ -496,6 +496,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ WindowState mInputMethodWindow; + /** + * This just indicates the window the input method is on top of, not + * necessarily the window its input is going to. + */ + WindowState mInputMethodTarget; + + /** If true hold off on modifying the animation layer of mInputMethodTarget */ + boolean mInputMethodTargetWaitingAnim; + private final PointerEventDispatcher mPointerEventDispatcher; private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { @@ -699,7 +708,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final Consumer<WindowState> mApplyPostLayoutPolicy = w -> mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(), - mService.mInputMethodTarget); + mInputMethodTarget); private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> { final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; @@ -1917,7 +1926,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * rather than directly above their target. */ private boolean skipTraverseChild(WindowContainer child) { - if (child == mImeWindowsContainers && mService.mInputMethodTarget != null + if (child == mImeWindowsContainers && mInputMethodTarget != null && !hasSplitScreenPrimaryStack()) { return true; } @@ -2793,7 +2802,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (!focusFound) { final WindowState imWindow = mInputMethodWindow; if (imWindow != null) { - final WindowState prevTarget = mService.mInputMethodTarget; + final WindowState prevTarget = mInputMethodTarget; final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/); imWindowChanged = prevTarget != newTarget; @@ -2985,13 +2994,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // There isn't an IME so there shouldn't be a target...That was easy! if (updateImeTarget) { if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " - + mService.mInputMethodTarget + " to null since mInputMethodWindow is null"); - setInputMethodTarget(null, mService.mInputMethodTargetWaitingAnim); + + mInputMethodTarget + " to null since mInputMethodWindow is null"); + setInputMethodTarget(null, mInputMethodTargetWaitingAnim); } return null; } - final WindowState curTarget = mService.mInputMethodTarget; + final WindowState curTarget = mInputMethodTarget; if (!canUpdateImeTarget()) { if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Defer updating IME target"); return curTarget; @@ -3018,7 +3027,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } if (DEBUG_INPUT_METHOD && updateImeTarget) Slog.v(TAG_WM, - "Proposed new IME target: " + target); + "Proposed new IME target: " + target + " for display: " + getDisplayId()); // Now, a special case -- if the last target's window is in the process of exiting, but // not removed, and the new target is home, keep on the last target to avoid flicker. @@ -3039,7 +3048,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to null." + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : "")); - setInputMethodTarget(null, mService.mInputMethodTargetWaitingAnim); + setInputMethodTarget(null, mInputMethodTargetWaitingAnim); } return null; @@ -3078,14 +3087,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return target; } + /** + * Calling {@link #computeImeTarget(boolean)} to update the input method target window in + * the candidate app window token if needed. + */ + void computeImeTargetIfNeeded(AppWindowToken candidate) { + if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == candidate) { + computeImeTarget(true /* updateImeTarget */); + } + } + private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) { - if (target == mService.mInputMethodTarget - && mService.mInputMethodTargetWaitingAnim == targetWaitingAnim) { + if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) { return; } - mService.mInputMethodTarget = target; - mService.mInputMethodTargetWaitingAnim = targetWaitingAnim; + mInputMethodTarget = target; + mInputMethodTargetWaitingAnim = targetWaitingAnim; assignWindowLayers(false /* setLayoutNeeded */); } @@ -4474,7 +4492,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTaskStackContainers.assignLayer(t, 1); mAboveAppWindowsContainers.assignLayer(t, 2); - WindowState imeTarget = mService.mInputMethodTarget; + final WindowState imeTarget = mInputMethodTarget; boolean needAssignIme = true; // In the case where we have an IME target that is not in split-screen diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a641f75221d3..628d98f334f3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -609,13 +609,6 @@ public class WindowManagerService extends IWindowManager.Stub */ final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper()); - /** This just indicates the window the input method is on top of, not - * necessarily the window its input is going to. */ - WindowState mInputMethodTarget = null; - - /** If true hold off on modifying the animation layer of mInputMethodTarget */ - boolean mInputMethodTargetWaitingAnim; - boolean mHardKeyboardAvailable; WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; SettingsObserver mSettingsObserver; @@ -5920,9 +5913,13 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mGlobalConfiguration="); pw.println(mRoot.getConfiguration()); pw.print(" mHasPermanentDpad="); pw.println(mHasPermanentDpad); mRoot.dumpTopFocusedDisplayId(pw); - if (mInputMethodTarget != null) { - pw.print(" mInputMethodTarget="); pw.println(mInputMethodTarget); - } + mRoot.forAllDisplays(dc -> { + final WindowState inputMethodTarget = dc.mInputMethodTarget; + if (inputMethodTarget != null) { + pw.print(" mInputMethodTarget in display# "); pw.print(dc.getDisplayId()); + pw.print(' '); pw.println(inputMethodTarget); + } + }); pw.print(" mInTouchMode="); pw.println(mInTouchMode); pw.print(" mLastDisplayFreezeDuration="); TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 99f65c327375..aed8fa3fdbcd 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4476,8 +4476,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean needsZBoost() { - if (mIsImWindow && mService.mInputMethodTarget != null) { - final AppWindowToken appToken = mService.mInputMethodTarget.mAppToken; + final WindowState inputMethodTarget = getDisplayContent().mInputMethodTarget; + if (mIsImWindow && inputMethodTarget != null) { + final AppWindowToken appToken = inputMethodTarget.mAppToken; if (appToken != null) { return appToken.needsZBoost(); } @@ -4607,7 +4608,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Likewise if we share a token with the Input method target and are ordered // above it but not necessarily a child (e.g. a Dialog) then we also need // this promotion. - final WindowState imeTarget = mService.mInputMethodTarget; + final WindowState imeTarget = getDisplayContent().mInputMethodTarget; boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0; return inTokenWithAndAboveImeTarget; @@ -4684,7 +4685,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override public boolean isInputMethodTarget() { - return mService.mInputMethodTarget == this; + return getDisplayContent().mInputMethodTarget == this; } long getFrameNumber() { diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index dd374e959a63..ea93326abf3b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -40,6 +40,7 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doNothing; @@ -108,7 +109,7 @@ public class DisplayContentTests extends WindowTestsBase { final WindowState imeAppTarget = createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); - mWm.mInputMethodTarget = imeAppTarget; + mDisplayContent.mInputMethodTarget = imeAppTarget; assertForAllWindowsOrder(Arrays.asList( mWallpaperWindow, @@ -124,8 +125,8 @@ public class DisplayContentTests extends WindowTestsBase { } @Test - public void testForAllWindows_WithChildWindowImeTarget() { - mWm.mInputMethodTarget = mChildAppWindowAbove; + public void testForAllWindows_WithChildWindowImeTarget() throws Exception { + mDisplayContent.mInputMethodTarget = mChildAppWindowAbove; assertForAllWindowsOrder(Arrays.asList( mWallpaperWindow, @@ -140,8 +141,8 @@ public class DisplayContentTests extends WindowTestsBase { } @Test - public void testForAllWindows_WithStatusBarImeTarget() { - mWm.mInputMethodTarget = mStatusBarWindow; + public void testForAllWindows_WithStatusBarImeTarget() throws Exception { + mDisplayContent.mInputMethodTarget = mStatusBarWindow; assertForAllWindowsOrder(Arrays.asList( mWallpaperWindow, @@ -568,6 +569,32 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); } + @Test + public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() { + final DisplayContent newDisplay = createNewDisplay(); + + final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); + final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1"); + appWin.setHasSurface(true); + appWin1.setHasSurface(true); + + // Set current input method window on default display, make sure the input method target + // is appWin & null on the other display. + mDisplayContent.setInputMethodWindowLocked(mImeWindow); + newDisplay.setInputMethodWindowLocked(null); + assertTrue("appWin should be IME target window", + appWin.equals(mDisplayContent.mInputMethodTarget)); + assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget); + + // Switch input method window on new display & make sure the input method target also + // switched as expected. + newDisplay.setInputMethodWindowLocked(mImeWindow); + mDisplayContent.setInputMethodWindowLocked(null); + assertTrue("appWin1 should be IME target window", + appWin1.equals(newDisplay.mInputMethodTarget)); + assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java index 2b8b93428701..fcde08e18a6f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java @@ -52,7 +52,7 @@ public class WindowContainerTraversalTests extends WindowTestsBase { WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); - mWm.mInputMethodTarget = splitScreenWindow; + mDisplayContent.mInputMethodTarget = splitScreenWindow; Consumer<WindowState> c = mock(Consumer.class); mDisplayContent.forAllWindows(c, false); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 945cbb91b029..990ef54ab711 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -192,7 +192,7 @@ class WindowTestsBase { mWm.getDefaultDisplayContentLocked().mAppTransition .removeAppTransitionTimeoutCallbacks(); mWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT); - mWm.mInputMethodTarget = null; + mDisplayContent.mInputMethodTarget = null; } // Wait until everything is really cleaned up. diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index 32e4e0265193..0ff2a0d5a8fa 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -211,7 +211,7 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testAssignWindowLayers_ForImeWithNoTarget() { - mWm.mInputMethodTarget = null; + mDisplayContent.mInputMethodTarget = null; mDisplayContent.assignChildLayers(mTransaction); // The Ime has an higher base layer than app windows and lower base layer than system @@ -229,7 +229,7 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testAssignWindowLayers_ForImeWithAppTarget() { final WindowState imeAppTarget = createWindow("imeAppTarget"); - mWm.mInputMethodTarget = imeAppTarget; + mDisplayContent.mInputMethodTarget = imeAppTarget; mDisplayContent.assignChildLayers(mTransaction); @@ -255,7 +255,7 @@ public class ZOrderingTests extends WindowTestsBase { TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken, "imeAppTargetChildBelowWindow"); - mWm.mInputMethodTarget = imeAppTarget; + mDisplayContent.mInputMethodTarget = imeAppTarget; mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for child windows that are z-ordered above it @@ -277,7 +277,7 @@ public class ZOrderingTests extends WindowTestsBase { final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); - mWm.mInputMethodTarget = imeAppTarget; + mDisplayContent.mInputMethodTarget = imeAppTarget; mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for non-fullscreen app window above it and @@ -300,7 +300,7 @@ public class ZOrderingTests extends WindowTestsBase { mDisplayContent, "imeSystemOverlayTarget", true /* ownerCanAddInternalSystemWindow */); - mWm.mInputMethodTarget = imeSystemOverlayTarget; + mDisplayContent.mInputMethodTarget = imeSystemOverlayTarget; mDisplayContent.assignChildLayers(mTransaction); // The IME target base layer is higher than all window except for the nav bar window, so the @@ -323,7 +323,7 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testAssignWindowLayers_ForStatusBarImeTarget() { - mWm.mInputMethodTarget = mStatusBarWindow; + mDisplayContent.mInputMethodTarget = mStatusBarWindow; mDisplayContent.assignChildLayers(mTransaction); assertWindowHigher(mImeWindow, mChildAppWindowAbove); |