Merge "Track event flow to IMMS#setImeWindowStatus"

This commit is contained in:
Yohei Yukawa
2017-02-13 22:08:33 +00:00
committed by Android (Google) Code Review
9 changed files with 163 additions and 26 deletions

View File

@@ -166,8 +166,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
final SomeArgs args = (SomeArgs) msg.obj;
final int missingMethods = msg.arg1;
final boolean restarting = msg.arg2 != 0;
final IInputContext inputContext = (IInputContext) args.arg1;
final EditorInfo info = (EditorInfo) args.arg2;
final IBinder startInputToken = (IBinder) args.arg1;
final IInputContext inputContext = (IInputContext) args.arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final InputConnection ic = inputContext != null
? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null;
info.makeCompatible(mTargetSdkVersion);
@@ -176,6 +177,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
} else {
inputMethod.startInput(ic, info);
}
inputMethod.dispatchStartInputWithToken(ic, info, true /* initial */,
startInputToken);
args.recycle();
return;
}
@@ -255,11 +258,11 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
@Override
public void startInput(IInputContext inputContext,
public void startInput(IBinder startInputToken, IInputContext inputContext,
@InputConnectionInspector.MissingMethodFlags final int missingMethods,
EditorInfo attribute, boolean restarting) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIIOO(DO_START_INPUT,
missingMethods, restarting ? 1 : 0, inputContext, attribute));
mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_START_INPUT,
missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute));
}
@Override

View File

@@ -24,6 +24,7 @@ import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
@@ -291,7 +292,20 @@ public class InputMethodService extends AbstractInputMethodService {
boolean mCandidatesViewStarted;
InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
/**
* A token to keep tracking the last IPC that triggered
* {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If
* {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from
* {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged.
*
* <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to
* disentangle event flows for various purposes such as better window animation and providing
* fine-grained debugging information.</p>
*/
@Nullable
private IBinder mStartInputToken;
int mShowInputFlags;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
@@ -415,6 +429,23 @@ public class InputMethodService extends AbstractInputMethodService {
doStartInput(ic, attribute, true);
}
/**
* {@inheritDoc}
* @hide
*/
@Override
public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken) {
mStartInputToken = startInputToken;
// This needs to be dispatched to interface methods rather than doStartInput().
// Otherwise IME developers who have overridden those interface methods will lose
// notifications.
super.dispatchStartInputWithToken(inputConnection, editorInfo, restarting,
startInputToken);
}
/**
* Handle a request by the system to hide the soft input area.
*/
@@ -454,8 +485,8 @@ public class InputMethodService extends AbstractInputMethodService {
clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
boolean showing = isInputViewShown();
mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
mBackDisposition);
mImm.setImeWindowStatus(mToken, mStartInputToken,
IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_SHOWN
@@ -926,8 +957,8 @@ public class InputMethodService extends AbstractInputMethodService {
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
mBackDisposition);
mImm.setImeWindowStatus(mToken, mStartInputToken,
IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
}
}
@@ -1653,7 +1684,8 @@ public class InputMethodService extends AbstractInputMethodService {
final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
if (previousImeWindowStatus != nextImeWindowStatus) {
mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
mBackDisposition);
}
if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
@@ -1678,7 +1710,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
private void doHideWindow() {
mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition);
hideWindow();
}
@@ -2643,7 +2675,8 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mInputStarted=" + mInputStarted
+ " mInputViewStarted=" + mInputViewStarted
+ " mCandidatesViewStarted=" + mCandidatesViewStarted);
p.println(" mStartInputToken=" + mStartInputToken);
if (mInputEditorInfo != null) {
p.println(" mInputEditorInfo:");
mInputEditorInfo.dump(p, " ");

View File

@@ -16,6 +16,8 @@
package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.inputmethodservice.InputMethodService;
@@ -147,6 +149,44 @@ public interface InputMethod {
*/
public void restartInput(InputConnection inputConnection, EditorInfo attribute);
/**
* This method is called when {@code {@link #startInput(InputConnection, EditorInfo)} or
* {@code {@link #restartInput(InputConnection, EditorInfo)} needs to be dispatched.
*
* <p>Note: This method is hidden because the {@code startInputToken} that this method is
* dealing with is one of internal details, which should not be exposed to the IME developers.
* If you override this method, you are responsible for not breaking existing IMEs that expect
* {@link #startInput(InputConnection, EditorInfo)} to be still called back.</p>
*
* @param inputConnection optional specific input connection for communicating with the text
* box; if {@code null}, you should use the generic bound input
* connection
* @param editorInfo information about the text box (typically, an EditText) that requests input
* @param restarting {@code false} if this corresponds to
* {@link #startInput(InputConnection, EditorInfo)}. Otherwise this
* corresponds to {@link #restartInput(InputConnection, EditorInfo)}.
* @param startInputToken a token that identifies a logical session that starts with this method
* call. Some internal IPCs such as {@link
* InputMethodManager#setImeWindowStatus(IBinder, IBinder, int, int)}
* require this token to work, and you have to keep the token alive until
* the next {@link #startInput(InputConnection, EditorInfo, IBinder)} as
* long as your implementation of {@link InputMethod} relies on such
* IPCs
* @see #startInput(InputConnection, EditorInfo)
* @see #restartInput(InputConnection, EditorInfo)
* @see EditorInfo
* @hide
*/
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
startInput(inputConnection, editorInfo);
}
}
/**
* Create a new {@link InputMethodSession} that can be handed to client
* applications for interacting with the input method. You can later

View File

@@ -739,9 +739,10 @@ public final class InputMethodManager {
}
/** @hide */
public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
public void setImeWindowStatus(IBinder imeToken, IBinder startInputToken, int vis,
int backDisposition) {
try {
mService.setImeWindowStatus(imeToken, vis, backDisposition);
mService.setImeWindowStatus(imeToken, startInputToken, vis, backDisposition);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -173,6 +173,16 @@ public class HandlerCaller {
return mH.obtainMessage(what, arg1, arg2, args);
}
public Message obtainMessageIIOOOO(int what, int arg1, int arg2, Object arg3, Object arg4,
Object arg5, Object arg6) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = arg3;
args.arg2 = arg4;
args.arg3 = arg5;
args.arg4 = arg6;
return mH.obtainMessage(what, arg1, arg2, args);
}
public Message obtainMessageOO(int what, Object arg1, Object arg2) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = arg1;

View File

@@ -38,8 +38,8 @@ oneway interface IInputMethod {
void unbindInput();
void startInput(in IInputContext inputContext, int missingMethods, in EditorInfo attribute,
boolean restarting);
void startInput(in IBinder startInputToken, in IInputContext inputContext, int missingMethods,
in EditorInfo attribute, boolean restarting);
void createSession(in InputChannel channel, IInputSessionCallback callback);

View File

@@ -70,7 +70,8 @@ interface IInputMethodManager {
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
void setImeWindowStatus(in IBinder token, in IBinder startInputToken, int vis,
int backDisposition);
void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
InputMethodSubtype getCurrentInputMethodSubtype();

View File

@@ -124,6 +124,7 @@ import android.view.WindowManager;
import android.view.WindowManagerInternal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
@@ -152,6 +153,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.WeakHashMap;
/**
* This class provides a system service that manages input methods.
@@ -506,6 +508,36 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@HardKeyboardBehavior
private final int mHardKeyboardBehavior;
/**
* Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
* internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
* will not affect those tasks that are already posted.
*
* <p>Posting {@link #MSG_START_INPUT} message basically means that
* {@link InputMethodService#doStartInput(InputConnection, EditorInfo, boolean)} will be called
* back in the current IME process shortly, which will also affect what the current IME starts
* receiving from {@link InputMethodService#getCurrentInputConnection()}. In other words, this
* snapshot will be taken every time when {@link InputMethodManagerService} is initiating a new
* logical input session between the client application and the current IME.</p>
*
* <p>Be careful to not keep strong references to this object forever, which can prevent
* {@link StartInputInfo#mImeToken} and {@link StartInputInfo#mTargetWindow} from being GC-ed.
* </p>
*/
private static class StartInputInfo {
@NonNull
final IBinder mImeToken;
@Nullable
final IBinder mTargetWindow;
StartInputInfo(@NonNull IBinder imeToken, @Nullable IBinder targetWindow) {
mImeToken = imeToken;
mTargetWindow = targetWindow;
}
}
private WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
class SettingsObserver extends ContentObserver {
int mUserId;
boolean mRegistered = false;
@@ -1345,10 +1377,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
mBoundToMethod = true;
}
final Binder startInputToken = new Binder();
final StartInputInfo info = new StartInputInfo(mCurToken, mCurFocusedWindow);
mStartInputMap.put(startInputToken, info);
final SessionState session = mCurClient.curSession;
executeOrSendMessage(session.method, mCaller.obtainMessageIIOOO(
executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
session, mCurInputContext, mCurAttribute));
startInputToken, session, mCurInputContext, mCurAttribute));
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
@@ -1816,12 +1853,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@SuppressWarnings("deprecation")
@Override
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
int backDisposition) {
if (startInputToken == null) {
throw new InvalidParameterException("startInputToken cannot be null");
}
if (!calledWithValidToken(token)) {
return;
}
synchronized (mMethodMap) {
final StartInputInfo info = mStartInputMap.get(startInputToken);
if (info == null) {
throw new InvalidParameterException("Unknown startInputToken=" + startInputToken);
}
mImeWindowVis = vis;
mBackDisposition = backDisposition;
updateSystemUiLocked(token, vis, backDisposition);
@@ -2901,12 +2947,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final int missingMethods = msg.arg1;
final boolean restarting = msg.arg2 != 0;
args = (SomeArgs) msg.obj;
final SessionState session = (SessionState) args.arg1;
final IInputContext inputContext = (IInputContext) args.arg2;
final EditorInfo editorInfo = (EditorInfo) args.arg3;
final IBinder startInputToken = (IBinder) args.arg1;
final SessionState session = (SessionState) args.arg2;
final IInputContext inputContext = (IInputContext) args.arg3;
final EditorInfo editorInfo = (EditorInfo) args.arg4;
try {
setEnabledSessionInMainThread(session);
session.method.startInput(inputContext, missingMethods, editorInfo, restarting);
session.method.startInput(startInputToken, inputContext, missingMethods,
editorInfo, restarting);
} catch (RemoteException e) {
}
args.recycle();

View File

@@ -135,7 +135,8 @@ public class BridgeIInputMethodManager implements IInputMethodManager {
}
@Override
public void setImeWindowStatus(IBinder arg0, int arg1, int arg2) throws RemoteException {
public void setImeWindowStatus(IBinder arg0, IBinder arg1, int arg2, int arg3)
throws RemoteException {
// TODO Auto-generated method stub
}