Loading services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +69 −12 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_ import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget; import android.accessibilityservice.AccessibilityService; import android.annotation.AnyThread; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UserIdInt; Loading Loading @@ -83,6 +84,7 @@ public final class ImeVisibilityStateComputer { * A map used to track the requested IME target window and its state. The key represents the * token of the window and the value is the corresponding IME window state. */ @GuardedBy("ImfLock.class") private final WeakHashMap<IBinder, ImeTargetWindowState> mRequestWindowStateMap = new WeakHashMap<>(); Loading @@ -93,6 +95,7 @@ public final class ImeVisibilityStateComputer { * @see InputMethodManager#HIDE_IMPLICIT_ONLY that system will not hide IME when the value is * {@code true}. */ @GuardedBy("ImfLock.class") boolean mRequestedShowExplicitly; /** Loading @@ -101,23 +104,28 @@ public final class ImeVisibilityStateComputer { * @see InputMethodManager#SHOW_FORCED * @see InputMethodManager#HIDE_NOT_ALWAYS */ @GuardedBy("ImfLock.class") boolean mShowForced; /** * Set if we last told the input method to show itself. */ @GuardedBy("ImfLock.class") private boolean mInputShown; /** * Set if we called * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}. */ @GuardedBy("ImfLock.class") private boolean mRequestedImeScreenshot; /** The window token of the current visible IME layering target overlay. */ @GuardedBy("ImfLock.class") private IBinder mCurVisibleImeLayeringOverlay; /** The window token of the current visible IME input target. */ @GuardedBy("ImfLock.class") private IBinder mCurVisibleImeInputTarget; /** Represent the invalid IME visibility state */ Loading Loading @@ -203,25 +211,32 @@ public final class ImeVisibilityStateComputer { public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, @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) final var overlay = (visible && !removed && windowType != TYPE_APPLICATION_STARTING) ? overlayWindowToken : null; synchronized (ImfLock.class) { mCurVisibleImeLayeringOverlay = overlay; } } @Override public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget, boolean visibleRequested, boolean removed) { if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed) synchronized (ImfLock.class) { if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed) && mCurVisibleImeLayeringOverlay != null) { final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE; final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */); mService.onApplyImeVisibilityFromComputer(imeInputTarget, statsToken, mService.onApplyImeVisibilityFromComputerLocked(imeInputTarget, statsToken, new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason)); } mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; } } }); } Loading @@ -232,6 +247,7 @@ public final class ImeVisibilityStateComputer { * @param statsToken The token tracking the current IME request. * @return {@code true} when the show request can proceed. */ @GuardedBy("ImfLock.class") boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int showFlags) { if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) { Loading @@ -258,6 +274,7 @@ public final class ImeVisibilityStateComputer { * @param statsToken The token tracking the current IME request. * @return {@code true} when the hide request can proceed. */ @GuardedBy("ImfLock.class") boolean canHideIme(@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int hideFlags) { if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 Loading @@ -279,6 +296,7 @@ public final class ImeVisibilityStateComputer { * Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags} * to {@link InputMethod.ShowFlags}. */ @GuardedBy("ImfLock.class") @InputMethod.ShowFlags int getShowFlagsForInputMethodServiceOnly() { int flags = 0; Loading @@ -294,6 +312,7 @@ public final class ImeVisibilityStateComputer { * Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags} * to {@link InputMethodManager.ShowFlags}. */ @GuardedBy("ImfLock.class") @InputMethodManager.ShowFlags int getShowFlags() { int flags = 0; Loading @@ -305,12 +324,14 @@ public final class ImeVisibilityStateComputer { return flags; } @GuardedBy("ImfLock.class") void clearImeShowFlags() { mRequestedShowExplicitly = false; mShowForced = false; mInputShown = false; } @GuardedBy("ImfLock.class") int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) { final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator); state.setImeDisplayId(displayToShowIme); Loading @@ -328,6 +349,7 @@ public final class ImeVisibilityStateComputer { * visibility state, it could be {@link #STATE_SHOW_IME} or * {@link #STATE_HIDE_IME}. */ @GuardedBy("ImfLock.class") void requestImeVisibility(IBinder windowToken, boolean showIme) { ImeTargetWindowState state = getOrCreateWindowState(windowToken); if (!mPolicy.mPendingA11yRequestingHideKeyboard) { Loading @@ -343,6 +365,7 @@ public final class ImeVisibilityStateComputer { setWindowStateInner(windowToken, state); } @GuardedBy("ImfLock.class") ImeTargetWindowState getOrCreateWindowState(IBinder windowToken) { ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); if (state == null) { Loading @@ -351,11 +374,13 @@ public final class ImeVisibilityStateComputer { return state; } @GuardedBy("ImfLock.class") ImeTargetWindowState getWindowStateOrNull(IBinder windowToken) { ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); return state; } @GuardedBy("ImfLock.class") void setWindowState(IBinder windowToken, @NonNull ImeTargetWindowState newState) { final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); if (state != null && newState.hasEditorFocused() Loading @@ -367,6 +392,7 @@ public final class ImeVisibilityStateComputer { setWindowStateInner(windowToken, newState); } @GuardedBy("ImfLock.class") private void setWindowStateInner(IBinder windowToken, @NonNull ImeTargetWindowState newState) { if (DEBUG) Slog.d(TAG, "setWindowStateInner, windowToken=" + windowToken + ", state=" + newState); Loading @@ -391,6 +417,7 @@ public final class ImeVisibilityStateComputer { } } @GuardedBy("ImfLock.class") ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible) { // TODO: Output the request IME visibility state according to the requested window state final int softInputVisibility = state.mSoftInputModeState & SOFT_INPUT_MASK_STATE; Loading Loading @@ -540,6 +567,7 @@ public final class ImeVisibilityStateComputer { return null; } @GuardedBy("ImfLock.class") @VisibleForTesting ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) { final ImeTargetWindowState state = getWindowStateOrNull(windowToken); Loading Loading @@ -568,6 +596,7 @@ public final class ImeVisibilityStateComputer { return userData.mImeBindingState.mFocusedWindow; } @GuardedBy("ImfLock.class") IBinder getWindowTokenFrom(ImeTargetWindowState windowState) { for (IBinder windowToken : mRequestWindowStateMap.keySet()) { final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); Loading @@ -578,6 +607,7 @@ public final class ImeVisibilityStateComputer { return null; } @GuardedBy("ImfLock.class") boolean shouldRestoreImeVisibility(@NonNull ImeTargetWindowState state) { final int softInputMode = state.getSoftInputModeState(); switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { Loading @@ -591,14 +621,17 @@ public final class ImeVisibilityStateComputer { return mWindowManagerInternal.shouldRestoreImeVisibility(getWindowTokenFrom(state)); } @GuardedBy("ImfLock.class") boolean isInputShown() { return mInputShown; } @GuardedBy("ImfLock.class") void setInputShown(boolean inputShown) { mInputShown = inputShown; } @GuardedBy("ImfLock.class") void dumpDebug(ProtoOutputStream proto, long fieldId) { proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly); proto.write(SHOW_FORCED, mShowForced); Loading @@ -607,6 +640,7 @@ public final class ImeVisibilityStateComputer { proto.write(INPUT_SHOWN, mInputShown); } @GuardedBy("ImfLock.class") void dump(@NonNull PrintWriter pw, @NonNull String prefix) { final Printer p = new PrintWriterPrinter(pw); p.println(prefix + "mRequestedShowExplicitly=" + mRequestedShowExplicitly Loading @@ -629,12 +663,14 @@ public final class ImeVisibilityStateComputer { * * This prevents the IME from showing when it otherwise may have shown. */ @GuardedBy("ImfLock.class") private boolean mImeHiddenByDisplayPolicy; /** * Set when the accessibility service requests to hide IME by * {@link AccessibilityService.SoftKeyboardController#setShowMode} */ @GuardedBy("ImfLock.class") private boolean mA11yRequestingNoSoftKeyboard; /** Loading @@ -643,16 +679,20 @@ public final class ImeVisibilityStateComputer { * {@link android.provider.Settings.Secure#ACCESSIBILITY_SOFT_KEYBOARD_MODE} without * changing the requested IME visible state. */ @GuardedBy("ImfLock.class") private boolean mPendingA11yRequestingHideKeyboard; @GuardedBy("ImfLock.class") void setImeHiddenByDisplayPolicy(boolean hideIme) { mImeHiddenByDisplayPolicy = hideIme; } @GuardedBy("ImfLock.class") boolean isImeHiddenByDisplayPolicy() { return mImeHiddenByDisplayPolicy; } @GuardedBy("ImfLock.class") void setA11yRequestNoSoftKeyboard(int keyboardShowMode) { mA11yRequestingNoSoftKeyboard = (keyboardShowMode & AccessibilityService.SHOW_MODE_MASK) == SHOW_MODE_HIDDEN; Loading @@ -661,11 +701,13 @@ public final class ImeVisibilityStateComputer { } } @GuardedBy("ImfLock.class") boolean isA11yRequestNoSoftKeyboard() { return mA11yRequestingNoSoftKeyboard; } } @GuardedBy("ImfLock.class") ImeVisibilityPolicy getImePolicy() { return mPolicy; } Loading Loading @@ -721,63 +763,78 @@ public final class ImeVisibilityStateComputer { /** * Set if the client has asked for the input method to be shown. */ @GuardedBy("ImfLock.class") private boolean mRequestedImeVisible; /** * A identifier for knowing the requester of {@link InputMethodManager#showSoftInput} or * {@link InputMethodManager#hideSoftInputFromWindow}. */ @GuardedBy("ImfLock.class") private IBinder mRequestImeToken; /** * The IME target display id for which the latest startInput was called. */ @GuardedBy("ImfLock.class") private int mImeDisplayId = DEFAULT_DISPLAY; @AnyThread boolean hasImeFocusChanged() { return mImeFocusChanged; } @AnyThread boolean hasEditorFocused() { return mHasFocusedEditor; } @AnyThread boolean isStartInputByGainFocus() { return mIsStartInputByGainFocus; } @AnyThread int getSoftInputModeState() { return mSoftInputModeState; } @AnyThread int getWindowFlags() { return mWindowFlags; } @AnyThread int getToolType() { return mToolType; } @GuardedBy("ImfLock.class") private void setImeDisplayId(int imeDisplayId) { mImeDisplayId = imeDisplayId; } @GuardedBy("ImfLock.class") int getImeDisplayId() { return mImeDisplayId; } @GuardedBy("ImfLock.class") private void setRequestedImeVisible(boolean requestedImeVisible) { mRequestedImeVisible = requestedImeVisible; } @GuardedBy("ImfLock.class") boolean isRequestedImeVisible() { return mRequestedImeVisible; } @GuardedBy("ImfLock.class") void setRequestImeToken(IBinder token) { mRequestImeToken = token; } @GuardedBy("ImfLock.class") IBinder getRequestImeToken() { return mRequestImeToken; } Loading services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +7 −8 Original line number Diff line number Diff line Loading @@ -4862,15 +4862,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return mVisibilityApplier; } void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @NonNull ImeVisibilityResult result) { synchronized (ImfLock.class) { @GuardedBy("ImfLock.class") void onApplyImeVisibilityFromComputerLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @NonNull ImeVisibilityResult result) { // TODO(b/305849394): Infer userId from windowToken final int userId = mCurrentUserId; mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(), result.getReason(), userId); } } @GuardedBy("ImfLock.class") void setEnabledSessionLocked(SessionState session, @NonNull UserData userData) { Loading services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java +183 −155 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import android.view.inputmethod.InputMethodManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.annotations.GuardedBy; import com.android.server.wm.ImeTargetChangeListener; import com.android.server.wm.WindowManagerInternal; Loading Loading @@ -93,6 +94,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes @Test public void testRequestImeVisibility_showImplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_IMPLICIT); Loading @@ -106,9 +108,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isFalse(); } } @Test public void testRequestImeVisibility_showExplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); mComputer.requestImeVisibility(mWindowToken, res); Loading @@ -121,6 +125,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isTrue(); } } /** * This checks that the state after an explicit show request does not get reset during Loading @@ -128,6 +133,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes */ @Test public void testRequestImeVisibility_showExplicit_thenShowImplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); assertThat(mComputer.mRequestedShowExplicitly).isTrue(); Loading @@ -135,6 +141,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT); assertThat(mComputer.mRequestedShowExplicitly).isTrue(); } } /** * This checks that the state after a forced show request does not get reset during Loading @@ -142,6 +149,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes */ @Test public void testRequestImeVisibility_showForced_thenShowExplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_FORCED); assertThat(mComputer.mShowForced).isTrue(); Loading @@ -149,9 +157,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); assertThat(mComputer.mShowForced).isTrue(); } } @Test public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() { synchronized (ImfLock.class) { // Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN); Loading @@ -168,9 +178,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isFalse(); } } @Test public void testRequestImeVisibility_showImplicit_imeHiddenPolicy() { synchronized (ImfLock.class) { // Precondition: set IME hidden display policy before calling showSoftInput mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); Loading @@ -187,9 +199,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isFalse(); } } @Test public void testRequestImeVisibility_hideNotAlways() { synchronized (ImfLock.class) { // Precondition: ensure IME has shown before hiding request. mComputer.setInputShown(true); Loading @@ -204,9 +218,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED); assertThat(state.isRequestedImeVisible()).isFalse(); } } @Test public void testComputeImeDisplayId() { synchronized (ImfLock.class) { final ImeTargetWindowState state = mComputer.getOrCreateWindowState(mWindowToken); mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL; Loading @@ -228,9 +244,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse(); assertThat(state.getImeDisplayId()).isEqualTo(FALLBACK_DISPLAY_ID); } } @Test public void testComputeState_lastImeRequestedVisible_preserved_When_StateUnChanged() { synchronized (ImfLock.class) { // Assume the last IME targeted window has requested IME visible final IBinder lastImeTargetWindowToken = new Binder(); mInputMethodManagerService.mLastImeTargetWindow = lastImeTargetWindowToken; Loading @@ -241,14 +259,17 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes // Verify when focusing the next window with STATE_UNCHANGED flag, the last IME // visibility state will be preserved to the current window state. final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState(mWindowToken); final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState( mWindowToken); mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */); assertThat(stateWithUnChangedFlag.isRequestedImeVisible()).isEqualTo( lastState.isRequestedImeVisible()); } } @Test public void testOnInteractiveChanged() { synchronized (ImfLock.class) { mComputer.getOrCreateWindowState(mWindowToken); // Precondition: ensure IME has shown before hiding request. mComputer.requestImeVisibility(mWindowToken, true); Loading @@ -259,7 +280,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mWindowToken, true /* interactive */); assertThat(result).isNull(); // Show the IME screenshot to capture the last IME visible state when the device inactive. // Show the IME screenshot to capture the last IME visible state when the device // inactive. result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */); assertThat(result).isNotNull(); assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT); Loading @@ -271,9 +293,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT); assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS); } } @Test public void testOnApplyImeVisibilityFromComputer() { synchronized (ImfLock.class) { final IBinder testImeTargetOverlay = new Binder(); final IBinder testImeInputTarget = new Binder(); Loading @@ -287,8 +311,10 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes final ArgumentCaptor<IBinder> targetCaptor = ArgumentCaptor.forClass(IBinder.class); final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass( ImeVisibilityResult.class); verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(), notNull() /* statsToken */, resultCaptor.capture()); synchronized (ImfLock.class) { verify(mInputMethodManagerService).onApplyImeVisibilityFromComputerLocked( targetCaptor.capture(), notNull() /* statsToken */, resultCaptor.capture()); } final IBinder imeInputTarget = targetCaptor.getValue(); final ImeVisibilityResult result = resultCaptor.getValue(); Loading @@ -297,7 +323,9 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(result.getState()).isEqualTo(STATE_HIDE_IME_EXPLICIT); assertThat(result.getReason()).isEqualTo(HIDE_WHEN_INPUT_TARGET_INVISIBLE); } } @GuardedBy("ImfLock.class") private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) { final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED, 0, true, true, true); Loading Loading
services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +69 −12 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_ import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget; import android.accessibilityservice.AccessibilityService; import android.annotation.AnyThread; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UserIdInt; Loading Loading @@ -83,6 +84,7 @@ public final class ImeVisibilityStateComputer { * A map used to track the requested IME target window and its state. The key represents the * token of the window and the value is the corresponding IME window state. */ @GuardedBy("ImfLock.class") private final WeakHashMap<IBinder, ImeTargetWindowState> mRequestWindowStateMap = new WeakHashMap<>(); Loading @@ -93,6 +95,7 @@ public final class ImeVisibilityStateComputer { * @see InputMethodManager#HIDE_IMPLICIT_ONLY that system will not hide IME when the value is * {@code true}. */ @GuardedBy("ImfLock.class") boolean mRequestedShowExplicitly; /** Loading @@ -101,23 +104,28 @@ public final class ImeVisibilityStateComputer { * @see InputMethodManager#SHOW_FORCED * @see InputMethodManager#HIDE_NOT_ALWAYS */ @GuardedBy("ImfLock.class") boolean mShowForced; /** * Set if we last told the input method to show itself. */ @GuardedBy("ImfLock.class") private boolean mInputShown; /** * Set if we called * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}. */ @GuardedBy("ImfLock.class") private boolean mRequestedImeScreenshot; /** The window token of the current visible IME layering target overlay. */ @GuardedBy("ImfLock.class") private IBinder mCurVisibleImeLayeringOverlay; /** The window token of the current visible IME input target. */ @GuardedBy("ImfLock.class") private IBinder mCurVisibleImeInputTarget; /** Represent the invalid IME visibility state */ Loading Loading @@ -203,25 +211,32 @@ public final class ImeVisibilityStateComputer { public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, @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) final var overlay = (visible && !removed && windowType != TYPE_APPLICATION_STARTING) ? overlayWindowToken : null; synchronized (ImfLock.class) { mCurVisibleImeLayeringOverlay = overlay; } } @Override public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget, boolean visibleRequested, boolean removed) { if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed) synchronized (ImfLock.class) { if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed) && mCurVisibleImeLayeringOverlay != null) { final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE; final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */); mService.onApplyImeVisibilityFromComputer(imeInputTarget, statsToken, mService.onApplyImeVisibilityFromComputerLocked(imeInputTarget, statsToken, new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason)); } mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; } } }); } Loading @@ -232,6 +247,7 @@ public final class ImeVisibilityStateComputer { * @param statsToken The token tracking the current IME request. * @return {@code true} when the show request can proceed. */ @GuardedBy("ImfLock.class") boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int showFlags) { if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) { Loading @@ -258,6 +274,7 @@ public final class ImeVisibilityStateComputer { * @param statsToken The token tracking the current IME request. * @return {@code true} when the hide request can proceed. */ @GuardedBy("ImfLock.class") boolean canHideIme(@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int hideFlags) { if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 Loading @@ -279,6 +296,7 @@ public final class ImeVisibilityStateComputer { * Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags} * to {@link InputMethod.ShowFlags}. */ @GuardedBy("ImfLock.class") @InputMethod.ShowFlags int getShowFlagsForInputMethodServiceOnly() { int flags = 0; Loading @@ -294,6 +312,7 @@ public final class ImeVisibilityStateComputer { * Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags} * to {@link InputMethodManager.ShowFlags}. */ @GuardedBy("ImfLock.class") @InputMethodManager.ShowFlags int getShowFlags() { int flags = 0; Loading @@ -305,12 +324,14 @@ public final class ImeVisibilityStateComputer { return flags; } @GuardedBy("ImfLock.class") void clearImeShowFlags() { mRequestedShowExplicitly = false; mShowForced = false; mInputShown = false; } @GuardedBy("ImfLock.class") int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) { final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator); state.setImeDisplayId(displayToShowIme); Loading @@ -328,6 +349,7 @@ public final class ImeVisibilityStateComputer { * visibility state, it could be {@link #STATE_SHOW_IME} or * {@link #STATE_HIDE_IME}. */ @GuardedBy("ImfLock.class") void requestImeVisibility(IBinder windowToken, boolean showIme) { ImeTargetWindowState state = getOrCreateWindowState(windowToken); if (!mPolicy.mPendingA11yRequestingHideKeyboard) { Loading @@ -343,6 +365,7 @@ public final class ImeVisibilityStateComputer { setWindowStateInner(windowToken, state); } @GuardedBy("ImfLock.class") ImeTargetWindowState getOrCreateWindowState(IBinder windowToken) { ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); if (state == null) { Loading @@ -351,11 +374,13 @@ public final class ImeVisibilityStateComputer { return state; } @GuardedBy("ImfLock.class") ImeTargetWindowState getWindowStateOrNull(IBinder windowToken) { ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); return state; } @GuardedBy("ImfLock.class") void setWindowState(IBinder windowToken, @NonNull ImeTargetWindowState newState) { final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); if (state != null && newState.hasEditorFocused() Loading @@ -367,6 +392,7 @@ public final class ImeVisibilityStateComputer { setWindowStateInner(windowToken, newState); } @GuardedBy("ImfLock.class") private void setWindowStateInner(IBinder windowToken, @NonNull ImeTargetWindowState newState) { if (DEBUG) Slog.d(TAG, "setWindowStateInner, windowToken=" + windowToken + ", state=" + newState); Loading @@ -391,6 +417,7 @@ public final class ImeVisibilityStateComputer { } } @GuardedBy("ImfLock.class") ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible) { // TODO: Output the request IME visibility state according to the requested window state final int softInputVisibility = state.mSoftInputModeState & SOFT_INPUT_MASK_STATE; Loading Loading @@ -540,6 +567,7 @@ public final class ImeVisibilityStateComputer { return null; } @GuardedBy("ImfLock.class") @VisibleForTesting ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) { final ImeTargetWindowState state = getWindowStateOrNull(windowToken); Loading Loading @@ -568,6 +596,7 @@ public final class ImeVisibilityStateComputer { return userData.mImeBindingState.mFocusedWindow; } @GuardedBy("ImfLock.class") IBinder getWindowTokenFrom(ImeTargetWindowState windowState) { for (IBinder windowToken : mRequestWindowStateMap.keySet()) { final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); Loading @@ -578,6 +607,7 @@ public final class ImeVisibilityStateComputer { return null; } @GuardedBy("ImfLock.class") boolean shouldRestoreImeVisibility(@NonNull ImeTargetWindowState state) { final int softInputMode = state.getSoftInputModeState(); switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { Loading @@ -591,14 +621,17 @@ public final class ImeVisibilityStateComputer { return mWindowManagerInternal.shouldRestoreImeVisibility(getWindowTokenFrom(state)); } @GuardedBy("ImfLock.class") boolean isInputShown() { return mInputShown; } @GuardedBy("ImfLock.class") void setInputShown(boolean inputShown) { mInputShown = inputShown; } @GuardedBy("ImfLock.class") void dumpDebug(ProtoOutputStream proto, long fieldId) { proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly); proto.write(SHOW_FORCED, mShowForced); Loading @@ -607,6 +640,7 @@ public final class ImeVisibilityStateComputer { proto.write(INPUT_SHOWN, mInputShown); } @GuardedBy("ImfLock.class") void dump(@NonNull PrintWriter pw, @NonNull String prefix) { final Printer p = new PrintWriterPrinter(pw); p.println(prefix + "mRequestedShowExplicitly=" + mRequestedShowExplicitly Loading @@ -629,12 +663,14 @@ public final class ImeVisibilityStateComputer { * * This prevents the IME from showing when it otherwise may have shown. */ @GuardedBy("ImfLock.class") private boolean mImeHiddenByDisplayPolicy; /** * Set when the accessibility service requests to hide IME by * {@link AccessibilityService.SoftKeyboardController#setShowMode} */ @GuardedBy("ImfLock.class") private boolean mA11yRequestingNoSoftKeyboard; /** Loading @@ -643,16 +679,20 @@ public final class ImeVisibilityStateComputer { * {@link android.provider.Settings.Secure#ACCESSIBILITY_SOFT_KEYBOARD_MODE} without * changing the requested IME visible state. */ @GuardedBy("ImfLock.class") private boolean mPendingA11yRequestingHideKeyboard; @GuardedBy("ImfLock.class") void setImeHiddenByDisplayPolicy(boolean hideIme) { mImeHiddenByDisplayPolicy = hideIme; } @GuardedBy("ImfLock.class") boolean isImeHiddenByDisplayPolicy() { return mImeHiddenByDisplayPolicy; } @GuardedBy("ImfLock.class") void setA11yRequestNoSoftKeyboard(int keyboardShowMode) { mA11yRequestingNoSoftKeyboard = (keyboardShowMode & AccessibilityService.SHOW_MODE_MASK) == SHOW_MODE_HIDDEN; Loading @@ -661,11 +701,13 @@ public final class ImeVisibilityStateComputer { } } @GuardedBy("ImfLock.class") boolean isA11yRequestNoSoftKeyboard() { return mA11yRequestingNoSoftKeyboard; } } @GuardedBy("ImfLock.class") ImeVisibilityPolicy getImePolicy() { return mPolicy; } Loading Loading @@ -721,63 +763,78 @@ public final class ImeVisibilityStateComputer { /** * Set if the client has asked for the input method to be shown. */ @GuardedBy("ImfLock.class") private boolean mRequestedImeVisible; /** * A identifier for knowing the requester of {@link InputMethodManager#showSoftInput} or * {@link InputMethodManager#hideSoftInputFromWindow}. */ @GuardedBy("ImfLock.class") private IBinder mRequestImeToken; /** * The IME target display id for which the latest startInput was called. */ @GuardedBy("ImfLock.class") private int mImeDisplayId = DEFAULT_DISPLAY; @AnyThread boolean hasImeFocusChanged() { return mImeFocusChanged; } @AnyThread boolean hasEditorFocused() { return mHasFocusedEditor; } @AnyThread boolean isStartInputByGainFocus() { return mIsStartInputByGainFocus; } @AnyThread int getSoftInputModeState() { return mSoftInputModeState; } @AnyThread int getWindowFlags() { return mWindowFlags; } @AnyThread int getToolType() { return mToolType; } @GuardedBy("ImfLock.class") private void setImeDisplayId(int imeDisplayId) { mImeDisplayId = imeDisplayId; } @GuardedBy("ImfLock.class") int getImeDisplayId() { return mImeDisplayId; } @GuardedBy("ImfLock.class") private void setRequestedImeVisible(boolean requestedImeVisible) { mRequestedImeVisible = requestedImeVisible; } @GuardedBy("ImfLock.class") boolean isRequestedImeVisible() { return mRequestedImeVisible; } @GuardedBy("ImfLock.class") void setRequestImeToken(IBinder token) { mRequestImeToken = token; } @GuardedBy("ImfLock.class") IBinder getRequestImeToken() { return mRequestImeToken; } Loading
services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +7 −8 Original line number Diff line number Diff line Loading @@ -4862,15 +4862,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return mVisibilityApplier; } void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @NonNull ImeVisibilityResult result) { synchronized (ImfLock.class) { @GuardedBy("ImfLock.class") void onApplyImeVisibilityFromComputerLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @NonNull ImeVisibilityResult result) { // TODO(b/305849394): Infer userId from windowToken final int userId = mCurrentUserId; mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(), result.getReason(), userId); } } @GuardedBy("ImfLock.class") void setEnabledSessionLocked(SessionState session, @NonNull UserData userData) { Loading
services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java +183 −155 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import android.view.inputmethod.InputMethodManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.annotations.GuardedBy; import com.android.server.wm.ImeTargetChangeListener; import com.android.server.wm.WindowManagerInternal; Loading Loading @@ -93,6 +94,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes @Test public void testRequestImeVisibility_showImplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_IMPLICIT); Loading @@ -106,9 +108,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isFalse(); } } @Test public void testRequestImeVisibility_showExplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); mComputer.requestImeVisibility(mWindowToken, res); Loading @@ -121,6 +125,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isTrue(); } } /** * This checks that the state after an explicit show request does not get reset during Loading @@ -128,6 +133,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes */ @Test public void testRequestImeVisibility_showExplicit_thenShowImplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); assertThat(mComputer.mRequestedShowExplicitly).isTrue(); Loading @@ -135,6 +141,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT); assertThat(mComputer.mRequestedShowExplicitly).isTrue(); } } /** * This checks that the state after a forced show request does not get reset during Loading @@ -142,6 +149,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes */ @Test public void testRequestImeVisibility_showForced_thenShowExplicit() { synchronized (ImfLock.class) { initImeTargetWindowState(mWindowToken); mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_FORCED); assertThat(mComputer.mShowForced).isTrue(); Loading @@ -149,9 +157,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); assertThat(mComputer.mShowForced).isTrue(); } } @Test public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() { synchronized (ImfLock.class) { // Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN); Loading @@ -168,9 +178,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isFalse(); } } @Test public void testRequestImeVisibility_showImplicit_imeHiddenPolicy() { synchronized (ImfLock.class) { // Precondition: set IME hidden display policy before calling showSoftInput mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); Loading @@ -187,9 +199,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isFalse(); } } @Test public void testRequestImeVisibility_hideNotAlways() { synchronized (ImfLock.class) { // Precondition: ensure IME has shown before hiding request. mComputer.setInputShown(true); Loading @@ -204,9 +218,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED); assertThat(state.isRequestedImeVisible()).isFalse(); } } @Test public void testComputeImeDisplayId() { synchronized (ImfLock.class) { final ImeTargetWindowState state = mComputer.getOrCreateWindowState(mWindowToken); mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL; Loading @@ -228,9 +244,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse(); assertThat(state.getImeDisplayId()).isEqualTo(FALLBACK_DISPLAY_ID); } } @Test public void testComputeState_lastImeRequestedVisible_preserved_When_StateUnChanged() { synchronized (ImfLock.class) { // Assume the last IME targeted window has requested IME visible final IBinder lastImeTargetWindowToken = new Binder(); mInputMethodManagerService.mLastImeTargetWindow = lastImeTargetWindowToken; Loading @@ -241,14 +259,17 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes // Verify when focusing the next window with STATE_UNCHANGED flag, the last IME // visibility state will be preserved to the current window state. final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState(mWindowToken); final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState( mWindowToken); mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */); assertThat(stateWithUnChangedFlag.isRequestedImeVisible()).isEqualTo( lastState.isRequestedImeVisible()); } } @Test public void testOnInteractiveChanged() { synchronized (ImfLock.class) { mComputer.getOrCreateWindowState(mWindowToken); // Precondition: ensure IME has shown before hiding request. mComputer.requestImeVisibility(mWindowToken, true); Loading @@ -259,7 +280,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mWindowToken, true /* interactive */); assertThat(result).isNull(); // Show the IME screenshot to capture the last IME visible state when the device inactive. // Show the IME screenshot to capture the last IME visible state when the device // inactive. result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */); assertThat(result).isNotNull(); assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT); Loading @@ -271,9 +293,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT); assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS); } } @Test public void testOnApplyImeVisibilityFromComputer() { synchronized (ImfLock.class) { final IBinder testImeTargetOverlay = new Binder(); final IBinder testImeInputTarget = new Binder(); Loading @@ -287,8 +311,10 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes final ArgumentCaptor<IBinder> targetCaptor = ArgumentCaptor.forClass(IBinder.class); final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass( ImeVisibilityResult.class); verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(), notNull() /* statsToken */, resultCaptor.capture()); synchronized (ImfLock.class) { verify(mInputMethodManagerService).onApplyImeVisibilityFromComputerLocked( targetCaptor.capture(), notNull() /* statsToken */, resultCaptor.capture()); } final IBinder imeInputTarget = targetCaptor.getValue(); final ImeVisibilityResult result = resultCaptor.getValue(); Loading @@ -297,7 +323,9 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(result.getState()).isEqualTo(STATE_HIDE_IME_EXPLICIT); assertThat(result.getReason()).isEqualTo(HIDE_WHEN_INPUT_TARGET_INVISIBLE); } } @GuardedBy("ImfLock.class") private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) { final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED, 0, true, true, true); Loading