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

Commit e345e90a authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Make IInputMethodSession async even for in-proc cases

This CL applies the same approach I used for IInputMethodClient
recently [1], that is, to use a dedicated worker thread to invoke
binder IPCs to achieve the async semantics even if IInputMethodSession
is not a BinderProxy, which happens when both the IME and IME client
are running in the same process.

When the IME and IME client are running in different processes, this
CL literally changes nothing.

 [1]: I0a7dd5162c4b94599b896420b8427913fa772414
      1112d366

Fix: 236147652
Test: atest CtsInputMethodTestCases:InProcessImeTest
Change-Id: I56f3198e54f689adc9541f2c2644ee19bcc6deed
parent 026d885c
Loading
Loading
Loading
Loading
+126 −3
Original line number Diff line number Diff line
@@ -20,10 +20,14 @@ import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputMethodSession;
import com.android.internal.inputmethod.IRemoteInputConnection;

@@ -42,8 +46,25 @@ final class IInputMethodSessionInvoker {
    @NonNull
    private final IInputMethodSession mSession;

    private IInputMethodSessionInvoker(@NonNull IInputMethodSession inputMethodSession) {
    /**
     * An optional {@link Handler} to dispatch {@link IInputMethodSession} method invocations to
     * a background thread to emulate async (one-way) {@link Binder} call.
     *
     * {@code null} if {@code Binder.isProxy(mSession)} is {@code true}.
     */
    @Nullable
    private final Handler mCustomHandler;

    private static final Object sAsyncBinderEmulationHandlerLock = new Object();

    @GuardedBy("sAsyncBinderEmulationHandlerLock")
    @Nullable
    private static Handler sAsyncBinderEmulationHandler;

    private IInputMethodSessionInvoker(@NonNull IInputMethodSession inputMethodSession,
            @Nullable Handler customHandler) {
        mSession = inputMethodSession;
        mCustomHandler = customHandler;
    }

    /**
@@ -56,12 +77,37 @@ final class IInputMethodSessionInvoker {
    @Nullable
    public static IInputMethodSessionInvoker createOrNull(
            @NonNull IInputMethodSession inputMethodSession) {
        return inputMethodSession != null ? new IInputMethodSessionInvoker(inputMethodSession)
                : null;

        final Handler customHandler;
        if (inputMethodSession != null && !Binder.isProxy(inputMethodSession)) {
            synchronized (sAsyncBinderEmulationHandlerLock) {
                if (sAsyncBinderEmulationHandler == null) {
                    final HandlerThread thread = new HandlerThread("IMM.binder-emu");
                    thread.start();
                    // Use an async handler instead of Handler#getThreadHandler().
                    sAsyncBinderEmulationHandler = Handler.createAsync(thread.getLooper());
                }
                customHandler = sAsyncBinderEmulationHandler;
            }
        } else {
            customHandler = null;
        }

        return inputMethodSession != null
                ? new IInputMethodSessionInvoker(inputMethodSession, customHandler) : null;
    }

    @AnyThread
    void finishInput() {
        if (mCustomHandler == null) {
            finishInputInternal();
        } else {
            mCustomHandler.post(this::finishInputInternal);
        }
    }

    @AnyThread
    private void finishInputInternal() {
        try {
            mSession.finishInput();
        } catch (RemoteException e) {
@@ -71,6 +117,15 @@ final class IInputMethodSessionInvoker {

    @AnyThread
    void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
        if (mCustomHandler == null) {
            updateCursorAnchorInfoInternal(cursorAnchorInfo);
        } else {
            mCustomHandler.post(() -> updateCursorAnchorInfoInternal(cursorAnchorInfo));
        }
    }

    @AnyThread
    private void updateCursorAnchorInfoInternal(CursorAnchorInfo cursorAnchorInfo) {
        try {
            mSession.updateCursorAnchorInfo(cursorAnchorInfo);
        } catch (RemoteException e) {
@@ -80,6 +135,15 @@ final class IInputMethodSessionInvoker {

    @AnyThread
    void displayCompletions(CompletionInfo[] completions) {
        if (mCustomHandler == null) {
            displayCompletionsInternal(completions);
        } else {
            mCustomHandler.post(() -> displayCompletionsInternal(completions));
        }
    }

    @AnyThread
    void displayCompletionsInternal(CompletionInfo[] completions) {
        try {
            mSession.displayCompletions(completions);
        } catch (RemoteException e) {
@@ -89,6 +153,15 @@ final class IInputMethodSessionInvoker {

    @AnyThread
    void updateExtractedText(int token, ExtractedText text) {
        if (mCustomHandler == null) {
            updateExtractedTextInternal(token, text);
        } else {
            mCustomHandler.post(() -> updateExtractedTextInternal(token, text));
        }
    }

    @AnyThread
    private void updateExtractedTextInternal(int token, ExtractedText text) {
        try {
            mSession.updateExtractedText(token, text);
        } catch (RemoteException e) {
@@ -98,6 +171,15 @@ final class IInputMethodSessionInvoker {

    @AnyThread
    void appPrivateCommand(String action, Bundle data) {
        if (mCustomHandler == null) {
            appPrivateCommandInternal(action, data);
        } else {
            mCustomHandler.post(() -> appPrivateCommandInternal(action, data));
        }
    }

    @AnyThread
    private void appPrivateCommandInternal(String action, Bundle data) {
        try {
            mSession.appPrivateCommand(action, data);
        } catch (RemoteException e) {
@@ -107,6 +189,15 @@ final class IInputMethodSessionInvoker {

    @AnyThread
    void viewClicked(boolean focusChanged) {
        if (mCustomHandler == null) {
            viewClickedInternal(focusChanged);
        } else {
            mCustomHandler.post(() -> viewClickedInternal(focusChanged));
        }
    }

    @AnyThread
    private void viewClickedInternal(boolean focusChanged) {
        try {
            mSession.viewClicked(focusChanged);
        } catch (RemoteException e) {
@@ -116,6 +207,15 @@ final class IInputMethodSessionInvoker {

    @AnyThread
    void updateCursor(Rect newCursor) {
        if (mCustomHandler == null) {
            updateCursorInternal(newCursor);
        } else {
            mCustomHandler.post(() -> updateCursorInternal(newCursor));
        }
    }

    @AnyThread
    private void updateCursorInternal(Rect newCursor) {
        try {
            mSession.updateCursor(newCursor);
        } catch (RemoteException e) {
@@ -126,6 +226,18 @@ final class IInputMethodSessionInvoker {
    @AnyThread
    void updateSelection(int oldSelStart, int oldSelEnd, int selStart, int selEnd,
            int candidatesStart, int candidatesEnd) {
        if (mCustomHandler == null) {
            updateSelectionInternal(
                    oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
        } else {
            mCustomHandler.post(() -> updateSelectionInternal(
                    oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd));
        }
    }

    @AnyThread
    private void updateSelectionInternal(int oldSelStart, int oldSelEnd, int selStart, int selEnd,
            int candidatesStart, int candidatesEnd) {
        try {
            mSession.updateSelection(
                    oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
@@ -137,6 +249,17 @@ final class IInputMethodSessionInvoker {
    @AnyThread
    void invalidateInput(EditorInfo editorInfo, IRemoteInputConnection inputConnection,
            int sessionId) {
        if (mCustomHandler == null) {
            invalidateInputInternal(editorInfo, inputConnection, sessionId);
        } else {
            mCustomHandler.post(() -> invalidateInputInternal(editorInfo, inputConnection,
                    sessionId));
        }
    }

    @AnyThread
    private void invalidateInputInternal(EditorInfo editorInfo,
            IRemoteInputConnection inputConnection, int sessionId) {
        try {
            mSession.invalidateInput(editorInfo, inputConnection, sessionId);
        } catch (RemoteException e) {