From 8fb9afd6559357f6583f19b04422312749ca910e Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Fri, 5 May 2023 09:34:05 +0000 Subject: [PATCH] Fix ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest flaky As CL[1] that ImeVisibilityStateComputer leverages onImeInputTargetVisibilityChanged callback to check whether the current IME input target has been covered and became invisible by non-IME focusable layering target. If so then ImeVisibilityStateComputer will hide the IME to address the IME persists on top of layering target but the input target was invisible case. But there is an edge case that the starting window could possible shown and cover the IME input target befor it drawn during animating (e.g. quick-switching app tasks by recents animation) then leads to IME may hidden by CL[1]'s case. Luckly the above scenario has caught by ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest, to fix this flaky case, add the windowType in onImeTargetOverlayVisibilityChanged to give more context for ImeVisibilityStateComputer and ignore the starting window case, since it since it's ok to cover the IME input target window in temporary without affecting the IME visibility. [1]: Ia71b975898efb19439c3a1b1a9a2bdcf21b78650 Fix: 278698977 Test: atest FlickerTest:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest --rerun-until-failure 10 Test: atest ImeVisibilityStateComputerTest Change-Id: I3a950423f73f0a97432589d0a90ac8fd1c84f05e --- .../inputmethod/ImeVisibilityStateComputer.java | 15 +++++++++++---- .../server/wm/ImeTargetChangeListener.java | 3 +++ .../android/server/wm/WindowManagerService.java | 13 ++++++++----- .../java/com/android/server/wm/WindowState.java | 2 +- .../ImeVisibilityStateComputerTest.java | 7 ++++++- .../com/android/server/wm/WindowStateTests.java | 3 ++- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 19d6fa00a270f..f012d917b05ef 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVI import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.SoftInputModeFlags; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString; import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS; @@ -195,19 +196,25 @@ public final class ImeVisibilityStateComputer { mWindowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() { @Override public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, - boolean visible, boolean removed) { - mCurVisibleImeLayeringOverlay = (visible && !removed) ? overlayWindowToken : null; + @WindowManager.LayoutParams.WindowType int windowType, boolean visible, + boolean removed) { + mCurVisibleImeLayeringOverlay = + // Ignoring the starting window since it's ok to cover the IME target + // window in temporary without affecting the IME visibility. + (visible && !removed && windowType != TYPE_APPLICATION_STARTING) + ? overlayWindowToken : null; } @Override public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget, boolean visibleRequested, boolean removed) { - mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; - if (mCurVisibleImeInputTarget == null && mCurVisibleImeLayeringOverlay != null) { + if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed) + && mCurVisibleImeLayeringOverlay != null) { mService.onApplyImeVisibilityFromComputer(imeInputTarget, new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE)); } + mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; } }); } diff --git a/services/core/java/com/android/server/wm/ImeTargetChangeListener.java b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java index 8bc445bc97bb9..88b76aaa69926 100644 --- a/services/core/java/com/android/server/wm/ImeTargetChangeListener.java +++ b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java @@ -18,6 +18,7 @@ package com.android.server.wm; import android.annotation.NonNull; import android.os.IBinder; +import android.view.WindowManager; /** * Callback the IME targeting window visibility change state for @@ -32,11 +33,13 @@ public interface ImeTargetChangeListener { * has changed its window visibility. * * @param overlayWindowToken the window token of the overlay window. + * @param windowType the window type of the overlay window. * @param visible the visibility of the overlay window, {@code true} means visible * and {@code false} otherwise. * @param removed Whether the IME target overlay window has being removed. */ default void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken, + @WindowManager.LayoutParams.WindowType int windowType, boolean visible, boolean removed) { } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8baf048980edd..546be2c0b46c8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -283,6 +283,7 @@ import android.view.SurfaceControlViewHost; import android.view.SurfaceSession; import android.view.TaskTransitionSpec; import android.view.View; +import android.view.ViewDebug; import android.view.WindowContentFrameStats; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; @@ -1811,7 +1812,7 @@ public class WindowManagerService extends IWindowManager.Stub if (imMayMove) { displayContent.computeImeTarget(true /* updateImeTarget */); if (win.isImeOverlayLayeringTarget()) { - dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, win.isVisibleRequestedOrAdding(), false /* removed */); } } @@ -2521,7 +2522,7 @@ public class WindowManagerService extends IWindowManager.Stub final boolean winVisibleChanged = win.isVisible() != wasVisible; if (win.isImeOverlayLayeringTarget() && winVisibleChanged) { - dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, win.isVisible(), false /* removed */); } // Notify listeners about IME input target window visibility change. @@ -3355,15 +3356,17 @@ public class WindowManagerService extends IWindowManager.Stub }); } - void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token, boolean visible, + void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token, + @WindowManager.LayoutParams.WindowType int windowType, boolean visible, boolean removed) { if (mImeTargetChangeListener != null) { if (DEBUG_INPUT_METHOD) { Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token) - + "visible=" + visible + ", removed=" + removed); + + ", type=" + ViewDebug.intToString(WindowManager.LayoutParams.class, + "type", windowType) + "visible=" + visible + ", removed=" + removed); } mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token, - visible, removed)); + windowType, visible, removed)); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 032f08a92abb2..b69262e4b1542 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2349,7 +2349,7 @@ class WindowState extends WindowContainer implements WindowManagerP super.removeImmediately(); if (isImeOverlayLayeringTarget()) { - mWmService.dispatchImeTargetOverlayVisibilityChanged(mClient.asBinder(), + mWmService.dispatchImeTargetOverlayVisibilityChanged(mClient.asBinder(), mAttrs.type, false /* visible */, true /* removed */); } final DisplayContent dc = getDisplayContent(); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java index 3871e1dfd5b0e..a38c1626aea12 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE; @@ -241,8 +242,12 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes final IBinder testImeTargetOverlay = new Binder(); final IBinder testImeInputTarget = new Binder(); + // Simulate a test IME input target was visible. + mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, true, false); + // Simulate a test IME layering target overlay fully occluded the IME input target. - mListener.onImeTargetOverlayVisibilityChanged(testImeTargetOverlay, true, false); + mListener.onImeTargetOverlayVisibilityChanged(testImeTargetOverlay, + TYPE_APPLICATION_OVERLAY, true, false); mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, false, false); final ArgumentCaptor targetCaptor = ArgumentCaptor.forClass(IBinder.class); final ArgumentCaptor resultCaptor = ArgumentCaptor.forClass( diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index ee1afcf318fa5..0ddd3135506e2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -1390,7 +1390,8 @@ public class WindowStateTests extends WindowTestsBase { private boolean mIsVisibleForImeInputTarget; @Override - public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, boolean visible, + public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, + @WindowManager.LayoutParams.WindowType int windowType, boolean visible, boolean removed) { mImeTargetToken = overlayWindowToken; mIsVisibleForImeTargetOverlay = visible;