Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7be78787 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Skip blocking InputConnection APIs after unbind"

parents c1f1380c 1d113d04
Loading
Loading
Loading
Loading
+38 −4
Original line number Diff line number Diff line
@@ -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
@@ -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;
@@ -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);
@@ -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));
    }
@@ -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));
    }

@@ -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
+40 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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();
@@ -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();
@@ -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;
@@ -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();
@@ -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();
@@ -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.
@@ -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.