Loading core/java/android/app/SystemServiceRegistry.java +3 −7 Original line number Diff line number Diff line Loading @@ -377,15 +377,11 @@ final class SystemServiceRegistry { return new DisplayManager(ctx.getOuterContext()); }}); // InputMethodManager has its own cache strategy based on display id to support apps that // still assume InputMethodManager is a per-process singleton and it's safe to directly // access internal fields via reflection. Hence directly use ServiceFetcher instead of // StaticServiceFetcher/CachedServiceFetcher. registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class, new ServiceFetcher<InputMethodManager>() { new StaticServiceFetcher<InputMethodManager>() { @Override public InputMethodManager getService(ContextImpl ctx) { return InputMethodManager.forContext(ctx); public InputMethodManager createService() { return InputMethodManager.getInstanceInternal(); }}); registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, Loading core/java/android/view/inputmethod/InputMethodManager.java +15 −155 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.util.Pools.SimplePool; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; import android.view.Display; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; Loading Loading @@ -266,7 +265,7 @@ public final class InputMethodManager { * @hide */ public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper()); getInstanceInternal(); } private static final Object sLock = new Object(); Loading @@ -279,17 +278,6 @@ public final class InputMethodManager { @UnsupportedAppUsage static InputMethodManager sInstance; /** * Global map between display to {@link InputMethodManager}. * * <p>Currently this map works like a so-called leaky singleton. Once an instance is registered * for the associated display ID, that instance will never be garbage collected.</p> * * <p>TODO(Bug 116699479): Implement instance clean up mechanism.</p> */ @GuardedBy("sLock") private static final SparseArray<InputMethodManager> sInstanceMap = new SparseArray<>(); /** * @hide Flag for IInputMethodManager.windowGainedFocus: a view in * the window has input focus. Loading Loading @@ -347,8 +335,6 @@ public final class InputMethodManager { // Our generic input connection if the current target does not have its own. final IInputContext mIInputContext; private final int mDisplayId; /** * True if this input method client is active, initially false. */ Loading Loading @@ -466,29 +452,6 @@ public final class InputMethodManager { return afm != null && afm.isAutofillUiShowing(); } /** * Checks the consistency between {@link InputMethodManager} state and {@link View} state. * * @param view {@link View} to be checked * @return {@code true} if {@code view} is not {@code null} and there is a {@link Context} * mismatch between {@link InputMethodManager} and {@code view} */ private boolean shouldDispatchToViewContext(@Nullable View view) { if (view == null) { return false; } final int viewDisplayId = getDisplayId(view.getContext()); if (viewDisplayId != mDisplayId) { Log.w(TAG, "b/117267690: Context mismatch found. view=" + view + " belongs to" + " displayId=" + viewDisplayId + " but InputMethodManager belongs to displayId=" + mDisplayId + ". Use the right InputMethodManager instance to avoid performance overhead.", new Throwable()); return true; } return false; } private static boolean canStartInput(View servedView) { // We can start input ether the servedView has window focus // or the activity is showing autofill ui. Loading Loading @@ -770,57 +733,33 @@ public final class InputMethodManager { }); } InputMethodManager(int displayId, Looper looper) throws ServiceNotFoundException { InputMethodManager(Looper looper) throws ServiceNotFoundException { mService = getIInputMethodManager(); mMainLooper = looper; mH = new H(looper); mDisplayId = displayId; mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this); } private static int getDisplayId(Context context) { final Display display = context.getDisplay(); return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY; } /** * Retrieve an instance for the given {@link Context}, creating it if it doesn't already exist. * Retrieve the global {@link InputMethodManager} instance, creating it if it doesn't already * exist. * * @param context {@link Context} for which IME APIs need to work * @return {@link InputMethodManager} instance * @return global {@link InputMethodManager} instance * @hide */ @Nullable public static InputMethodManager forContext(Context context) { final int displayId = getDisplayId(context); // For better backward compatibility, we always use Looper.getMainLooper() for the default // display case. final Looper looper = displayId == Display.DEFAULT_DISPLAY ? Looper.getMainLooper() : context.getMainLooper(); return forContextInternal(displayId, looper); } @Nullable private static InputMethodManager forContextInternal(int displayId, Looper looper) { final boolean isDefaultDisplay = displayId == Display.DEFAULT_DISPLAY; public static InputMethodManager getInstanceInternal() { synchronized (sLock) { InputMethodManager instance = sInstanceMap.get(displayId); if (instance != null) { return instance; } if (sInstance == null) { try { instance = new InputMethodManager(displayId, looper); instance.mService.addClient(instance.mClient, instance.mIInputContext, displayId); final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper()); imm.mService.addClient(imm.mClient, imm.mIInputContext); sInstance = imm; } catch (ServiceNotFoundException | RemoteException e) { throw new IllegalStateException(e); } // For backward compatibility, store the instance also to sInstance for default display. if (sInstance == null && isDefaultDisplay) { sInstance = instance; } sInstanceMap.put(displayId, instance); return instance; return sInstance; } } Loading Loading @@ -977,11 +916,6 @@ public final class InputMethodManager { * input method. */ public boolean isActive(View view) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { return view.getContext().getSystemService(InputMethodManager.class).isActive(view); } checkFocus(); synchronized (mH) { return (mServedView == view Loading Loading @@ -1072,13 +1006,6 @@ public final class InputMethodManager { } public void displayCompletions(View view, CompletionInfo[] completions) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .displayCompletions(view, completions); return; } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading @@ -1097,13 +1024,6 @@ public final class InputMethodManager { } public void updateExtractedText(View view, int token, ExtractedText text) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateExtractedText(view, token, text); return; } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading Loading @@ -1145,12 +1065,6 @@ public final class InputMethodManager { * 0 or have the {@link #SHOW_IMPLICIT} bit set. */ public boolean showSoftInput(View view, int flags) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { return view.getContext().getSystemService(InputMethodManager.class) .showSoftInput(view, flags); } return showSoftInput(view, flags, null); } Loading Loading @@ -1213,12 +1127,6 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. */ public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { return view.getContext().getSystemService(InputMethodManager.class) .showSoftInput(view, flags, resultReceiver); } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading Loading @@ -1382,12 +1290,6 @@ public final class InputMethodManager { * @param view The view whose text has changed. */ public void restartInput(View view) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class).restartInput(view); return; } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading Loading @@ -1812,13 +1714,6 @@ public final class InputMethodManager { */ public void updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && (mServedView == null Loading Loading @@ -1856,12 +1751,6 @@ public final class InputMethodManager { * Notify the event when the user tapped or clicked the text view. */ public void viewClicked(View view) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class).viewClicked(view); return; } final boolean focusChanged = mServedView != mNextServedView; checkFocus(); synchronized (mH) { Loading Loading @@ -1926,13 +1815,6 @@ public final class InputMethodManager { */ @Deprecated public void updateCursor(View view, int left, int top, int right, int bottom) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateCursor(view, left, top, right, bottom); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && (mServedView == null Loading Loading @@ -1964,13 +1846,6 @@ public final class InputMethodManager { if (view == null || cursorAnchorInfo == null) { return; } // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateCursorAnchorInfo(view, cursorAnchorInfo); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && Loading Loading @@ -2016,13 +1891,6 @@ public final class InputMethodManager { * @param data Any data to include with the command. */ public void sendAppPrivateCommand(View view, String action, Bundle data) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .sendAppPrivateCommand(view, action, data); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && (mServedView == null Loading Loading @@ -2194,13 +2062,6 @@ public final class InputMethodManager { */ public void dispatchKeyEventFromInputMethod(@Nullable View targetView, @NonNull KeyEvent event) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(targetView)) { targetView.getContext().getSystemService(InputMethodManager.class) .dispatchKeyEventFromInputMethod(targetView, event); return; } synchronized (mH) { ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; if (viewRootImpl == null) { Loading Loading @@ -2690,7 +2551,6 @@ public final class InputMethodManager { sb.append(",windowFocus=" + view.hasWindowFocus()); sb.append(",autofillUiShowing=" + isAutofillUIShowing(view)); sb.append(",window=" + view.getWindowToken()); sb.append(",displayId=" + getDisplayId(view.getContext())); sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); return sb.toString(); } Loading core/java/com/android/internal/view/IInputMethodManager.aidl +1 −2 Original line number Diff line number Diff line Loading @@ -31,8 +31,7 @@ import com.android.internal.view.IInputMethodClient; * applications. */ interface IInputMethodManager { void addClient(in IInputMethodClient client, in IInputContext inputContext, int untrustedDisplayId); void addClient(in IInputMethodClient client, in IInputContext inputContext); // TODO: Use ParceledListSlice instead List<InputMethodInfo> getInputMethodList(); Loading core/java/com/android/internal/view/InputBindResult.java +1 −28 Original line number Diff line number Diff line Loading @@ -51,9 +51,6 @@ public final class InputBindResult implements Parcelable { ResultCode.ERROR_INVALID_USER, ResultCode.ERROR_NULL_EDITOR_INFO, ResultCode.ERROR_NOT_IME_TARGET_WINDOW, ResultCode.ERROR_NO_EDITOR, ResultCode.ERROR_DISPLAY_ID_MISMATCH, ResultCode.ERROR_INVALID_DISPLAY_ID, }) public @interface ResultCode { /** Loading Loading @@ -142,22 +139,13 @@ public final class InputBindResult implements Parcelable { * The client should try to restart input when its {@link android.view.Window} is focused * again.</p> * * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int) * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int) */ int ERROR_NOT_IME_TARGET_WINDOW = 11; /** * Indicates that focused view in the current window is not an editor. */ int ERROR_NO_EDITOR = 12; /** * Indicates that there is a mismatch in display ID between IME client and focused Window. */ int ERROR_DISPLAY_ID_MISMATCH = 13; /** * Indicates that current IME client is no longer allowed to access to the associated * display. */ int ERROR_INVALID_DISPLAY_ID = 14; } @ResultCode Loading Loading @@ -283,10 +271,6 @@ public final class InputBindResult implements Parcelable { return "ERROR_NULL_EDITOR_INFO"; case ResultCode.ERROR_NOT_IME_TARGET_WINDOW: return "ERROR_NOT_IME_TARGET_WINDOW"; case ResultCode.ERROR_DISPLAY_ID_MISMATCH: return "ERROR_DISPLAY_ID_MISMATCH"; case ResultCode.ERROR_INVALID_DISPLAY_ID: return "ERROR_INVALID_DISPLAY_ID"; default: return "Unknown(" + result + ")"; } Loading Loading @@ -332,15 +316,4 @@ public final class InputBindResult implements Parcelable { */ public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER); /** * Predefined error object for {@link ResultCode#ERROR_DISPLAY_ID_MISMATCH}. */ public static final InputBindResult DISPLAY_ID_MISMATCH = error(ResultCode.ERROR_DISPLAY_ID_MISMATCH); /** * Predefined error object for {@link ResultCode#ERROR_INVALID_DISPLAY_ID}. */ public static final InputBindResult INVALID_DISPLAY_ID = error(ResultCode.ERROR_INVALID_DISPLAY_ID); } services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +16 −58 Original line number Diff line number Diff line Loading @@ -421,7 +421,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final IInputContext inputContext; final int uid; final int pid; final int selfReportedDisplayId; final InputBinding binding; final ClientDeathRecipient clientDeathRecipient; Loading @@ -431,18 +430,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public String toString() { return "ClientState{" + Integer.toHexString( System.identityHashCode(this)) + " uid=" + uid + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}"; System.identityHashCode(this)) + " uid " + uid + " pid " + pid + "}"; } ClientState(IInputMethodClient _client, IInputContext _inputContext, int _uid, int _pid, int _selfReportedDisplayId, ClientDeathRecipient _clientDeathRecipient) { int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) { client = _client; inputContext = _inputContext; uid = _uid; pid = _pid; selfReportedDisplayId = _selfReportedDisplayId; binding = new InputBinding(null, inputContext.asBinder(), uid, pid); clientDeathRecipient = _clientDeathRecipient; } Loading Loading @@ -1748,21 +1745,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * process * @param inputContext communication channel for the dummy * {@link android.view.inputmethod.InputConnection} * @param selfReportedDisplayId self-reported display ID to which the client is associated. * Whether the client is still allowed to access to this display * or not needs to be evaluated every time the client interacts * with the display */ @Override public void addClient(IInputMethodClient client, IInputContext inputContext, int selfReportedDisplayId) { public void addClient(IInputMethodClient client, IInputContext inputContext) { final int callerUid = Binder.getCallingUid(); final int callerPid = Binder.getCallingPid(); synchronized (mMethodMap) { // TODO: Optimize this linear search. for (ClientState state : mClients.values()) { if (state.uid == callerUid && state.pid == callerPid && state.selfReportedDisplayId == selfReportedDisplayId) { if (state.uid == callerUid && state.pid == callerPid) { throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid + " is already registered"); } Loading @@ -1773,25 +1764,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { throw new IllegalStateException(e); } // We cannot fully avoid race conditions where the client UID already lost the access to // the given self-reported display ID, even if the client is not maliciously reporting // a fake display ID. Unconditionally returning SecurityException just because the // client doesn't pass display ID verification can cause many test failures hence not an // option right now. At the same time // context.getSystemService(InputMethodManager.class) // is expected to return a valid non-null instance at any time if we do not choose to // have the client crash. Thus we do not verify the display ID at all here. Instead we // later check the display ID every time the client needs to interact with the specified // display. mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid, callerPid, selfReportedDisplayId, deathRecipient)); mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid, callerPid, deathRecipient)); } } private boolean verifyDisplayId(ClientState cs) { return mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid); } void removeClient(IInputMethodClient client) { synchronized (mMethodMap) { ClientState cs = mClients.remove(client.asBinder()); Loading Loading @@ -1941,9 +1918,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurAttribute = attribute; // Check if the input method is changing. // We expect the caller has already verified that the client is allowed to access this // display ID. final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; final int displayId = mWindowManagerInternal.getDisplayIdForWindow( mCurFocusedWindow); if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) { if (cs.curSession != null) { // Fast case: if we are already connected to the input method, Loading Loading @@ -2008,11 +1984,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub com.android.internal.R.string.input_method_binding_label); mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); if (!verifyDisplayId(mCurFocusedWindowClient)) { // Wait, the client no longer has access to the display. return InputBindResult.INVALID_DISPLAY_ID; } final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow); mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY; if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) { Loading Loading @@ -2612,8 +2584,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, cs.selfReportedDisplayId)) { if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); return false; } Loading Loading @@ -2697,8 +2668,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, cs.selfReportedDisplayId)) { if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { if (DEBUG) { Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); } Loading Loading @@ -2797,8 +2767,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputBindResult res = null; long ident = Binder.clearCallingIdentity(); try { final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); synchronized (mMethodMap) { if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason=" + InputMethodClient.getStartInputReason(startInputReason) Loading @@ -2817,15 +2785,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (cs.selfReportedDisplayId != windowDisplayId) { Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch." + " from client:" + cs.selfReportedDisplayId + " from window:" + windowDisplayId); return InputBindResult.DISPLAY_ID_MISMATCH; } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, cs.selfReportedDisplayId)) { if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { // Check with the window manager to make sure this client actually // has a window with focus. If not, reject. This is thread safe // because if the focus changes some time before or after, the Loading Loading @@ -2897,9 +2858,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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.selfReportedDisplayId != mCurTokenDisplayId) { final int newFocusDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); if (newFocusDisplayId != mCurTokenDisplayId) { unbindCurrentMethodLocked(); } } Loading Loading @@ -2997,7 +2958,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { // TODO(yukawa): multi-display support. final int uid = Binder.getCallingUid(); if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { return true; Loading Loading @@ -3074,7 +3034,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void showInputMethodAndSubtypeEnablerFromClient( IInputMethodClient client, String inputMethodId) { // TODO(yukawa): Should we verify the display ID? if (!calledFromValidUser()) { return; } Loading Loading @@ -3270,7 +3229,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ @Override public int getInputMethodWindowVisibleHeight() { // TODO(yukawa): Should we verify the display ID? return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); } Loading Loading
core/java/android/app/SystemServiceRegistry.java +3 −7 Original line number Diff line number Diff line Loading @@ -377,15 +377,11 @@ final class SystemServiceRegistry { return new DisplayManager(ctx.getOuterContext()); }}); // InputMethodManager has its own cache strategy based on display id to support apps that // still assume InputMethodManager is a per-process singleton and it's safe to directly // access internal fields via reflection. Hence directly use ServiceFetcher instead of // StaticServiceFetcher/CachedServiceFetcher. registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class, new ServiceFetcher<InputMethodManager>() { new StaticServiceFetcher<InputMethodManager>() { @Override public InputMethodManager getService(ContextImpl ctx) { return InputMethodManager.forContext(ctx); public InputMethodManager createService() { return InputMethodManager.getInstanceInternal(); }}); registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, Loading
core/java/android/view/inputmethod/InputMethodManager.java +15 −155 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.util.Pools.SimplePool; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; import android.view.Display; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; Loading Loading @@ -266,7 +265,7 @@ public final class InputMethodManager { * @hide */ public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper()); getInstanceInternal(); } private static final Object sLock = new Object(); Loading @@ -279,17 +278,6 @@ public final class InputMethodManager { @UnsupportedAppUsage static InputMethodManager sInstance; /** * Global map between display to {@link InputMethodManager}. * * <p>Currently this map works like a so-called leaky singleton. Once an instance is registered * for the associated display ID, that instance will never be garbage collected.</p> * * <p>TODO(Bug 116699479): Implement instance clean up mechanism.</p> */ @GuardedBy("sLock") private static final SparseArray<InputMethodManager> sInstanceMap = new SparseArray<>(); /** * @hide Flag for IInputMethodManager.windowGainedFocus: a view in * the window has input focus. Loading Loading @@ -347,8 +335,6 @@ public final class InputMethodManager { // Our generic input connection if the current target does not have its own. final IInputContext mIInputContext; private final int mDisplayId; /** * True if this input method client is active, initially false. */ Loading Loading @@ -466,29 +452,6 @@ public final class InputMethodManager { return afm != null && afm.isAutofillUiShowing(); } /** * Checks the consistency between {@link InputMethodManager} state and {@link View} state. * * @param view {@link View} to be checked * @return {@code true} if {@code view} is not {@code null} and there is a {@link Context} * mismatch between {@link InputMethodManager} and {@code view} */ private boolean shouldDispatchToViewContext(@Nullable View view) { if (view == null) { return false; } final int viewDisplayId = getDisplayId(view.getContext()); if (viewDisplayId != mDisplayId) { Log.w(TAG, "b/117267690: Context mismatch found. view=" + view + " belongs to" + " displayId=" + viewDisplayId + " but InputMethodManager belongs to displayId=" + mDisplayId + ". Use the right InputMethodManager instance to avoid performance overhead.", new Throwable()); return true; } return false; } private static boolean canStartInput(View servedView) { // We can start input ether the servedView has window focus // or the activity is showing autofill ui. Loading Loading @@ -770,57 +733,33 @@ public final class InputMethodManager { }); } InputMethodManager(int displayId, Looper looper) throws ServiceNotFoundException { InputMethodManager(Looper looper) throws ServiceNotFoundException { mService = getIInputMethodManager(); mMainLooper = looper; mH = new H(looper); mDisplayId = displayId; mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this); } private static int getDisplayId(Context context) { final Display display = context.getDisplay(); return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY; } /** * Retrieve an instance for the given {@link Context}, creating it if it doesn't already exist. * Retrieve the global {@link InputMethodManager} instance, creating it if it doesn't already * exist. * * @param context {@link Context} for which IME APIs need to work * @return {@link InputMethodManager} instance * @return global {@link InputMethodManager} instance * @hide */ @Nullable public static InputMethodManager forContext(Context context) { final int displayId = getDisplayId(context); // For better backward compatibility, we always use Looper.getMainLooper() for the default // display case. final Looper looper = displayId == Display.DEFAULT_DISPLAY ? Looper.getMainLooper() : context.getMainLooper(); return forContextInternal(displayId, looper); } @Nullable private static InputMethodManager forContextInternal(int displayId, Looper looper) { final boolean isDefaultDisplay = displayId == Display.DEFAULT_DISPLAY; public static InputMethodManager getInstanceInternal() { synchronized (sLock) { InputMethodManager instance = sInstanceMap.get(displayId); if (instance != null) { return instance; } if (sInstance == null) { try { instance = new InputMethodManager(displayId, looper); instance.mService.addClient(instance.mClient, instance.mIInputContext, displayId); final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper()); imm.mService.addClient(imm.mClient, imm.mIInputContext); sInstance = imm; } catch (ServiceNotFoundException | RemoteException e) { throw new IllegalStateException(e); } // For backward compatibility, store the instance also to sInstance for default display. if (sInstance == null && isDefaultDisplay) { sInstance = instance; } sInstanceMap.put(displayId, instance); return instance; return sInstance; } } Loading Loading @@ -977,11 +916,6 @@ public final class InputMethodManager { * input method. */ public boolean isActive(View view) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { return view.getContext().getSystemService(InputMethodManager.class).isActive(view); } checkFocus(); synchronized (mH) { return (mServedView == view Loading Loading @@ -1072,13 +1006,6 @@ public final class InputMethodManager { } public void displayCompletions(View view, CompletionInfo[] completions) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .displayCompletions(view, completions); return; } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading @@ -1097,13 +1024,6 @@ public final class InputMethodManager { } public void updateExtractedText(View view, int token, ExtractedText text) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateExtractedText(view, token, text); return; } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading Loading @@ -1145,12 +1065,6 @@ public final class InputMethodManager { * 0 or have the {@link #SHOW_IMPLICIT} bit set. */ public boolean showSoftInput(View view, int flags) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { return view.getContext().getSystemService(InputMethodManager.class) .showSoftInput(view, flags); } return showSoftInput(view, flags, null); } Loading Loading @@ -1213,12 +1127,6 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. */ public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { return view.getContext().getSystemService(InputMethodManager.class) .showSoftInput(view, flags, resultReceiver); } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading Loading @@ -1382,12 +1290,6 @@ public final class InputMethodManager { * @param view The view whose text has changed. */ public void restartInput(View view) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class).restartInput(view); return; } checkFocus(); synchronized (mH) { if (mServedView != view && (mServedView == null Loading Loading @@ -1812,13 +1714,6 @@ public final class InputMethodManager { */ public void updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && (mServedView == null Loading Loading @@ -1856,12 +1751,6 @@ public final class InputMethodManager { * Notify the event when the user tapped or clicked the text view. */ public void viewClicked(View view) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class).viewClicked(view); return; } final boolean focusChanged = mServedView != mNextServedView; checkFocus(); synchronized (mH) { Loading Loading @@ -1926,13 +1815,6 @@ public final class InputMethodManager { */ @Deprecated public void updateCursor(View view, int left, int top, int right, int bottom) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateCursor(view, left, top, right, bottom); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && (mServedView == null Loading Loading @@ -1964,13 +1846,6 @@ public final class InputMethodManager { if (view == null || cursorAnchorInfo == null) { return; } // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateCursorAnchorInfo(view, cursorAnchorInfo); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && Loading Loading @@ -2016,13 +1891,6 @@ public final class InputMethodManager { * @param data Any data to include with the command. */ public void sendAppPrivateCommand(View view, String action, Bundle data) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .sendAppPrivateCommand(view, action, data); return; } checkFocus(); synchronized (mH) { if ((mServedView != view && (mServedView == null Loading Loading @@ -2194,13 +2062,6 @@ public final class InputMethodManager { */ public void dispatchKeyEventFromInputMethod(@Nullable View targetView, @NonNull KeyEvent event) { // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(targetView)) { targetView.getContext().getSystemService(InputMethodManager.class) .dispatchKeyEventFromInputMethod(targetView, event); return; } synchronized (mH) { ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; if (viewRootImpl == null) { Loading Loading @@ -2690,7 +2551,6 @@ public final class InputMethodManager { sb.append(",windowFocus=" + view.hasWindowFocus()); sb.append(",autofillUiShowing=" + isAutofillUIShowing(view)); sb.append(",window=" + view.getWindowToken()); sb.append(",displayId=" + getDisplayId(view.getContext())); sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); return sb.toString(); } Loading
core/java/com/android/internal/view/IInputMethodManager.aidl +1 −2 Original line number Diff line number Diff line Loading @@ -31,8 +31,7 @@ import com.android.internal.view.IInputMethodClient; * applications. */ interface IInputMethodManager { void addClient(in IInputMethodClient client, in IInputContext inputContext, int untrustedDisplayId); void addClient(in IInputMethodClient client, in IInputContext inputContext); // TODO: Use ParceledListSlice instead List<InputMethodInfo> getInputMethodList(); Loading
core/java/com/android/internal/view/InputBindResult.java +1 −28 Original line number Diff line number Diff line Loading @@ -51,9 +51,6 @@ public final class InputBindResult implements Parcelable { ResultCode.ERROR_INVALID_USER, ResultCode.ERROR_NULL_EDITOR_INFO, ResultCode.ERROR_NOT_IME_TARGET_WINDOW, ResultCode.ERROR_NO_EDITOR, ResultCode.ERROR_DISPLAY_ID_MISMATCH, ResultCode.ERROR_INVALID_DISPLAY_ID, }) public @interface ResultCode { /** Loading Loading @@ -142,22 +139,13 @@ public final class InputBindResult implements Parcelable { * The client should try to restart input when its {@link android.view.Window} is focused * again.</p> * * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int) * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int) */ int ERROR_NOT_IME_TARGET_WINDOW = 11; /** * Indicates that focused view in the current window is not an editor. */ int ERROR_NO_EDITOR = 12; /** * Indicates that there is a mismatch in display ID between IME client and focused Window. */ int ERROR_DISPLAY_ID_MISMATCH = 13; /** * Indicates that current IME client is no longer allowed to access to the associated * display. */ int ERROR_INVALID_DISPLAY_ID = 14; } @ResultCode Loading Loading @@ -283,10 +271,6 @@ public final class InputBindResult implements Parcelable { return "ERROR_NULL_EDITOR_INFO"; case ResultCode.ERROR_NOT_IME_TARGET_WINDOW: return "ERROR_NOT_IME_TARGET_WINDOW"; case ResultCode.ERROR_DISPLAY_ID_MISMATCH: return "ERROR_DISPLAY_ID_MISMATCH"; case ResultCode.ERROR_INVALID_DISPLAY_ID: return "ERROR_INVALID_DISPLAY_ID"; default: return "Unknown(" + result + ")"; } Loading Loading @@ -332,15 +316,4 @@ public final class InputBindResult implements Parcelable { */ public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER); /** * Predefined error object for {@link ResultCode#ERROR_DISPLAY_ID_MISMATCH}. */ public static final InputBindResult DISPLAY_ID_MISMATCH = error(ResultCode.ERROR_DISPLAY_ID_MISMATCH); /** * Predefined error object for {@link ResultCode#ERROR_INVALID_DISPLAY_ID}. */ public static final InputBindResult INVALID_DISPLAY_ID = error(ResultCode.ERROR_INVALID_DISPLAY_ID); }
services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +16 −58 Original line number Diff line number Diff line Loading @@ -421,7 +421,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final IInputContext inputContext; final int uid; final int pid; final int selfReportedDisplayId; final InputBinding binding; final ClientDeathRecipient clientDeathRecipient; Loading @@ -431,18 +430,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public String toString() { return "ClientState{" + Integer.toHexString( System.identityHashCode(this)) + " uid=" + uid + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}"; System.identityHashCode(this)) + " uid " + uid + " pid " + pid + "}"; } ClientState(IInputMethodClient _client, IInputContext _inputContext, int _uid, int _pid, int _selfReportedDisplayId, ClientDeathRecipient _clientDeathRecipient) { int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) { client = _client; inputContext = _inputContext; uid = _uid; pid = _pid; selfReportedDisplayId = _selfReportedDisplayId; binding = new InputBinding(null, inputContext.asBinder(), uid, pid); clientDeathRecipient = _clientDeathRecipient; } Loading Loading @@ -1748,21 +1745,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * process * @param inputContext communication channel for the dummy * {@link android.view.inputmethod.InputConnection} * @param selfReportedDisplayId self-reported display ID to which the client is associated. * Whether the client is still allowed to access to this display * or not needs to be evaluated every time the client interacts * with the display */ @Override public void addClient(IInputMethodClient client, IInputContext inputContext, int selfReportedDisplayId) { public void addClient(IInputMethodClient client, IInputContext inputContext) { final int callerUid = Binder.getCallingUid(); final int callerPid = Binder.getCallingPid(); synchronized (mMethodMap) { // TODO: Optimize this linear search. for (ClientState state : mClients.values()) { if (state.uid == callerUid && state.pid == callerPid && state.selfReportedDisplayId == selfReportedDisplayId) { if (state.uid == callerUid && state.pid == callerPid) { throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid + " is already registered"); } Loading @@ -1773,25 +1764,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { throw new IllegalStateException(e); } // We cannot fully avoid race conditions where the client UID already lost the access to // the given self-reported display ID, even if the client is not maliciously reporting // a fake display ID. Unconditionally returning SecurityException just because the // client doesn't pass display ID verification can cause many test failures hence not an // option right now. At the same time // context.getSystemService(InputMethodManager.class) // is expected to return a valid non-null instance at any time if we do not choose to // have the client crash. Thus we do not verify the display ID at all here. Instead we // later check the display ID every time the client needs to interact with the specified // display. mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid, callerPid, selfReportedDisplayId, deathRecipient)); mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid, callerPid, deathRecipient)); } } private boolean verifyDisplayId(ClientState cs) { return mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid); } void removeClient(IInputMethodClient client) { synchronized (mMethodMap) { ClientState cs = mClients.remove(client.asBinder()); Loading Loading @@ -1941,9 +1918,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurAttribute = attribute; // Check if the input method is changing. // We expect the caller has already verified that the client is allowed to access this // display ID. final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; final int displayId = mWindowManagerInternal.getDisplayIdForWindow( mCurFocusedWindow); if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) { if (cs.curSession != null) { // Fast case: if we are already connected to the input method, Loading Loading @@ -2008,11 +1984,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub com.android.internal.R.string.input_method_binding_label); mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); if (!verifyDisplayId(mCurFocusedWindowClient)) { // Wait, the client no longer has access to the display. return InputBindResult.INVALID_DISPLAY_ID; } final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow); mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY; if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) { Loading Loading @@ -2612,8 +2584,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, cs.selfReportedDisplayId)) { if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); return false; } Loading Loading @@ -2697,8 +2668,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, cs.selfReportedDisplayId)) { if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { if (DEBUG) { Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); } Loading Loading @@ -2797,8 +2767,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputBindResult res = null; long ident = Binder.clearCallingIdentity(); try { final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); synchronized (mMethodMap) { if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason=" + InputMethodClient.getStartInputReason(startInputReason) Loading @@ -2817,15 +2785,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (cs.selfReportedDisplayId != windowDisplayId) { Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch." + " from client:" + cs.selfReportedDisplayId + " from window:" + windowDisplayId); return InputBindResult.DISPLAY_ID_MISMATCH; } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, cs.selfReportedDisplayId)) { if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { // Check with the window manager to make sure this client actually // has a window with focus. If not, reject. This is thread safe // because if the focus changes some time before or after, the Loading Loading @@ -2897,9 +2858,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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.selfReportedDisplayId != mCurTokenDisplayId) { final int newFocusDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); if (newFocusDisplayId != mCurTokenDisplayId) { unbindCurrentMethodLocked(); } } Loading Loading @@ -2997,7 +2958,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { // TODO(yukawa): multi-display support. final int uid = Binder.getCallingUid(); if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { return true; Loading Loading @@ -3074,7 +3034,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void showInputMethodAndSubtypeEnablerFromClient( IInputMethodClient client, String inputMethodId) { // TODO(yukawa): Should we verify the display ID? if (!calledFromValidUser()) { return; } Loading Loading @@ -3270,7 +3229,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ @Override public int getInputMethodWindowVisibleHeight() { // TODO(yukawa): Should we verify the display ID? return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); } Loading