Loading core/java/android/app/SystemServiceRegistry.java +3 −7 Original line number Original line Diff line number Diff line Loading @@ -377,15 +377,11 @@ final class SystemServiceRegistry { return new DisplayManager(ctx.getOuterContext()); 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, registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class, new ServiceFetcher<InputMethodManager>() { new StaticServiceFetcher<InputMethodManager>() { @Override @Override public InputMethodManager getService(ContextImpl ctx) { public InputMethodManager createService() { return InputMethodManager.forContext(ctx); return InputMethodManager.getInstanceInternal(); }}); }}); registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, Loading core/java/android/view/inputmethod/InputMethodManager.java +15 −155 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.util.Pools.SimplePool; import android.util.PrintWriterPrinter; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Printer; import android.util.SparseArray; import android.util.SparseArray; import android.view.Display; import android.view.InputChannel; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEvent; import android.view.InputEventSender; import android.view.InputEventSender; Loading Loading @@ -266,7 +265,7 @@ public final class InputMethodManager { * @hide * @hide */ */ public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper()); getInstanceInternal(); } } private static final Object sLock = new Object(); private static final Object sLock = new Object(); Loading @@ -279,17 +278,6 @@ public final class InputMethodManager { @UnsupportedAppUsage @UnsupportedAppUsage static InputMethodManager sInstance; 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 * @hide Flag for IInputMethodManager.windowGainedFocus: a view in * the window has input focus. * 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. // Our generic input connection if the current target does not have its own. final IInputContext mIInputContext; final IInputContext mIInputContext; private final int mDisplayId; /** /** * True if this input method client is active, initially false. * 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(); 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) { private static boolean canStartInput(View servedView) { // We can start input ether the servedView has window focus // We can start input ether the servedView has window focus // or the activity is showing autofill ui. // 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(); mService = getIInputMethodManager(); mMainLooper = looper; mMainLooper = looper; mH = new H(looper); mH = new H(looper); mDisplayId = displayId; mIInputContext = new ControlledInputConnectionWrapper(looper, mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this); 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 global {@link InputMethodManager} instance * @return {@link InputMethodManager} instance * @hide * @hide */ */ @Nullable public static InputMethodManager getInstanceInternal() { 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; synchronized (sLock) { synchronized (sLock) { InputMethodManager instance = sInstanceMap.get(displayId); if (sInstance == null) { if (instance != null) { return instance; } try { try { instance = new InputMethodManager(displayId, looper); final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper()); instance.mService.addClient(instance.mClient, instance.mIInputContext, displayId); imm.mService.addClient(imm.mClient, imm.mIInputContext); sInstance = imm; } catch (ServiceNotFoundException | RemoteException e) { } catch (ServiceNotFoundException | RemoteException e) { throw new IllegalStateException(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 sInstance; return instance; } } } } Loading Loading @@ -977,11 +916,6 @@ public final class InputMethodManager { * input method. * input method. */ */ public boolean isActive(View view) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { return (mServedView == view return (mServedView == view Loading Loading @@ -1072,13 +1006,6 @@ public final class InputMethodManager { } } public void displayCompletions(View view, CompletionInfo[] completions) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading @@ -1097,13 +1024,6 @@ public final class InputMethodManager { } } public void updateExtractedText(View view, int token, ExtractedText text) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading Loading @@ -1145,12 +1065,6 @@ public final class InputMethodManager { * 0 or have the {@link #SHOW_IMPLICIT} bit set. * 0 or have the {@link #SHOW_IMPLICIT} bit set. */ */ public boolean showSoftInput(View view, int flags) { 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); return showSoftInput(view, flags, null); } } Loading Loading @@ -1213,12 +1127,6 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. * {@link #RESULT_HIDDEN}. */ */ public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading Loading @@ -1382,12 +1290,6 @@ public final class InputMethodManager { * @param view The view whose text has changed. * @param view The view whose text has changed. */ */ public void restartInput(View view) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading Loading @@ -1812,13 +1714,6 @@ public final class InputMethodManager { */ */ public void updateSelection(View view, int selStart, int selEnd, public void updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && (mServedView == null 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. * Notify the event when the user tapped or clicked the text view. */ */ public void viewClicked(View 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; final boolean focusChanged = mServedView != mNextServedView; checkFocus(); checkFocus(); synchronized (mH) { synchronized (mH) { Loading Loading @@ -1926,13 +1815,6 @@ public final class InputMethodManager { */ */ @Deprecated @Deprecated public void updateCursor(View view, int left, int top, int right, int bottom) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && (mServedView == null if ((mServedView != view && (mServedView == null Loading Loading @@ -1964,13 +1846,6 @@ public final class InputMethodManager { if (view == null || cursorAnchorInfo == null) { if (view == null || cursorAnchorInfo == null) { return; return; } } // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateCursorAnchorInfo(view, cursorAnchorInfo); return; } checkFocus(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && if ((mServedView != view && Loading Loading @@ -2016,13 +1891,6 @@ public final class InputMethodManager { * @param data Any data to include with the command. * @param data Any data to include with the command. */ */ public void sendAppPrivateCommand(View view, String action, Bundle data) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && (mServedView == null if ((mServedView != view && (mServedView == null Loading Loading @@ -2194,13 +2062,6 @@ public final class InputMethodManager { */ */ public void dispatchKeyEventFromInputMethod(@Nullable View targetView, public void dispatchKeyEventFromInputMethod(@Nullable View targetView, @NonNull KeyEvent event) { @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) { synchronized (mH) { ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; if (viewRootImpl == null) { if (viewRootImpl == null) { Loading Loading @@ -2690,7 +2551,6 @@ public final class InputMethodManager { sb.append(",windowFocus=" + view.hasWindowFocus()); sb.append(",windowFocus=" + view.hasWindowFocus()); sb.append(",autofillUiShowing=" + isAutofillUIShowing(view)); sb.append(",autofillUiShowing=" + isAutofillUIShowing(view)); sb.append(",window=" + view.getWindowToken()); sb.append(",window=" + view.getWindowToken()); sb.append(",displayId=" + getDisplayId(view.getContext())); sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); return sb.toString(); return sb.toString(); } } Loading core/java/com/android/internal/view/IInputMethodManager.aidl +1 −2 Original line number Original line Diff line number Diff line Loading @@ -31,8 +31,7 @@ import com.android.internal.view.IInputMethodClient; * applications. * applications. */ */ interface IInputMethodManager { interface IInputMethodManager { void addClient(in IInputMethodClient client, in IInputContext inputContext, void addClient(in IInputMethodClient client, in IInputContext inputContext); int untrustedDisplayId); // TODO: Use ParceledListSlice instead // TODO: Use ParceledListSlice instead List<InputMethodInfo> getInputMethodList(); List<InputMethodInfo> getInputMethodList(); Loading core/java/com/android/internal/view/InputBindResult.java +1 −28 Original line number Original line Diff line number Diff line Loading @@ -51,9 +51,6 @@ public final class InputBindResult implements Parcelable { ResultCode.ERROR_INVALID_USER, ResultCode.ERROR_INVALID_USER, ResultCode.ERROR_NULL_EDITOR_INFO, ResultCode.ERROR_NULL_EDITOR_INFO, ResultCode.ERROR_NOT_IME_TARGET_WINDOW, ResultCode.ERROR_NOT_IME_TARGET_WINDOW, ResultCode.ERROR_NO_EDITOR, ResultCode.ERROR_DISPLAY_ID_MISMATCH, ResultCode.ERROR_INVALID_DISPLAY_ID, }) }) public @interface ResultCode { 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 * The client should try to restart input when its {@link android.view.Window} is focused * again.</p> * 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; int ERROR_NOT_IME_TARGET_WINDOW = 11; /** /** * Indicates that focused view in the current window is not an editor. * Indicates that focused view in the current window is not an editor. */ */ int ERROR_NO_EDITOR = 12; 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 @ResultCode Loading Loading @@ -283,10 +271,6 @@ public final class InputBindResult implements Parcelable { return "ERROR_NULL_EDITOR_INFO"; return "ERROR_NULL_EDITOR_INFO"; case ResultCode.ERROR_NOT_IME_TARGET_WINDOW: case ResultCode.ERROR_NOT_IME_TARGET_WINDOW: return "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: default: return "Unknown(" + result + ")"; 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); 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 Original line Diff line number Diff line Loading @@ -421,7 +421,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final IInputContext inputContext; final IInputContext inputContext; final int uid; final int uid; final int pid; final int pid; final int selfReportedDisplayId; final InputBinding binding; final InputBinding binding; final ClientDeathRecipient clientDeathRecipient; final ClientDeathRecipient clientDeathRecipient; Loading @@ -431,18 +430,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override @Override public String toString() { public String toString() { return "ClientState{" + Integer.toHexString( return "ClientState{" + Integer.toHexString( System.identityHashCode(this)) + " uid=" + uid System.identityHashCode(this)) + " uid " + uid + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}"; + " pid " + pid + "}"; } } ClientState(IInputMethodClient _client, IInputContext _inputContext, ClientState(IInputMethodClient _client, IInputContext _inputContext, int _uid, int _pid, int _selfReportedDisplayId, int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) { ClientDeathRecipient _clientDeathRecipient) { client = _client; client = _client; inputContext = _inputContext; inputContext = _inputContext; uid = _uid; uid = _uid; pid = _pid; pid = _pid; selfReportedDisplayId = _selfReportedDisplayId; binding = new InputBinding(null, inputContext.asBinder(), uid, pid); binding = new InputBinding(null, inputContext.asBinder(), uid, pid); clientDeathRecipient = _clientDeathRecipient; clientDeathRecipient = _clientDeathRecipient; } } Loading Loading @@ -1748,21 +1745,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * process * process * @param inputContext communication channel for the dummy * @param inputContext communication channel for the dummy * {@link android.view.inputmethod.InputConnection} * {@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 @Override public void addClient(IInputMethodClient client, IInputContext inputContext, public void addClient(IInputMethodClient client, IInputContext inputContext) { int selfReportedDisplayId) { final int callerUid = Binder.getCallingUid(); final int callerUid = Binder.getCallingUid(); final int callerPid = Binder.getCallingPid(); final int callerPid = Binder.getCallingPid(); synchronized (mMethodMap) { synchronized (mMethodMap) { // TODO: Optimize this linear search. // TODO: Optimize this linear search. for (ClientState state : mClients.values()) { for (ClientState state : mClients.values()) { if (state.uid == callerUid && state.pid == callerPid if (state.uid == callerUid && state.pid == callerPid) { && state.selfReportedDisplayId == selfReportedDisplayId) { throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid + " is already registered"); + " is already registered"); } } Loading @@ -1773,25 +1764,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { } catch (RemoteException e) { throw new IllegalStateException(e); throw new IllegalStateException(e); } } // We cannot fully avoid race conditions where the client UID already lost the access to mClients.put(client.asBinder(), // the given self-reported display ID, even if the client is not maliciously reporting new ClientState(client, inputContext, callerUid, callerPid, deathRecipient)); // 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)); } } } } private boolean verifyDisplayId(ClientState cs) { return mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid); } void removeClient(IInputMethodClient client) { void removeClient(IInputMethodClient client) { synchronized (mMethodMap) { synchronized (mMethodMap) { ClientState cs = mClients.remove(client.asBinder()); ClientState cs = mClients.remove(client.asBinder()); Loading Loading @@ -1941,9 +1918,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurAttribute = attribute; mCurAttribute = attribute; // Check if the input method is changing. // Check if the input method is changing. // We expect the caller has already verified that the client is allowed to access this final int displayId = mWindowManagerInternal.getDisplayIdForWindow( // display ID. mCurFocusedWindow); final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) { if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) { if (cs.curSession != null) { if (cs.curSession != null) { // Fast case: if we are already connected to the input method, // 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); com.android.internal.R.string.input_method_binding_label); mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); if (!verifyDisplayId(mCurFocusedWindowClient)) { final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow); // Wait, the client no longer has access to the display. return InputBindResult.INVALID_DISPLAY_ID; } final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY; mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY; if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) { if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) { Loading Loading @@ -2612,8 +2584,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); throw new IllegalArgumentException("unknown client " + client.asBinder()); } } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { cs.selfReportedDisplayId)) { Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); return false; return false; } } Loading Loading @@ -2697,8 +2668,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); throw new IllegalArgumentException("unknown client " + client.asBinder()); } } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { cs.selfReportedDisplayId)) { if (DEBUG) { if (DEBUG) { Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); } } Loading Loading @@ -2797,8 +2767,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputBindResult res = null; InputBindResult res = null; long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity(); try { try { final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); synchronized (mMethodMap) { synchronized (mMethodMap) { if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason=" if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason=" + InputMethodClient.getStartInputReason(startInputReason) + InputMethodClient.getStartInputReason(startInputReason) Loading @@ -2817,15 +2785,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("unknown client " throw new IllegalArgumentException("unknown client " + client.asBinder()); + 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, if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { cs.selfReportedDisplayId)) { // Check with the window manager to make sure this client actually // Check with the window manager to make sure this client actually // has a window with focus. If not, reject. This is thread safe // has a window with focus. If not, reject. This is thread safe // because if the focus changes some time before or after, the // 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 // If focused display changed, we should unbind current method // to make app window in previous display relayout after Ime // to make app window in previous display relayout after Ime // window token removed. // window token removed. // Note that we can trust client's display ID as long as it matches final int newFocusDisplayId = // to the display ID obtained from the window. mWindowManagerInternal.getDisplayIdForWindow(windowToken); if (cs.selfReportedDisplayId != mCurTokenDisplayId) { if (newFocusDisplayId != mCurTokenDisplayId) { unbindCurrentMethodLocked(); unbindCurrentMethodLocked(); } } } } Loading Loading @@ -2997,7 +2958,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { // TODO(yukawa): multi-display support. final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid(); if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { return true; return true; Loading Loading @@ -3074,7 +3034,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override @Override public void showInputMethodAndSubtypeEnablerFromClient( public void showInputMethodAndSubtypeEnablerFromClient( IInputMethodClient client, String inputMethodId) { IInputMethodClient client, String inputMethodId) { // TODO(yukawa): Should we verify the display ID? if (!calledFromValidUser()) { if (!calledFromValidUser()) { return; return; } } Loading Loading @@ -3270,7 +3229,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ */ @Override @Override public int getInputMethodWindowVisibleHeight() { public int getInputMethodWindowVisibleHeight() { // TODO(yukawa): Should we verify the display ID? return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); } } Loading Loading
core/java/android/app/SystemServiceRegistry.java +3 −7 Original line number Original line Diff line number Diff line Loading @@ -377,15 +377,11 @@ final class SystemServiceRegistry { return new DisplayManager(ctx.getOuterContext()); 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, registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class, new ServiceFetcher<InputMethodManager>() { new StaticServiceFetcher<InputMethodManager>() { @Override @Override public InputMethodManager getService(ContextImpl ctx) { public InputMethodManager createService() { return InputMethodManager.forContext(ctx); return InputMethodManager.getInstanceInternal(); }}); }}); registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, Loading
core/java/android/view/inputmethod/InputMethodManager.java +15 −155 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,6 @@ import android.util.Pools.SimplePool; import android.util.PrintWriterPrinter; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Printer; import android.util.SparseArray; import android.util.SparseArray; import android.view.Display; import android.view.InputChannel; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEvent; import android.view.InputEventSender; import android.view.InputEventSender; Loading Loading @@ -266,7 +265,7 @@ public final class InputMethodManager { * @hide * @hide */ */ public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper()); getInstanceInternal(); } } private static final Object sLock = new Object(); private static final Object sLock = new Object(); Loading @@ -279,17 +278,6 @@ public final class InputMethodManager { @UnsupportedAppUsage @UnsupportedAppUsage static InputMethodManager sInstance; 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 * @hide Flag for IInputMethodManager.windowGainedFocus: a view in * the window has input focus. * 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. // Our generic input connection if the current target does not have its own. final IInputContext mIInputContext; final IInputContext mIInputContext; private final int mDisplayId; /** /** * True if this input method client is active, initially false. * 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(); 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) { private static boolean canStartInput(View servedView) { // We can start input ether the servedView has window focus // We can start input ether the servedView has window focus // or the activity is showing autofill ui. // 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(); mService = getIInputMethodManager(); mMainLooper = looper; mMainLooper = looper; mH = new H(looper); mH = new H(looper); mDisplayId = displayId; mIInputContext = new ControlledInputConnectionWrapper(looper, mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this); 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 global {@link InputMethodManager} instance * @return {@link InputMethodManager} instance * @hide * @hide */ */ @Nullable public static InputMethodManager getInstanceInternal() { 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; synchronized (sLock) { synchronized (sLock) { InputMethodManager instance = sInstanceMap.get(displayId); if (sInstance == null) { if (instance != null) { return instance; } try { try { instance = new InputMethodManager(displayId, looper); final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper()); instance.mService.addClient(instance.mClient, instance.mIInputContext, displayId); imm.mService.addClient(imm.mClient, imm.mIInputContext); sInstance = imm; } catch (ServiceNotFoundException | RemoteException e) { } catch (ServiceNotFoundException | RemoteException e) { throw new IllegalStateException(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 sInstance; return instance; } } } } Loading Loading @@ -977,11 +916,6 @@ public final class InputMethodManager { * input method. * input method. */ */ public boolean isActive(View view) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { return (mServedView == view return (mServedView == view Loading Loading @@ -1072,13 +1006,6 @@ public final class InputMethodManager { } } public void displayCompletions(View view, CompletionInfo[] completions) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading @@ -1097,13 +1024,6 @@ public final class InputMethodManager { } } public void updateExtractedText(View view, int token, ExtractedText text) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading Loading @@ -1145,12 +1065,6 @@ public final class InputMethodManager { * 0 or have the {@link #SHOW_IMPLICIT} bit set. * 0 or have the {@link #SHOW_IMPLICIT} bit set. */ */ public boolean showSoftInput(View view, int flags) { 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); return showSoftInput(view, flags, null); } } Loading Loading @@ -1213,12 +1127,6 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. * {@link #RESULT_HIDDEN}. */ */ public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading Loading @@ -1382,12 +1290,6 @@ public final class InputMethodManager { * @param view The view whose text has changed. * @param view The view whose text has changed. */ */ public void restartInput(View view) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if (mServedView != view && (mServedView == null if (mServedView != view && (mServedView == null Loading Loading @@ -1812,13 +1714,6 @@ public final class InputMethodManager { */ */ public void updateSelection(View view, int selStart, int selEnd, public void updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && (mServedView == null 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. * Notify the event when the user tapped or clicked the text view. */ */ public void viewClicked(View 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; final boolean focusChanged = mServedView != mNextServedView; checkFocus(); checkFocus(); synchronized (mH) { synchronized (mH) { Loading Loading @@ -1926,13 +1815,6 @@ public final class InputMethodManager { */ */ @Deprecated @Deprecated public void updateCursor(View view, int left, int top, int right, int bottom) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && (mServedView == null if ((mServedView != view && (mServedView == null Loading Loading @@ -1964,13 +1846,6 @@ public final class InputMethodManager { if (view == null || cursorAnchorInfo == null) { if (view == null || cursorAnchorInfo == null) { return; return; } } // Re-dispatch if there is a context mismatch. if (shouldDispatchToViewContext(view)) { view.getContext().getSystemService(InputMethodManager.class) .updateCursorAnchorInfo(view, cursorAnchorInfo); return; } checkFocus(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && if ((mServedView != view && Loading Loading @@ -2016,13 +1891,6 @@ public final class InputMethodManager { * @param data Any data to include with the command. * @param data Any data to include with the command. */ */ public void sendAppPrivateCommand(View view, String action, Bundle data) { 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(); checkFocus(); synchronized (mH) { synchronized (mH) { if ((mServedView != view && (mServedView == null if ((mServedView != view && (mServedView == null Loading Loading @@ -2194,13 +2062,6 @@ public final class InputMethodManager { */ */ public void dispatchKeyEventFromInputMethod(@Nullable View targetView, public void dispatchKeyEventFromInputMethod(@Nullable View targetView, @NonNull KeyEvent event) { @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) { synchronized (mH) { ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; if (viewRootImpl == null) { if (viewRootImpl == null) { Loading Loading @@ -2690,7 +2551,6 @@ public final class InputMethodManager { sb.append(",windowFocus=" + view.hasWindowFocus()); sb.append(",windowFocus=" + view.hasWindowFocus()); sb.append(",autofillUiShowing=" + isAutofillUIShowing(view)); sb.append(",autofillUiShowing=" + isAutofillUIShowing(view)); sb.append(",window=" + view.getWindowToken()); sb.append(",window=" + view.getWindowToken()); sb.append(",displayId=" + getDisplayId(view.getContext())); sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); return sb.toString(); return sb.toString(); } } Loading
core/java/com/android/internal/view/IInputMethodManager.aidl +1 −2 Original line number Original line Diff line number Diff line Loading @@ -31,8 +31,7 @@ import com.android.internal.view.IInputMethodClient; * applications. * applications. */ */ interface IInputMethodManager { interface IInputMethodManager { void addClient(in IInputMethodClient client, in IInputContext inputContext, void addClient(in IInputMethodClient client, in IInputContext inputContext); int untrustedDisplayId); // TODO: Use ParceledListSlice instead // TODO: Use ParceledListSlice instead List<InputMethodInfo> getInputMethodList(); List<InputMethodInfo> getInputMethodList(); Loading
core/java/com/android/internal/view/InputBindResult.java +1 −28 Original line number Original line Diff line number Diff line Loading @@ -51,9 +51,6 @@ public final class InputBindResult implements Parcelable { ResultCode.ERROR_INVALID_USER, ResultCode.ERROR_INVALID_USER, ResultCode.ERROR_NULL_EDITOR_INFO, ResultCode.ERROR_NULL_EDITOR_INFO, ResultCode.ERROR_NOT_IME_TARGET_WINDOW, ResultCode.ERROR_NOT_IME_TARGET_WINDOW, ResultCode.ERROR_NO_EDITOR, ResultCode.ERROR_DISPLAY_ID_MISMATCH, ResultCode.ERROR_INVALID_DISPLAY_ID, }) }) public @interface ResultCode { 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 * The client should try to restart input when its {@link android.view.Window} is focused * again.</p> * 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; int ERROR_NOT_IME_TARGET_WINDOW = 11; /** /** * Indicates that focused view in the current window is not an editor. * Indicates that focused view in the current window is not an editor. */ */ int ERROR_NO_EDITOR = 12; 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 @ResultCode Loading Loading @@ -283,10 +271,6 @@ public final class InputBindResult implements Parcelable { return "ERROR_NULL_EDITOR_INFO"; return "ERROR_NULL_EDITOR_INFO"; case ResultCode.ERROR_NOT_IME_TARGET_WINDOW: case ResultCode.ERROR_NOT_IME_TARGET_WINDOW: return "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: default: return "Unknown(" + result + ")"; 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); 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 Original line Diff line number Diff line Loading @@ -421,7 +421,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final IInputContext inputContext; final IInputContext inputContext; final int uid; final int uid; final int pid; final int pid; final int selfReportedDisplayId; final InputBinding binding; final InputBinding binding; final ClientDeathRecipient clientDeathRecipient; final ClientDeathRecipient clientDeathRecipient; Loading @@ -431,18 +430,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override @Override public String toString() { public String toString() { return "ClientState{" + Integer.toHexString( return "ClientState{" + Integer.toHexString( System.identityHashCode(this)) + " uid=" + uid System.identityHashCode(this)) + " uid " + uid + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}"; + " pid " + pid + "}"; } } ClientState(IInputMethodClient _client, IInputContext _inputContext, ClientState(IInputMethodClient _client, IInputContext _inputContext, int _uid, int _pid, int _selfReportedDisplayId, int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) { ClientDeathRecipient _clientDeathRecipient) { client = _client; client = _client; inputContext = _inputContext; inputContext = _inputContext; uid = _uid; uid = _uid; pid = _pid; pid = _pid; selfReportedDisplayId = _selfReportedDisplayId; binding = new InputBinding(null, inputContext.asBinder(), uid, pid); binding = new InputBinding(null, inputContext.asBinder(), uid, pid); clientDeathRecipient = _clientDeathRecipient; clientDeathRecipient = _clientDeathRecipient; } } Loading Loading @@ -1748,21 +1745,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * process * process * @param inputContext communication channel for the dummy * @param inputContext communication channel for the dummy * {@link android.view.inputmethod.InputConnection} * {@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 @Override public void addClient(IInputMethodClient client, IInputContext inputContext, public void addClient(IInputMethodClient client, IInputContext inputContext) { int selfReportedDisplayId) { final int callerUid = Binder.getCallingUid(); final int callerUid = Binder.getCallingUid(); final int callerPid = Binder.getCallingPid(); final int callerPid = Binder.getCallingPid(); synchronized (mMethodMap) { synchronized (mMethodMap) { // TODO: Optimize this linear search. // TODO: Optimize this linear search. for (ClientState state : mClients.values()) { for (ClientState state : mClients.values()) { if (state.uid == callerUid && state.pid == callerPid if (state.uid == callerUid && state.pid == callerPid) { && state.selfReportedDisplayId == selfReportedDisplayId) { throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid + " is already registered"); + " is already registered"); } } Loading @@ -1773,25 +1764,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { } catch (RemoteException e) { throw new IllegalStateException(e); throw new IllegalStateException(e); } } // We cannot fully avoid race conditions where the client UID already lost the access to mClients.put(client.asBinder(), // the given self-reported display ID, even if the client is not maliciously reporting new ClientState(client, inputContext, callerUid, callerPid, deathRecipient)); // 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)); } } } } private boolean verifyDisplayId(ClientState cs) { return mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid); } void removeClient(IInputMethodClient client) { void removeClient(IInputMethodClient client) { synchronized (mMethodMap) { synchronized (mMethodMap) { ClientState cs = mClients.remove(client.asBinder()); ClientState cs = mClients.remove(client.asBinder()); Loading Loading @@ -1941,9 +1918,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurAttribute = attribute; mCurAttribute = attribute; // Check if the input method is changing. // Check if the input method is changing. // We expect the caller has already verified that the client is allowed to access this final int displayId = mWindowManagerInternal.getDisplayIdForWindow( // display ID. mCurFocusedWindow); final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) { if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) { if (cs.curSession != null) { if (cs.curSession != null) { // Fast case: if we are already connected to the input method, // 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); com.android.internal.R.string.input_method_binding_label); mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); if (!verifyDisplayId(mCurFocusedWindowClient)) { final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow); // Wait, the client no longer has access to the display. return InputBindResult.INVALID_DISPLAY_ID; } final int displayId = mCurFocusedWindowClient.selfReportedDisplayId; mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY; mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY; if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) { if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) { Loading Loading @@ -2612,8 +2584,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); throw new IllegalArgumentException("unknown client " + client.asBinder()); } } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { cs.selfReportedDisplayId)) { Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); return false; return false; } } Loading Loading @@ -2697,8 +2668,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs == null) { if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); throw new IllegalArgumentException("unknown client " + client.asBinder()); } } if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { cs.selfReportedDisplayId)) { if (DEBUG) { if (DEBUG) { Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); } } Loading Loading @@ -2797,8 +2767,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputBindResult res = null; InputBindResult res = null; long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity(); try { try { final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); synchronized (mMethodMap) { synchronized (mMethodMap) { if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason=" if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason=" + InputMethodClient.getStartInputReason(startInputReason) + InputMethodClient.getStartInputReason(startInputReason) Loading @@ -2817,15 +2785,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("unknown client " throw new IllegalArgumentException("unknown client " + client.asBinder()); + 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, if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) { cs.selfReportedDisplayId)) { // Check with the window manager to make sure this client actually // Check with the window manager to make sure this client actually // has a window with focus. If not, reject. This is thread safe // has a window with focus. If not, reject. This is thread safe // because if the focus changes some time before or after, the // 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 // If focused display changed, we should unbind current method // to make app window in previous display relayout after Ime // to make app window in previous display relayout after Ime // window token removed. // window token removed. // Note that we can trust client's display ID as long as it matches final int newFocusDisplayId = // to the display ID obtained from the window. mWindowManagerInternal.getDisplayIdForWindow(windowToken); if (cs.selfReportedDisplayId != mCurTokenDisplayId) { if (newFocusDisplayId != mCurTokenDisplayId) { unbindCurrentMethodLocked(); unbindCurrentMethodLocked(); } } } } Loading Loading @@ -2997,7 +2958,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { // TODO(yukawa): multi-display support. final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid(); if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { return true; return true; Loading Loading @@ -3074,7 +3034,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override @Override public void showInputMethodAndSubtypeEnablerFromClient( public void showInputMethodAndSubtypeEnablerFromClient( IInputMethodClient client, String inputMethodId) { IInputMethodClient client, String inputMethodId) { // TODO(yukawa): Should we verify the display ID? if (!calledFromValidUser()) { if (!calledFromValidUser()) { return; return; } } Loading Loading @@ -3270,7 +3229,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ */ @Override @Override public int getInputMethodWindowVisibleHeight() { public int getInputMethodWindowVisibleHeight() { // TODO(yukawa): Should we verify the display ID? return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); } } Loading