Pipe windowToken of window requesting IME

It takes time from when IME is requested to the time when IME is ready
to be shown. When its ready to be shown, we need to make sure that
window that requested IME is still the IME target in DisplayContent. The
only realistic way of knowing originating window is passing windowToken
from IMM API.

Bug: 111084606
Test: CtsInputMethodTestCases

Change-Id: Ia49e23dd077d264a58d28a7b8acffde54b7db187
This commit is contained in:
Tarandeep Singh
2020-01-15 13:58:29 -08:00
parent 1dc6ec5bf5
commit bb0e2f753b
10 changed files with 133 additions and 33 deletions

View File

@@ -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

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -757,6 +757,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("mMethodMap")
private final WeakHashMap<IBinder, IBinder> 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<IBinder, IBinder> 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);
}
}
}

View File

@@ -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);