diff options
8 files changed, 732 insertions, 572 deletions
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index b77f47d640e9..aa4b3386e548 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -81,6 +81,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, @UserIdInt int userId) { final var bindingController = mService.getInputMethodBindingController(userId); + final var userData = mService.getUserData(userId); final IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { if (DEBUG) { @@ -93,10 +94,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_SHOW_IME, statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE, - Objects.toString(mService.mImeBindingState.mFocusedWindow), + Objects.toString(userData.mImeBindingState.mFocusedWindow), InputMethodDebug.softInputDisplayReasonToString(reason), InputMethodDebug.softInputModeToString( - mService.mImeBindingState.mFocusedWindowSoftInputMode)); + userData.mImeBindingState.mFocusedWindowSoftInputMode)); } mService.onShowHideSoftInputRequested(true /* show */, showInputToken, reason, statsToken, userId); @@ -111,6 +112,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @UserIdInt int userId) { final var bindingController = mService.getInputMethodBindingController(userId); final IInputMethodInvoker curMethod = bindingController.getCurMethod(); + final var userData = mService.getUserData(userId); if (curMethod != null) { // 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 @@ -126,10 +128,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_HIDE_IME, statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE, - Objects.toString(mService.mImeBindingState.mFocusedWindow), + Objects.toString(userData.mImeBindingState.mFocusedWindow), InputMethodDebug.softInputDisplayReasonToString(reason), InputMethodDebug.softInputModeToString( - mService.mImeBindingState.mFocusedWindowSoftInputMode)); + userData.mImeBindingState.mFocusedWindowSoftInputMode)); } mService.onShowHideSoftInputRequested(false /* show */, hideInputToken, reason, statsToken, userId); @@ -179,29 +181,30 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { break; case STATE_HIDE_IME_EXPLICIT: if (Flags.refactorInsetsController()) { - setImeVisibilityOnFocusedWindowClient(false); + setImeVisibilityOnFocusedWindowClient(false, userId); } else { mService.hideCurrentInputLocked(windowToken, statsToken, - 0 /* flags */, null /* resultReceiver */, reason); + 0 /* flags */, null /* resultReceiver */, reason, userId); } break; case STATE_HIDE_IME_NOT_ALWAYS: if (Flags.refactorInsetsController()) { - setImeVisibilityOnFocusedWindowClient(false); + setImeVisibilityOnFocusedWindowClient(false, userId); } else { mService.hideCurrentInputLocked(windowToken, statsToken, - InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason); + InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason, + userId); } break; case STATE_SHOW_IME_IMPLICIT: if (Flags.refactorInsetsController()) { // This can be triggered by IMMS#startInputOrWindowGainedFocus. We need to // set the requestedVisibleTypes in InsetsController first, before applying it. - setImeVisibilityOnFocusedWindowClient(true); + setImeVisibilityOnFocusedWindowClient(true, userId); } else { mService.showCurrentInputLocked(windowToken, statsToken, InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN, - null /* resultReceiver */, reason); + null /* resultReceiver */, reason, userId); } break; case STATE_SHOW_IME_SNAPSHOT: @@ -230,20 +233,23 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override public boolean removeImeScreenshot(int displayId, @UserIdInt int userId) { + final var userData = mService.getUserData(userId); if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) { mService.onShowHideSoftInputRequested(false /* show */, - mService.mImeBindingState.mFocusedWindow, + userData.mImeBindingState.mFocusedWindow, REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */, userId); return true; } return false; } - private void setImeVisibilityOnFocusedWindowClient(boolean visibility) { - if (mService.mImeBindingState != null - && mService.mImeBindingState.mFocusedWindowClient != null - && mService.mImeBindingState.mFocusedWindowClient.mClient != null) { - mService.mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(visibility); + @GuardedBy("ImfLock.class") + private void setImeVisibilityOnFocusedWindowClient(boolean visibility, @UserIdInt int userId) { + final var userData = mService.getUserData(userId); + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient != null) { + userData.mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(visibility); } else { // TODO(b/329229469): ImeTracker? } diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 9d80844ee9eb..7ebf5950de16 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -38,6 +38,7 @@ import static com.android.server.inputmethod.InputMethodManagerService.computeIm import android.accessibilityservice.AccessibilityService; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.content.res.Configuration; import android.os.Binder; import android.os.IBinder; @@ -52,6 +53,7 @@ import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.server.LocalServices; @@ -553,15 +555,17 @@ public final class ImeVisibilityStateComputer { return null; } - IBinder getWindowTokenFrom(IBinder requestImeToken) { + @GuardedBy("ImfLock.class") + IBinder getWindowTokenFrom(IBinder requestImeToken, @UserIdInt int userId) { for (IBinder windowToken : mRequestWindowStateMap.keySet()) { final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); if (state.getRequestImeToken() == requestImeToken) { return windowToken; } } + final var userData = mService.getUserData(userId); // Fallback to the focused window for some edge cases (e.g. relaunching the activity) - return mService.mImeBindingState.mFocusedWindow; + return userData.mImeBindingState.mFocusedWindow; } IBinder getWindowTokenFrom(ImeTargetWindowState windowState) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 60d647dc710f..afc10290ee02 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -382,9 +382,9 @@ final class InputMethodBindingController { InputMethodManager .invalidateLocalConnectionlessStylusHandwritingAvailabilityCaches(); } - mService.initializeImeLocked(mCurMethod, mCurToken); + mService.initializeImeLocked(mCurMethod, mCurToken, mUserId); mService.scheduleNotifyImeUidToAudioService(mCurMethodUid); - mService.reRequestCurrentClientSessionLocked(); + mService.reRequestCurrentClientSessionLocked(mUserId); mAutofillController.performOnCreateInlineSuggestionsRequest(); } @@ -437,7 +437,7 @@ final class InputMethodBindingController { mLastBindTime = SystemClock.uptimeMillis(); clearCurMethodAndSessions(); mService.clearInputShownLocked(); - mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME); + mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME, mUserId); } } } @@ -483,7 +483,7 @@ final class InputMethodBindingController { @GuardedBy("ImfLock.class") private void clearCurMethodAndSessions() { - mService.clearClientSessionsLocked(); + mService.clearClientSessionsLocked(this); mCurMethod = null; mCurMethodUid = Process.INVALID_UID; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0c46c5b6222e..3c74b23fc8c2 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -468,12 +468,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private final ClientController mClientController; /** - * Holds the current IME binding state info. - */ - @MultiUserUnawareField - ImeBindingState mImeBindingState; - - /** * Set once the system is ready to run third party code. */ @SharedByAllUsersField @@ -492,25 +486,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } - /** - * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. - * This is to be synchronized with the secure settings keyed with - * {@link Settings.Secure#DEFAULT_INPUT_METHOD}. - * - * <p>This can be transiently {@code null} when the system is re-initializing input method - * settings, e.g., the system locale is just changed.</p> - * - * <p>Note that {@link InputMethodBindingController#getCurId()} is used to track which IME - * is being connected to {@link InputMethodManagerService}.</p> - * - * @see InputMethodBindingController#getCurId() - */ - @GuardedBy("ImfLock.class") - @Nullable - String getSelectedMethodIdLocked() { - return getInputMethodBindingController(mCurrentUserId).getSelectedMethodId(); - } - @GuardedBy("ImfLock.class") @Nullable InputMethodInfo queryInputMethodForCurrentUserLocked(@NonNull String imeId) { @@ -518,13 +493,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } /** - * The client that is currently bound to an input method. - */ - @MultiUserUnawareField - @Nullable - private ClientState mCurClient; - - /** * The last window token that we confirmed that IME started talking to. This is always updated * upon reports from the input method. If the window state is already changed before the report * is handled, this field just keeps the last value. @@ -533,33 +501,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. IBinder mLastImeTargetWindow; /** - * The {@link IRemoteInputConnection} last provided by the current client. - */ - @MultiUserUnawareField - IRemoteInputConnection mCurInputConnection; - - /** - * The {@link ImeOnBackInvokedDispatcher} last provided by the current client to - * receive {@link android.window.OnBackInvokedCallback}s forwarded from IME. - */ - @MultiUserUnawareField - ImeOnBackInvokedDispatcher mCurImeDispatcher; - - /** - * The {@link IRemoteAccessibilityInputConnection} last provided by the current client. - */ - @MultiUserUnawareField - @Nullable - IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection; - - /** - * The {@link EditorInfo} last provided by the current client. - */ - @MultiUserUnawareField - @Nullable - EditorInfo mCurEditorInfo; - - /** * Map of window perceptible states indexed by their associated window tokens. * * The value {@code true} indicates that IME has not been mostly hidden via @@ -570,20 +511,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>(); /** - * The token tracking the current IME show request that is waiting for a connection to an IME, - * otherwise {@code null}. - */ - @Nullable - @MultiUserUnawareField - private ImeTracker.Token mCurStatsToken; - - /** - * {@code true} if the current input method is in fullscreen mode. - */ - @MultiUserUnawareField - boolean mInFullscreenMode; - - /** * The token we have made for the currently active input method, to * identify it in the future. */ @@ -618,27 +545,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } /** - * Have we called mCurMethod.bindInput()? - */ - @MultiUserUnawareField - boolean mBoundToMethod; - - /** - * Have we called bindInput() for accessibility services? - */ - @MultiUserUnawareField - boolean mBoundToAccessibility; - - /** - * Currently enabled session. - */ - @GuardedBy("ImfLock.class") - @MultiUserUnawareField - SessionState mEnabledSession; - @MultiUserUnawareField - SparseArray<AccessibilitySessionState> mEnabledAccessibilitySessions = new SparseArray<>(); - - /** * True if the device is currently interactive with user. The value is true initially. */ @MultiUserUnawareField @@ -763,13 +669,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId); mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard( accessibilitySoftKeyboardSetting); + final var userData = getUserData(mUserId); if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) { - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE); - } else if (isShowRequestedForCurrentWindow()) { - showCurrentInputLocked(mImeBindingState.mFocusedWindow, + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, + 0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE, + mUserId); + } else if (isShowRequestedForCurrentWindow(mUserId)) { + showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, InputMethodManager.SHOW_IMPLICIT, - SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); + SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, mUserId); } } else if (stylusHandwritingEnabledUri.equals(uri)) { InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); @@ -777,13 +685,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. .invalidateLocalConnectionlessStylusHandwritingAvailabilityCaches(); } else { boolean enabledChanged = false; - String newEnabled = InputMethodSettingsRepository.get(mCurrentUserId) + String newEnabled = InputMethodSettingsRepository.get(mUserId) .getEnabledInputMethodsStr(); if (!mLastEnabled.equals(newEnabled)) { mLastEnabled = newEnabled; enabledChanged = true; } - updateInputMethodsFromSettingsLocked(enabledChanged); + updateInputMethodsFromSettingsLocked(enabledChanged, mUserId); } } } @@ -846,10 +754,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. DirectBootAwareness.AUTO); InputMethodSettingsRepository.put(userId, settings); } - postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */); + // TODO(b/305849394): Dispatch this to non-current users. + final int userId = mCurrentUserId; + postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */, userId); // If the locale is changed, needs to reset the default ime - resetDefaultImeLocked(mContext); - updateFromSettingsLocked(true); + resetDefaultImeLocked(mContext, userId); + updateFromSettingsLocked(true, userId); } } @@ -1004,7 +914,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!isCurrentUser) { return; } - postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */); + postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId); boolean changed = false; @@ -1046,7 +956,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } if (changed) { - updateFromSettingsLocked(false); + updateFromSettingsLocked(false, userId); } } } @@ -1188,8 +1098,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. InputMethodSettingsRepository.put(userId, newSettings); if (mCurrentUserId == userId) { // We need to rebuild IMEs. - postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */); - updateInputMethodsFromSettingsLocked(true /* enabledChanged */); + postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId); + updateInputMethodsFromSettingsLocked(true /* enabledChanged */, userId); } else if (mExperimentalConcurrentMultiUserModeEnabled) { experimentalInitializeVisibleBackgroundUserLocked(userId); } @@ -1208,8 +1118,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // Hide soft input before user switch task since switch task may block main handler a while // and delayed the hideCurrentInputLocked(). - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_SWITCH_USER); + final var userData = getUserData(userId); + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_SWITCH_USER, userId); final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId, clientToBeReset); mUserSwitchHandlerTask = task; @@ -1301,7 +1212,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mClientController = new ClientController(mPackageManagerInternal); mClientController.addClientControllerCallback(c -> onClientRemoved(c)); - mImeBindingState = ImeBindingState.newEmptyState(); mPreventImeStartupUnlessTextEditor = mRes.getBoolean( com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor); @@ -1350,10 +1260,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - private void resetDefaultImeLocked(Context context) { + private void resetDefaultImeLocked(Context context, @UserIdInt int userId) { + final var bindingController = getInputMethodBindingController(userId); // Do not reset the default (current) IME when it is a 3rd-party IME - String selectedMethodId = getSelectedMethodIdLocked(); - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + String selectedMethodId = bindingController.getSelectedMethodId(); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (selectedMethodId != null && !settings.getMethodMap().get(selectedMethodId).isSystem()) { return; @@ -1368,7 +1279,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (DEBUG) { Slog.i(TAG, "Default found, using " + defIm.getId()); } - setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false); + setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false, userId); } @GuardedBy("ImfLock.class") @@ -1413,22 +1324,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void switchUserOnHandlerLocked(@UserIdInt int newUserId, IInputMethodClientInvoker clientToBeReset) { + final int prevUserId = mCurrentUserId; if (DEBUG) { Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId - + " currentUserId=" + mCurrentUserId); + + " prevUserId=" + prevUserId); } // Clean up stuff for mCurrentUserId, which soon becomes the previous user. // TODO(b/338461930): Check if this is still necessary or not. - onUnbindCurrentMethodByReset(); + onUnbindCurrentMethodByReset(prevUserId); // Note that in b/197848765 we want to see if we can keep the binding alive for better // profile switching. - final var bindingController = getInputMethodBindingController(mCurrentUserId); + final var bindingController = getInputMethodBindingController(prevUserId); bindingController.unbindCurrentMethod(); - unbindCurrentClientLocked(UnbindReason.SWITCH_USER); + unbindCurrentClientLocked(UnbindReason.SWITCH_USER, prevUserId); // Hereafter we start initializing things for "newUserId". @@ -1438,6 +1350,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mSettingsObserver.registerContentObserverLocked(newUserId); mCurrentUserId = newUserId; + final var newUserData = getUserData(newUserId); final String defaultImiId = SecureSettingsWrapper.getString( Settings.Secure.DEFAULT_INPUT_METHOD, null, newUserId); @@ -1454,13 +1367,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId); final InputMethodSettings newSettings = InputMethodSettingsRepository.get(newUserId); - postInputMethodSettingUpdatedLocked(initialUserSwitch /* resetDefaultEnabledIme */); + postInputMethodSettingUpdatedLocked(initialUserSwitch /* resetDefaultEnabledIme */, + newUserId); if (TextUtils.isEmpty(newSettings.getSelectedInputMethod())) { // This is the first time of the user switch and // set the current ime to the proper one. - resetDefaultImeLocked(mContext); + resetDefaultImeLocked(mContext, newUserId); } - updateFromSettingsLocked(true); + updateFromSettingsLocked(true, newUserId); if (initialUserSwitch) { InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( @@ -1479,7 +1393,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // The client is already gone. return; } - cs.mClient.scheduleStartInputIfNecessary(mInFullscreenMode); + cs.mClient.scheduleStartInputIfNecessary(newUserData.mInFullscreenMode); } } @@ -1541,8 +1455,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. DirectBootAwareness.AUTO); InputMethodSettingsRepository.put(currentUserId, newSettings); postInputMethodSettingUpdatedLocked( - !imeSelectedOnBoot /* resetDefaultEnabledIme */); - updateFromSettingsLocked(true); + !imeSelectedOnBoot /* resetDefaultEnabledIme */, currentUserId); + updateFromSettingsLocked(true, currentUserId); InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( getPackageManagerForUser(mContext, currentUserId), newSettings.getEnabledInputMethodList()); @@ -1859,20 +1773,31 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } + @GuardedBy("ImfLock.class") + private void onClientRemoved(ClientState client) { + clearClientSessionLocked(client); + clearClientSessionForAccessibilityLocked(client); + // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. + @SuppressWarnings("GuardedBy") Consumer<UserDataRepository.UserData> clientRemovedForUser = + userData -> onClientRemovedInternalLocked(client, userData); + mUserDataRepository.forAllUserData(clientRemovedForUser); + } + /** * Hide the IME if the removed user is the current user. */ // TODO(b/325515685): Move this method to InputMethodBindingController @GuardedBy("ImfLock.class") - private void onClientRemoved(ClientState client) { - clearClientSessionLocked(client); - clearClientSessionForAccessibilityLocked(client); - if (mCurClient == client) { - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_REMOVE_CLIENT); - if (mBoundToMethod) { - mBoundToMethod = false; - IInputMethodInvoker curMethod = getCurMethodLocked(); + private void onClientRemovedInternalLocked(ClientState client, + @NonNull UserDataRepository.UserData userData) { + final int userId = userData.mUserId; + if (userData.mCurClient == client) { + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_REMOVE_CLIENT, userId); + if (userData.mBoundToMethod) { + userData.mBoundToMethod = false; + final var userBindingController = getInputMethodBindingController(userId); + IInputMethodInvoker curMethod = userBindingController.getCurMethod(); if (curMethod != null) { // When we unbind input, we are unbinding the client, so we always // unbind ime and a11y together. @@ -1880,10 +1805,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. AccessibilityManagerInternal.get().unbindInput(); } } - mBoundToAccessibility = false; - mCurClient = null; - if (mImeBindingState.mFocusedWindowClient == client) { - mImeBindingState = ImeBindingState.newEmptyState(); + userData.mBoundToAccessibility = false; + userData.mCurClient = null; + if (userData.mImeBindingState.mFocusedWindowClient == client) { + userData.mImeBindingState = ImeBindingState.newEmptyState(); } } } @@ -1896,49 +1821,51 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) { - if (mCurClient != null) { + void unbindCurrentClientLocked(@UnbindReason int unbindClientReason, @UserIdInt int userId) { + final var userData = getUserData(userId); + if (userData.mCurClient != null) { if (DEBUG) { - Slog.v(TAG, "unbindCurrentInputLocked: client=" + mCurClient.mClient.asBinder()); + Slog.v(TAG, "unbindCurrentInputLocked: client=" + + userData.mCurClient.mClient.asBinder()); } - if (mBoundToMethod) { - mBoundToMethod = false; - IInputMethodInvoker curMethod = getCurMethodLocked(); + final var bindingController = getInputMethodBindingController(userId); + if (userData.mBoundToMethod) { + userData.mBoundToMethod = false; + IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { curMethod.unbindInput(); } } - mBoundToAccessibility = false; + userData.mBoundToAccessibility = false; // Since we set active false to current client and set mCurClient to null, let's unbind // all accessibility too. That means, when input method get disconnected (including // switching ime), we also unbind accessibility - mCurClient.mClient.setActive(false /* active */, false /* fullscreen */); + userData.mCurClient.mClient.setActive(false /* active */, false /* fullscreen */); - // TODO(b/325515685): make binding controller user independent. Before this change, the - // following dependencies also need to be user independent: mCurClient, mBoundToMethod, - // getCurMethodLocked(), and mMenuController. - final var bindingController = getInputMethodBindingController(mCurrentUserId); - mCurClient.mClient.onUnbindMethod(bindingController.getSequenceNumber(), + userData.mCurClient.mClient.onUnbindMethod(bindingController.getSequenceNumber(), unbindClientReason); - mCurClient.mSessionRequested = false; - mCurClient.mSessionRequestedForAccessibility = false; - mCurClient = null; - ImeTracker.forLogging().onFailed(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); - mCurStatsToken = null; + userData.mCurClient.mSessionRequested = false; + userData.mCurClient.mSessionRequestedForAccessibility = false; + userData.mCurClient = null; + ImeTracker.forLogging().onFailed(userData.mCurStatsToken, + ImeTracker.PHASE_SERVER_WAIT_IME); + userData.mCurStatsToken = null; + // TODO: Make mMenuController multi-user aware mMenuController.hideInputMethodMenuLocked(); } } /** * TODO(b/338404383) Remove - * Called when {@link #resetCurrentMethodAndClientLocked(int)} invoked for clean-up states + * Called when {@link #resetCurrentMethodAndClientLocked(int, int)} invoked for clean-up states * before unbinding the current method. */ @GuardedBy("ImfLock.class") - void onUnbindCurrentMethodByReset() { + void onUnbindCurrentMethodByReset(@UserIdInt int userId) { + final var userData = getUserData(userId); final ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull( - mImeBindingState.mFocusedWindow); + userData.mImeBindingState.mFocusedWindow); if (winState != null && !winState.isRequestedImeVisible() && !mVisibilityStateComputer.isInputShown()) { // Normally, the focus window will apply the IME visibility state to @@ -1949,9 +1876,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // As a result, we have to notify WM to apply IME visibility before clearing the // binding states in the first place. final var statsToken = createStatsTokenForFocusedClient(false /* show */, - SoftInputShowHideReason.UNBIND_CURRENT_METHOD); - mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken, - STATE_HIDE_IME, mCurrentUserId); + SoftInputShowHideReason.UNBIND_CURRENT_METHOD, userId); + mVisibilityApplier.applyImeVisibility(userData.mImeBindingState.mFocusedWindow, + statsToken, STATE_HIDE_IME, userId); } } @@ -1961,13 +1888,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. */ @GuardedBy("ImfLock.class") boolean hasAttachedClient() { - return mCurClient != null; + return getUserData(mCurrentUserId).mCurClient != null; } @VisibleForTesting void setAttachedClientForTesting(@NonNull ClientState cs) { synchronized (ImfLock.class) { - mCurClient = cs; + getUserData(mCurrentUserId).mCurClient = cs; } } @@ -1983,20 +1910,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - private boolean isShowRequestedForCurrentWindow() { + private boolean isShowRequestedForCurrentWindow(@UserIdInt int userId) { + final var userData = getUserData(userId); + // TODO(b/349904272): Make mVisibilityStateComputer multi-user aware final ImeTargetWindowState state = mVisibilityStateComputer.getWindowStateOrNull( - mImeBindingState.mFocusedWindow); + userData.mImeBindingState.mFocusedWindow); return state != null && state.isRequestedImeVisible(); } @GuardedBy("ImfLock.class") @NonNull - InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) { - final int userId = mCurrentUserId; + InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial, + @UserIdInt int userId) { final var bindingController = getInputMethodBindingController(userId); - if (!mBoundToMethod) { - bindingController.getCurMethod().bindInput(mCurClient.mBinding); - mBoundToMethod = true; + final var userData = getUserData(userId); + if (!userData.mBoundToMethod) { + bindingController.getCurMethod().bindInput(userData.mCurClient.mBinding); + userData.mBoundToMethod = true; } final boolean restarting = !initial; @@ -2004,11 +1934,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final StartInputInfo info = new StartInputInfo(mCurrentUserId, bindingController.getCurToken(), bindingController.getCurTokenDisplayId(), bindingController.getCurId(), startInputReason, - restarting, UserHandle.getUserId(mCurClient.mUid), - mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo, - mImeBindingState.mFocusedWindowSoftInputMode, + restarting, UserHandle.getUserId(userData.mCurClient.mUid), + userData.mCurClient.mSelfReportedDisplayId, + userData.mImeBindingState.mFocusedWindow, userData.mCurEditorInfo, + userData.mImeBindingState.mFocusedWindowSoftInputMode, bindingController.getSequenceNumber()); - mImeTargetWindowMap.put(startInputToken, mImeBindingState.mFocusedWindow); + mImeTargetWindowMap.put(startInputToken, userData.mImeBindingState.mFocusedWindow); mStartInputHistory.addEntry(info); // Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user @@ -2016,33 +1947,34 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // same-user scenarios. // That said ignoring cross-user scenario will never affect IMEs that do not have // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case. - if (userId == UserHandle.getUserId(mCurClient.mUid)) { + if (userId == UserHandle.getUserId(userData.mCurClient.mUid)) { mPackageManagerInternal.grantImplicitAccess(userId, null /* intent */, UserHandle.getAppId(bindingController.getCurMethodUid()), - mCurClient.mUid, true /* direct */); + userData.mCurClient.mUid, true /* direct */); } @InputMethodNavButtonFlags final int navButtonFlags = getInputMethodNavButtonFlagsLocked(); - final SessionState session = mCurClient.mCurSession; - setEnabledSessionLocked(session); - session.mMethod.startInput(startInputToken, mCurInputConnection, mCurEditorInfo, restarting, - navButtonFlags, mCurImeDispatcher); + final SessionState session = userData.mCurClient.mCurSession; + setEnabledSessionLocked(session, userData); + session.mMethod.startInput(startInputToken, userData.mCurInputConnection, + userData.mCurEditorInfo, restarting, navButtonFlags, userData.mCurImeDispatcher); if (Flags.refactorInsetsController()) { - if (isShowRequestedForCurrentWindow() && mImeBindingState != null - && mImeBindingState.mFocusedWindow != null) { - showSoftInputInternal(mImeBindingState.mFocusedWindow); + if (isShowRequestedForCurrentWindow(userId) && userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindow != null) { + showSoftInputInternal(userData.mImeBindingState.mFocusedWindow); } } else { - if (isShowRequestedForCurrentWindow()) { + if (isShowRequestedForCurrentWindow(userId)) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); // Re-use current statsToken, if it exists. - final var statsToken = mCurStatsToken != null ? mCurStatsToken + final var statsToken = userData.mCurStatsToken != null ? userData.mCurStatsToken : createStatsTokenForFocusedClient(true /* show */, - SoftInputShowHideReason.ATTACH_NEW_INPUT); - mCurStatsToken = null; - showCurrentInputLocked(mImeBindingState.mFocusedWindow, statsToken, + SoftInputShowHideReason.ATTACH_NEW_INPUT, userId); + userData.mCurStatsToken = null; + showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, statsToken, mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN, - null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); + null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT, + userId); } } @@ -2052,7 +1984,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final boolean suppressesSpellChecker = curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker(); final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions = - createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions); + createAccessibilityInputMethodSessions( + userData.mCurClient.mAccessibilitySessions); if (bindingController.supportsStylusHandwriting() && hasSupportedStylusLocked()) { mHwController.setInkWindowInitializer(new InkWindowInitializer()); } @@ -2064,10 +1997,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void attachNewAccessibilityLocked(@StartInputReason int startInputReason, - boolean initial) { - if (!mBoundToAccessibility) { + boolean initial, @UserIdInt int userId) { + final var userData = getUserData(userId); + + if (!userData.mBoundToAccessibility) { AccessibilityManagerInternal.get().bindInput(); - mBoundToAccessibility = true; + userData.mBoundToAccessibility = true; } // TODO(b/187453053): grantImplicitAccess to accessibility services access? if so, need to @@ -2076,9 +2011,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // We don't start input when session for a11y is created. We start input when // input method start input, a11y manager service is always on. if (startInputReason != StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY) { - setEnabledSessionForAccessibilityLocked(mCurClient.mAccessibilitySessions); - AccessibilityManagerInternal.get().startInput(mCurRemoteAccessibilityInputConnection, - mCurEditorInfo, !initial /* restarting */); + setEnabledSessionForAccessibilityLocked(userData.mCurClient.mAccessibilitySessions, + userData); + AccessibilityManagerInternal.get().startInput( + userData.mCurRemoteAccessibilityInputConnection, + userData.mCurEditorInfo, !initial /* restarting */); } } @@ -2114,10 +2051,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @NonNull ImeOnBackInvokedDispatcher imeDispatcher, @NonNull InputMethodBindingController bindingController) { + final int userId = bindingController.mUserId; + final var userData = getUserData(userId); + // Compute the final shown display ID with validated cs.selfReportedDisplayId for this // session & other conditions. ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull( - mImeBindingState.mFocusedWindow); + userData.mImeBindingState.mFocusedWindow); if (winState == null) { return InputBindResult.NOT_IME_TARGET_WINDOW; } @@ -2134,13 +2074,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); } else if (!Objects.equals(deviceMethodId, selectedMethodId)) { setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, - bindingController.getDeviceIdToShowIme()); + bindingController.getDeviceIdToShowIme(), userId); selectedMethodId = deviceMethodId; } if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) { - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE); + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, userId); return InputBindResult.NO_IME; } @@ -2149,19 +2089,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return InputBindResult.NO_IME; } - if (mCurClient != cs) { - prepareClientSwitchLocked(cs); + if (userData.mCurClient != cs) { + prepareClientSwitchLocked(cs, userId); } - final boolean connectionWasActive = mCurInputConnection != null; + final boolean connectionWasActive = userData.mCurInputConnection != null; // Bump up the sequence for this client and attach it. bindingController.advanceSequenceNumber(); - mCurClient = cs; - mCurInputConnection = inputConnection; - mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection; - mCurImeDispatcher = imeDispatcher; + userData.mCurClient = cs; + userData.mCurInputConnection = inputConnection; + userData.mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection; + userData.mCurImeDispatcher = imeDispatcher; // Override the locale hints if the app is running on a virtual device. if (mVdmInternal == null) { mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class); @@ -2172,17 +2112,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. editorInfo.hintLocales = hintsFromVirtualDevice; } } - mCurEditorInfo = editorInfo; + userData.mCurEditorInfo = editorInfo; // Notify input manager if the connection state changes. - final boolean connectionIsActive = mCurInputConnection != null; + final boolean connectionIsActive = userData.mCurInputConnection != null; if (connectionIsActive != connectionWasActive) { mInputManagerInternal.notifyInputMethodConnectionActive(connectionIsActive); } // If configured, we want to avoid starting up the IME if it is not supposed to be showing if (shouldPreventImeStartupLocked(selectedMethodId, startInputFlags, - unverifiedTargetSdkVersion)) { + unverifiedTargetSdkVersion, userId)) { if (DEBUG) { Slog.d(TAG, "Avoiding IME startup and unbinding current input method."); } @@ -2197,7 +2137,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final String curId = bindingController.getCurId(); final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); if (curId != null && curId.equals(bindingController.getSelectedMethodId()) - && displayIdToShowIme == getCurTokenDisplayIdLocked()) { + && displayIdToShowIme == bindingController.getCurTokenDisplayId()) { if (cs.mCurSession != null) { // Fast case: if we are already connected to the input method, // then just return it. @@ -2211,12 +2151,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // we can always attach to accessibility because AccessibilityManagerService is // always on. attachNewAccessibilityLocked(startInputReason, - (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0); + (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0, userId); return attachNewInputLocked(startInputReason, - (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0); + (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0, userId); } - InputBindResult bindResult = tryReuseConnectionLocked(bindingController, cs); + InputBindResult bindResult = tryReuseConnectionLocked(bindingController, cs, userId); if (bindResult != null) { return bindResult; } @@ -2293,18 +2233,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private boolean shouldPreventImeStartupLocked( @NonNull String selectedMethodId, @StartInputFlags int startInputFlags, - int unverifiedTargetSdkVersion) { + int unverifiedTargetSdkVersion, + @UserIdInt int userId) { // Fast-path for the majority of cases if (!mPreventImeStartupUnlessTextEditor) { return false; } - if (isShowRequestedForCurrentWindow()) { + if (isShowRequestedForCurrentWindow(userId)) { return false; } if (isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags)) { return false; } - final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId) + final InputMethodInfo imi = InputMethodSettingsRepository.get(userId) .getMethodMap().get(selectedMethodId); if (imi == null) { return false; @@ -2316,10 +2257,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - private void prepareClientSwitchLocked(ClientState cs) { + private void prepareClientSwitchLocked(ClientState cs, @UserIdInt int userId) { // If the client is changing, we need to switch over to the new // one. - unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT); + unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT, userId); // If the screen is on, inform the new client it is active if (mIsInteractive) { cs.mClient.setActive(true /* active */, false /* fullscreen */); @@ -2329,13 +2270,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") @Nullable private InputBindResult tryReuseConnectionLocked( - @NonNull InputMethodBindingController bindingController, @NonNull ClientState cs) { + @NonNull InputMethodBindingController bindingController, @NonNull ClientState cs, + @UserIdInt int userId) { if (bindingController.hasMainConnection()) { - if (getCurMethodLocked() != null) { + if (bindingController.getCurMethod() != null) { if (!Flags.useZeroJankProxy()) { // Return to client, and we will get back with it when // we have had a session made for it. - requestClientSessionLocked(cs); + requestClientSessionLocked(cs, userId); requestClientSessionForAccessibilityLocked(cs); } return new InputBindResult( @@ -2361,7 +2303,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. bindingController.getSequenceNumber(), false); } else { EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, - getSelectedMethodIdLocked(), bindingDuration, 0); + bindingController.getSelectedMethodId(), bindingDuration, 0); } } } @@ -2402,12 +2344,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token) { + void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token, + @UserIdInt int userId) { if (DEBUG) { Slog.v(TAG, "Sending attach of token: " + token + " for display: " - + getCurTokenDisplayIdLocked()); + + getInputMethodBindingController(userId).getCurTokenDisplayId()); } - inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token), + inputMethod.initializeInternal(token, + new InputMethodPrivilegedOperationsImpl(this, token, userId), + // TODO(b/345519864): Make getInputMethodNavButtonFlagsLocked() multi-user aware getInputMethodNavButtonFlagsLocked()); } @@ -2438,7 +2383,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread void onSessionCreated(IInputMethodInvoker method, IInputMethodSession session, - InputChannel channel) { + InputChannel channel, @UserIdInt int userId) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onSessionCreated"); try { synchronized (ImfLock.class) { @@ -2448,18 +2393,21 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. channel.dispose(); return; } - IInputMethodInvoker curMethod = getCurMethodLocked(); + final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); + IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null && method != null && curMethod.asBinder() == method.asBinder()) { - if (mCurClient != null) { - clearClientSessionLocked(mCurClient); - mCurClient.mCurSession = new SessionState( - mCurClient, method, session, channel); + if (userData.mCurClient != null) { + clearClientSessionLocked(userData.mCurClient); + userData.mCurClient.mCurSession = new SessionState( + userData.mCurClient, method, session, channel); InputBindResult res = attachNewInputLocked( - StartInputReason.SESSION_CREATED_BY_IME, true); - attachNewAccessibilityLocked(StartInputReason.SESSION_CREATED_BY_IME, true); + StartInputReason.SESSION_CREATED_BY_IME, true, userId); + attachNewAccessibilityLocked(StartInputReason.SESSION_CREATED_BY_IME, true, + userId); if (res.method != null) { - mCurClient.mClient.onBindMethod(res); + userData.mCurClient.mClient.onBindMethod(res); } return; } @@ -2482,29 +2430,31 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) { - final var bindingController = getInputMethodBindingController(mCurrentUserId); + void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason, + @UserIdInt int userId) { + final var bindingController = getInputMethodBindingController(userId); bindingController.setSelectedMethodId(null); // Callback before clean-up binding states. // TODO(b/338461930): Check if this is still necessary or not. - onUnbindCurrentMethodByReset(); + onUnbindCurrentMethodByReset(userId); bindingController.unbindCurrentMethod(); - unbindCurrentClientLocked(unbindClientReason); + unbindCurrentClientLocked(unbindClientReason, userId); } @GuardedBy("ImfLock.class") - void reRequestCurrentClientSessionLocked() { - if (mCurClient != null) { - clearClientSessionLocked(mCurClient); - clearClientSessionForAccessibilityLocked(mCurClient); - requestClientSessionLocked(mCurClient); - requestClientSessionForAccessibilityLocked(mCurClient); + void reRequestCurrentClientSessionLocked(@UserIdInt int userId) { + final var userData = getUserData(userId); + if (userData.mCurClient != null) { + clearClientSessionLocked(userData.mCurClient); + clearClientSessionForAccessibilityLocked(userData.mCurClient); + requestClientSessionLocked(userData.mCurClient, userId); + requestClientSessionForAccessibilityLocked(userData.mCurClient); } } @GuardedBy("ImfLock.class") - void requestClientSessionLocked(ClientState cs) { + void requestClientSessionLocked(ClientState cs, @UserIdInt int userId) { if (!cs.mSessionRequested) { if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs); final InputChannel serverChannel; @@ -2517,14 +2467,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. cs.mSessionRequested = true; - final IInputMethodInvoker curMethod = getCurMethodLocked(); + final var bindingController = getInputMethodBindingController(userId); + final IInputMethodInvoker curMethod = bindingController.getCurMethod(); final IInputMethodSessionCallback.Stub callback = new IInputMethodSessionCallback.Stub() { @Override public void sessionCreated(IInputMethodSession session) { final long ident = Binder.clearCallingIdentity(); try { - onSessionCreated(curMethod, session, serverChannel); + onSessionCreated(curMethod, session, serverChannel, userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -2615,25 +2566,34 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void clearClientSessionsLocked() { - if (getCurMethodLocked() != null) { + void clearClientSessionsLocked(@NonNull InputMethodBindingController bindingController) { + final int userId = bindingController.mUserId; + final var userData = getUserData(userId); + if (bindingController.getCurMethod() != null) { // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = c -> { - clearClientSessionLocked(c); - clearClientSessionForAccessibilityLocked(c); + // TODO(b/305849394): Figure out what we should do for single user IME mode. + final boolean shouldClearClientSession = + !mExperimentalConcurrentMultiUserModeEnabled + || UserHandle.getUserId(c.mUid) == userId; + if (shouldClearClientSession) { + clearClientSessionLocked(c); + clearClientSessionForAccessibilityLocked(c); + } }; mClientController.forAllClients(clearClientSession); - finishSessionLocked(mEnabledSession); - for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) { - finishSessionForAccessibilityLocked(mEnabledAccessibilitySessions.valueAt(i)); + finishSessionLocked(userData.mEnabledSession); + for (int i = 0; i < userData.mEnabledAccessibilitySessions.size(); i++) { + finishSessionForAccessibilityLocked( + userData.mEnabledAccessibilitySessions.valueAt(i)); } - mEnabledSession = null; - mEnabledAccessibilitySessions.clear(); + userData.mEnabledSession = null; + userData.mEnabledAccessibilitySessions.clear(); scheduleNotifyImeUidToAudioService(Process.INVALID_UID); } hideStatusBarIconLocked(); - mInFullscreenMode = false; + getUserData(userId).mInFullscreenMode = false; mWindowManagerInternal.setDismissImeOnBackKeyPressed(false); } @@ -2857,6 +2817,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void updateSystemUiLocked(int vis, int backDisposition, @UserIdInt int userId) { final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); final var curToken = bindingController.getCurToken(); if (curToken == null) { return; @@ -2868,8 +2829,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. + " inv: " + (vis & InputMethodService.IME_INVISIBLE) + " displayId: " + curTokenDisplayId); } - final IBinder focusedWindowToken = mImeBindingState != null - ? mImeBindingState.mFocusedWindow : null; + final IBinder focusedWindowToken = userData.mImeBindingState != null + ? userData.mImeBindingState.mFocusedWindow : null; final Boolean windowPerceptible = focusedWindowToken != null ? mFocusedWindowPerceptible.get(focusedWindowToken) : null; @@ -2904,8 +2865,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void updateFromSettingsLocked(boolean enabledMayChange) { - updateInputMethodsFromSettingsLocked(enabledMayChange); + void updateFromSettingsLocked(boolean enabledMayChange, @UserIdInt int userId) { + updateInputMethodsFromSettingsLocked(enabledMayChange, userId); mMenuController.updateKeyboardFromSettingsLocked(); } @@ -2915,7 +2876,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * * <p>Never assume what this method is doing is officially supported. For the canonical and * desired behaviors always refer to single-user code paths such as - * {@link #updateInputMethodsFromSettingsLocked(boolean)}.</p> + * {@link #updateInputMethodsFromSettingsLocked(boolean, int)}.</p> * * <p>Here are examples of missing features.</p> * <ul> @@ -2964,8 +2925,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) { - final int userId = mCurrentUserId; + void updateInputMethodsFromSettingsLocked(boolean enabledMayChange, @UserIdInt int userId) { final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (enabledMayChange) { final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext, @@ -2996,7 +2956,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } - final var bindingController = getInputMethodBindingController(mCurrentUserId); + final var bindingController = getInputMethodBindingController(userId); if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) { String ime = SecureSettingsWrapper.getString( Settings.Secure.DEFAULT_INPUT_METHOD, null, userId); @@ -3026,14 +2986,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } if (!TextUtils.isEmpty(id)) { try { - setInputMethodLocked(id, settings.getSelectedInputMethodSubtypeId(id)); + setInputMethodLocked(id, settings.getSelectedInputMethodSubtypeId(id), userId); } catch (IllegalArgumentException e) { Slog.w(TAG, "Unknown input method from prefs: " + id, e); - resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_IME_FAILED); + resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_IME_FAILED, userId); } } else { // There is no longer an input method set, so stop any current one. - resetCurrentMethodAndClientLocked(UnbindReason.NO_IME); + resetCurrentMethodAndClientLocked(UnbindReason.NO_IME, userId); } final var userData = getUserData(userId); @@ -3055,13 +3015,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void setInputMethodLocked(String id, int subtypeId) { - setInputMethodLocked(id, subtypeId, DEVICE_ID_DEFAULT); + void setInputMethodLocked(String id, int subtypeId, @UserIdInt int userId) { + setInputMethodLocked(id, subtypeId, DEVICE_ID_DEFAULT, userId); } @GuardedBy("ImfLock.class") - void setInputMethodLocked(String id, int subtypeId, int deviceId) { - final int userId = mCurrentUserId; + void setInputMethodLocked(String id, int subtypeId, int deviceId, @UserIdInt int userId) { final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); InputMethodInfo info = settings.getMethodMap().get(id); if (info == null) { @@ -3096,8 +3055,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } if (!Objects.equals(newSubtype, oldSubtype)) { - setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true); - IInputMethodInvoker curMethod = getCurMethodLocked(); + setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true, userId); + IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { updateSystemUiLocked(mImeWindowVis, mBackDisposition); curMethod.changeInputMethodSubtype(newSubtype); @@ -3116,7 +3075,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. settings.putSelectedDefaultDeviceInputMethod(id); return; } - IInputMethodInvoker curMethod = getCurMethodLocked(); + IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { curMethod.removeStylusHandwritingWindow(); } @@ -3124,7 +3083,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. try { // Set a subtype to this input method. // subtypeId the name of a subtype which will be set. - setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false); + setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false, userId); // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked() // because mCurMethodId is stored as a history in // setSelectedInputMethodAndSubtypeLocked(). @@ -3136,7 +3095,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. intent.putExtra("input_method_id", id); mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); } - unbindCurrentClientLocked(UnbindReason.SWITCH_IME); + unbindCurrentClientLocked(UnbindReason.SWITCH_IME, userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -3158,14 +3117,20 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return false; } + // TODO(b/305849394): Create a utility method for the following policy. + final int userId = mExperimentalConcurrentMultiUserModeEnabled + ? UserHandle.getCallingUserId() : mCurrentUserId; final long ident = Binder.clearCallingIdentity(); + final var userData = getUserData(userId); try { if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); if (Flags.refactorInsetsController()) { boolean wasVisible = isInputShownLocked(); - if (mImeBindingState != null && mImeBindingState.mFocusedWindowClient != null - && mImeBindingState.mFocusedWindowClient.mClient != null) { - mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(true); + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient != null) { + userData.mImeBindingState.mFocusedWindowClient.mClient + .setImeVisibility(true); if (resultReceiver != null) { resultReceiver.send( wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN @@ -3176,7 +3141,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } else { return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType, - resultReceiver, reason); + resultReceiver, reason, userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -3190,12 +3155,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ImeTracing.getInstance().triggerManagerServiceDump( "InputMethodManagerService#showSoftInput", mDumper); synchronized (ImfLock.class) { + // TODO(b/305849394): Infer userId from windowToken + final int userId = mCurrentUserId; final long ident = Binder.clearCallingIdentity(); try { if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); return showCurrentInputLocked(windowToken, null /* statsToken */, 0 /* flags */, 0 /* lastClickTooType */, null /* resultReceiver */, - SoftInputShowHideReason.SHOW_SOFT_INPUT); + SoftInputShowHideReason.SHOW_SOFT_INPUT, userId); } finally { Binder.restoreCallingIdentity(ident); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -3208,11 +3175,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ImeTracing.getInstance().triggerManagerServiceDump( "InputMethodManagerService#hideSoftInput", mDumper); synchronized (ImfLock.class) { + // TODO(b/305849394): Infer userId from windowToken + final int userId = mCurrentUserId; final long ident = Binder.clearCallingIdentity(); try { if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); return hideCurrentInputLocked(windowToken, null /* statsToken */, 0 /* flags */, - null /* resultReceiver */, SoftInputShowHideReason.HIDE_SOFT_INPUT); + null /* resultReceiver */, SoftInputShowHideReason.HIDE_SOFT_INPUT, + userId); } finally { Binder.restoreCallingIdentity(ident); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -3342,7 +3312,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } if (DEBUG) Slog.v(TAG, "Client requesting Stylus Handwriting to be started"); - final IInputMethodInvoker curMethod = getCurMethodLocked(); + final IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod != null) { curMethod.canStartStylusHandwriting(requestId.getAsInt(), connectionlessCallback, cursorAnchorInfo, @@ -3414,8 +3384,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } synchronized (ImfLock.class) { + final var bindingController = getInputMethodBindingController(userId); if (mHwController.isDelegationUsingConnectionlessFlow()) { - final IInputMethodInvoker curMethod = getCurMethodLocked(); + final IInputMethodInvoker curMethod = bindingController.getCurMethod(); if (curMethod == null) { return false; } @@ -3465,7 +3436,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Objects.requireNonNull(windowToken, "windowToken must not be null"); synchronized (ImfLock.class) { Boolean windowPerceptible = mFocusedWindowPerceptible.get(windowToken); - if (mImeBindingState.mFocusedWindow != windowToken + final int userId = mCurrentUserId; + final var userData = getUserData(userId); + if (userData.mImeBindingState.mFocusedWindow != windowToken || (windowPerceptible != null && windowPerceptible == perceptible)) { return; } @@ -3477,17 +3450,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean showCurrentInputLocked(IBinder windowToken, - @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) { - final var statsToken = createStatsTokenForFocusedClient(true /* show */, reason); + @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason, + @UserIdInt int userId) { + final var statsToken = createStatsTokenForFocusedClient(true /* show */, reason, userId); return showCurrentInputLocked(windowToken, statsToken, flags, - MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason); + MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason, userId); } @GuardedBy("ImfLock.class") boolean showCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, @MotionEvent.ToolType int lastClickToolType, @Nullable ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + @SoftInputShowHideReason int reason, @UserIdInt int userId) { if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) { return false; } @@ -3500,22 +3474,24 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mVisibilityStateComputer.requestImeVisibility(windowToken, true); - final int userId = mCurrentUserId; // Ensure binding the connection when IME is going to show. final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); bindingController.setCurrentMethodVisible(); final IInputMethodInvoker curMethod = bindingController.getCurMethod(); - ImeTracker.forLogging().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); + ImeTracker.forLogging().onCancelled(userData.mCurStatsToken, + ImeTracker.PHASE_SERVER_WAIT_IME); final boolean readyToDispatchToIme; if (Flags.deferShowSoftInputUntilSessionCreation()) { readyToDispatchToIme = - curMethod != null && mCurClient != null && mCurClient.mCurSession != null; + curMethod != null && userData.mCurClient != null + && userData.mCurClient.mCurSession != null; } else { readyToDispatchToIme = curMethod != null; } if (readyToDispatchToIme) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME); - mCurStatsToken = null; + userData.mCurStatsToken = null; if (Flags.useHandwritingListenerForTooltype()) { maybeReportToolType(); @@ -3529,7 +3505,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return true; } else { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_WAIT_IME); - mCurStatsToken = statsToken; + userData.mCurStatsToken = statsToken; } return false; } @@ -3575,16 +3551,22 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } return false; } + // TODO(b/305849394): Create a utility method for the following policy. + final int userId = mExperimentalConcurrentMultiUserModeEnabled + ? UserHandle.getCallingUserId() : mCurrentUserId; final long ident = Binder.clearCallingIdentity(); + final var userData = getUserData(userId); try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput"); if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); if (Flags.refactorInsetsController()) { - if (mImeBindingState != null && mImeBindingState.mFocusedWindowClient != null - && mImeBindingState.mFocusedWindowClient.mClient != null) { + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient != null) { boolean wasVisible = isInputShownLocked(); // TODO add windowToken to interface - mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(false); + userData.mImeBindingState.mFocusedWindowClient.mClient + .setImeVisibility(false); if (resultReceiver != null) { resultReceiver.send(wasVisible ? InputMethodManager.RESULT_HIDDEN : InputMethodManager.RESULT_UNCHANGED_HIDDEN, null); @@ -3594,7 +3576,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } else { return InputMethodManagerService.this.hideCurrentInputLocked(windowToken, - statsToken, flags, resultReceiver, reason); + statsToken, flags, resultReceiver, reason, userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -3607,23 +3589,27 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest() { synchronized (ImfLock.class) { - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_SOFT_INPUT); + // TODO(b/305849394): Get userId from caller. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_SOFT_INPUT, userId); } } @GuardedBy("ImfLock.class") private boolean hideCurrentInputLocked(IBinder windowToken, - @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) { - final var statsToken = createStatsTokenForFocusedClient(false /* show */, reason); + @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason, + @UserIdInt int userId) { + final var statsToken = createStatsTokenForFocusedClient(false /* show */, reason, userId); return hideCurrentInputLocked(windowToken, statsToken, flags, null /* resultReceiver */, - reason); + reason, userId); } @GuardedBy("ImfLock.class") boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + @SoftInputShowHideReason int reason, @UserIdInt int userId) { if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) { return false; } @@ -3636,8 +3622,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only // IMMS#InputShown indicates that the software keyboard is shown. // TODO(b/246309664): Clean up IMMS#mImeWindowVis - final int userId = mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); IInputMethodInvoker curMethod = bindingController.getCurMethod(); final boolean shouldHideSoftInput = curMethod != null && (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0); @@ -3657,8 +3643,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. bindingController.setCurrentMethodNotVisible(); mVisibilityStateComputer.clearImeShowFlags(); // Cancel existing statsToken for show IME as we got a hide request. - ImeTracker.forLogging().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); - mCurStatsToken = null; + ImeTracker.forLogging().onCancelled(userData.mCurStatsToken, + ImeTracker.PHASE_SERVER_WAIT_IME); + userData.mCurStatsToken = null; return shouldHideSoftInput; } @@ -3728,7 +3715,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return new InputBindResult( InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY, null /* method */, null /* accessibilitySessions */, null /* channel */, - getSelectedMethodIdLocked(), + bindingController.getSelectedMethodId(), bindingController.getSequenceNumber(), false /* isInputMethodSuppressingSpellChecker */); } @@ -3788,7 +3775,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.mUid); final boolean showForced = mVisibilityStateComputer.mShowForced; - if (mImeBindingState.mFocusedWindow != windowToken + final var userData = getUserData(userId); + if (userData.mImeBindingState.mFocusedWindow != windowToken && showForced && shouldClearFlag) { mVisibilityStateComputer.mShowForced = false; } @@ -3807,8 +3795,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Slog.w(TAG, "If you need to impersonate a foreground user/profile from" + " a background user, use EditorInfo.targetInputMethodUser with" + " INTERACT_ACROSS_USERS_FULL permission."); - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_INVALID_USER); + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, + 0 /* flags */, SoftInputShowHideReason.HIDE_INVALID_USER, userId); return InputBindResult.INVALID_USER; } @@ -3868,7 +3856,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. + " cs=" + cs); } - final boolean sameWindowFocused = mImeBindingState.mFocusedWindow == windowToken; + final int userId = bindingController.mUserId; + final var userData = getUserData(userId); + final boolean sameWindowFocused = userData.mImeBindingState.mFocusedWindow == windowToken; final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; final boolean startInputByWinGainedFocus = (startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0; @@ -3900,7 +3890,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. null, null, null, null, -1, false); } - mImeBindingState = new ImeBindingState(bindingController.mUserId, windowToken, + userData.mImeBindingState = new ImeBindingState(bindingController.mUserId, windowToken, softInputMode, cs, editorInfo); mFocusedWindowPerceptible.put(windowToken, true); @@ -3931,16 +3921,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } break; } - final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason()); - mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken, - imeVisRes.getState(), imeVisRes.getReason(), bindingController.mUserId); + final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason(), + userId); + mVisibilityApplier.applyImeVisibility(userData.mImeBindingState.mFocusedWindow, + statsToken, imeVisRes.getState(), imeVisRes.getReason(), userId); if (imeVisRes.getReason() == SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW) { // If focused display changed, we should unbind current method // to make app window in previous display relayout after Ime // window token removed. // Note that we can trust client's display ID as long as it matches // to the display ID obtained from the window. - if (cs.mSelfReportedDisplayId != getCurTokenDisplayIdLocked()) { + if (cs.mSelfReportedDisplayId != bindingController.getCurTokenDisplayId()) { bindingController.unbindCurrentMethod(); } } @@ -3961,8 +3952,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName, @Nullable ImeTracker.Token statsToken) { - if (mCurClient == null || client == null - || mCurClient.mClient.asBinder() != client.asBinder()) { + // TODO(b/305849394): Get userId from callers. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); + if (userData.mCurClient == null || client == null + || userData.mCurClient.mClient.asBinder() != client.asBinder()) { // We need to check if this is the current client with // focus in the window manager, to allow this call to // be made before input is started in it. @@ -3972,7 +3966,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. throw new IllegalArgumentException("unknown client " + client.asBinder()); } ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN); - if (!isImeClientFocused(mImeBindingState.mFocusedWindow, cs)) { + if (!isImeClientFocused(userData.mImeBindingState.mFocusedWindow, cs)) { Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client)); return false; } @@ -3984,14 +3978,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { final int uid = Binder.getCallingUid(); - if (mImeBindingState.mFocusedWindowClient != null && client != null - && mImeBindingState.mFocusedWindowClient.mClient.asBinder() == client.asBinder()) { + // TODO(b/305849394): Get userId from callers. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); + if (userData.mImeBindingState.mFocusedWindowClient != null && client != null + && userData.mImeBindingState.mFocusedWindowClient.mClient.asBinder() + == client.asBinder()) { return true; } - if (mCurrentUserId != UserHandle.getUserId(uid)) { + if (userId != UserHandle.getUserId(uid)) { return false; } - final var curIntent = getInputMethodBindingController(mCurrentUserId).getCurIntent(); + final var curIntent = getInputMethodBindingController(userId).getCurIntent(); if (curIntent != null && InputMethodUtils.checkIfPackageBelongsToUid( mPackageManagerInternal, uid, curIntent.getComponent().getPackageName())) { return true; @@ -4008,11 +4006,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. + Binder.getCallingUid() + ": " + client); return; } - + // TODO(b/305849394): Create a utility method for the following policy. + final int userId = mExperimentalConcurrentMultiUserModeEnabled + ? UserHandle.getCallingUserId() : mCurrentUserId; + final var userData = getUserData(userId); // Always call subtype picker, because subtype picker is a superset of input method // picker. - final int displayId = - (mCurClient != null) ? mCurClient.mSelfReportedDisplayId : DEFAULT_DISPLAY; + final int displayId = (userData.mCurClient != null) + ? userData.mCurClient.mSelfReportedDisplayId : DEFAULT_DISPLAY; mHandler.obtainMessage(MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId) .sendToTarget(); } @@ -4044,9 +4045,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @BinderThread - private void setInputMethod(@NonNull IBinder token, String id) { + private void setInputMethod(@NonNull IBinder token, String id, @UserIdInt int userId) { final int callingUid = Binder.getCallingUid(); - final int userId = UserHandle.getUserId(callingUid); synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { return; @@ -4057,15 +4057,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. imi.getPackageName(), callingUid, userId, settings)) { throw getExceptionForUnknownImeId(id); } - setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID); + setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID, userId); } } @BinderThread private void setInputMethodAndSubtype(@NonNull IBinder token, String id, - InputMethodSubtype subtype) { + InputMethodSubtype subtype, @UserIdInt int userId) { final int callingUid = Binder.getCallingUid(); - final int userId = UserHandle.getUserId(callingUid); synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { return; @@ -4078,20 +4077,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } if (subtype != null) { setInputMethodWithSubtypeIdLocked(token, id, - SubtypeUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode())); + SubtypeUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode()), userId); } else { - setInputMethod(token, id); + setInputMethod(token, id, userId); } } } @BinderThread - private boolean switchToPreviousInputMethod(@NonNull IBinder token) { + private boolean switchToPreviousInputMethod(@NonNull IBinder token, @UserIdInt int userId) { synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { return false; } - final int userId = mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final Pair<String, String> lastIme = settings.getLastInputMethodAndSubtype(); @@ -4156,9 +4154,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!TextUtils.isEmpty(targetLastImiId)) { if (DEBUG) { Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second - + ", from: " + getSelectedMethodIdLocked() + ", " + subtypeId); + + ", from: " + bindingController.getSelectedMethodId() + ", " + + subtypeId); } - setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId); + setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId, userId); return true; } else { return false; @@ -4167,18 +4166,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @BinderThread - private boolean switchToNextInputMethod(@NonNull IBinder token, boolean onlyCurrentIme) { + private boolean switchToNextInputMethod(@NonNull IBinder token, boolean onlyCurrentIme, + @UserIdInt int userId) { synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { return false; } - return switchToNextInputMethodLocked(token, onlyCurrentIme); + return switchToNextInputMethodLocked(token, onlyCurrentIme, userId); } } @GuardedBy("ImfLock.class") - private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) { - final int userId = mCurrentUserId; + private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme, + @UserIdInt int userId) { final var bindingController = getInputMethodBindingController(userId); final var currentImi = bindingController.getSelectedMethod(); final ImeSubtypeListItem nextSubtype = getUserData(userId).mSwitchingController @@ -4188,17 +4188,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(), - nextSubtype.mSubtypeId); + nextSubtype.mSubtypeId, userId); return true; } @BinderThread - private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) { + private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token, + @UserIdInt int userId) { synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { return false; } - final int userId = mCurrentUserId; final var bindingController = getInputMethodBindingController(userId); final var currentImi = bindingController.getSelectedMethod(); final ImeSubtypeListItem nextSubtype = getUserData(userId).mSwitchingController @@ -4260,7 +4260,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. DirectBootAwareness.AUTO); InputMethodSettingsRepository.put(userId, newSettings); if (isCurrentUser) { - postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */); + postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, + userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -4299,7 +4300,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mSettingsObserver != null) { mSettingsObserver.mLastEnabled = settings.getEnabledInputMethodsStr(); } - updateInputMethodsFromSettingsLocked(false /* enabledChanged */); + updateInputMethodsFromSettingsLocked(false /* enabledChanged */, userId); } } } finally { @@ -4318,7 +4319,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override @Deprecated public int getInputMethodWindowVisibleHeight(@NonNull IInputMethodClient client) { - int callingUid = Binder.getCallingUid(); + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getCallingUserId(); return Binder.withCleanCallingIdentity(() -> { final int curTokenDisplayId; synchronized (ImfLock.class) { @@ -4326,9 +4328,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. "getInputMethodWindowVisibleHeight", null /* statsToken */)) { return 0; } + // TODO(b/305849394): Create a utility method for the following policy. + final int userId = mExperimentalConcurrentMultiUserModeEnabled + ? callingUserId : mCurrentUserId; + final var bindingController = getInputMethodBindingController(userId); // This should probably use the caller's display id, but because this is unsupported // and maintained only for compatibility, there's no point in fixing it. - curTokenDisplayId = getCurTokenDisplayIdLocked(); + curTokenDisplayId = bindingController.getCurTokenDisplayId(); } return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId); }); @@ -4593,27 +4599,29 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void dumpDebug(ProtoOutputStream proto, long fieldId) { synchronized (ImfLock.class) { - final var bindingController = getInputMethodBindingController(mCurrentUserId); + final int userId = mCurrentUserId; + final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); final long token = proto.start(fieldId); - proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked()); + proto.write(CUR_METHOD_ID, bindingController.getSelectedMethodId()); proto.write(CUR_SEQ, bindingController.getSequenceNumber()); - proto.write(CUR_CLIENT, Objects.toString(mCurClient)); - mImeBindingState.dumpDebug(proto, mWindowManagerInternal); + proto.write(CUR_CLIENT, Objects.toString(userData.mCurClient)); + userData.mImeBindingState.dumpDebug(proto, mWindowManagerInternal); proto.write(LAST_IME_TARGET_WINDOW_NAME, mWindowManagerInternal.getWindowName(mLastImeTargetWindow)); proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString( - mImeBindingState.mFocusedWindowSoftInputMode)); - if (mCurEditorInfo != null) { - mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE); + userData.mImeBindingState.mFocusedWindowSoftInputMode)); + if (userData.mCurEditorInfo != null) { + userData.mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE); } proto.write(CUR_ID, bindingController.getCurId()); mVisibilityStateComputer.dumpDebug(proto, fieldId); - proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode); - proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked())); - proto.write(CUR_TOKEN_DISPLAY_ID, getCurTokenDisplayIdLocked()); + proto.write(IN_FULLSCREEN_MODE, userData.mInFullscreenMode); + proto.write(CUR_TOKEN, Objects.toString(bindingController.getCurToken())); + proto.write(CUR_TOKEN_DISPLAY_ID, bindingController.getCurTokenDisplayId()); proto.write(SYSTEM_READY, mSystemReady); proto.write(HAVE_CONNECTION, bindingController.hasMainConnection()); - proto.write(BOUND_TO_METHOD, mBoundToMethod); + proto.write(BOUND_TO_METHOD, userData.mBoundToMethod); proto.write(IS_INTERACTIVE, mIsInteractive); proto.write(BACK_DISPOSITION, mBackDisposition); proto.write(IME_WINDOW_VISIBILITY, mImeWindowVis); @@ -4623,20 +4631,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @BinderThread - private void notifyUserAction(@NonNull IBinder token) { + private void notifyUserAction(@NonNull IBinder token, @UserIdInt int userId) { if (DEBUG) { Slog.d(TAG, "Got the notification of a user action."); } synchronized (ImfLock.class) { - if (getCurTokenLocked() != token) { + final var bindingController = getInputMethodBindingController(userId); + if (bindingController.getCurToken() != token) { if (DEBUG) { Slog.d(TAG, "Ignoring the user action notification from IMEs that are no longer" + " active."); } return; } - final int userId = mCurrentUserId; - final var bindingController = getInputMethodBindingController(userId); final InputMethodInfo imi = bindingController.getSelectedMethod(); if (imi != null) { getUserData(userId).mSwitchingController.onUserActionLocked(imi, @@ -4647,7 +4654,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible, - @NonNull ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken, @UserIdInt int userId) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility"); synchronized (ImfLock.class) { @@ -4659,10 +4666,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom( - windowToken); + windowToken, userId); mVisibilityApplier.applyImeVisibility(requestToken, statsToken, setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME - : ImeVisibilityStateComputer.STATE_HIDE_IME, mCurrentUserId); + : ImeVisibilityStateComputer.STATE_HIDE_IME, userId); } } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -4683,7 +4690,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) { + private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId, + @UserIdInt int userId) { + final var bindingController = getInputMethodBindingController(userId); if (token == null) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) @@ -4692,7 +4701,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. "Using null token requires permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - } else if (getCurTokenLocked() != token) { + } else if (bindingController.getCurToken() != token) { Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid() + " token: " + token); return; @@ -4708,7 +4717,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final long ident = Binder.clearCallingIdentity(); try { - setInputMethodLocked(id, subtypeId); + setInputMethodLocked(id, subtypeId, userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -4721,17 +4730,20 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. void onShowHideSoftInputRequested(boolean show, IBinder requestImeToken, @SoftInputShowHideReason int reason, @Nullable ImeTracker.Token statsToken, @UserIdInt int userId) { - final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken); + final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken, + userId); final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); final WindowManagerInternal.ImeTargetInfo info = mWindowManagerInternal.onToggleImeRequested( - show, mImeBindingState.mFocusedWindow, requestToken, + show, userData.mImeBindingState.mFocusedWindow, requestToken, bindingController.getCurTokenDisplayId()); mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( - mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo, - info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason, - mInFullscreenMode, info.requestWindowName, info.imeControlTargetName, - info.imeLayerTargetName, info.imeSurfaceParentName)); + userData.mImeBindingState.mFocusedWindowClient, + userData.mImeBindingState.mFocusedWindowEditorInfo, + info.focusedWindowName, userData.mImeBindingState.mFocusedWindowSoftInputMode, + reason, userData.mInFullscreenMode, info.requestWindowName, + info.imeControlTargetName, info.imeLayerTargetName, info.imeSurfaceParentName)); if (statsToken != null) { mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName); @@ -4740,7 +4752,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread private void hideMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken, - @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) { + @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason, + @UserIdInt int userId) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput"); synchronized (ImfLock.class) { @@ -4749,21 +4762,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; } + final var userData = getUserData(userId); ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final long ident = Binder.clearCallingIdentity(); try { if (Flags.refactorInsetsController()) { - mCurClient.mClient.setImeVisibility(false); + userData.mCurClient.mClient.setImeVisibility(false); // TODO we will loose the flags here - if (mImeBindingState != null - && mImeBindingState.mFocusedWindowClient != null - && mImeBindingState.mFocusedWindowClient.mClient != null) { - mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(false); + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient != null) { + userData.mImeBindingState.mFocusedWindowClient.mClient + .setImeVisibility(false); } } else { hideCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, - null /* resultReceiver */, reason); + null /* resultReceiver */, reason, userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -4776,7 +4791,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread private void showMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken, - @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) { + @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason, + @UserIdInt int userId) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput"); synchronized (ImfLock.class) { @@ -4785,21 +4801,24 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; } + final var userData = getUserData(userId); ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final long ident = Binder.clearCallingIdentity(); try { if (Flags.refactorInsetsController()) { - mCurClient.mClient.setImeVisibility(false); + userData.mCurClient.mClient.setImeVisibility(false); // TODO we will loose the flags here - if (mImeBindingState != null - && mImeBindingState.mFocusedWindowClient != null - && mImeBindingState.mFocusedWindowClient.mClient != null) { - mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(true); + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient != null) { + userData.mImeBindingState.mFocusedWindowClient.mClient + .setImeVisibility(true); } } else { showCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, - MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason); + MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason, + userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -4820,36 +4839,43 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @NonNull ImeVisibilityResult result) { synchronized (ImfLock.class) { + // TODO(b/305849394): Infer userId from windowToken + final int userId = mCurrentUserId; mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(), - result.getReason(), mCurrentUserId); + result.getReason(), userId); } } @GuardedBy("ImfLock.class") - void setEnabledSessionLocked(SessionState session) { - if (mEnabledSession != session) { - if (mEnabledSession != null && mEnabledSession.mSession != null) { - if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession); - mEnabledSession.mMethod.setSessionEnabled(mEnabledSession.mSession, false); + void setEnabledSessionLocked(SessionState session, + @NonNull UserDataRepository.UserData userData) { + if (userData.mEnabledSession != session) { + if (userData.mEnabledSession != null && userData.mEnabledSession.mSession != null) { + if (DEBUG) Slog.v(TAG, "Disabling: " + userData.mEnabledSession); + userData.mEnabledSession.mMethod.setSessionEnabled( + userData.mEnabledSession.mSession, false); } - mEnabledSession = session; - if (mEnabledSession != null && mEnabledSession.mSession != null) { - if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession); - mEnabledSession.mMethod.setSessionEnabled(mEnabledSession.mSession, true); + userData.mEnabledSession = session; + if (userData.mEnabledSession != null && userData.mEnabledSession.mSession != null) { + if (DEBUG) Slog.v(TAG, "Enabling: " + userData.mEnabledSession); + userData.mEnabledSession.mMethod.setSessionEnabled( + userData.mEnabledSession.mSession, true); } } } @GuardedBy("ImfLock.class") void setEnabledSessionForAccessibilityLocked( - SparseArray<AccessibilitySessionState> accessibilitySessions) { + SparseArray<AccessibilitySessionState> accessibilitySessions, + @NonNull UserDataRepository.UserData userData) { // mEnabledAccessibilitySessions could the same object as accessibilitySessions. SparseArray<IAccessibilityInputMethodSession> disabledSessions = new SparseArray<>(); - for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) { - if (!accessibilitySessions.contains(mEnabledAccessibilitySessions.keyAt(i))) { - AccessibilitySessionState sessionState = mEnabledAccessibilitySessions.valueAt(i); + for (int i = 0; i < userData.mEnabledAccessibilitySessions.size(); i++) { + if (!accessibilitySessions.contains(userData.mEnabledAccessibilitySessions.keyAt(i))) { + AccessibilitySessionState sessionState = + userData.mEnabledAccessibilitySessions.valueAt(i); if (sessionState != null) { - disabledSessions.append(mEnabledAccessibilitySessions.keyAt(i), + disabledSessions.append(userData.mEnabledAccessibilitySessions.keyAt(i), sessionState.mSession); } } @@ -4860,7 +4886,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } SparseArray<IAccessibilityInputMethodSession> enabledSessions = new SparseArray<>(); for (int i = 0; i < accessibilitySessions.size(); i++) { - if (!mEnabledAccessibilitySessions.contains(accessibilitySessions.keyAt(i))) { + if (!userData.mEnabledAccessibilitySessions.contains(accessibilitySessions.keyAt(i))) { AccessibilitySessionState sessionState = accessibilitySessions.valueAt(i); if (sessionState != null) { enabledSessions.append(accessibilitySessions.keyAt(i), sessionState.mSession); @@ -4871,7 +4897,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. AccessibilityManagerInternal.get().setImeSessionEnabled(enabledSessions, true); } - mEnabledAccessibilitySessions = accessibilitySessions; + userData.mEnabledAccessibilitySessions = accessibilitySessions; } @SuppressWarnings("unchecked") @@ -4931,25 +4957,33 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. case MSG_HIDE_ALL_INPUT_METHODS: synchronized (ImfLock.class) { + // TODO(b/305849394): Needs to figure out what to do where for background users. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); if (Flags.refactorInsetsController()) { - if (mImeBindingState != null - && mImeBindingState.mFocusedWindowClient != null - && mImeBindingState.mFocusedWindowClient.mClient != null) { - mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(false); + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient != null) { + userData.mImeBindingState.mFocusedWindowClient.mClient + .setImeVisibility(false); } } else { @SoftInputShowHideReason final int reason = (int) msg.obj; - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - reason); + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, + 0 /* flags */, reason, userId); } } return true; case MSG_REMOVE_IME_SURFACE: { synchronized (ImfLock.class) { + // TODO(b/305849394): Needs to figure out what to do where for background users. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); try { - if (mEnabledSession != null && mEnabledSession.mSession != null - && !isShowRequestedForCurrentWindow()) { - mEnabledSession.mSession.removeImeSurface(); + if (userData.mEnabledSession != null + && userData.mEnabledSession.mSession != null + && !isShowRequestedForCurrentWindow(userId)) { + userData.mEnabledSession.mSession.removeImeSurface(); } } catch (RemoteException e) { } @@ -4959,10 +4993,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: { IBinder windowToken = (IBinder) msg.obj; synchronized (ImfLock.class) { + // TODO(b/305849394): Infer userId from windowToken. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); try { - if (windowToken == mImeBindingState.mFocusedWindow - && mEnabledSession != null && mEnabledSession.mSession != null) { - mEnabledSession.mSession.removeImeSurface(); + if (windowToken == userData.mImeBindingState.mFocusedWindow + && userData.mEnabledSession != null + && userData.mEnabledSession.mSession != null) { + userData.mEnabledSession.mSession.removeImeSurface(); } } catch (RemoteException e) { } @@ -5015,9 +5053,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. synchronized (ImfLock.class) { final var bindingController = getInputMethodBindingController(mCurrentUserId); if (bindingController.supportsStylusHandwriting() - && getCurMethodLocked() != null && hasSupportedStylusLocked()) { + && bindingController.getCurMethod() != null + && hasSupportedStylusLocked()) { Slog.d(TAG, "Initializing Handwriting Spy"); - mHwController.initializeHandwritingSpy(getCurTokenDisplayIdLocked()); + mHwController.initializeHandwritingSpy( + bindingController.getCurTokenDisplayId()); } else { mHwController.reset(); } @@ -5034,18 +5074,21 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } return true; case MSG_START_HANDWRITING: + final var handwritingRequest = (HandwritingRequest) msg.obj; synchronized (ImfLock.class) { - IInputMethodInvoker curMethod = getCurMethodLocked(); - if (curMethod == null || mImeBindingState.mFocusedWindow == null) { + final int userId = handwritingRequest.userId; + final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); + IInputMethodInvoker curMethod = bindingController.getCurMethod(); + if (curMethod == null || userData.mImeBindingState.mFocusedWindow == null) { return true; } - final var bindingController = getInputMethodBindingController(mCurrentUserId); final HandwritingModeController.HandwritingSession session = mHwController.startHandwritingSession( - msg.arg1 /*requestId*/, - msg.arg2 /*pid*/, + handwritingRequest.requestId, + handwritingRequest.pid, bindingController.getCurMethodUid(), - mImeBindingState.mFocusedWindow); + userData.mImeBindingState.mFocusedWindow); if (session == null) { Slog.e(TAG, "Failed to start handwriting session for requestId: " + msg.arg1); @@ -5080,9 +5123,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } + private record HandwritingRequest(int requestId, int pid, @UserIdInt int userId) { } + @BinderThread - private void onStylusHandwritingReady(int requestId, int pid) { - mHandler.obtainMessage(MSG_START_HANDWRITING, requestId, pid).sendToTarget(); + private void onStylusHandwritingReady(int requestId, int pid, @UserIdInt int userId) { + mHandler.obtainMessage(MSG_START_HANDWRITING, + new HandwritingRequest(requestId, pid, userId)).sendToTarget(); } private void handleSetInteractive(final boolean interactive) { @@ -5090,8 +5136,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mIsInteractive = interactive; updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition); + // TODO(b/305849394): Support multiple IMEs. + final var userId = mCurrentUserId; + final var userData = getUserData(userId); // Inform the current client of the change in active status - if (mCurClient == null || mCurClient.mClient == null) { + if (userData.mCurClient == null || userData.mCurClient.mClient == null) { return; } // TODO(b/325515685): user data must be retrieved by a userId parameter @@ -5101,18 +5150,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Handle IME visibility when interactive changed before finishing the input to // ensure we preserve the last state as possible. final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged( - mImeBindingState.mFocusedWindow, interactive); + userData.mImeBindingState.mFocusedWindow, interactive); if (imeVisRes != null) { // Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker. - mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, + mVisibilityApplier.applyImeVisibility(userData.mImeBindingState.mFocusedWindow, null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason(), - mCurrentUserId); + userId); } // Eligible IME processes use new "setInteractive" protocol. - mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode); + userData.mCurClient.mClient.setInteractive(mIsInteractive, + userData.mInFullscreenMode); } else { // Legacy IME processes continue using legacy "setActive" protocol. - mCurClient.mClient.setActive(mIsInteractive, mInFullscreenMode); + userData.mCurClient.mClient.setActive(mIsInteractive, userData.mInFullscreenMode); } } } @@ -5229,7 +5279,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - void postInputMethodSettingUpdatedLocked(boolean resetDefaultEnabledIme) { + void postInputMethodSettingUpdatedLocked(boolean resetDefaultEnabledIme, + @UserIdInt int userId) { if (DEBUG) { Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme + " \n ------ caller=" + Debug.getCallers(10)); @@ -5239,7 +5290,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return; } - final int userId = mCurrentUserId; final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); boolean reenableMinimumNonAuxSystemImes = false; @@ -5292,7 +5342,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!settings.getMethodMap().containsKey(defaultImiId)) { Slog.w(TAG, "Default IME is uninstalled. Choose new default IME."); if (chooseNewDefaultIMELocked()) { - updateInputMethodsFromSettingsLocked(true); + updateInputMethodsFromSettingsLocked(true, userId); } } else { // Double check that the default IME is certainly enabled. @@ -5418,11 +5468,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId, - boolean setSubtypeOnly) { - final int userId = mCurrentUserId; + boolean setSubtypeOnly, @UserIdInt int userId) { final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final var bindingController = getInputMethodBindingController(userId); - settings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(), + settings.saveCurrentInputMethodAndSubtypeToHistory(bindingController.getSelectedMethodId(), bindingController.getCurrentSubtype()); // Set Subtype here @@ -5456,11 +5505,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) { - final var bindingController = getInputMethodBindingController(mCurrentUserId); + // TODO(b/305849394): get userId from callers + final int userId = mCurrentUserId; + final var bindingController = getInputMethodBindingController(userId); bindingController.setDisplayIdToShowIme(INVALID_DISPLAY); bindingController.setDeviceIdToShowIme(DEVICE_ID_DEFAULT); - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); settings.putSelectedDefaultDeviceInputMethod(null); InputMethodInfo imi = settings.getMethodMap().get(newDefaultIme); @@ -5477,7 +5528,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } } - setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false); + setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false, userId); } /** @@ -5561,7 +5612,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. .contains(settings.getMethodMap().get(imeId))) { return false; // IME is not found or not enabled. } - setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID); + setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID, userId); return true; } if (!settings.getMethodMap().containsKey(imeId) @@ -5603,15 +5654,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @GuardedBy("ImfLock.class") - private void switchKeyboardLayoutLocked(int direction) { - final int userId = mCurrentUserId; + private void switchKeyboardLayoutLocked(int direction, @UserIdInt int userId) { final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - final InputMethodInfo currentImi = settings.getMethodMap().get(getSelectedMethodIdLocked()); + final var bindingController = getInputMethodBindingController(userId); + final InputMethodInfo currentImi = settings.getMethodMap().get( + bindingController.getSelectedMethodId()); if (currentImi == null) { return; } - final var bindingController = getInputMethodBindingController(userId); final InputMethodSubtypeHandle currentSubtypeHandle = InputMethodSubtypeHandle.of(currentImi, bindingController.getCurrentSubtype()); final InputMethodSubtypeHandle nextSubtypeHandle = @@ -5628,7 +5679,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final int subtypeCount = nextImi.getSubtypeCount(); if (subtypeCount == 0) { if (nextSubtypeHandle.equals(InputMethodSubtypeHandle.of(nextImi, null))) { - setInputMethodLocked(nextImi.getId(), NOT_A_SUBTYPE_ID); + setInputMethodLocked(nextImi.getId(), NOT_A_SUBTYPE_ID, userId); } return; } @@ -5636,7 +5687,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. for (int i = 0; i < subtypeCount; ++i) { if (nextSubtypeHandle.equals( InputMethodSubtypeHandle.of(nextImi, nextImi.getSubtypeAt(i)))) { - setInputMethodLocked(nextImi.getId(), i); + setInputMethodLocked(nextImi.getId(), i, userId); return; } } @@ -5753,7 +5804,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. //TODO(b/150843766): Check if Input Token is valid. final IBinder curHostInputToken; synchronized (ImfLock.class) { - if (displayId != getCurTokenDisplayIdLocked()) { + final var bindingController = getInputMethodBindingController(userId); + if (displayId != bindingController.getCurTokenDisplayId()) { return false; } curHostInputToken = getInputMethodBindingController(userId).getCurHostInputToken(); @@ -5767,7 +5819,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void reportImeControl(@Nullable IBinder windowToken) { synchronized (ImfLock.class) { - if (mImeBindingState.mFocusedWindow != windowToken) { + // TODO(b/305849394): Need to infer userId or get userId from callers. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); + if (userData.mImeBindingState.mFocusedWindow != windowToken) { // A perceptible value was set for the focused window, but it is no longer in // control, so we reset the perceptible for the window passed as argument. // TODO(b/314149476): Investigate whether this logic is still relevant, if not @@ -5780,10 +5835,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void onImeParentChanged(int displayId) { synchronized (ImfLock.class) { + // TODO(b/305849394): Need to infer userId or get userId from callers. + final int userId = mCurrentUserId; + final var userData = getUserData(userId); // Hide the IME method menu only when the IME surface parent is changed by the // input target changed, in case seeing the dialog dismiss flickering during // the next focused window starting the input connection. - if (mLastImeTargetWindow != mImeBindingState.mFocusedWindow) { + if (mLastImeTargetWindow != userData.mImeBindingState.mFocusedWindow) { mMenuController.hideInputMethodMenuLocked(); } } @@ -5806,33 +5864,36 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public void onSessionForAccessibilityCreated(int accessibilityConnectionId, IAccessibilityInputMethodSession session, @UserIdInt int userId) { synchronized (ImfLock.class) { - final var bindingController = getInputMethodBindingController(mCurrentUserId); + final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); // TODO(b/305829876): Implement user ID verification - if (mCurClient != null) { - clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId); - mCurClient.mAccessibilitySessions.put( + if (userData.mCurClient != null) { + clearClientSessionForAccessibilityLocked(userData.mCurClient, + accessibilityConnectionId); + userData.mCurClient.mAccessibilitySessions.put( accessibilityConnectionId, - new AccessibilitySessionState(mCurClient, + new AccessibilitySessionState(userData.mCurClient, accessibilityConnectionId, session)); attachNewAccessibilityLocked(StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY, - true); + true, userId); - final SessionState sessionState = mCurClient.mCurSession; + final SessionState sessionState = userData.mCurClient.mCurSession; final IInputMethodSession imeSession = sessionState == null ? null : sessionState.mSession; final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions = createAccessibilityInputMethodSessions( - mCurClient.mAccessibilitySessions); + userData.mCurClient.mAccessibilitySessions); final InputBindResult res = new InputBindResult( InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION, imeSession, accessibilityInputMethodSessions, /* channel= */ null, bindingController.getCurId(), bindingController.getSequenceNumber(), /* isInputMethodSuppressingSpellChecker= */ false); - mCurClient.mClient.onBindAccessibilityService(res, accessibilityConnectionId); + userData.mCurClient.mClient.onBindAccessibilityService(res, + accessibilityConnectionId); } } } @@ -5841,33 +5902,34 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId, @UserIdInt int userId) { synchronized (ImfLock.class) { - final var bindingController = getInputMethodBindingController(mCurrentUserId); + final var bindingController = getInputMethodBindingController(userId); + final var userData = getUserData(userId); // TODO(b/305829876): Implement user ID verification - if (mCurClient != null) { + if (userData.mCurClient != null) { if (DEBUG) { Slog.v(TAG, "unbindAccessibilityFromCurrentClientLocked: client=" - + mCurClient.mClient.asBinder()); + + userData.mCurClient.mClient.asBinder()); } // A11yManagerService unbinds the disabled accessibility service. We don't need // to do it here. - mCurClient.mClient.onUnbindAccessibilityService( + userData.mCurClient.mClient.onUnbindAccessibilityService( bindingController.getSequenceNumber(), accessibilityConnectionId); } // We only have sessions when we bound to an input method. Remove this session // from all clients. - if (getCurMethodLocked() != null) { + if (bindingController.getCurMethod() != null) { // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = c -> clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId); mClientController.forAllClients(clearClientSession); - AccessibilitySessionState session = mEnabledAccessibilitySessions.get( + AccessibilitySessionState session = userData.mEnabledAccessibilitySessions.get( accessibilityConnectionId); if (session != null) { finishSessionForAccessibilityLocked(session); - mEnabledAccessibilitySessions.remove(accessibilityConnectionId); + userData.mEnabledAccessibilitySessions.remove(accessibilityConnectionId); } } } @@ -5884,14 +5946,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public void onSwitchKeyboardLayoutShortcut(int direction, int displayId, IBinder targetWindowToken) { synchronized (ImfLock.class) { - switchKeyboardLayoutLocked(direction); + // TODO(b/305849394): Infer userId from displayId + switchKeyboardLayoutLocked(direction, mCurrentUserId); } } } @BinderThread private IInputContentUriToken createInputContentUriToken(@Nullable IBinder token, - @Nullable Uri contentUri, @Nullable String packageName) { + @Nullable Uri contentUri, @Nullable String packageName, @UserIdInt int imeUserId) { if (token == null) { throw new NullPointerException("token"); } @@ -5908,15 +5971,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. synchronized (ImfLock.class) { final int uid = Binder.getCallingUid(); - final int imeUserId = UserHandle.getUserId(uid); - if (imeUserId != mCurrentUserId) { - // Currently concurrent multi-user is not supported here due to the remaining - // dependency on mCurEditorInfo and mCurClient. - // TODO(b/341558132): Remove this early-exit once it becomes multi-user ready. - Slog.i(TAG, "Ignoring createInputContentUriToken due to user ID mismatch." - + " imeUserId=" + imeUserId + " mCurrentUserId=" + mCurrentUserId); - return null; - } final var bindingController = getInputMethodBindingController(imeUserId); if (bindingController.getSelectedMethodId() == null) { return null; @@ -5929,16 +5983,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // We cannot simply distinguish a bad IME that reports an arbitrary package name from // an unfortunate IME whose internal state is already obsolete due to the asynchronous // nature of our system. Let's compare it with our internal record. - // TODO(b/341558132): Use "imeUserId" to query per-user "curEditorInfo" - final var curPackageName = mCurEditorInfo != null ? mCurEditorInfo.packageName : null; + final var userData = getUserData(imeUserId); + final var curPackageName = userData.mCurEditorInfo != null + ? userData.mCurEditorInfo.packageName : null; if (!TextUtils.equals(curPackageName, packageName)) { Slog.e(TAG, "Ignoring createInputContentUriToken mCurEditorInfo.packageName=" + curPackageName + " packageName=" + packageName); return null; } // This user ID can never be spoofed. - // TODO(b/341558132): Use "imeUserId" to query per-user "curClient" - final int appUserId = UserHandle.getUserId(mCurClient.mUid); + final int appUserId = UserHandle.getUserId(userData.mCurClient.mUid); // This user ID may be invalid if "contentUri" embedded an invalid user ID. final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri, imeUserId); @@ -5955,14 +6009,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } @BinderThread - private void reportFullscreenMode(@NonNull IBinder token, boolean fullscreen) { + private void reportFullscreenMode(@NonNull IBinder token, boolean fullscreen, + @UserIdInt int userId) { synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { return; } - if (mCurClient != null && mCurClient.mClient != null) { - mInFullscreenMode = fullscreen; - mCurClient.mClient.reportFullscreenMode(fullscreen); + final var userData = getUserData(userId); + if (userData.mCurClient != null && userData.mCurClient.mClient != null) { + userData.mInFullscreenMode = fullscreen; + userData.mCurClient.mClient.reportFullscreenMode(fullscreen); } } } @@ -6055,7 +6111,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final Printer p = new PrintWriterPrinter(pw); synchronized (ImfLock.class) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); + final var userData = getUserData(userId); p.println("Current Input Method Manager state:"); final List<InputMethodInfo> methodList = settings.getMethodList(); int numImes = methodList.size(); @@ -6085,16 +6143,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mClientController.forAllClients(clientControllerDump); final var bindingController = getInputMethodBindingController(mCurrentUserId); p.println(" mCurrentUserId=" + mCurrentUserId); - p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); - client = mCurClient; + p.println(" mCurMethodId=" + bindingController.getSelectedMethodId()); + client = userData.mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + bindingController.getSequenceNumber()); p.println(" mFocusedWindowPerceptible=" + mFocusedWindowPerceptible); - mImeBindingState.dump(/* prefix= */ " ", p); + userData.mImeBindingState.dump(/* prefix= */ " ", p); p.println(" mCurId=" + bindingController.getCurId() + " mHaveConnection=" + bindingController.hasMainConnection() - + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + + " mBoundToMethod=" + userData.mBoundToMethod + " mVisibleBound=" + bindingController.isVisibleBound()); p.println(" mUserDataRepository="); @@ -6110,15 +6168,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. }; mUserDataRepository.forAllUserData(userDataDump); - p.println(" mCurToken=" + getCurTokenLocked()); - p.println(" mCurTokenDisplayId=" + getCurTokenDisplayIdLocked()); + p.println(" mCurToken=" + bindingController.getCurToken()); + p.println(" mCurTokenDisplayId=" + bindingController.getCurTokenDisplayId()); p.println(" mCurHostInputToken=" + bindingController.getCurHostInputToken()); p.println(" mCurIntent=" + bindingController.getCurIntent()); - method = getCurMethodLocked(); - p.println(" mCurMethod=" + getCurMethodLocked()); - p.println(" mEnabledSession=" + mEnabledSession); + method = bindingController.getCurMethod(); + p.println(" mCurMethod=" + method); + p.println(" mEnabledSession=" + userData.mEnabledSession); mVisibilityStateComputer.dump(pw, " "); - p.println(" mInFullscreenMode=" + mInFullscreenMode); + p.println(" mInFullscreenMode=" + userData.mInFullscreenMode); p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive); p.println(" mExperimentalConcurrentMultiUserModeEnabled=" + mExperimentalConcurrentMultiUserModeEnabled); @@ -6154,20 +6212,24 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } else { p.println("No input method client."); } - - if (mImeBindingState.mFocusedWindowClient != null - && client != mImeBindingState.mFocusedWindowClient) { - p.println(" "); - p.println("Warning: Current input method client doesn't match the last focused. " - + "window."); - p.println("Dumping input method client in the last focused window just in case."); - p.println(" "); - pw.flush(); - try { - TransferPipe.dumpAsync( - mImeBindingState.mFocusedWindowClient.mClient.asBinder(), fd, args); - } catch (IOException | RemoteException e) { - p.println("Failed to dump input method client in focused window: " + e); + synchronized (ImfLock.class) { + final int userId = mCurrentUserId; + final var userData = getUserData(userId); + if (userData.mImeBindingState.mFocusedWindowClient != null + && client != userData.mImeBindingState.mFocusedWindowClient) { + p.println(" "); + p.println("Warning: Current input method client doesn't match the last focused. " + + "window."); + p.println("Dumping input method client in the last focused window just in case."); + p.println(" "); + pw.flush(); + try { + TransferPipe.dumpAsync( + userData.mImeBindingState.mFocusedWindowClient.mClient.asBinder(), fd, + args); + } catch (IOException | RemoteException e) { + p.println("Failed to dump input method client in focused window: " + e); + } } } @@ -6603,18 +6665,21 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final List<InputMethodInfo> nextEnabledImes; final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (userId == mCurrentUserId) { + final var userData = getUserData(userId); if (Flags.refactorInsetsController()) { - if (mImeBindingState != null - && mImeBindingState.mFocusedWindowClient != null - && mImeBindingState.mFocusedWindowClient.mClient != null) { - mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility( - false); + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient + != null) { + userData.mImeBindingState.mFocusedWindowClient.mClient + .setImeVisibility(false); } else { // TODO(b329229469): ImeTracker? } } else { - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, + 0 /* flags */, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, userId); } final var bindingController = getInputMethodBindingController(userId); bindingController.unbindCurrentMethod(); @@ -6634,7 +6699,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!chooseNewDefaultIMELocked()) { resetSelectedInputMethodAndSubtypeLocked(null); } - updateInputMethodsFromSettingsLocked(true /* enabledMayChange */); + updateInputMethodsFromSettingsLocked(true /* enabledMayChange */, userId); InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( getPackageManagerForUser(mContext, settings.getUserId()), settings.getEnabledInputMethodList()); @@ -6743,13 +6808,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * @param reason the reason why the IME request was created */ @NonNull + @GuardedBy("ImfLock.class") private ImeTracker.Token createStatsTokenForFocusedClient(boolean show, - @SoftInputShowHideReason int reason) { - final int uid = mImeBindingState.mFocusedWindowClient != null - ? mImeBindingState.mFocusedWindowClient.mUid + @SoftInputShowHideReason int reason, @UserIdInt int userId) { + final var userData = getUserData(userId); + final int uid = userData.mImeBindingState.mFocusedWindowClient != null + ? userData.mImeBindingState.mFocusedWindowClient.mUid : -1; - final var packageName = mImeBindingState.mFocusedWindowEditorInfo != null - ? mImeBindingState.mFocusedWindowEditorInfo.packageName + final var packageName = userData.mImeBindingState.mFocusedWindowEditorInfo != null + ? userData.mImeBindingState.mFocusedWindowEditorInfo.packageName : "uid(" + uid + ")"; return ImeTracker.forLogging().onStart(packageName, uid, @@ -6762,11 +6829,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private final InputMethodManagerService mImms; @NonNull private final IBinder mToken; + @UserIdInt + private final int mUserId; InputMethodPrivilegedOperationsImpl(InputMethodManagerService imms, - @NonNull IBinder token) { + @NonNull IBinder token, @UserIdInt int userId) { mImms = imms; mToken = token; + mUserId = userId; } @BinderThread @@ -6794,7 +6864,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @SuppressWarnings("unchecked") final AndroidFuture<IBinder> typedFuture = future; try { typedFuture.complete(mImms.createInputContentUriToken( - mToken, contentUri, packageName).asBinder()); + mToken, contentUri, packageName, mUserId).asBinder()); } catch (Throwable e) { typedFuture.completeExceptionally(e); } @@ -6803,7 +6873,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread @Override public void reportFullscreenModeAsync(boolean fullscreen) { - mImms.reportFullscreenMode(mToken, fullscreen); + mImms.reportFullscreenMode(mToken, fullscreen, mUserId); } @BinderThread @@ -6811,7 +6881,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public void setInputMethod(String id, AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { - mImms.setInputMethod(mToken, id); + mImms.setInputMethod(mToken, id, mUserId); typedFuture.complete(null); } catch (Throwable e) { typedFuture.completeExceptionally(e); @@ -6824,7 +6894,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { - mImms.setInputMethodAndSubtype(mToken, id, subtype); + mImms.setInputMethodAndSubtype(mToken, id, subtype, mUserId); typedFuture.complete(null); } catch (Throwable e) { typedFuture.completeExceptionally(e); @@ -6838,7 +6908,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { - mImms.hideMySoftInput(mToken, statsToken, flags, reason); + mImms.hideMySoftInput(mToken, statsToken, flags, reason, mUserId); typedFuture.complete(null); } catch (Throwable e) { typedFuture.completeExceptionally(e); @@ -6852,7 +6922,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { - mImms.showMySoftInput(mToken, statsToken, flags, reason); + mImms.showMySoftInput(mToken, statsToken, flags, reason, mUserId); typedFuture.complete(null); } catch (Throwable e) { typedFuture.completeExceptionally(e); @@ -6870,7 +6940,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public void switchToPreviousInputMethod(AndroidFuture future /* T=Boolean */) { @SuppressWarnings("unchecked") final AndroidFuture<Boolean> typedFuture = future; try { - typedFuture.complete(mImms.switchToPreviousInputMethod(mToken)); + typedFuture.complete(mImms.switchToPreviousInputMethod(mToken, mUserId)); } catch (Throwable e) { typedFuture.completeExceptionally(e); } @@ -6882,7 +6952,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. AndroidFuture future /* T=Boolean */) { @SuppressWarnings("unchecked") final AndroidFuture<Boolean> typedFuture = future; try { - typedFuture.complete(mImms.switchToNextInputMethod(mToken, onlyCurrentIme)); + typedFuture.complete(mImms.switchToNextInputMethod(mToken, onlyCurrentIme, + mUserId)); } catch (Throwable e) { typedFuture.completeExceptionally(e); } @@ -6893,7 +6964,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. public void shouldOfferSwitchingToNextInputMethod(AndroidFuture future /* T=Boolean */) { @SuppressWarnings("unchecked") final AndroidFuture<Boolean> typedFuture = future; try { - typedFuture.complete(mImms.shouldOfferSwitchingToNextInputMethod(mToken)); + typedFuture.complete(mImms.shouldOfferSwitchingToNextInputMethod(mToken, mUserId)); } catch (Throwable e) { typedFuture.completeExceptionally(e); } @@ -6902,20 +6973,20 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @BinderThread @Override public void notifyUserActionAsync() { - mImms.notifyUserAction(mToken); + mImms.notifyUserAction(mToken, mUserId); } @BinderThread @Override public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible, @NonNull ImeTracker.Token statsToken) { - mImms.applyImeVisibility(mToken, windowToken, setVisible, statsToken); + mImms.applyImeVisibility(mToken, windowToken, setVisible, statsToken, mUserId); } @BinderThread @Override public void onStylusHandwritingReady(int requestId, int pid) { - mImms.onStylusHandwritingReady(requestId, pid); + mImms.onStylusHandwritingReady(requestId, pid, mUserId); } @BinderThread @@ -6933,7 +7004,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } final long ident = Binder.clearCallingIdentity(); try { - mImms.switchKeyboardLayoutLocked(direction); + mImms.switchKeyboardLayoutLocked(direction, mUserId); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java index 326ef7e055fa..89a31e7a9a0b 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java @@ -79,6 +79,7 @@ final class InputMethodMenuController { if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes); final int userId = mService.getCurrentImeUserIdLocked(); + final var bindingController = mService.getInputMethodBindingController(userId); hideInputMethodMenuLocked(); @@ -86,9 +87,9 @@ final class InputMethodMenuController { final InputMethodSubtype currentSubtype = mService.getCurrentInputMethodSubtypeLocked(); if (currentSubtype != null) { - final String curMethodId = mService.getSelectedMethodIdLocked(); + final String curMethodId = bindingController.getSelectedMethodId(); final InputMethodInfo currentImi = - mService.queryInputMethodForCurrentUserLocked(curMethodId); + InputMethodSettingsRepository.get(userId).getMethodMap().get(curMethodId); preferredInputMethodSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode( currentImi, currentSubtype.hashCode()); } @@ -179,7 +180,7 @@ final class InputMethodMenuController { if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) { subtypeId = NOT_A_SUBTYPE_ID; } - mService.setInputMethodLocked(im.getId(), subtypeId); + mService.setInputMethodLocked(im.getId(), subtypeId, userId); } hideInputMethodMenuLocked(); } diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java index 3da4a14b10be..48284fb3163c 100644 --- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java +++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java @@ -17,12 +17,18 @@ package com.android.server.inputmethod; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.UserInfo; import android.os.Handler; import android.util.SparseArray; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ImeTracker; +import android.window.ImeOnBackInvokedDispatcher; import com.android.internal.annotations.GuardedBy; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; +import com.android.internal.inputmethod.IRemoteInputConnection; import com.android.server.pm.UserManagerInternal; import java.util.function.Consumer; @@ -96,6 +102,78 @@ final class UserDataRepository { final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController; /** + * Have we called mCurMethod.bindInput()? + */ + @GuardedBy("ImfLock.class") + boolean mBoundToMethod = false; + + /** + * Have we called bindInput() for accessibility services? + */ + @GuardedBy("ImfLock.class") + boolean mBoundToAccessibility; + + @GuardedBy("ImfLock.class") + @NonNull + ImeBindingState mImeBindingState = ImeBindingState.newEmptyState(); + + @GuardedBy("ImfLock.class") + @Nullable + ClientState mCurClient = null; + + @GuardedBy("ImfLock.class") + boolean mInFullscreenMode; + + /** + * The {@link IRemoteInputConnection} last provided by the current client. + */ + @GuardedBy("ImfLock.class") + @Nullable + IRemoteInputConnection mCurInputConnection; + + /** + * The {@link ImeOnBackInvokedDispatcher} last provided by the current client to + * receive {@link android.window.OnBackInvokedCallback}s forwarded from IME. + */ + @GuardedBy("ImfLock.class") + @Nullable + ImeOnBackInvokedDispatcher mCurImeDispatcher; + + /** + * The {@link IRemoteAccessibilityInputConnection} last provided by the current client. + */ + @GuardedBy("ImfLock.class") + @Nullable + IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection; + + /** + * The {@link EditorInfo} last provided by the current client. + */ + @GuardedBy("ImfLock.class") + @Nullable + EditorInfo mCurEditorInfo; + + /** + * The token tracking the current IME show request that is waiting for a connection to an + * IME, otherwise {@code null}. + */ + @GuardedBy("ImfLock.class") + @Nullable + ImeTracker.Token mCurStatsToken; + + /** + * Currently enabled session. + */ + @GuardedBy("ImfLock.class") + @Nullable + InputMethodManagerService.SessionState mEnabledSession; + + @GuardedBy("ImfLock.class") + @Nullable + SparseArray<InputMethodManagerService.AccessibilitySessionState> + mEnabledAccessibilitySessions = new SparseArray<>(); + + /** * Intended to be instantiated only from this file. */ private UserData(@UserIdInt int userId, diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java index 757c07c0b683..41aac32ad08f 100644 --- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java +++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java @@ -254,7 +254,7 @@ final class ZeroJankProxy implements IInputMethodManagerImpl.Callback { synchronized (ImfLock.class) { ClientState cs = imms.getClientStateLocked(client); if (cs != null) { - imms.requestClientSessionLocked(cs); + imms.requestClientSessionLocked(cs, userId); imms.requestClientSessionForAccessibilityLocked(cs); } } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index d4adba209cd9..9acebf71ee18 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -217,8 +217,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, statsToken, 0 /* flags */, null /* resultReceiver */, - HIDE_SWITCH_USER); - mInputMethodManagerService.onUnbindCurrentMethodByReset(); + HIDE_SWITCH_USER, mUserId); + mInputMethodManagerService.onUnbindCurrentMethodByReset(mUserId); // Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing // the IME hidden state. |