Loading core/java/android/inputmethodservice/IInputMethodWrapper.java +38 −4 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Implements the internal IInputMethod interface to convert incoming calls Loading Loading @@ -76,6 +77,20 @@ class IInputMethodWrapper extends IInputMethod.Stub final WeakReference<InputMethod> mInputMethod; final int mTargetSdkVersion; /** * This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()} * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been * called or not, mainly to avoid unnecessary blocking operations. * * <p>This field must be set and cleared only from the binder thread(s), where the system * guarantees that {@link #bindInput(InputBinding)}, * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean)}, and * {@link #unbindInput()} are called with the same order as the original calls * in {@link com.android.server.InputMethodManagerService}. See {@link IBinder#FLAG_ONEWAY} * for detailed semantics.</p> */ AtomicBoolean mIsUnbindIssued = null; // NOTE: we should have a cache of these. static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; Loading Loading @@ -163,8 +178,10 @@ class IInputMethodWrapper extends IInputMethod.Stub final IBinder startInputToken = (IBinder) args.arg1; final IInputContext inputContext = (IInputContext) args.arg2; final EditorInfo info = (EditorInfo) args.arg3; final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4; final InputConnection ic = inputContext != null ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; ? new InputConnectionWrapper( mTarget, inputContext, missingMethods, isUnbindIssued) : null; info.makeCompatible(mTargetSdkVersion); inputMethod.dispatchStartInputWithToken(ic, info, restarting /* restarting */, startInputToken); Loading Loading @@ -236,10 +253,15 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void bindInput(InputBinding binding) { if (mIsUnbindIssued != null) { Log.e(TAG, "bindInput must be paired with unbindInput."); } mIsUnbindIssued = new AtomicBoolean(); // This IInputContext is guaranteed to implement all the methods. final int missingMethodFlags = 0; InputConnection ic = new InputConnectionWrapper(mTarget, IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags); IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags, mIsUnbindIssued); InputBinding nu = new InputBinding(ic, binding); mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); } Loading @@ -247,6 +269,13 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void unbindInput() { if (mIsUnbindIssued != null) { // Signal the flag then forget it. mIsUnbindIssued.set(true); mIsUnbindIssued = null; } else { Log.e(TAG, "unbindInput must be paired with bindInput."); } mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } Loading @@ -255,8 +284,13 @@ class IInputMethodWrapper extends IInputMethod.Stub public void startInput(IBinder startInputToken, IInputContext inputContext, @InputConnectionInspector.MissingMethodFlags final int missingMethods, EditorInfo attribute, boolean restarting) { mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_START_INPUT, missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute)); if (mIsUnbindIssued == null) { Log.e(TAG, "startInput must be called after bindInput."); mIsUnbindIssued = new AtomicBoolean(); } mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOOO(DO_START_INPUT, missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute, mIsUnbindIssued)); } @BinderThread Loading core/java/com/android/internal/view/InputConnectionWrapper.java +40 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; import android.view.inputmethod.InputContentInfo; import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicBoolean; public class InputConnectionWrapper implements InputConnection { private static final int MAX_WAIT_TIME_MILLIS = 2000; Loading @@ -46,6 +47,14 @@ public class InputConnectionWrapper implements InputConnection { @MissingMethodFlags private final int mMissingMethods; /** * {@code true} if the system already decided to take away IME focus from the target app. This * can be signaled even when the corresponding signal is in the task queue and * {@link InputMethodService#onUnbindInput()} is not yet called back on the UI thread. */ @NonNull private final AtomicBoolean mIsUnbindIssued; static class InputContextCallback extends IInputContextCallback.Stub { private static final String TAG = "InputConnectionWrapper.ICC"; public int mSeq; Loading Loading @@ -231,14 +240,20 @@ public class InputConnectionWrapper implements InputConnection { public InputConnectionWrapper( @NonNull WeakReference<AbstractInputMethodService> inputMethodService, IInputContext inputContext, @MissingMethodFlags final int missingMethods) { IInputContext inputContext, @MissingMethodFlags final int missingMethods, @NonNull AtomicBoolean isUnbindIssued) { mInputMethodService = inputMethodService; mIInputContext = inputContext; mMissingMethods = missingMethods; mIsUnbindIssued = isUnbindIssued; } @AnyThread public CharSequence getTextAfterCursor(int length, int flags) { if (mIsUnbindIssued.get()) { return null; } CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading @@ -258,6 +273,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public CharSequence getTextBeforeCursor(int length, int flags) { if (mIsUnbindIssued.get()) { return null; } CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading @@ -277,6 +296,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public CharSequence getSelectedText(int flags) { if (mIsUnbindIssued.get()) { return null; } if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) { // This method is not implemented. return null; Loading @@ -300,6 +323,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public int getCursorCapsMode(int reqModes) { if (mIsUnbindIssued.get()) { return 0; } int value = 0; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading @@ -319,6 +346,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (mIsUnbindIssued.get()) { return null; } ExtractedText value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading Loading @@ -516,6 +547,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public boolean requestCursorUpdates(int cursorUpdateMode) { if (mIsUnbindIssued.get()) { return false; } boolean result = false; if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) { // This method is not implemented. Loading Loading @@ -550,6 +585,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { if (mIsUnbindIssued.get()) { return false; } boolean result = false; if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) { // This method is not implemented. Loading Loading
core/java/android/inputmethodservice/IInputMethodWrapper.java +38 −4 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Implements the internal IInputMethod interface to convert incoming calls Loading Loading @@ -76,6 +77,20 @@ class IInputMethodWrapper extends IInputMethod.Stub final WeakReference<InputMethod> mInputMethod; final int mTargetSdkVersion; /** * This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()} * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been * called or not, mainly to avoid unnecessary blocking operations. * * <p>This field must be set and cleared only from the binder thread(s), where the system * guarantees that {@link #bindInput(InputBinding)}, * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean)}, and * {@link #unbindInput()} are called with the same order as the original calls * in {@link com.android.server.InputMethodManagerService}. See {@link IBinder#FLAG_ONEWAY} * for detailed semantics.</p> */ AtomicBoolean mIsUnbindIssued = null; // NOTE: we should have a cache of these. static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; Loading Loading @@ -163,8 +178,10 @@ class IInputMethodWrapper extends IInputMethod.Stub final IBinder startInputToken = (IBinder) args.arg1; final IInputContext inputContext = (IInputContext) args.arg2; final EditorInfo info = (EditorInfo) args.arg3; final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4; final InputConnection ic = inputContext != null ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; ? new InputConnectionWrapper( mTarget, inputContext, missingMethods, isUnbindIssued) : null; info.makeCompatible(mTargetSdkVersion); inputMethod.dispatchStartInputWithToken(ic, info, restarting /* restarting */, startInputToken); Loading Loading @@ -236,10 +253,15 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void bindInput(InputBinding binding) { if (mIsUnbindIssued != null) { Log.e(TAG, "bindInput must be paired with unbindInput."); } mIsUnbindIssued = new AtomicBoolean(); // This IInputContext is guaranteed to implement all the methods. final int missingMethodFlags = 0; InputConnection ic = new InputConnectionWrapper(mTarget, IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags); IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags, mIsUnbindIssued); InputBinding nu = new InputBinding(ic, binding); mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); } Loading @@ -247,6 +269,13 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void unbindInput() { if (mIsUnbindIssued != null) { // Signal the flag then forget it. mIsUnbindIssued.set(true); mIsUnbindIssued = null; } else { Log.e(TAG, "unbindInput must be paired with bindInput."); } mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } Loading @@ -255,8 +284,13 @@ class IInputMethodWrapper extends IInputMethod.Stub public void startInput(IBinder startInputToken, IInputContext inputContext, @InputConnectionInspector.MissingMethodFlags final int missingMethods, EditorInfo attribute, boolean restarting) { mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_START_INPUT, missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute)); if (mIsUnbindIssued == null) { Log.e(TAG, "startInput must be called after bindInput."); mIsUnbindIssued = new AtomicBoolean(); } mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOOO(DO_START_INPUT, missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute, mIsUnbindIssued)); } @BinderThread Loading
core/java/com/android/internal/view/InputConnectionWrapper.java +40 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; import android.view.inputmethod.InputContentInfo; import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicBoolean; public class InputConnectionWrapper implements InputConnection { private static final int MAX_WAIT_TIME_MILLIS = 2000; Loading @@ -46,6 +47,14 @@ public class InputConnectionWrapper implements InputConnection { @MissingMethodFlags private final int mMissingMethods; /** * {@code true} if the system already decided to take away IME focus from the target app. This * can be signaled even when the corresponding signal is in the task queue and * {@link InputMethodService#onUnbindInput()} is not yet called back on the UI thread. */ @NonNull private final AtomicBoolean mIsUnbindIssued; static class InputContextCallback extends IInputContextCallback.Stub { private static final String TAG = "InputConnectionWrapper.ICC"; public int mSeq; Loading Loading @@ -231,14 +240,20 @@ public class InputConnectionWrapper implements InputConnection { public InputConnectionWrapper( @NonNull WeakReference<AbstractInputMethodService> inputMethodService, IInputContext inputContext, @MissingMethodFlags final int missingMethods) { IInputContext inputContext, @MissingMethodFlags final int missingMethods, @NonNull AtomicBoolean isUnbindIssued) { mInputMethodService = inputMethodService; mIInputContext = inputContext; mMissingMethods = missingMethods; mIsUnbindIssued = isUnbindIssued; } @AnyThread public CharSequence getTextAfterCursor(int length, int flags) { if (mIsUnbindIssued.get()) { return null; } CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading @@ -258,6 +273,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public CharSequence getTextBeforeCursor(int length, int flags) { if (mIsUnbindIssued.get()) { return null; } CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading @@ -277,6 +296,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public CharSequence getSelectedText(int flags) { if (mIsUnbindIssued.get()) { return null; } if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) { // This method is not implemented. return null; Loading @@ -300,6 +323,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public int getCursorCapsMode(int reqModes) { if (mIsUnbindIssued.get()) { return 0; } int value = 0; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading @@ -319,6 +346,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (mIsUnbindIssued.get()) { return null; } ExtractedText value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); Loading Loading @@ -516,6 +547,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public boolean requestCursorUpdates(int cursorUpdateMode) { if (mIsUnbindIssued.get()) { return false; } boolean result = false; if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) { // This method is not implemented. Loading Loading @@ -550,6 +585,10 @@ public class InputConnectionWrapper implements InputConnection { @AnyThread public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { if (mIsUnbindIssued.get()) { return false; } boolean result = false; if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) { // This method is not implemented. Loading