diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 4d5fabbae9615..2a441de067c6d 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -221,7 +221,9 @@ class IInputMethodWrapper extends IInputMethod.Stub inputMethod.revokeSession((InputMethodSession)msg.obj); return; case DO_SHOW_SOFT_INPUT: - inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj); + SomeArgs args = (SomeArgs)msg.obj; + inputMethod.showSoftInputWithToken( + msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1); return; case DO_HIDE_SOFT_INPUT: inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj); @@ -230,10 +232,11 @@ class IInputMethodWrapper extends IInputMethod.Stub inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj); return; case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: - SomeArgs args = (SomeArgs) msg.obj; + args = (SomeArgs) msg.obj; inputMethod.onCreateInlineSuggestionsRequest((ComponentName) args.arg1, (AutofillId) args.arg2, (IInlineSuggestionsRequestCallback) args.arg3); return; + } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -371,9 +374,9 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override - public void showSoftInput(int flags, ResultReceiver resultReceiver) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT, - flags, resultReceiver)); + public void showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) { + mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_SHOW_SOFT_INPUT, + flags, showInputToken, resultReceiver)); } @BinderThread diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 8e52ee944c1cf..81a0d629380ac 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -46,11 +46,13 @@ import android.database.ContentObserver; import android.graphics.Rect; import android.graphics.Region; import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; @@ -450,6 +452,16 @@ public class InputMethodService extends AbstractInputMethodService { @Nullable private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null; + /** + * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} + * The original app window token is passed from client app window. + * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy + * token to identify this window. + * This dummy token is only valid for a single call to {@link InputMethodImpl#showSoftInput}, + * after which it is set null until next call. + */ + private IBinder mCurShowInputToken; + private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> { @@ -491,6 +503,9 @@ public class InputMethodService extends AbstractInputMethodService { * all of the standard behavior for an input method. */ public class InputMethodImpl extends AbstractInputMethodImpl { + + private boolean mSystemCallingShowSoftInput; + /** * {@inheritDoc} * @hide @@ -657,6 +672,21 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * {@inheritDoc} + * @hide + */ + @MainThread + @Override + public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver, + IBinder showInputToken) { + mSystemCallingShowSoftInput = true; + mCurShowInputToken = showInputToken; + showSoftInput(flags, resultReceiver); + mCurShowInputToken = null; + mSystemCallingShowSoftInput = false; + } + /** * {@inheritDoc} */ @@ -664,6 +694,13 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void showSoftInput(int flags, ResultReceiver resultReceiver) { if (DEBUG) Log.v(TAG, "showSoftInput()"); + // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods. + if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R + && !mSystemCallingShowSoftInput) { + Log.e(TAG," IME shouldn't call showSoftInput on itself." + + " Use requestShowSelf(int) itself"); + return; + } final boolean wasVisible = mIsPreRendered ? mDecorViewVisible && mWindowVisible : isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { @@ -698,6 +735,15 @@ public class InputMethodService extends AbstractInputMethodService { public void changeInputMethodSubtype(InputMethodSubtype subtype) { dispatchOnCurrentInputMethodSubtypeChanged(subtype); } + + /** + * {@inheritDoc} + * @hide + */ + @Override + public void setCurrentShowInputToken(IBinder showInputToken) { + mCurShowInputToken = showInputToken; + } } // TODO(b/137800469): Add detailed docs explaining the inline suggestions process. @@ -2181,7 +2227,7 @@ public class InputMethodService extends AbstractInputMethodService { if (!isVisibilityAppliedUsingInsetsConsumer()) { return; } - mPrivOps.applyImeVisibility(setVisible); + mPrivOps.applyImeVisibility(mCurShowInputToken, setVisible); } private boolean isVisibilityAppliedUsingInsetsConsumer() { diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index cf494aec26f6f..91e15c1949b62 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -27,6 +27,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; +import android.view.View; import android.view.autofill.AutofillId; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; @@ -298,7 +299,30 @@ public interface InputMethod { * until deliberated dismissed by the user in its UI. */ public static final int SHOW_FORCED = 0x00002; - + + /** + * Request that any soft input part of the input method be shown to the user. + * + * @param flags Provides additional information about the show request. + * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set. + * @param resultReceiver The client requesting the show may wish to + * be told the impact of their request, which should be supplied here. + * The result code should be + * {@link InputMethodManager#RESULT_UNCHANGED_SHOWN InputMethodManager.RESULT_UNCHANGED_SHOWN}, + * {@link InputMethodManager#RESULT_UNCHANGED_HIDDEN InputMethodManager.RESULT_UNCHANGED_HIDDEN}, + * {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or + * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}. + * @param showInputToken an opaque {@link android.os.Binder} token to identify which API call + * of {@link InputMethodManager#showSoftInput(View, int)} is associated with + * this callback. + * @hide + */ + @MainThread + default public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver, + IBinder showInputToken) { + showSoftInput(flags, resultReceiver); + } + /** * Request that any soft input part of the input method be shown to the user. * @@ -314,7 +338,7 @@ public interface InputMethod { */ @MainThread public void showSoftInput(int flags, ResultReceiver resultReceiver); - + /** * Request that any soft input part of the input method be hidden from the user. * @param flags Provides additional information about the show request. @@ -336,4 +360,12 @@ public interface InputMethod { */ @MainThread public void changeInputMethodSubtype(InputMethodSubtype subtype); + + /** + * Update token of the client window requesting {@link #showSoftInput(int, ResultReceiver)} + * @param showInputToken dummy app window token for window requesting + * {@link InputMethodManager#showSoftInput(View, int)} + * @hide + */ + public void setCurrentShowInputToken(IBinder showInputToken); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 904e736d214e4..d57668e0abba7 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1617,7 +1617,8 @@ public final class InputMethodManager { } try { - return mService.showSoftInput(mClient, flags, resultReceiver); + return mService.showSoftInput( + mClient, view.getWindowToken(), flags, resultReceiver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1639,7 +1640,8 @@ public final class InputMethodManager { Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be removed " + "soon. If you are using android.support.v7.widget.SearchView, please update " + "to version 26.0 or newer version."); - mService.showSoftInput(mClient, flags, resultReceiver); + mService.showSoftInput( + mClient, mCurRootView.getView().getWindowToken(), flags, resultReceiver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index e27ff00760545..20cd7c21d512f 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -41,5 +41,5 @@ interface IInputMethodPrivilegedOperations { boolean shouldOfferSwitchingToNextInputMethod(); void notifyUserAction(); void reportPreRendered(in EditorInfo info); - void applyImeVisibility(boolean setVisible); + void applyImeVisibility(IBinder showInputToken, boolean setVisible); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index d42c607b98bfd..9eeef963de7f3 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; @@ -368,18 +369,20 @@ public final class InputMethodPrivilegedOperations { } /** - * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(boolean)}. + * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}. * + * @param showInputToken dummy token that maps to window requesting + * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} * @param setVisible {@code true} to set IME visible, else hidden. */ @AnyThread - public void applyImeVisibility(boolean setVisible) { + public void applyImeVisibility(IBinder showInputToken, boolean setVisible) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; } try { - ops.applyImeVisibility(setVisible); + ops.applyImeVisibility(showInputToken, setVisible); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index 58aaa80b51bea..475a3214096d7 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -54,7 +54,7 @@ oneway interface IInputMethod { void revokeSession(IInputMethodSession session); - void showSoftInput(int flags, in ResultReceiver resultReceiver); + void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver); void hideSoftInput(int flags, in ResultReceiver resultReceiver); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index c29e823218eb2..0337ddd1ab49f 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -41,7 +41,7 @@ interface IInputMethodManager { boolean allowsImplicitlySelectedSubtypes); InputMethodSubtype getLastInputMethodSubtype(); - boolean showSoftInput(in IInputMethodClient client, int flags, + boolean showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags, in ResultReceiver resultReceiver); boolean hideSoftInput(in IInputMethodClient client, int flags, in ResultReceiver resultReceiver); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0bf65bd6f7392..905a94f572623 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -757,6 +757,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("mMethodMap") private final WeakHashMap mImeTargetWindowMap = new WeakHashMap<>(); + /** + * Map of generated token to windowToken that is requesting + * {@link InputMethodManager#showSoftInput(View, int)}. + * This map tracks origin of showSoftInput requests. + */ + @GuardedBy("mMethodMap") + private final WeakHashMap mShowRequestWindowMap = new WeakHashMap<>(); + /** * A ring buffer to store the history of {@link StartInputInfo}. */ @@ -974,7 +982,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideCurrentInputLocked(0, null); mShowRequested = showRequested; } else if (mShowRequested) { - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked( + mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, null); } } else { boolean enabledChanged = false; @@ -2075,7 +2084,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub startInputToken, session, mCurInputContext, mCurAttribute)); if (mShowRequested) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(getAppShowFlags(), null); + showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null); } return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, session.session, (session.channel != null ? session.channel.dup() : null), @@ -2789,7 +2798,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public boolean showSoftInput(IInputMethodClient client, int flags, + public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags, ResultReceiver resultReceiver) { int uid = Binder.getCallingUid(); synchronized (mMethodMap) { @@ -2814,7 +2823,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(flags, resultReceiver); + return showCurrentInputLocked(windowToken, flags, resultReceiver); } finally { Binder.restoreCallingIdentity(ident); } @@ -2822,7 +2831,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("mMethodMap") - boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) { + boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver) { mShowRequested = true; if (mAccessibilityRequestingNoSoftKeyboard) { return false; @@ -2842,9 +2851,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean res = false; if (mCurMethod != null) { if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO( + // create a dummy token for IMS so that IMS cannot inject windows into client app. + Binder showInputToken = new Binder(); + mShowRequestWindowMap.put(showInputToken, windowToken); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO( MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, - resultReceiver)); + resultReceiver, showInputToken)); mInputShown = true; if (mHaveConnection && !mVisibleBound) { bindCurrentInputMethodServiceLocked( @@ -3145,7 +3157,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); } break; case LayoutParams.SOFT_INPUT_STATE_UNCHANGED: @@ -3171,7 +3183,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); } else { Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3188,7 +3200,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); } else { Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3627,7 +3639,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void applyImeVisibility(IBinder token, boolean setVisible) { + private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) { synchronized (mMethodMap) { if (!calledWithValidTokenLocked(token)) { return; @@ -3644,7 +3656,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } else { // Send to window manager to show IME after IME layout finishes. - mWindowManagerInternal.showImePostLayout(mLastImeTargetWindow); + mWindowManagerInternal.showImePostLayout(mShowRequestWindowMap.get(windowToken)); } } } @@ -3695,7 +3707,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } long ident = Binder.clearCallingIdentity(); try { - showCurrentInputLocked(flags, null); + showCurrentInputLocked(mLastImeTargetWindow, flags, null); } finally { Binder.restoreCallingIdentity(ident); } @@ -3780,7 +3792,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput(" + msg.arg1 + ", " + args.arg2 + ")"); - ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2); + ((IInputMethod) args.arg1).showSoftInput( + (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2); } catch (RemoteException e) { } args.recycle(); @@ -5346,8 +5359,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void applyImeVisibility(boolean setVisible) { - mImms.applyImeVisibility(mToken, setVisible); + public void applyImeVisibility(IBinder windowToken, boolean setVisible) { + mImms.applyImeVisibility(mToken, windowToken, setVisible); } } } diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index f09795fbea010..d09c478e320f3 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1449,7 +1449,8 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override public boolean showSoftInput( - IInputMethodClient client, int flags, ResultReceiver resultReceiver) { + IInputMethodClient client, IBinder token, int flags, + ResultReceiver resultReceiver) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int userId = UserHandle.getUserId(callingUid);