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
      1112d366b6

Fix: 236147652
Test: atest CtsInputMethodTestCases:InProcessImeTest
Change-Id: I56f3198e54f689adc9541f2c2644ee19bcc6deed
This commit is contained in:
Yohei Yukawa
2022-06-16 09:47:12 -07:00
parent 026d885cf7
commit e345e90a01

View File

@@ -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) {