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

Commit 3f733cc7 authored by lumark's avatar lumark
Browse files

Fix some thread handling issues in InputMethodManager

- Make sure onCheckIsTextEditor should be called on the UI thread for
preventing exception in some cases that will break start input background
thread.

- Refine IMM#checkFocus and DelegateImpl#startInput to be out of
lock object to prevent start input background thread may blocked by other
methods which already inside the lock.

- Cache InputMethodManagerDelegate object for ImeFocusController#getDelegate.

Fix: 147447217
Bug: 141738570
Test: manual as test steps:
     1) Launch Keeeps app, type some web-URL link.
     2) Long press on any URL link > From the toolbar select "open / browser"
        and observe.
     3) Expect the soft-keyboard should be hidden after entering the
     browser page.
Test: atest CtsInputMethodTestCases

Change-Id: I098df41f935438bf1889fb030c7a3e7a0645a168
parent 843cd473
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ public final class ImeFocusController {
    private boolean mHasImeFocus = false;
    private View mServedView;
    private View mNextServedView;
    private InputMethodManagerDelegate mDelegate;

    @UiThread
    ImeFocusController(@NonNull ViewRootImpl viewRootImpl) {
@@ -45,7 +46,11 @@ public final class ImeFocusController {
    }

    private InputMethodManagerDelegate getImmDelegate() {
        return mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate();
        if (mDelegate == null) {
            mDelegate = mViewRootImpl.mContext.getSystemService(
                    InputMethodManager.class).getDelegate();
        }
        return mDelegate;
    }

    @UiThread
+48 −22
Original line number Diff line number Diff line
@@ -59,12 +59,12 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
import android.view.Display;
import android.view.ImeFocusController;
import android.view.ImeInsetsSourceConsumer;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventSender;
import android.view.KeyEvent;
import android.view.ImeFocusController;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -562,15 +562,24 @@ public final class InputMethodManager {
        public boolean startInput(@StartInputReason int startInputReason, View focusedView,
                @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
                int windowFlags) {
            final View servedView;
            synchronized (mH) {
                mCurrentTextBoxAttribute = null;
                mCompletions = null;
                mServedConnecting = true;
                if (getServedViewLocked() != null && !getServedViewLocked().onCheckIsTextEditor()) {
                servedView = getServedViewLocked();
            }
            if (servedView != null && servedView.getHandler() != null) {
                // Make sure View checks should be on the UI thread.
                servedView.getHandler().post(() -> {
                    if (!servedView.onCheckIsTextEditor()) {
                        // servedView has changed and it's not editable.
                        synchronized (mH) {
                            maybeCallServedViewChangedLocked(null);
                        }
                    }
                });
            }
            return startInputInner(startInputReason,
                    focusedView != null ? focusedView.getWindowToken() : null, startInputFlags,
                    softInputMode, windowFlags);
@@ -607,11 +616,11 @@ public final class InputMethodManager {
                mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
            }
            mWindowFocusGainFuture = mStartInputWorker.submit(() -> {
                synchronized (mH) {
                    if (mCurRootView == null) {
                final ImeFocusController controller = getFocusController();
                if (controller == null) {
                    return;
                }
                    if (mCurRootView.getImeFocusController().checkFocus(forceNewFocus1, false)) {
                if (controller.checkFocus(forceNewFocus1, false)) {
                    // We need to restart input on the current focus view.  This
                    // should be done in conjunction with telling the system service
                    // about the window gaining focus, to help make the transition
@@ -622,6 +631,7 @@ public final class InputMethodManager {
                    }
                }

                synchronized (mH) {
                    // For some reason we didn't do a startInput + windowFocusGain, so
                    // we'll just do a window focus gain and call it a day.
                    try {
@@ -716,6 +726,15 @@ public final class InputMethodManager {
        }
    }

    private ImeFocusController getFocusController() {
        synchronized (mH) {
            if (mCurRootView != null) {
                return mCurRootView.getImeFocusController();
            }
            return null;
        }
    }

    /**
     * Returns {@code true} when the given view has been served by Input Method.
     */
@@ -1793,6 +1812,12 @@ public final class InputMethodManager {
        startInputInner(StartInputReason.APP_CALLED_RESTART_INPUT_API, null, 0, 0, 0);
    }

    /**
     * Called when {@link DelegateImpl#startInput}, {@link #restartInput(View)},
     * {@link #MSG_BIND} or {@link #MSG_UNBIND}.
     * Note that this method should *NOT* be called inside of {@code mH} lock to prevent start input
     * background thread may blocked by other methods which already inside {@code mH} lock.
     */
    boolean startInputInner(@StartInputReason int startInputReason,
            @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
            @SoftInputModeFlags int softInputMode, int windowFlags) {
@@ -1976,15 +2001,16 @@ public final class InputMethodManager {
    }

    /**
     * Check the next served view from {@link ImeFocusController} if needs to start input.
     * Note that this method should *NOT* be called inside of {@code mH} lock to prevent start input
     * background thread may blocked by other methods which already inside {@code mH} lock.
     * @hide
     */
    @UnsupportedAppUsage
    public void checkFocus() {
        synchronized (mH) {
            if (mCurRootView != null) {
                mCurRootView.getImeFocusController().checkFocus(false /* forceNewFocus */,
                        true /* startInput */);
            }
        final ImeFocusController controller = getFocusController();
        if (controller != null) {
            controller.checkFocus(false /* forceNewFocus */, true /* startInput */);
        }
    }