Loading services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +47 −1 Original line number Original line Diff line number Diff line Loading @@ -96,6 +96,32 @@ final class InputMethodBindingController { @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; /** * A set of status bits regarding the active IME. * * <p>This value is a combination of following two bits:</p> * <dl> * <dt>{@link InputMethodService#IME_ACTIVE}</dt> * <dd> * If this bit is ON, connected IME is ready to accept touch/key events. * </dd> * <dt>{@link InputMethodService#IME_VISIBLE}</dt> * <dd> * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible. * </dd> * <dt>{@link InputMethodService#IME_INVISIBLE}</dt> * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is * currently invisible. * </dd> * </dl> * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and * {@link InputMethodBindingController#unbindCurrentMethod()}.</em> */ @GuardedBy("ImfLock.class") private int mImeWindowVis; @GuardedBy("ImfLock.class") private int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; @Nullable private CountDownLatch mLatchForTesting; @Nullable private CountDownLatch mLatchForTesting; /** /** Loading Loading @@ -473,7 +499,7 @@ final class InputMethodBindingController { if (getCurToken() != null) { if (getCurToken() != null) { removeCurrentToken(); removeCurrentToken(); mService.resetSystemUiLocked(); mService.resetSystemUiLocked(this); mAutofillController.onResetSystemUi(); mAutofillController.onResetSystemUi(); } } Loading Loading @@ -662,4 +688,24 @@ final class InputMethodBindingController { int getUserId() { int getUserId() { return mUserId; return mUserId; } } @GuardedBy("ImfLock.class") void setImeWindowVis(int imeWindowVis) { mImeWindowVis = imeWindowVis; } @GuardedBy("ImfLock.class") int getImeWindowVis() { return mImeWindowVis; } @GuardedBy("ImfLock.class") int getBackDisposition() { return mBackDisposition; } @GuardedBy("ImfLock.class") void setBackDisposition(int backDisposition) { mBackDisposition = backDisposition; } } } services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +47 −61 Original line number Original line Diff line number Diff line Loading @@ -552,33 +552,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @MultiUserUnawareField @MultiUserUnawareField boolean mIsInteractive = true; boolean mIsInteractive = true; @MultiUserUnawareField int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** * A set of status bits regarding the active IME. * * <p>This value is a combination of following two bits:</p> * <dl> * <dt>{@link InputMethodService#IME_ACTIVE}</dt> * <dd> * If this bit is ON, connected IME is ready to accept touch/key events. * </dd> * <dt>{@link InputMethodService#IME_VISIBLE}</dt> * <dd> * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible. * </dd> * <dt>{@link InputMethodService#IME_INVISIBLE}</dt> * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is * currently invisible. * </dd> * </dl> * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and * {@link InputMethodBindingController#unbindCurrentMethod()}.</em> */ @MultiUserUnawareField int mImeWindowVis; @SharedByAllUsersField @SharedByAllUsersField private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); Loading Loading @@ -936,7 +909,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Uh oh, current input method is no longer around! // Uh oh, current input method is no longer around! // Pick another one... // Pick another one... Slog.i(TAG, "Current input method removed: " + curInputMethodId); Slog.i(TAG, "Current input method removed: " + curInputMethodId); updateSystemUiLocked(0 /* vis */, mBackDisposition); final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(), userId); if (!chooseNewDefaultIMELocked()) { if (!chooseNewDefaultIMELocked()) { changed = true; changed = true; curIm = null; curIm = null; Loading Loading @@ -1412,7 +1387,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mStatusBarManagerInternal = mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); LocalServices.getService(StatusBarManagerInternal.class); hideStatusBarIconLocked(); hideStatusBarIconLocked(); updateSystemUiLocked(mImeWindowVis, mBackDisposition); final var bindingController = getInputMethodBindingController(currentUserId); updateSystemUiLocked(bindingController.getImeWindowVis(), bindingController.getBackDisposition(), currentUserId); mShowOngoingImeSwitcherForPhones = mRes.getBoolean( mShowOngoingImeSwitcherForPhones = mRes.getBoolean( com.android.internal.R.bool.show_ongoing_ime_switcher); com.android.internal.R.bool.show_ongoing_ime_switcher); if (mShowOngoingImeSwitcherForPhones) { if (mShowOngoingImeSwitcherForPhones) { Loading Loading @@ -2426,11 +2403,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") void resetSystemUiLocked() { void resetSystemUiLocked(InputMethodBindingController bindingController) { // Set IME window status as invisible when unbinding current method. // Set IME window status as invisible when unbinding current method. mImeWindowVis = 0; final int imeWindowVis = 0; mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; final int backDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; updateSystemUiLocked(mImeWindowVis, mBackDisposition); bindingController.setImeWindowVis(imeWindowVis); bindingController.setBackDisposition(backDisposition); updateSystemUiLocked(imeWindowVis, backDisposition, bindingController.getUserId()); } } @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") Loading Loading @@ -2544,7 +2523,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. sessionState.mSession.finishSession(); sessionState.mSession.finishSession(); } catch (RemoteException e) { } catch (RemoteException e) { Slog.w(TAG, "Session failed to close due to remote exception", e); Slog.w(TAG, "Session failed to close due to remote exception", e); updateSystemUiLocked(0 /* vis */, mBackDisposition); // TODO(b/350386877): Propagate userId from the caller or infer it from // sessionState final int userId = mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(), userId); } } sessionState.mSession = null; sessionState.mSession = null; } } Loading Loading @@ -2764,8 +2748,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) { if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) { return; return; } } mImeWindowVis = vis; bindingController.setImeWindowVis(vis); mBackDisposition = backDisposition; bindingController.setBackDisposition(backDisposition); updateSystemUiLocked(vis, backDisposition, userId); updateSystemUiLocked(vis, backDisposition, userId); } } Loading Loading @@ -2802,23 +2786,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void updateImeWindowStatus(boolean disableImeIcon) { private void updateImeWindowStatus(boolean disableImeIcon) { synchronized (ImfLock.class) { synchronized (ImfLock.class) { // TODO(b/350386877): Propagate userId from the caller. final int userId = mCurrentUserId; if (disableImeIcon) { if (disableImeIcon) { updateSystemUiLocked(0, mBackDisposition); final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(0, bindingController.getBackDisposition(), userId); } else { } else { updateSystemUiLocked(); updateSystemUiLocked(userId); } } } } } } @GuardedBy("ImfLock.class") void updateSystemUiLocked() { updateSystemUiLocked(mImeWindowVis, mBackDisposition); } // Caution! This method is called in this class. Handle multi-user carefully // Caution! This method is called in this class. Handle multi-user carefully @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") private void updateSystemUiLocked(int vis, int backDisposition) { void updateSystemUiLocked(@UserIdInt int userId) { updateSystemUiLocked(vis, backDisposition, mCurrentUserId); final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(bindingController.getImeWindowVis(), bindingController.getBackDisposition(), userId); } } @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") Loading Loading @@ -3068,7 +3052,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true, userId); setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true, userId); IInputMethodInvoker curMethod = bindingController.getCurMethod(); IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { if (curMethod != null) { updateSystemUiLocked(mImeWindowVis, mBackDisposition); updateSystemUiLocked(bindingController.getImeWindowVis(), bindingController.getBackDisposition(), userId); curMethod.changeInputMethodSubtype(newSubtype); curMethod.changeInputMethodSubtype(newSubtype); } } } } Loading Loading @@ -3455,7 +3440,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return; return; } } mFocusedWindowPerceptible.put(windowToken, windowPerceptible); mFocusedWindowPerceptible.put(windowToken, windowPerceptible); updateSystemUiLocked(); updateSystemUiLocked(mCurrentUserId); } } }); }); } } Loading Loading @@ -3621,29 +3606,30 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, @UserIdInt int userId) { @SoftInputShowHideReason int reason, @UserIdInt int userId) { final var bindingController = getInputMethodBindingController(userId); if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) { if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) { return false; return false; } } // There is a chance that IMM#hideSoftInput() is called in a transient state where // There is a chance that IMM#hideSoftInput() is called in a transient state where // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting // IMMS#InputShown is already updated to be true whereas the user's ImeWindowVis is still // to be updated with the new value sent from IME process. Even in such a transient state // waiting to be updated with the new value sent from IME process. Even in such a transient // historically we have accepted an incoming call of IMM#hideSoftInput() from the // state historically we have accepted an incoming call of IMM#hideSoftInput() from the // application process as a valid request, and have even promised such a behavior with CTS // application process as a valid request, and have even promised such a behavior with CTS // since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only // since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only // IMMS#InputShown indicates that the software keyboard is shown. // IMMS#InputShown indicates that the software keyboard is shown. // TODO(b/246309664): Clean up IMMS#mImeWindowVis // TODO(b/246309664): Clean up IMMS#mImeWindowVis final var bindingController = getInputMethodBindingController(userId); final var userData = getUserData(userId); final var userData = getUserData(userId); IInputMethodInvoker curMethod = bindingController.getCurMethod(); IInputMethodInvoker curMethod = bindingController.getCurMethod(); final boolean shouldHideSoftInput = curMethod != null final boolean shouldHideSoftInput = curMethod != null && (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0); && (isInputShownLocked() || (bindingController.getImeWindowVis() & InputMethodService.IME_ACTIVE) != 0); mVisibilityStateComputer.requestImeVisibility(windowToken, false); mVisibilityStateComputer.requestImeVisibility(windowToken, false); if (shouldHideSoftInput) { if (shouldHideSoftInput) { // The IME will report its visible state again after the following message finally // The IME will report its visible state again after the following message finally // delivered to the IME process as an IPC. Hence the inconsistency between // delivered to the IME process as an IPC. Hence the inconsistency between // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in // IMMS#mInputShown and the user's ImeWindowVis should be resolved spontaneously in // the final state. // the final state. ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE); ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE); mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason, mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason, Loading Loading @@ -4633,8 +4619,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. proto.write(HAVE_CONNECTION, bindingController.hasMainConnection()); proto.write(HAVE_CONNECTION, bindingController.hasMainConnection()); proto.write(BOUND_TO_METHOD, userData.mBoundToMethod); proto.write(BOUND_TO_METHOD, userData.mBoundToMethod); proto.write(IS_INTERACTIVE, mIsInteractive); proto.write(IS_INTERACTIVE, mIsInteractive); proto.write(BACK_DISPOSITION, mBackDisposition); proto.write(BACK_DISPOSITION, bindingController.getBackDisposition()); proto.write(IME_WINDOW_VISIBILITY, mImeWindowVis); proto.write(IME_WINDOW_VISIBILITY, bindingController.getImeWindowVis()); proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard()); proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard()); proto.end(token); proto.end(token); } } Loading Loading @@ -5143,18 +5129,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void handleSetInteractive(final boolean interactive) { private void handleSetInteractive(final boolean interactive) { synchronized (ImfLock.class) { synchronized (ImfLock.class) { mIsInteractive = interactive; updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition); // TODO(b/305849394): Support multiple IMEs. // TODO(b/305849394): Support multiple IMEs. final var userId = mCurrentUserId; final int userId = mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); mIsInteractive = interactive; updateSystemUiLocked( interactive ? bindingController.getImeWindowVis() : 0, bindingController.getBackDisposition(), userId); final var userData = getUserData(userId); final var userData = getUserData(userId); // Inform the current client of the change in active status // Inform the current client of the change in active status if (userData.mCurClient == null || userData.mCurClient.mClient == null) { if (userData.mCurClient == null || userData.mCurClient.mClient == null) { return; return; } } // TODO(b/325515685): user data must be retrieved by a userId parameter final var bindingController = getInputMethodBindingController(mCurrentUserId); if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol( if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol( bindingController.getCurMethodUid())) { bindingController.getCurMethodUid())) { // Handle IME visibility when interactive changed before finishing the input to // Handle IME visibility when interactive changed before finishing the input to Loading services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +4 −2 Original line number Original line Diff line number Diff line Loading @@ -201,7 +201,7 @@ final class InputMethodMenuController { attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; attrs.setTitle("Select input method"); attrs.setTitle("Select input method"); w.setAttributes(attrs); w.setAttributes(attrs); mService.updateSystemUiLocked(); mService.updateSystemUiLocked(userId); mService.sendOnNavButtonFlagsChangedLocked(); mService.sendOnNavButtonFlagsChangedLocked(); mSwitchingDialog.show(); mSwitchingDialog.show(); } } Loading Loading @@ -239,7 +239,9 @@ final class InputMethodMenuController { mSwitchingDialog = null; mSwitchingDialog = null; mSwitchingDialogTitleView = null; mSwitchingDialogTitleView = null; mService.updateSystemUiLocked(); // TODO(b/305849394): Make InputMethodMenuController multi-user aware final int userId = mService.getCurrentImeUserIdLocked(); mService.updateSystemUiLocked(userId); mService.sendOnNavButtonFlagsChangedLocked(); mService.sendOnNavButtonFlagsChangedLocked(); mDialogBuilder = null; mDialogBuilder = null; mIms = null; mIms = null; Loading services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +7 −2 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull; Loading Loading @@ -134,8 +135,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe @Test @Test public void testApplyImeVisibility_hideImeExplicit() throws Exception { public void testApplyImeVisibility_hideImeExplicit() throws Exception { mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { synchronized (ImfLock.class) { final var bindingController = mInputMethodManagerService.getInputMethodBindingController(mUserId); when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE); mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId); STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId); } } Loading @@ -144,8 +147,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe @Test @Test public void testApplyImeVisibility_hideNotAlways() throws Exception { public void testApplyImeVisibility_hideNotAlways() throws Exception { mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { synchronized (ImfLock.class) { final var bindingController = mInputMethodManagerService.getInputMethodBindingController(mUserId); when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE); mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId); STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId); } } Loading Loading
services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +47 −1 Original line number Original line Diff line number Diff line Loading @@ -96,6 +96,32 @@ final class InputMethodBindingController { @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; /** * A set of status bits regarding the active IME. * * <p>This value is a combination of following two bits:</p> * <dl> * <dt>{@link InputMethodService#IME_ACTIVE}</dt> * <dd> * If this bit is ON, connected IME is ready to accept touch/key events. * </dd> * <dt>{@link InputMethodService#IME_VISIBLE}</dt> * <dd> * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible. * </dd> * <dt>{@link InputMethodService#IME_INVISIBLE}</dt> * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is * currently invisible. * </dd> * </dl> * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and * {@link InputMethodBindingController#unbindCurrentMethod()}.</em> */ @GuardedBy("ImfLock.class") private int mImeWindowVis; @GuardedBy("ImfLock.class") private int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; @Nullable private CountDownLatch mLatchForTesting; @Nullable private CountDownLatch mLatchForTesting; /** /** Loading Loading @@ -473,7 +499,7 @@ final class InputMethodBindingController { if (getCurToken() != null) { if (getCurToken() != null) { removeCurrentToken(); removeCurrentToken(); mService.resetSystemUiLocked(); mService.resetSystemUiLocked(this); mAutofillController.onResetSystemUi(); mAutofillController.onResetSystemUi(); } } Loading Loading @@ -662,4 +688,24 @@ final class InputMethodBindingController { int getUserId() { int getUserId() { return mUserId; return mUserId; } } @GuardedBy("ImfLock.class") void setImeWindowVis(int imeWindowVis) { mImeWindowVis = imeWindowVis; } @GuardedBy("ImfLock.class") int getImeWindowVis() { return mImeWindowVis; } @GuardedBy("ImfLock.class") int getBackDisposition() { return mBackDisposition; } @GuardedBy("ImfLock.class") void setBackDisposition(int backDisposition) { mBackDisposition = backDisposition; } } }
services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +47 −61 Original line number Original line Diff line number Diff line Loading @@ -552,33 +552,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @MultiUserUnawareField @MultiUserUnawareField boolean mIsInteractive = true; boolean mIsInteractive = true; @MultiUserUnawareField int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** * A set of status bits regarding the active IME. * * <p>This value is a combination of following two bits:</p> * <dl> * <dt>{@link InputMethodService#IME_ACTIVE}</dt> * <dd> * If this bit is ON, connected IME is ready to accept touch/key events. * </dd> * <dt>{@link InputMethodService#IME_VISIBLE}</dt> * <dd> * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible. * </dd> * <dt>{@link InputMethodService#IME_INVISIBLE}</dt> * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is * currently invisible. * </dd> * </dl> * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and * {@link InputMethodBindingController#unbindCurrentMethod()}.</em> */ @MultiUserUnawareField int mImeWindowVis; @SharedByAllUsersField @SharedByAllUsersField private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); Loading Loading @@ -936,7 +909,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Uh oh, current input method is no longer around! // Uh oh, current input method is no longer around! // Pick another one... // Pick another one... Slog.i(TAG, "Current input method removed: " + curInputMethodId); Slog.i(TAG, "Current input method removed: " + curInputMethodId); updateSystemUiLocked(0 /* vis */, mBackDisposition); final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(), userId); if (!chooseNewDefaultIMELocked()) { if (!chooseNewDefaultIMELocked()) { changed = true; changed = true; curIm = null; curIm = null; Loading Loading @@ -1412,7 +1387,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mStatusBarManagerInternal = mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); LocalServices.getService(StatusBarManagerInternal.class); hideStatusBarIconLocked(); hideStatusBarIconLocked(); updateSystemUiLocked(mImeWindowVis, mBackDisposition); final var bindingController = getInputMethodBindingController(currentUserId); updateSystemUiLocked(bindingController.getImeWindowVis(), bindingController.getBackDisposition(), currentUserId); mShowOngoingImeSwitcherForPhones = mRes.getBoolean( mShowOngoingImeSwitcherForPhones = mRes.getBoolean( com.android.internal.R.bool.show_ongoing_ime_switcher); com.android.internal.R.bool.show_ongoing_ime_switcher); if (mShowOngoingImeSwitcherForPhones) { if (mShowOngoingImeSwitcherForPhones) { Loading Loading @@ -2426,11 +2403,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") void resetSystemUiLocked() { void resetSystemUiLocked(InputMethodBindingController bindingController) { // Set IME window status as invisible when unbinding current method. // Set IME window status as invisible when unbinding current method. mImeWindowVis = 0; final int imeWindowVis = 0; mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; final int backDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; updateSystemUiLocked(mImeWindowVis, mBackDisposition); bindingController.setImeWindowVis(imeWindowVis); bindingController.setBackDisposition(backDisposition); updateSystemUiLocked(imeWindowVis, backDisposition, bindingController.getUserId()); } } @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") Loading Loading @@ -2544,7 +2523,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. sessionState.mSession.finishSession(); sessionState.mSession.finishSession(); } catch (RemoteException e) { } catch (RemoteException e) { Slog.w(TAG, "Session failed to close due to remote exception", e); Slog.w(TAG, "Session failed to close due to remote exception", e); updateSystemUiLocked(0 /* vis */, mBackDisposition); // TODO(b/350386877): Propagate userId from the caller or infer it from // sessionState final int userId = mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(), userId); } } sessionState.mSession = null; sessionState.mSession = null; } } Loading Loading @@ -2764,8 +2748,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) { if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) { return; return; } } mImeWindowVis = vis; bindingController.setImeWindowVis(vis); mBackDisposition = backDisposition; bindingController.setBackDisposition(backDisposition); updateSystemUiLocked(vis, backDisposition, userId); updateSystemUiLocked(vis, backDisposition, userId); } } Loading Loading @@ -2802,23 +2786,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void updateImeWindowStatus(boolean disableImeIcon) { private void updateImeWindowStatus(boolean disableImeIcon) { synchronized (ImfLock.class) { synchronized (ImfLock.class) { // TODO(b/350386877): Propagate userId from the caller. final int userId = mCurrentUserId; if (disableImeIcon) { if (disableImeIcon) { updateSystemUiLocked(0, mBackDisposition); final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(0, bindingController.getBackDisposition(), userId); } else { } else { updateSystemUiLocked(); updateSystemUiLocked(userId); } } } } } } @GuardedBy("ImfLock.class") void updateSystemUiLocked() { updateSystemUiLocked(mImeWindowVis, mBackDisposition); } // Caution! This method is called in this class. Handle multi-user carefully // Caution! This method is called in this class. Handle multi-user carefully @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") private void updateSystemUiLocked(int vis, int backDisposition) { void updateSystemUiLocked(@UserIdInt int userId) { updateSystemUiLocked(vis, backDisposition, mCurrentUserId); final var bindingController = getInputMethodBindingController(userId); updateSystemUiLocked(bindingController.getImeWindowVis(), bindingController.getBackDisposition(), userId); } } @GuardedBy("ImfLock.class") @GuardedBy("ImfLock.class") Loading Loading @@ -3068,7 +3052,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true, userId); setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true, userId); IInputMethodInvoker curMethod = bindingController.getCurMethod(); IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { if (curMethod != null) { updateSystemUiLocked(mImeWindowVis, mBackDisposition); updateSystemUiLocked(bindingController.getImeWindowVis(), bindingController.getBackDisposition(), userId); curMethod.changeInputMethodSubtype(newSubtype); curMethod.changeInputMethodSubtype(newSubtype); } } } } Loading Loading @@ -3455,7 +3440,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return; return; } } mFocusedWindowPerceptible.put(windowToken, windowPerceptible); mFocusedWindowPerceptible.put(windowToken, windowPerceptible); updateSystemUiLocked(); updateSystemUiLocked(mCurrentUserId); } } }); }); } } Loading Loading @@ -3621,29 +3606,30 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, @UserIdInt int userId) { @SoftInputShowHideReason int reason, @UserIdInt int userId) { final var bindingController = getInputMethodBindingController(userId); if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) { if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) { return false; return false; } } // There is a chance that IMM#hideSoftInput() is called in a transient state where // There is a chance that IMM#hideSoftInput() is called in a transient state where // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting // IMMS#InputShown is already updated to be true whereas the user's ImeWindowVis is still // to be updated with the new value sent from IME process. Even in such a transient state // waiting to be updated with the new value sent from IME process. Even in such a transient // historically we have accepted an incoming call of IMM#hideSoftInput() from the // state historically we have accepted an incoming call of IMM#hideSoftInput() from the // application process as a valid request, and have even promised such a behavior with CTS // application process as a valid request, and have even promised such a behavior with CTS // since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only // since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only // IMMS#InputShown indicates that the software keyboard is shown. // IMMS#InputShown indicates that the software keyboard is shown. // TODO(b/246309664): Clean up IMMS#mImeWindowVis // TODO(b/246309664): Clean up IMMS#mImeWindowVis final var bindingController = getInputMethodBindingController(userId); final var userData = getUserData(userId); final var userData = getUserData(userId); IInputMethodInvoker curMethod = bindingController.getCurMethod(); IInputMethodInvoker curMethod = bindingController.getCurMethod(); final boolean shouldHideSoftInput = curMethod != null final boolean shouldHideSoftInput = curMethod != null && (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0); && (isInputShownLocked() || (bindingController.getImeWindowVis() & InputMethodService.IME_ACTIVE) != 0); mVisibilityStateComputer.requestImeVisibility(windowToken, false); mVisibilityStateComputer.requestImeVisibility(windowToken, false); if (shouldHideSoftInput) { if (shouldHideSoftInput) { // The IME will report its visible state again after the following message finally // The IME will report its visible state again after the following message finally // delivered to the IME process as an IPC. Hence the inconsistency between // delivered to the IME process as an IPC. Hence the inconsistency between // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in // IMMS#mInputShown and the user's ImeWindowVis should be resolved spontaneously in // the final state. // the final state. ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE); ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE); mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason, mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason, Loading Loading @@ -4633,8 +4619,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. proto.write(HAVE_CONNECTION, bindingController.hasMainConnection()); proto.write(HAVE_CONNECTION, bindingController.hasMainConnection()); proto.write(BOUND_TO_METHOD, userData.mBoundToMethod); proto.write(BOUND_TO_METHOD, userData.mBoundToMethod); proto.write(IS_INTERACTIVE, mIsInteractive); proto.write(IS_INTERACTIVE, mIsInteractive); proto.write(BACK_DISPOSITION, mBackDisposition); proto.write(BACK_DISPOSITION, bindingController.getBackDisposition()); proto.write(IME_WINDOW_VISIBILITY, mImeWindowVis); proto.write(IME_WINDOW_VISIBILITY, bindingController.getImeWindowVis()); proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard()); proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard()); proto.end(token); proto.end(token); } } Loading Loading @@ -5143,18 +5129,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void handleSetInteractive(final boolean interactive) { private void handleSetInteractive(final boolean interactive) { synchronized (ImfLock.class) { synchronized (ImfLock.class) { mIsInteractive = interactive; updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition); // TODO(b/305849394): Support multiple IMEs. // TODO(b/305849394): Support multiple IMEs. final var userId = mCurrentUserId; final int userId = mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); mIsInteractive = interactive; updateSystemUiLocked( interactive ? bindingController.getImeWindowVis() : 0, bindingController.getBackDisposition(), userId); final var userData = getUserData(userId); final var userData = getUserData(userId); // Inform the current client of the change in active status // Inform the current client of the change in active status if (userData.mCurClient == null || userData.mCurClient.mClient == null) { if (userData.mCurClient == null || userData.mCurClient.mClient == null) { return; return; } } // TODO(b/325515685): user data must be retrieved by a userId parameter final var bindingController = getInputMethodBindingController(mCurrentUserId); if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol( if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol( bindingController.getCurMethodUid())) { bindingController.getCurMethodUid())) { // Handle IME visibility when interactive changed before finishing the input to // Handle IME visibility when interactive changed before finishing the input to Loading
services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +4 −2 Original line number Original line Diff line number Diff line Loading @@ -201,7 +201,7 @@ final class InputMethodMenuController { attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; attrs.setTitle("Select input method"); attrs.setTitle("Select input method"); w.setAttributes(attrs); w.setAttributes(attrs); mService.updateSystemUiLocked(); mService.updateSystemUiLocked(userId); mService.sendOnNavButtonFlagsChangedLocked(); mService.sendOnNavButtonFlagsChangedLocked(); mSwitchingDialog.show(); mSwitchingDialog.show(); } } Loading Loading @@ -239,7 +239,9 @@ final class InputMethodMenuController { mSwitchingDialog = null; mSwitchingDialog = null; mSwitchingDialogTitleView = null; mSwitchingDialogTitleView = null; mService.updateSystemUiLocked(); // TODO(b/305849394): Make InputMethodMenuController multi-user aware final int userId = mService.getCurrentImeUserIdLocked(); mService.updateSystemUiLocked(userId); mService.sendOnNavButtonFlagsChangedLocked(); mService.sendOnNavButtonFlagsChangedLocked(); mDialogBuilder = null; mDialogBuilder = null; mIms = null; mIms = null; Loading
services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +7 −2 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull; Loading Loading @@ -134,8 +135,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe @Test @Test public void testApplyImeVisibility_hideImeExplicit() throws Exception { public void testApplyImeVisibility_hideImeExplicit() throws Exception { mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { synchronized (ImfLock.class) { final var bindingController = mInputMethodManagerService.getInputMethodBindingController(mUserId); when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE); mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId); STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId); } } Loading @@ -144,8 +147,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe @Test @Test public void testApplyImeVisibility_hideNotAlways() throws Exception { public void testApplyImeVisibility_hideNotAlways() throws Exception { mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { synchronized (ImfLock.class) { final var bindingController = mInputMethodManagerService.getInputMethodBindingController(mUserId); when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE); mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId); STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId); } } Loading