From 6b880493378abf01513b85bc2b00bdb471e3bfbf Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Wed, 11 Aug 2021 22:15:36 +0800 Subject: Apply fixed rotation on IME window if its target is rotated This avoids flickering if the IME target activity and IME show in different orientations. A new callback in WMS will be called if IME window may be shown later, so the IME window can be told to use the rotated configuration if it will show on a rotated activity. Also consolidate multiple invocations from IMMS to WMS to a single call onToggleImeRequested after requesting to show/hide IME. So it doesn't need to acquire/release wm lock serveral times. Bug: 195816362 Test: DisplayContentTests#testApplyTopFixedRotationTransform Change-Id: I0b036594161898f9df6ad71f0adb5fe93880161a --- .../inputmethod/InputMethodManagerService.java | 42 +++++++++---------- .../java/com/android/server/wm/DisplayContent.java | 17 ++++++++ .../server/wm/FadeRotationAnimationController.java | 19 ++++++++- .../android/server/wm/WindowManagerInternal.java | 47 +++++++++++++++------- .../android/server/wm/WindowManagerService.java | 47 +++++++++++++--------- .../com/android/server/wm/DisplayContentTests.java | 11 ++++- 6 files changed, 124 insertions(+), 59 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1531cd589762..fe6f2da32da4 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4122,6 +4122,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + /** Called right after {@link IInputMethod#showSoftInput}. */ + private void onShowHideSoftInputRequested(boolean show, IBinder requestToken, + @SoftInputShowHideReason int reason) { + final WindowManagerInternal.ImeTargetInfo info = + mWindowManagerInternal.onToggleImeRequested( + show, mCurFocusedWindow, requestToken, mCurTokenDisplayId); + mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( + mCurFocusedWindowClient, mCurAttribute, info.focusedWindowName, + mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, + info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName)); + } + @BinderThread private void hideMySoftInput(@NonNull IBinder token, int flags) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput"); @@ -4240,18 +4252,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput(" + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: " + InputMethodDebug.softInputDisplayReasonToString(reason)); + final IBinder token = (IBinder) args.arg3; ((IInputMethod) args.arg1).showSoftInput( - (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2); - mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( - mCurFocusedWindowClient, mCurAttribute, - mWindowManagerInternal.getWindowName(mCurFocusedWindow), - mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, - mWindowManagerInternal.getWindowName( - mShowRequestWindowMap.get(args.arg3)), - mWindowManagerInternal.getImeControlTargetNameForLogging( - mCurTokenDisplayId), - mWindowManagerInternal.getImeTargetNameForLogging( - mCurTokenDisplayId))); + token, msg.arg1 /* flags */, (ResultReceiver) args.arg2); + final IBinder requestToken = mShowRequestWindowMap.get(token); + onShowHideSoftInputRequested(true /* show */, requestToken, reason); } catch (RemoteException e) { } args.recycle(); @@ -4263,18 +4268,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, " + args.arg3 + ", " + args.arg2 + ") for reason: " + InputMethodDebug.softInputDisplayReasonToString(reason)); + final IBinder token = (IBinder) args.arg3; ((IInputMethod)args.arg1).hideSoftInput( - (IBinder) args.arg3, 0, (ResultReceiver)args.arg2); - mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( - mCurFocusedWindowClient, mCurAttribute, - mWindowManagerInternal.getWindowName(mCurFocusedWindow), - mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, - mWindowManagerInternal.getWindowName( - mHideRequestWindowMap.get(args.arg3)), - mWindowManagerInternal.getImeControlTargetNameForLogging( - mCurTokenDisplayId), - mWindowManagerInternal.getImeTargetNameForLogging( - mCurTokenDisplayId))); + token, 0 /* flags */, (ResultReceiver) args.arg2); + final IBinder requestToken = mHideRequestWindowMap.get(token); + onShowHideSoftInputRequested(false /* show */, requestToken, reason); } catch (RemoteException e) { } args.recycle(); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 256d818061ba..db2fb2156c5d 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3789,6 +3789,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate(); } + /** @see WindowManagerInternal#onToggleImeRequested */ + void onShowImeRequested() { + if (mImeLayeringTarget == null || mInputMethodWindow == null) { + return; + } + // If IME window will be shown on the rotated activity, share the transformed state to + // IME window so it can compute rotated frame with rotated configuration. + if (mImeLayeringTarget.mToken.isFixedRotationTransforming()) { + mInputMethodWindow.mToken.linkFixedRotationTransform(mImeLayeringTarget.mToken); + // Hide the window until the rotation is done to avoid intermediate artifacts if the + // parent surface of IME container is changed. + if (mFadeRotationAnimationController != null) { + mFadeRotationAnimationController.hideImmediately(mInputMethodWindow.mToken); + } + } + } + @VisibleForTesting void setImeLayeringTarget(WindowState target) { mImeLayeringTarget = target; diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java index eab3f108d17a..52a7ac75e2dc 100644 --- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java +++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java @@ -42,6 +42,9 @@ public class FadeRotationAnimationController extends FadeAnimationController { /** A runnable which gets called when the {@link #show()} is called. */ private Runnable mOnShowRunnable; + /** Whether to use constant zero alpha animation. */ + private boolean mHideImmediately; + public FadeRotationAnimationController(DisplayContent displayContent) { super(displayContent); mService = displayContent.mWmService; @@ -51,6 +54,10 @@ public class FadeRotationAnimationController extends FadeAnimationController { mService.mWindowPlacerLocked.performSurfacePlacement(); } } : null; + if (mFrozenTimeoutRunnable != null) { + // Hide the windows immediately because screen should have been covered by screenshot. + mHideImmediately = true; + } final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); final WindowState navigationBar = displayPolicy.getNavigationBar(); if (navigationBar != null) { @@ -120,6 +127,15 @@ public class FadeRotationAnimationController extends FadeAnimationController { } } + /** Hides the window immediately until it is drawn in new rotation. */ + void hideImmediately(WindowToken windowToken) { + final boolean original = mHideImmediately; + mHideImmediately = true; + mTargetWindowTokens.add(windowToken); + fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); + mHideImmediately = original; + } + /** Returns {@code true} if the window is handled by this controller. */ boolean isHandledToken(WindowToken token) { return token == mNavBarToken || isTargetToken(token); @@ -145,8 +161,7 @@ public class FadeRotationAnimationController extends FadeAnimationController { @Override public Animation getFadeOutAnimation() { - if (mFrozenTimeoutRunnable != null) { - // Hide the window immediately because screen should have been covered by screenshot. + if (mHideImmediately) { return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */); } return super.getFadeOutAnimation(); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 5bc4d4997f17..132f1392cba7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -676,24 +676,43 @@ public abstract class WindowManagerInternal { public abstract String getWindowName(@NonNull IBinder binder); /** - * Return the window name of IME Insets control target. + * The callback after the request of show/hide input method is sent. * + * @param show Whether to show or hide input method. + * @param focusedToken The token of focused window. + * @param requestToken The token of window who requests the change. * @param displayId The ID of the display which input method is currently focused. - * @return The corresponding {@link WindowState#getName()} + * @return The information of the input method target. */ - public abstract @Nullable String getImeControlTargetNameForLogging(int displayId); + public abstract ImeTargetInfo onToggleImeRequested(boolean show, + @NonNull IBinder focusedToken, @NonNull IBinder requestToken, int displayId); - /** - * Return the current window name of the input method is on top of. - * - * Note that the concept of this window is only reparent the target window behind the input - * method window, it may different with the window which reported by - * {@code InputMethodManagerService#reportStartInput} which has input connection. - * - * @param displayId The ID of the display which input method is currently focused. - * @return The corresponding {@link WindowState#getName()} - */ - public abstract @Nullable String getImeTargetNameForLogging(int displayId); + /** The information of input method target when IME is requested to show or hide. */ + public static class ImeTargetInfo { + public final String focusedWindowName; + public final String requestWindowName; + + /** The window name of IME Insets control target. */ + public final String imeControlTargetName; + + /** + * The current window name of the input method is on top of. + *

+ * Note that the concept of this window is only used to reparent the target window behind + * the input method window, it may be different from the window reported by + * {@link com.android.server.inputmethod.InputMethodManagerService#reportStartInput} which + * has input connection. + */ + public final String imeLayerTargetName; + + public ImeTargetInfo(String focusedWindowName, String requestWindowName, + String imeControlTargetName, String imeLayerTargetName) { + this.focusedWindowName = focusedWindowName; + this.requestWindowName = requestWindowName; + this.imeControlTargetName = imeControlTargetName; + this.imeLayerTargetName = imeLayerTargetName; + } + } /** * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 36809b374525..bad612c11913 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7899,30 +7899,37 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public String getImeControlTargetNameForLogging(int displayId) { + public ImeTargetInfo onToggleImeRequested(boolean show, IBinder focusedToken, + IBinder requestToken, int displayId) { + final String focusedWindowName; + final String requestWindowName; + final String imeControlTargetName; + final String imeLayerTargetName; synchronized (mGlobalLock) { + final WindowState focusedWin = mWindowMap.get(focusedToken); + focusedWindowName = focusedWin != null ? focusedWin.getName() : "null"; + final WindowState requestWin = mWindowMap.get(requestToken); + requestWindowName = requestWin != null ? requestWin.getName() : "null"; final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null) { - return null; - } - final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_CONTROL); - if (target == null) { - return null; - } - final WindowState win = target.getWindow(); - return win != null ? win.getName() : target.toString(); - } - } - - @Override - public String getImeTargetNameForLogging(int displayId) { - synchronized (mGlobalLock) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null || dc.getImeTarget(IME_TARGET_LAYERING) == null) { - return null; + if (dc != null) { + final InsetsControlTarget controlTarget = dc.getImeTarget(IME_TARGET_CONTROL); + if (controlTarget != null) { + final WindowState w = InsetsControlTarget.asWindowOrNull(controlTarget); + imeControlTargetName = w != null ? w.getName() : controlTarget.toString(); + } else { + imeControlTargetName = "null"; + } + final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_LAYERING); + imeLayerTargetName = target != null ? target.getWindow().getName() : "null"; + if (show) { + dc.onShowImeRequested(); + } + } else { + imeControlTargetName = imeLayerTargetName = "no-display"; } - return dc.getImeTarget(IME_TARGET_LAYERING).getWindow().getName(); } + return new ImeTargetInfo(focusedWindowName, requestWindowName, imeControlTargetName, + imeLayerTargetName); } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index d086474aa03b..1389dadc6439 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -135,6 +135,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.WmDisplayCutout; @@ -1301,7 +1302,7 @@ public class DisplayContentTests extends WindowTestsBase { } @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR, - W_NOTIFICATION_SHADE }) + W_INPUT_METHOD, W_NOTIFICATION_SHADE }) @Test public void testApplyTopFixedRotationTransform() { final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); @@ -1405,6 +1406,14 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals("The process should receive rotated configuration for compatibility", expectedProcConfig, app2.app.getConfiguration()); + // If the rotated activity requests to show IME, the IME window should use the + // transformation from activity to lay out in the same orientation. + mDisplayContent.setImeLayeringTarget(mAppWindow); + LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */, + app.token, app.token, mDisplayContent.mDisplayId); + assertTrue(mImeWindow.mToken.hasFixedRotationTransform()); + assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM)); + // The fixed rotation transform can only be finished when all animation finished. doReturn(false).when(app2).isAnimating(anyInt(), anyInt()); mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token); -- cgit v1.2.3-59-g8ed1b