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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user