diff --git a/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java b/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java
new file mode 100644
index 0000000000000..ecf449d7936a9
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+import android.view.inputmethod.EditorInfo;
+
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+
+interface AccessibilityInputMethodSession {
+ void finishInput();
+
+ void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd);
+
+ void invalidateInput(EditorInfo editorInfo, IRemoteAccessibilityInputConnection connection,
+ int sessionId);
+
+ void setEnabled(boolean enabled);
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java b/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java
new file mode 100644
index 0000000000000..3252ab23486f0
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+final class AccessibilityInputMethodSessionWrapper extends IAccessibilityInputMethodSession.Stub {
+ private final Handler mHandler;
+
+ @NonNull
+ private final AtomicReference mSessionRef;
+
+ AccessibilityInputMethodSessionWrapper(
+ @NonNull Looper looper, @NonNull AccessibilityInputMethodSession session) {
+ mSessionRef = new AtomicReference<>(session);
+ mHandler = Handler.createAsync(looper);
+ }
+
+ @AnyThread
+ @Nullable
+ AccessibilityInputMethodSession getSession() {
+ return mSessionRef.get();
+ }
+
+ @Override
+ public void updateSelection(int oldSelStart, int oldSelEnd,
+ int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart,
+ candidatesEnd);
+ } else {
+ mHandler.post(() -> doUpdateSelection(oldSelStart, oldSelEnd, newSelStart,
+ newSelEnd, candidatesStart, candidatesEnd));
+ }
+ }
+
+ private void doUpdateSelection(int oldSelStart, int oldSelEnd,
+ int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
+ final AccessibilityInputMethodSession session = mSessionRef.get();
+ if (session != null) {
+ session.updateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart,
+ candidatesEnd);
+ }
+ }
+
+ @Override
+ public void finishInput() {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doFinishInput();
+ } else {
+ mHandler.post(this::doFinishInput);
+ }
+ }
+
+ private void doFinishInput() {
+ final AccessibilityInputMethodSession session = mSessionRef.get();
+ if (session != null) {
+ session.finishInput();
+ }
+ }
+
+ @Override
+ public void finishSession() {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doFinishSession();
+ } else {
+ mHandler.post(this::doFinishSession);
+ }
+ }
+
+ private void doFinishSession() {
+ mSessionRef.set(null);
+ }
+
+ @Override
+ public void invalidateInput(EditorInfo editorInfo,
+ IRemoteAccessibilityInputConnection connection, int sessionId) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doInvalidateInput(editorInfo, connection, sessionId);
+ } else {
+ mHandler.post(() -> doInvalidateInput(editorInfo, connection, sessionId));
+ }
+ }
+
+ private void doInvalidateInput(EditorInfo editorInfo,
+ IRemoteAccessibilityInputConnection connection, int sessionId) {
+ final AccessibilityInputMethodSession session = mSessionRef.get();
+ if (session != null) {
+ session.invalidateInput(editorInfo, connection, sessionId);
+ }
+ }
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3cb04e710d3be..c17fbf19516b7 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -40,8 +40,6 @@ import android.graphics.ParcelableColorSpace;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
-import android.inputmethodservice.IInputMethodSessionWrapper;
-import android.inputmethodservice.RemoteInputConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -68,22 +66,19 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodSession;
import com.android.internal.inputmethod.CancellationGroup;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.IInputSessionWithIdCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -639,20 +634,10 @@ public abstract class AccessibilityService extends Service {
/** This is called when the system action list is changed. */
void onSystemActionsChanged();
/** This is called when an app requests ime sessions or when the service is enabled. */
- void createImeSession(IInputSessionWithIdCallback callback);
- /**
- * This is called when InputMethodManagerService requests to set the session enabled or
- * disabled
- */
- void setImeSessionEnabled(InputMethodSession session, boolean enabled);
- /** This is called when an app binds input or when the service is enabled. */
- void bindInput(InputBinding binding);
- /** This is called when an app unbinds input or when the service is disabled. */
- void unbindInput();
+ void createImeSession(IAccessibilityInputMethodSessionCallback callback);
/** This is called when an app starts input or when the service is enabled. */
- void startInput(@Nullable InputConnection inputConnection,
- @NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken);
+ void startInput(@Nullable RemoteAccessibilityInputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, boolean restarting);
}
/**
@@ -2740,42 +2725,20 @@ public abstract class AccessibilityService extends Service {
}
@Override
- public void createImeSession(IInputSessionWithIdCallback callback) {
+ public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
if (mInputMethod != null) {
mInputMethod.createImeSession(callback);
}
}
@Override
- public void setImeSessionEnabled(InputMethodSession session, boolean enabled) {
- if (mInputMethod != null) {
- mInputMethod.setImeSessionEnabled(session, enabled);
- }
- }
-
- @Override
- public void bindInput(InputBinding binding) {
- if (mInputMethod != null) {
- mInputMethod.bindInput(binding);
- }
- }
-
- @Override
- public void unbindInput() {
- if (mInputMethod != null) {
- mInputMethod.unbindInput();
- }
- }
-
- @Override
- public void startInput(@Nullable InputConnection inputConnection,
- @NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ public void startInput(@Nullable RemoteAccessibilityInputConnection connection,
+ @NonNull EditorInfo editorInfo, boolean restarting) {
if (mInputMethod != null) {
if (restarting) {
- mInputMethod.restartInput(inputConnection, editorInfo);
+ mInputMethod.restartInput(connection, editorInfo);
} else {
- mInputMethod.startInput(inputConnection, editorInfo);
+ mInputMethod.startInput(connection, editorInfo);
}
}
}
@@ -2806,8 +2769,6 @@ public abstract class AccessibilityService extends Service {
private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14;
private static final int DO_CREATE_IME_SESSION = 15;
private static final int DO_SET_IME_SESSION_ENABLED = 16;
- private static final int DO_BIND_INPUT = 17;
- private static final int DO_UNBIND_INPUT = 18;
private static final int DO_START_INPUT = 19;
private final HandlerCaller mCaller;
@@ -2818,15 +2779,14 @@ public abstract class AccessibilityService extends Service {
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
/**
- * This is not {@null} only between {@link #bindInput(InputBinding)} and
- * {@link #unbindInput()} so that {@link RemoteInputConnection} can query if
- * {@link #unbindInput()} has already been called or not, mainly to avoid unnecessary
- * blocking operations.
+ * This is not {@code null} only between {@link #bindInput()} and {@link #unbindInput()} so
+ * that {@link RemoteAccessibilityInputConnection} can query if {@link #unbindInput()} has
+ * already been called or not, mainly to avoid unnecessary blocking operations.
*
* 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, EditorInfo, boolean)}, and
- * {@link #unbindInput()} are called with the same order as the original calls
+ * guarantees that {@link #bindInput()},
+ * {@link #startInput(IRemoteAccessibilityInputConnection, EditorInfo, boolean)},
+ * and {@link #unbindInput()} are called with the same order as the original calls
* in {@link com.android.server.inputmethod.InputMethodManagerService}.
* See {@link IBinder#FLAG_ONEWAY} for detailed semantics.
*/
@@ -2927,7 +2887,7 @@ public abstract class AccessibilityService extends Service {
}
/** This is called when an app requests ime sessions or when the service is enabled. */
- public void createImeSession(IInputSessionWithIdCallback callback) {
+ public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
final Message message = mCaller.obtainMessageO(DO_CREATE_IME_SESSION, callback);
mCaller.sendMessage(message);
}
@@ -2936,10 +2896,11 @@ public abstract class AccessibilityService extends Service {
* This is called when InputMethodManagerService requests to set the session enabled or
* disabled
*/
- public void setImeSessionEnabled(IInputMethodSession session, boolean enabled) {
+ public void setImeSessionEnabled(IAccessibilityInputMethodSession session,
+ boolean enabled) {
try {
- InputMethodSession ls = ((IInputMethodSessionWrapper)
- session).getInternalInputMethodSession();
+ AccessibilityInputMethodSession ls =
+ ((AccessibilityInputMethodSessionWrapper) session).getSession();
if (ls == null) {
Log.w(LOG_TAG, "Session is already finished: " + session);
return;
@@ -2952,17 +2913,11 @@ public abstract class AccessibilityService extends Service {
}
/** This is called when an app binds input or when the service is enabled. */
- public void bindInput(InputBinding binding) {
+ public void bindInput() {
if (mCancellationGroup != null) {
Log.e(LOG_TAG, "bindInput must be paired with unbindInput.");
}
mCancellationGroup = new CancellationGroup();
- InputConnection ic = new RemoteInputConnection(new WeakReference<>(() -> mContext),
- IInputContext.Stub.asInterface(binding.getConnectionToken()),
- mCancellationGroup);
- InputBinding nu = new InputBinding(ic, binding);
- final Message message = mCaller.obtainMessageO(DO_BIND_INPUT, nu);
- mCaller.sendMessage(message);
}
/** This is called when an app unbinds input or when the service is disabled. */
@@ -2974,18 +2929,17 @@ public abstract class AccessibilityService extends Service {
} else {
Log.e(LOG_TAG, "unbindInput must be paired with bindInput.");
}
- mCaller.sendMessage(mCaller.obtainMessage(DO_UNBIND_INPUT));
}
/** This is called when an app starts input or when the service is enabled. */
- public void startInput(IBinder startInputToken, IInputContext inputContext,
+ public void startInput(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
if (mCancellationGroup == null) {
Log.e(LOG_TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
- final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
- inputContext, editorInfo, mCancellationGroup, restarting ? 1 : 0,
+ final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, null /* unused */,
+ connection, editorInfo, mCancellationGroup, restarting ? 1 : 0,
0 /* unused */);
mCaller.sendMessage(message);
}
@@ -3157,44 +3111,33 @@ public abstract class AccessibilityService extends Service {
}
case DO_CREATE_IME_SESSION: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- IInputSessionWithIdCallback callback =
- (IInputSessionWithIdCallback) message.obj;
+ IAccessibilityInputMethodSessionCallback callback =
+ (IAccessibilityInputMethodSessionCallback) message.obj;
mCallback.createImeSession(callback);
}
return;
}
case DO_SET_IME_SESSION_ENABLED: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.setImeSessionEnabled((InputMethodSession) message.obj,
- message.arg1 != 0);
- }
- return;
- }
- case DO_BIND_INPUT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.bindInput((InputBinding) message.obj);
- }
- return;
- }
- case DO_UNBIND_INPUT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.unbindInput();
+ AccessibilityInputMethodSession session =
+ (AccessibilityInputMethodSession) message.obj;
+ session.setEnabled(message.arg1 != 0);
}
return;
}
case DO_START_INPUT: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final SomeArgs args = (SomeArgs) message.obj;
- final IBinder startInputToken = (IBinder) args.arg1;
- final IInputContext inputContext = (IInputContext) args.arg2;
+ final IRemoteAccessibilityInputConnection connection =
+ (IRemoteAccessibilityInputConnection) args.arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
- final InputConnection ic = inputContext != null
- ? new RemoteInputConnection(new WeakReference<>(() -> mContext),
- inputContext, cancellationGroup) : null;
+ final RemoteAccessibilityInputConnection ic = connection == null ? null
+ : new RemoteAccessibilityInputConnection(
+ connection, cancellationGroup);
info.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
- mCallback.startInput(ic, info, restarting, startInputToken);
+ mCallback.startInput(ic, info, restarting);
args.recycle();
}
return;
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 94da61f82d294..3bc61e560d8c5 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -25,10 +25,9 @@ import android.accessibilityservice.MagnificationConfig;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.IInputSessionWithIdCallback;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
/**
* Top-level interface to an accessibility service component.
@@ -69,14 +68,14 @@ import com.android.internal.view.IInputSessionWithIdCallback;
void onSystemActionsChanged();
- void createImeSession(IInputSessionWithIdCallback callback);
+ void createImeSession(in IAccessibilityInputMethodSessionCallback callback);
- void setImeSessionEnabled(IInputMethodSession session, boolean enabled);
+ void setImeSessionEnabled(in IAccessibilityInputMethodSession session, boolean enabled);
- void bindInput(in InputBinding binding);
+ void bindInput();
void unbindInput();
- void startInput(in IBinder startInputToken, in IInputContext inputContext,
- in EditorInfo editorInfo, boolean restarting);
+ void startInput(in IRemoteAccessibilityInputConnection connection, in EditorInfo editorInfo,
+ boolean restarting);
}
diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java
index 79bac9bce6204..1585f99759cd0 100644
--- a/core/java/android/accessibilityservice/InputMethod.java
+++ b/core/java/android/accessibilityservice/InputMethod.java
@@ -18,36 +18,23 @@ package android.accessibilityservice;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.graphics.Rect;
-import android.inputmethodservice.IInputMethodSessionWrapper;
-import android.inputmethodservice.RemoteInputConnection;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputSessionWithIdCallback;
-
-import java.util.concurrent.Executor;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
/**
* This class provides input method APIs. Some public methods such as
@@ -61,9 +48,8 @@ public class InputMethod {
private static final String LOG_TAG = "A11yInputMethod";
private final AccessibilityService mService;
- private InputBinding mInputBinding;
private boolean mInputStarted;
- private InputConnection mStartedInputConnection;
+ private RemoteAccessibilityInputConnection mStartedInputConnection;
private EditorInfo mInputEditorInfo;
/**
@@ -131,9 +117,7 @@ public class InputMethod {
* to perform whatever behavior you would like.
*/
public void onFinishInput() {
- if (mStartedInputConnection != null) {
- mStartedInputConnection.finishComposingText();
- }
+ // Intentionally empty
}
/**
@@ -152,41 +136,26 @@ public class InputMethod {
// Intentionally empty
}
- final void createImeSession(IInputSessionWithIdCallback callback) {
- InputMethodSession session = onCreateInputMethodSessionInterface();
+ final void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
+ final AccessibilityInputMethodSessionWrapper wrapper =
+ new AccessibilityInputMethodSessionWrapper(mService.getMainLooper(),
+ new SessionImpl());
try {
- IInputMethodSessionWrapper wrap =
- new IInputMethodSessionWrapper(mService, session, null);
- callback.sessionCreated(wrap, mService.getConnectionId());
+ callback.sessionCreated(wrapper, mService.getConnectionId());
} catch (RemoteException ignored) {
}
}
- final void setImeSessionEnabled(@NonNull InputMethodSession session, boolean enabled) {
- ((InputMethodSessionForAccessibility) session).setEnabled(enabled);
- }
-
- final void bindInput(@NonNull InputBinding binding) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.bindInput");
- mInputBinding = binding;
- Log.v(LOG_TAG, "bindInput(): binding=" + binding);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
-
- final void unbindInput() {
- Log.v(LOG_TAG, "unbindInput(): binding=" + mInputBinding);
- // Unbind input is per process per display.
- mInputBinding = null;
- }
-
- final void startInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
+ final void startInput(@Nullable RemoteAccessibilityInputConnection ic,
+ @NonNull EditorInfo attribute) {
Log.v(LOG_TAG, "startInput(): editor=" + attribute);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.startInput");
doStartInput(ic, attribute, false /* restarting */);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- final void restartInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
+ final void restartInput(@Nullable RemoteAccessibilityInputConnection ic,
+ @NonNull EditorInfo attribute) {
Log.v(LOG_TAG, "restartInput(): editor=" + attribute);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.restartInput");
doStartInput(ic, attribute, true /* restarting */);
@@ -194,7 +163,8 @@ public class InputMethod {
}
- final void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
+ final void doStartInput(RemoteAccessibilityInputConnection ic, EditorInfo attribute,
+ boolean restarting) {
if ((ic == null || !restarting) && mInputStarted) {
doFinishInput();
if (ic == null) {
@@ -220,17 +190,13 @@ public class InputMethod {
mInputEditorInfo = null;
}
- private InputMethodSession onCreateInputMethodSessionInterface() {
- return new InputMethodSessionForAccessibility();
- }
-
/**
* This class provides the allowed list of {@link InputConnection} APIs for
* accessibility services.
*/
public final class AccessibilityInputConnection {
- private InputConnection mIc;
- AccessibilityInputConnection(InputConnection ic) {
+ private final RemoteAccessibilityInputConnection mIc;
+ AccessibilityInputConnection(RemoteAccessibilityInputConnection ic) {
this.mIc = ic;
}
@@ -249,7 +215,7 @@ public class InputMethod {
* int, int)} on the current accessibility service after the batch input is over.
* Editor authors, for this to happen you need to
* make the changes known to the accessibility service by calling
- * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)},
* but be careful to wait until the batch edit is over if one is
* in progress.
*
@@ -282,7 +248,7 @@ public class InputMethod {
* int,int, int)} on the current IME after the batch input is over.
* Editor authors, for this to happen you need to
* make the changes known to the input method by calling
- * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)},
* but be careful to wait until the batch edit is over if one is
* in progress.
*
@@ -367,9 +333,8 @@ public class InputMethod {
* delete only half of a surrogate pair. Also take care not to
* delete more characters than are in the editor, as that may have
* ill effects on the application. Calling this method will cause
- * the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
- * int, int)} on your service after the batch input is over.
+ * the editor to call {@link InputMethod#onUpdateSelection(int, int, int, int, int, int)}
+ * on your service after the batch input is over.
*
* Editor authors: please be careful of race
* conditions in implementing this call. An IME can make a change
@@ -381,7 +346,7 @@ public class InputMethod {
* indices to the size of the contents to avoid crashes. Since
* this changes the contents of the editor, you need to make the
* changes known to the input method by calling
- * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)},
* but be careful to wait until the batch edit is over if one is
* in progress.
*
@@ -522,12 +487,13 @@ public class InputMethod {
}
/**
- * Concrete implementation of InputMethodSession that provides all of the standard behavior
- * for an input method session.
+ * Concrete implementation of {@link AccessibilityInputMethodSession} that provides all of the
+ * standard behavior for an A11y input method session.
*/
- private final class InputMethodSessionForAccessibility implements InputMethodSession {
+ private final class SessionImpl implements AccessibilityInputMethodSession {
boolean mEnabled = true;
+ @Override
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
@@ -549,86 +515,15 @@ public class InputMethod {
}
@Override
- public void viewClicked(boolean focusChanged) {
- }
-
- @Override
- public void updateCursor(@NonNull Rect newCursor) {
- }
-
- @Override
- public void displayCompletions(
- @SuppressLint("ArrayReturn") @NonNull CompletionInfo[] completions) {
- }
-
- @Override
- public void updateExtractedText(int token, @NonNull ExtractedText text) {
- }
-
- public void dispatchKeyEvent(int seq, @NonNull KeyEvent event,
- @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
- }
-
- @Override
- public void dispatchKeyEvent(int seq, @NonNull KeyEvent event,
- @NonNull EventCallback callback) {
- }
-
- public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event,
- @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
- }
-
- @Override
- public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event,
- @NonNull EventCallback callback) {
- }
-
- public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event,
- @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
- }
-
- @Override
- public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event,
- @NonNull EventCallback callback) {
- }
-
- @Override
- public void appPrivateCommand(@NonNull String action, @NonNull Bundle data) {
- }
-
- @Override
- public void toggleSoftInput(int showFlags, int hideFlags) {
- }
-
- @Override
- public void updateCursorAnchorInfo(@NonNull CursorAnchorInfo cursorAnchorInfo) {
- }
-
- @Override
- public void notifyImeHidden() {
- }
-
- @Override
- public void removeImeSurface() {
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext,
- int sessionId) {
- if (mStartedInputConnection instanceof RemoteInputConnection) {
- final RemoteInputConnection ric =
- (RemoteInputConnection) mStartedInputConnection;
- if (!ric.isSameConnection(inputContext)) {
- // This is not an error, and can be safely ignored.
- return;
- }
- editorInfo.makeCompatible(
- mService.getApplicationInfo().targetSdkVersion);
- restartInput(new RemoteInputConnection(ric, sessionId), editorInfo);
+ public void invalidateInput(EditorInfo editorInfo,
+ IRemoteAccessibilityInputConnection connection, int sessionId) {
+ if (!mStartedInputConnection.isSameConnection(connection)) {
+ // This is not an error, and can be safely ignored.
+ return;
}
+ editorInfo.makeCompatible(mService.getApplicationInfo().targetSdkVersion);
+ restartInput(new RemoteAccessibilityInputConnection(mStartedInputConnection, sessionId),
+ editorInfo);
}
}
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 893dc2f6ace46..ac6759396c8f8 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -64,13 +64,11 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodSession;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputSessionWithIdCallback;
import libcore.io.IoUtils;
@@ -1574,26 +1572,14 @@ public final class UiAutomation {
}
@Override
- public void createImeSession(IInputSessionWithIdCallback callback) {
+ public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
/* do nothing */
}
@Override
- public void setImeSessionEnabled(InputMethodSession session, boolean enabled) {
- }
-
- @Override
- public void bindInput(InputBinding binding) {
- }
-
- @Override
- public void unbindInput() {
- }
-
- @Override
- public void startInput(@Nullable InputConnection inputConnection,
- @NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ public void startInput(
+ @Nullable RemoteAccessibilityInputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, boolean restarting) {
}
@Override
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 75356d1ce9944..eccbb403b3068 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -41,9 +41,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodSession;
-/** @hide */
-// TODO(b/215636776): move IInputMethodSessionWrapper to proper package
-public class IInputMethodSessionWrapper extends IInputMethodSession.Stub
+class IInputMethodSessionWrapper extends IInputMethodSession.Stub
implements HandlerCaller.Callback {
private static final String TAG = "InputMethodWrapper";
diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
index 09dbb27359b03..f44f49d7dcaf2 100644
--- a/core/java/android/inputmethodservice/InputMethodServiceInternal.java
+++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
@@ -32,11 +32,8 @@ import java.io.PrintWriter;
* framework classes for internal use.
*
* CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.
- *
- * @hide
*/
-// TODO(b/215636776): move InputMethodServiceInternal to proper package
-public interface InputMethodServiceInternal {
+interface InputMethodServiceInternal {
/**
* @return {@link Context} associated with the service.
*/
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 5b0129ee814cd..86e59e9dcf2f5 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -53,11 +53,8 @@ import java.util.concurrent.CompletableFuture;
*
* See also {@link IInputContext} for the actual {@link android.os.Binder} IPC protocols under
* the hood.
- *
- * @hide
*/
-// TODO(b/215636776): move RemoteInputConnection to proper package
-public final class RemoteInputConnection implements InputConnection {
+final class RemoteInputConnection implements InputConnection {
private static final String TAG = "RemoteInputConnection";
private static final int MAX_WAIT_TIME_MILLIS = 2000;
@@ -98,7 +95,7 @@ public final class RemoteInputConnection implements InputConnection {
@NonNull
private final CancellationGroup mCancellationGroup;
- public RemoteInputConnection(
+ RemoteInputConnection(
@NonNull WeakReference inputMethodService,
IInputContext inputContext, @NonNull CancellationGroup cancellationGroup) {
mImsInternal = new InputMethodServiceInternalHolder(inputMethodService);
@@ -111,7 +108,7 @@ public final class RemoteInputConnection implements InputConnection {
return mInvoker.isSameConnection(inputContext);
}
- public RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
+ RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
mImsInternal = original.mImsInternal;
mInvoker = original.mInvoker.cloneWithSessionId(sessionId);
mCancellationGroup = original.mCancellationGroup;
diff --git a/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java b/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java
new file mode 100644
index 0000000000000..240e38b107a58
--- /dev/null
+++ b/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+
+final class IAccessibilityInputMethodSessionInvoker {
+ private static final String TAG = "IAccessibilityInputMethodSessionInvoker";
+
+ /**
+ * The actual instance of the method to make calls on it.
+ */
+ @NonNull
+ private final IAccessibilityInputMethodSession mSession;
+
+ private IAccessibilityInputMethodSessionInvoker(
+ @NonNull IAccessibilityInputMethodSession session) {
+ mSession = session;
+ }
+
+ /**
+ * Create a {@link IAccessibilityInputMethodSessionInvoker} instance if applicable.
+ *
+ * @param session {@link IAccessibilityInputMethodSession} object to be wrapped.
+ * @return an instance of {@link IAccessibilityInputMethodSessionInvoker} if
+ * {@code inputMethodSession} is not {@code null}. {@code null} otherwise.
+ */
+ @Nullable
+ public static IAccessibilityInputMethodSessionInvoker createOrNull(
+ @NonNull IAccessibilityInputMethodSession session) {
+ return session == null ? null : new IAccessibilityInputMethodSessionInvoker(session);
+ }
+
+ @AnyThread
+ void finishInput() {
+ try {
+ mSession.finishInput();
+ } catch (RemoteException e) {
+ Log.w(TAG, "A11yIME died", e);
+ }
+ }
+
+ @AnyThread
+ void updateSelection(int oldSelStart, int oldSelEnd, int selStart, int selEnd,
+ int candidatesStart, int candidatesEnd) {
+ try {
+ mSession.updateSelection(
+ oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
+ } catch (RemoteException e) {
+ Log.w(TAG, "A11yIME died", e);
+ }
+ }
+
+ @AnyThread
+ void invalidateInput(EditorInfo editorInfo, IRemoteAccessibilityInputConnection connection,
+ int sessionId) {
+ try {
+ mSession.invalidateInput(editorInfo, connection, sessionId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "A11yIME died", e);
+ }
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 805f8e7551a56..84f13930e03a8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -93,6 +93,7 @@ import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -506,8 +507,8 @@ public final class InputMethodManager {
*/
@Nullable
@GuardedBy("mH")
- private final SparseArray mAccessibilityInputMethodSession =
- new SparseArray<>();
+ private final SparseArray
+ mAccessibilityInputMethodSession = new SparseArray<>();
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
@@ -669,7 +670,8 @@ public final class InputMethodManager {
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.finishInput();
}
- forAccessibilitySessions(InputMethodSessionWrapper::finishInput);
+ forAccessibilitySessionsLocked(
+ IAccessibilityInputMethodSessionInvoker::finishInput);
}
}
@@ -730,7 +732,7 @@ public final class InputMethodManager {
focusedView.getWindowToken(), startInputFlags, softInputMode,
windowFlags,
null,
- null,
+ null, null,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -963,13 +965,13 @@ public final class InputMethodManager {
// we send a notification so that the a11y service knows the session is
// registered and update the a11y service with the current cursor positions.
if (res.accessibilitySessions != null) {
- InputMethodSessionWrapper wrapper =
- InputMethodSessionWrapper.createOrNull(
+ IAccessibilityInputMethodSessionInvoker invoker =
+ IAccessibilityInputMethodSessionInvoker.createOrNull(
res.accessibilitySessions.get(id));
- if (wrapper != null) {
- mAccessibilityInputMethodSession.put(id, wrapper);
+ if (invoker != null) {
+ mAccessibilityInputMethodSession.put(id, invoker);
if (mServedInputConnection != null) {
- wrapper.updateSelection(mInitialSelStart, mInitialSelEnd,
+ invoker.updateSelection(mInitialSelStart, mInitialSelEnd,
mCursorSelStart, mCursorSelEnd, mCursorCandStart,
mCursorCandEnd);
} else {
@@ -978,9 +980,7 @@ public final class InputMethodManager {
// binds before or after input starts, it may wonder if it binds
// after input starts, why it doesn't receive a notification of
// the current cursor positions.
- wrapper.updateSelection(-1, -1,
- -1, -1, -1,
- -1);
+ invoker.updateSelection(-1, -1, -1, -1, -1, -1);
}
}
}
@@ -2148,8 +2148,10 @@ public final class InputMethodManager {
editorInfo.setInitialSurroundingTextInternal(textSnapshot.getSurroundingText());
mCurrentInputMethodSession.invalidateInput(editorInfo, mServedInputConnection,
sessionId);
- forAccessibilitySessions(wrapper -> wrapper.invalidateInput(editorInfo,
- mServedInputConnection, sessionId));
+ final IRemoteAccessibilityInputConnection accessibilityInputConnection =
+ mServedInputConnection.asIRemoteAccessibilityInputConnection();
+ forAccessibilitySessionsLocked(wrapper -> wrapper.invalidateInput(editorInfo,
+ accessibilityInputConnection, sessionId));
return true;
}
}
@@ -2323,6 +2325,8 @@ public final class InputMethodManager {
res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
softInputMode, windowFlags, tba, servedInputConnection,
+ servedInputConnection == null ? null
+ : servedInputConnection.asIRemoteAccessibilityInputConnection(),
view.getContext().getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2347,8 +2351,9 @@ public final class InputMethodManager {
mAccessibilityInputMethodSession.clear();
if (res.accessibilitySessions != null) {
for (int i = 0; i < res.accessibilitySessions.size(); i++) {
- InputMethodSessionWrapper wrapper = InputMethodSessionWrapper.createOrNull(
- res.accessibilitySessions.valueAt(i));
+ IAccessibilityInputMethodSessionInvoker wrapper =
+ IAccessibilityInputMethodSessionInvoker.createOrNull(
+ res.accessibilitySessions.valueAt(i));
if (wrapper != null) {
mAccessibilityInputMethodSession.append(
res.accessibilitySessions.keyAt(i), wrapper);
@@ -2598,7 +2603,7 @@ public final class InputMethodManager {
mCursorCandEnd = candidatesEnd;
mCurrentInputMethodSession.updateSelection(
oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
- forAccessibilitySessions(wrapper -> wrapper.updateSelection(oldSelStart,
+ forAccessibilitySessionsLocked(wrapper -> wrapper.updateSelection(oldSelStart,
oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd));
}
}
@@ -3658,7 +3663,8 @@ public final class InputMethodManager {
}
}
- private void forAccessibilitySessions(Consumer consumer) {
+ private void forAccessibilitySessionsLocked(
+ Consumer consumer) {
for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) {
consumer.accept(mAccessibilityInputMethodSession.valueAt(i));
}
diff --git a/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSession.aidl b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSession.aidl
new file mode 100644
index 0000000000000..ccfe3fb014b4f
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSession.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.view.IInputContext;
+
+/**
+ * Sub-interface of IInputMethodSession which is safe to give to A11y IME.
+ */
+oneway interface IAccessibilityInputMethodSession {
+ void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd);
+
+ void finishInput();
+
+ void finishSession();
+
+ void invalidateInput(in EditorInfo editorInfo,
+ in IRemoteAccessibilityInputConnection connection, int sessionId);
+}
diff --git a/core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSessionCallback.aidl
similarity index 69%
rename from core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl
rename to core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSessionCallback.aidl
index 8fbdefe5931e8..bb42c60271fd8 100644
--- a/core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl
+++ b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSessionCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
- package com.android.internal.view;
+ package com.android.internal.inputmethod;
- import com.android.internal.view.IInputMethodSession;
+ import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
/**
* Helper interface for IInputMethod to allow the input method to notify the client when a new
* session has been created.
*/
-oneway interface IInputSessionWithIdCallback {
- void sessionCreated(IInputMethodSession session, int id);
-}
\ No newline at end of file
+oneway interface IAccessibilityInputMethodSessionCallback {
+ void sessionCreated(IAccessibilityInputMethodSession session, int id);
+}
diff --git a/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnection.aidl
new file mode 100644
index 0000000000000..f2064bb93e0cb
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnection.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.view.KeyEvent;
+import android.view.inputmethod.TextAttribute;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.inputmethod.InputConnectionCommandHeader;
+
+/**
+ * Interface from A11y IMEs to the application, allowing it to perform edits on the current input
+ * field and other interactions with the application.
+ */
+oneway interface IRemoteAccessibilityInputConnection {
+ void commitText(in InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, in TextAttribute textAttribute);
+
+ void setSelection(in InputConnectionCommandHeader header, int start, int end);
+
+ void getSurroundingText(in InputConnectionCommandHeader header, int beforeLength,
+ int afterLength, int flags, in AndroidFuture future /* T=SurroundingText */);
+
+ void deleteSurroundingText(in InputConnectionCommandHeader header, int beforeLength,
+ int afterLength);
+
+ void sendKeyEvent(in InputConnectionCommandHeader header, in KeyEvent event);
+
+ void performEditorAction(in InputConnectionCommandHeader header, int actionCode);
+
+ void performContextMenuAction(in InputConnectionCommandHeader header, int id);
+
+ void getCursorCapsMode(in InputConnectionCommandHeader header, int reqModes,
+ in AndroidFuture future /* T=Integer */);
+
+ void clearMetaKeyStates(in InputConnectionCommandHeader header, int states);
+}
diff --git a/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnectionInvoker.java b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnectionInvoker.java
new file mode 100644
index 0000000000000..dcc67d49251f2
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnectionInvoker.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.view.KeyEvent;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.Objects;
+
+final class IRemoteAccessibilityInputConnectionInvoker {
+ @NonNull
+ private final IRemoteAccessibilityInputConnection mConnection;
+ private final int mSessionId;
+
+ private IRemoteAccessibilityInputConnectionInvoker(
+ @NonNull IRemoteAccessibilityInputConnection inputContext, int sessionId) {
+ mConnection = inputContext;
+ mSessionId = sessionId;
+ }
+
+ /**
+ * Creates a new instance of {@link IRemoteAccessibilityInputConnectionInvoker} for the given
+ * {@link IRemoteAccessibilityInputConnection}.
+ *
+ * @param connection {@link IRemoteAccessibilityInputConnection} to be wrapped.
+ * @return A new instance of {@link IRemoteAccessibilityInputConnectionInvoker}.
+ */
+ public static IRemoteAccessibilityInputConnectionInvoker create(
+ @NonNull IRemoteAccessibilityInputConnection connection) {
+ Objects.requireNonNull(connection);
+ return new IRemoteAccessibilityInputConnectionInvoker(connection, 0);
+ }
+
+ /**
+ * Creates a new instance of {@link IRemoteAccessibilityInputConnectionInvoker} with the given
+ * {@code sessionId}.
+ *
+ * @param sessionId the new session ID to be used.
+ * @return A new instance of {@link IRemoteAccessibilityInputConnectionInvoker}.
+ */
+ @NonNull
+ public IRemoteAccessibilityInputConnectionInvoker cloneWithSessionId(int sessionId) {
+ return new IRemoteAccessibilityInputConnectionInvoker(mConnection, sessionId);
+ }
+
+ /**
+ * @param connection {@code IRemoteAccessibilityInputConnection} to be compared with
+ * @return {@code true} if the underlying {@code IRemoteAccessibilityInputConnection} is the
+ * same. {@code false} if {@code connection} is {@code null}.
+ */
+ @AnyThread
+ public boolean isSameConnection(@NonNull IRemoteAccessibilityInputConnection connection) {
+ if (connection == null) {
+ return false;
+ }
+ return mConnection.asBinder() == connection.asBinder();
+ }
+
+ @NonNull
+ InputConnectionCommandHeader createHeader() {
+ return new InputConnectionCommandHeader(mSessionId);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#commitText(InputConnectionCommandHeader,
+ * int, CharSequence)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ */
+ @AnyThread
+ public void commitText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ try {
+ mConnection.commitText(createHeader(), text, newCursorPosition, textAttribute);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#setSelection(InputConnectionCommandHeader,
+ * int, int)}.
+ *
+ * @param start {@code start} parameter to be passed.
+ * @param end {@code start} parameter to be passed.
+ */
+ @AnyThread
+ public void setSelection(int start, int end) {
+ try {
+ mConnection.setSelection(createHeader(), start, end);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getSurroundingText(
+ * InputConnectionCommandHeader, int, int, int, AndroidFuture)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link AndroidFuture< SurroundingText >} that can be used to retrieve the
+ * invocation result. {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public AndroidFuture getSurroundingText(int beforeLength, int afterLength,
+ int flags) {
+ final AndroidFuture future = new AndroidFuture<>();
+ try {
+ mConnection.getSurroundingText(createHeader(), beforeLength, afterLength, flags,
+ future);
+ } catch (RemoteException e) {
+ future.completeExceptionally(e);
+ }
+ return future;
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#deleteSurroundingText(
+ * InputConnectionCommandHeader, int, int)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ */
+ @AnyThread
+ public void deleteSurroundingText(int beforeLength, int afterLength) {
+ try {
+ mConnection.deleteSurroundingText(createHeader(), beforeLength, afterLength);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#sendKeyEvent(
+ * InputConnectionCommandHeader, KeyEvent)}.
+ *
+ * @param event {@code event} parameter to be passed.
+ */
+ @AnyThread
+ public void sendKeyEvent(KeyEvent event) {
+ try {
+ mConnection.sendKeyEvent(createHeader(), event);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performEditorAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param actionCode {@code start} parameter to be passed.
+ */
+ @AnyThread
+ public void performEditorAction(int actionCode) {
+ try {
+ mConnection.performEditorAction(createHeader(), actionCode);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performContextMenuAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param id {@code id} parameter to be passed.
+ */
+ @AnyThread
+ public void performContextMenuAction(int id) {
+ try {
+ mConnection.performContextMenuAction(createHeader(), id);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getCursorCapsMode(
+ * InputConnectionCommandHeader, int, AndroidFuture)}.
+ *
+ * @param reqModes {@code reqModes} parameter to be passed.
+ * @return {@link AndroidFuture} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public AndroidFuture getCursorCapsMode(int reqModes) {
+ final AndroidFuture future = new AndroidFuture<>();
+ try {
+ mConnection.getCursorCapsMode(createHeader(), reqModes, future);
+ } catch (RemoteException e) {
+ future.completeExceptionally(e);
+ }
+ return future;
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#clearMetaKeyStates(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param states {@code states} parameter to be passed.
+ */
+ @AnyThread
+ public void clearMetaKeyStates(int states) {
+ try {
+ mConnection.clearMetaKeyStates(createHeader(), states);
+ } catch (RemoteException e) {
+ }
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index f7341a5c45748..10c83c34f417c 100644
--- a/core/java/com/android/internal/inputmethod/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -186,7 +186,7 @@ public final class InputBindResult implements Parcelable {
/**
* The accessibility services.
*/
- public SparseArray accessibilitySessions;
+ public SparseArray accessibilitySessions;
/**
* The input channel used to send input events to this IME.
@@ -231,8 +231,8 @@ public final class InputBindResult implements Parcelable {
*
* @param result A result code defined in {@link ResultCode}.
* @param method {@link IInputMethodSession} to interact with the IME.
- * @param accessibilitySessions {@link IInputMethodSession} to interact with accessibility
- * services.
+ * @param accessibilitySessions {@link IAccessibilityInputMethodSession} to interact with
+ * accessibility services.
* @param channel {@link InputChannel} to forward input events to the IME.
* @param id The {@link String} representations of the IME, which is the same as
* {@link android.view.inputmethod.InputMethodInfo#getId()} and
@@ -242,7 +242,8 @@ public final class InputBindResult implements Parcelable {
* {@code suppressesSpellChecker="true"}.
*/
public InputBindResult(@ResultCode int result,
- IInputMethodSession method, SparseArray accessibilitySessions,
+ IInputMethodSession method,
+ SparseArray accessibilitySessions,
InputChannel channel, String id, int sequence,
@Nullable Matrix virtualDisplayToScreenMatrix,
boolean isInputMethodSuppressingSpellChecker) {
@@ -271,8 +272,9 @@ public final class InputBindResult implements Parcelable {
accessibilitySessions = new SparseArray<>(n);
while (n > 0) {
int key = source.readInt();
- IInputMethodSession value =
- IInputMethodSession.Stub.asInterface(source.readStrongBinder());
+ IAccessibilityInputMethodSession value =
+ IAccessibilityInputMethodSession.Stub.asInterface(
+ source.readStrongBinder());
accessibilitySessions.append(key, value);
n--;
}
diff --git a/core/java/com/android/internal/inputmethod/RemoteAccessibilityInputConnection.java b/core/java/com/android/internal/inputmethod/RemoteAccessibilityInputConnection.java
new file mode 100644
index 0000000000000..b2ab819eb8a59
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/RemoteAccessibilityInputConnection.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.DurationMillisLong;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.KeyEvent;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.view.IInputMethod;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A wrapper object for A11y IME.
+ *
+ * This needs to be public to be referenced from {@link android.app.UiAutomation}.
+ */
+public final class RemoteAccessibilityInputConnection {
+ private static final String TAG = "RemoteA11yInputConnection";
+
+ @DurationMillisLong
+ private static final int MAX_WAIT_TIME_MILLIS = 2000;
+
+ @NonNull
+ IRemoteAccessibilityInputConnectionInvoker mInvoker;
+
+ /**
+ * Signaled when the system decided to take away IME focus from the target app.
+ *
+ * This is expected to be signaled immediately when the IME process receives
+ * {@link IInputMethod#unbindInput()}.
+ */
+ @NonNull
+ private final CancellationGroup mCancellationGroup;
+
+ public RemoteAccessibilityInputConnection(
+ @NonNull IRemoteAccessibilityInputConnection connection,
+ @NonNull CancellationGroup cancellationGroup) {
+ mInvoker = IRemoteAccessibilityInputConnectionInvoker.create(connection);
+ mCancellationGroup = cancellationGroup;
+ }
+
+ public RemoteAccessibilityInputConnection(@NonNull RemoteAccessibilityInputConnection original,
+ int sessionId) {
+ mInvoker = original.mInvoker.cloneWithSessionId(sessionId);
+ mCancellationGroup = original.mCancellationGroup;
+ }
+
+ /**
+ * Test if this object holds the given {@link IRemoteAccessibilityInputConnection} or not.
+ *
+ * @param connection {@link IRemoteAccessibilityInputConnection} to be tested.
+ * @return {@code true} if this object holds the same object.
+ */
+ @AnyThread
+ public boolean isSameConnection(@NonNull IRemoteAccessibilityInputConnection connection) {
+ return mInvoker.isSameConnection(connection);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#commitText(InputConnectionCommandHeader,
+ * CharSequence, int, TextAttribute)}.
+ *
+ * @param text The {@code "text"} parameter to be passed.
+ * @param newCursorPosition The {@code "newCursorPosition"} parameter to be passed.
+ * @param textAttribute The {@code "textAttribute"} parameter to be passed.
+ */
+ @AnyThread
+ public void commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ mInvoker.commitText(text, newCursorPosition, textAttribute);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#setSelection(InputConnectionCommandHeader,
+ * int, int)}.
+ *
+ * @param start The {@code "start"} parameter to be passed.
+ * @param end The {@code "end"} parameter to be passed.
+ */
+ @AnyThread
+ public void setSelection(int start, int end) {
+ mInvoker.setSelection(start, end);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getSurroundingText(
+ * InputConnectionCommandHeader, int, int, int, AndroidFuture)}.
+ *
+ * @param beforeLength The {@code "beforeLength"} parameter to be passed.
+ * @param afterLength The {@code "afterLength"} parameter to be passed.
+ * @param flags The {@code "flags"} parameter to be passed.
+ * @return The {@link SurroundingText} object returned from the target application.
+ */
+ @AnyThread
+ public SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ if (beforeLength < 0) {
+ throw new IllegalArgumentException("beforeLength cannot be negative but was "
+ + beforeLength);
+ }
+ if (afterLength < 0) {
+ throw new IllegalArgumentException("afterLength cannot be negative but was "
+ + afterLength);
+ }
+ if (mCancellationGroup.isCanceled()) {
+ return null;
+ }
+
+ final CompletableFuture value = mInvoker.getSurroundingText(beforeLength,
+ afterLength, flags);
+ return CompletableFutureUtil.getResultOrNull(
+ value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#deleteSurroundingText(
+ * InputConnectionCommandHeader, int, int)}.
+ *
+ * @param beforeLength The {@code "beforeLength"} parameter to be passed.
+ * @param afterLength The {@code "afterLength"} parameter to be passed.
+ */
+ @AnyThread
+ public void deleteSurroundingText(int beforeLength, int afterLength) {
+ mInvoker.deleteSurroundingText(beforeLength, afterLength);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#sendKeyEvent(InputConnectionCommandHeader,
+ * KeyEvent)}.
+ *
+ * @param event The {@code "event"} parameter to be passed.
+ */
+ @AnyThread
+ public void sendKeyEvent(KeyEvent event) {
+ mInvoker.sendKeyEvent(event);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performEditorAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param actionCode The {@code "actionCode"} parameter to be passed.
+ */
+ @AnyThread
+ public void performEditorAction(int actionCode) {
+ mInvoker.performEditorAction(actionCode);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performContextMenuAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param id The {@code "id"} parameter to be passed.
+ */
+ @AnyThread
+ public void performContextMenuAction(int id) {
+ mInvoker.performContextMenuAction(id);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getCursorCapsMode(
+ * InputConnectionCommandHeader, int, AndroidFuture)}.
+ *
+ * @param reqModes The {@code "reqModes"} parameter to be passed.
+ * @return integer result returned from the target application.
+ */
+ @AnyThread
+ public int getCursorCapsMode(int reqModes) {
+ if (mCancellationGroup.isCanceled()) {
+ return 0;
+ }
+
+ final CompletableFuture value = mInvoker.getCursorCapsMode(reqModes);
+
+ return CompletableFutureUtil.getResultOrZero(
+ value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#clearMetaKeyStates(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param states The {@code "states"} parameter to be passed.
+ */
+ @AnyThread
+ public void clearMetaKeyStates(int states) {
+ mInvoker.clearMetaKeyStates(states);
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 7f11e3018849e..66c4b68995572 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -66,6 +66,13 @@ import java.util.function.Supplier;
* {@link IInputContext} binder calls in the IME client (editor app) process, and forwards them to
* {@link InputConnection} that the IME client provided, on the {@link Looper} associated to the
* {@link InputConnection}.
+ *
+ * {@link com.android.internal.inputmethod.RemoteAccessibilityInputConnection} code is executed
+ * in the {@link android.accessibilityservice.AccessibilityService} process. It makes
+ * {@link com.android.internal.inputmethod.IRemoteAccessibilityInputConnection} binder calls under
+ * the hood. {@link #mAccessibilityInputConnection} receives the binder calls in the IME client
+ * (editor app) process, and forwards them to {@link InputConnection} that the IME client provided,
+ * on the {@link Looper} associated to the {@link InputConnection}.
*/
public final class RemoteInputConnectionImpl extends IInputContext.Stub {
private static final String TAG = "RemoteInputConnectionImpl";
@@ -1043,6 +1050,175 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
});
}
+ private final IRemoteAccessibilityInputConnection mAccessibilityInputConnection =
+ new IRemoteAccessibilityInputConnection.Stub() {
+ @Dispatching(cancellable = true)
+ @Override
+ public void commitText(InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("commitTextFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitText on inactive InputConnection");
+ return;
+ }
+ ic.commitText(text, newCursorPosition, textAttribute);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void setSelection(InputConnectionCommandHeader header, int start, int end) {
+ dispatchWithTracing("setSelectionFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setSelection on inactive InputConnection");
+ return;
+ }
+ ic.setSelection(start, end);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
+ int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
+ dispatchWithTracing("getSurroundingTextFromA11yIme", future, () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return null; // cancelled
+ }
+ final InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getSurroundingText on inactive InputConnection");
+ return null;
+ }
+ if (beforeLength < 0) {
+ Log.i(TAG, "Returning null to getSurroundingText due to an invalid"
+ + " beforeLength=" + beforeLength);
+ return null;
+ }
+ if (afterLength < 0) {
+ Log.i(TAG, "Returning null to getSurroundingText due to an invalid"
+ + " afterLength=" + afterLength);
+ return null;
+ }
+ return ic.getSurroundingText(beforeLength, afterLength, flags);
+ }, useImeTracing() ? result -> buildGetSurroundingTextProto(
+ beforeLength, afterLength, flags, result) : null);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
+ int afterLength) {
+ dispatchWithTracing("deleteSurroundingTextFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
+ return;
+ }
+ ic.deleteSurroundingText(beforeLength, afterLength);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
+ dispatchWithTracing("sendKeyEventFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "sendKeyEvent on inactive InputConnection");
+ return;
+ }
+ ic.sendKeyEvent(event);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void performEditorAction(InputConnectionCommandHeader header, int id) {
+ dispatchWithTracing("performEditorActionFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performEditorAction on inactive InputConnection");
+ return;
+ }
+ ic.performEditorAction(id);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
+ dispatchWithTracing("performContextMenuActionFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performContextMenuAction on inactive InputConnection");
+ return;
+ }
+ ic.performContextMenuAction(id);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
+ AndroidFuture future /* T=Integer */) {
+ dispatchWithTracing("getCursorCapsModeFromA11yIme", future, () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return 0; // cancelled
+ }
+ final InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
+ return 0;
+ }
+ return ic.getCursorCapsMode(reqModes);
+ }, useImeTracing() ? result -> buildGetCursorCapsModeProto(reqModes, result) : null);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
+ dispatchWithTracing("clearMetaKeyStatesFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
+ return;
+ }
+ ic.clearMetaKeyStates(states);
+ });
+ }
+ };
+
+ /**
+ * @return {@link IRemoteAccessibilityInputConnection} associated with this object.
+ */
+ public IRemoteAccessibilityInputConnection asIRemoteAccessibilityInputConnection() {
+ return mAccessibilityInputConnection;
+ }
+
private void dispatch(@NonNull Runnable runnable) {
// If we are calling this from the target thread, then we can call right through.
// Otherwise, we need to send the message to the target thread.
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 616411fee5f26..d7bb2cb10b8c4 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -22,6 +22,7 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -54,7 +55,8 @@ interface IInputMethodManager {
in IInputMethodClient client, in IBinder windowToken,
/* @StartInputFlags */ int startInputFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
- int windowFlags, in EditorInfo attribute, IInputContext inputContext,
+ int windowFlags, in EditorInfo attribute, in IInputContext inputContext,
+ in IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion);
void showInputMethodPickerFromClient(in IInputMethodClient client,
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 562d11a0b1971..649328d1eef39 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -84,16 +84,15 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.IInputSessionWithIdCallback;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
@@ -1655,21 +1654,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mInvocationHandler.createImeSessionLocked();
}
- public void setImeSessionEnabledLocked(IInputMethodSession session, boolean enabled) {
+ public void setImeSessionEnabledLocked(IAccessibilityInputMethodSession session,
+ boolean enabled) {
mInvocationHandler.setImeSessionEnabledLocked(session, enabled);
}
- public void bindInputLocked(InputBinding binding) {
- mInvocationHandler.bindInputLocked(binding);
+ public void bindInputLocked() {
+ mInvocationHandler.bindInputLocked();
}
public void unbindInputLocked() {
mInvocationHandler.unbindInputLocked();
}
- public void startInputLocked(IBinder startInputToken, IInputContext inputContext,
+ public void startInputLocked(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
- mInvocationHandler.startInputLocked(startInputToken, inputContext, editorInfo, restarting);
+ mInvocationHandler.startInputLocked(connection, editorInfo, restarting);
}
@@ -1827,7 +1827,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void setImeSessionEnabledInternal(IInputMethodSession session, boolean enabled) {
+ private void setImeSessionEnabledInternal(IAccessibilityInputMethodSession session,
+ boolean enabled) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null && session != null) {
try {
@@ -1842,14 +1843,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void bindInputInternal(InputBinding binding) {
+ private void bindInputInternal() {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
- logTraceSvcClient("bindInput", binding.toString());
+ logTraceSvcClient("bindInput", "");
}
- listener.bindInput(binding);
+ listener.bindInput();
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error binding input to " + mService, re);
@@ -1872,16 +1873,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void startInputInternal(IBinder startInputToken, IInputContext inputContext,
+ private void startInputInternal(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
- logTraceSvcClient("startInput", startInputToken + " "
- + inputContext + " " + editorInfo + restarting);
+ logTraceSvcClient("startInput", "editorInfo=" + editorInfo
+ + " restarting=" + restarting);
}
- listener.startInput(startInputToken, inputContext, editorInfo, restarting);
+ listener.startInput(connection, editorInfo, restarting);
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error starting input to " + mService, re);
@@ -2141,12 +2142,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
break;
case MSG_SET_IME_SESSION_ENABLED:
final boolean enabled = (message.arg1 != 0);
- final IInputMethodSession session = (IInputMethodSession) message.obj;
+ final IAccessibilityInputMethodSession session =
+ (IAccessibilityInputMethodSession) message.obj;
setImeSessionEnabledInternal(session, enabled);
break;
case MSG_BIND_INPUT:
- final InputBinding binding = (InputBinding) message.obj;
- bindInputInternal(binding);
+ bindInputInternal();
break;
case MSG_UNBIND_INPUT:
unbindInputInternal();
@@ -2154,10 +2155,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
case MSG_START_INPUT:
final boolean restarting = (message.arg1 != 0);
final SomeArgs args = (SomeArgs) message.obj;
- final IBinder startInputToken = (IBinder) args.arg1;
- final IInputContext inputContext = (IInputContext) args.arg2;
- final EditorInfo editorInfo = (EditorInfo) args.arg3;
- startInputInternal(startInputToken, inputContext, editorInfo, restarting);
+ final IRemoteAccessibilityInputConnection connection =
+ (IRemoteAccessibilityInputConnection) args.arg1;
+ final EditorInfo editorInfo = (EditorInfo) args.arg2;
+ startInputInternal(connection, editorInfo, restarting);
+ args.recycle();
break;
default: {
throw new IllegalArgumentException("Unknown message: " + type);
@@ -2227,14 +2229,15 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
msg.sendToTarget();
}
- public void setImeSessionEnabledLocked(IInputMethodSession session, boolean enabled) {
+ public void setImeSessionEnabledLocked(IAccessibilityInputMethodSession session,
+ boolean enabled) {
final Message msg = obtainMessage(MSG_SET_IME_SESSION_ENABLED, (enabled ? 1 : 0),
0, session);
msg.sendToTarget();
}
- public void bindInputLocked(InputBinding binding) {
- final Message msg = obtainMessage(MSG_BIND_INPUT, binding);
+ public void bindInputLocked() {
+ final Message msg = obtainMessage(MSG_BIND_INPUT);
msg.sendToTarget();
}
@@ -2243,12 +2246,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
msg.sendToTarget();
}
- public void startInputLocked(IBinder startInputToken, IInputContext inputContext,
+ public void startInputLocked(
+ IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = startInputToken;
- args.arg2 = inputContext;
- args.arg3 = editorInfo;
+ args.arg1 = connection;
+ args.arg2 = editorInfo;
final Message msg = obtainMessage(MSG_START_INPUT, restarting ? 1 : 0, 0, args);
msg.sendToTarget();
}
@@ -2402,10 +2405,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private static final class AccessibilityCallback extends IInputSessionWithIdCallback.Stub {
+ private static final class AccessibilityCallback
+ extends IAccessibilityInputMethodSessionCallback.Stub {
@Override
- public void sessionCreated(IInputMethodSession session, int id) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.sessionCreated");
+ public void sessionCreated(IAccessibilityInputMethodSession session, int id) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AACS.sessionCreated");
final long ident = Binder.clearCallingIdentity();
try {
InputMethodManagerInternal.get().onSessionForAccessibilityCreated(id, session);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ac0c051794b30..cbeb01a68778a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -118,7 +118,6 @@ import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -128,12 +127,12 @@ import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserAct
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -281,9 +280,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
- private InputBinding mInputBinding;
- IBinder mStartInputToken;
- IInputContext mInputContext;
+ private boolean mInputBound;
+ IRemoteAccessibilityInputConnection mRemoteInputConnection;
EditorInfo mEditorInfo;
boolean mRestarting;
boolean mInputSessionRequested;
@@ -322,7 +320,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void setImeSessionEnabled(SparseArray sessions,
+ public void setImeSessionEnabled(SparseArray sessions,
boolean enabled) {
mService.scheduleSetImeSessionEnabled(sessions, enabled);
}
@@ -333,8 +331,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void bindInput(InputBinding binding) {
- mService.scheduleBindInput(binding);
+ public void bindInput() {
+ mService.scheduleBindInput();
}
@Override
@@ -343,9 +341,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void startInput(IBinder startInputToken, IInputContext inputContext,
+ public void startInput(
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
EditorInfo editorInfo, boolean restarting) {
- mService.scheduleStartInput(startInputToken, inputContext, editorInfo, restarting);
+ mService.scheduleStartInput(remoteAccessibilityInputConnection, editorInfo, restarting);
}
}
@@ -4350,10 +4349,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void bindAndStartInputForConnection(AbstractAccessibilityServiceConnection connection) {
synchronized (mLock) {
- if (mInputBinding != null) {
- connection.bindInputLocked(mInputBinding);
- connection.startInputLocked(mStartInputToken, mInputContext, mEditorInfo,
- mRestarting);
+ if (mInputBound) {
+ connection.bindInputLocked();
+ connection.startInputLocked(mRemoteInputConnection, mEditorInfo, mRestarting);
}
}
}
@@ -4396,23 +4394,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/**
* Bind input for accessibility services which request ime capabilities.
- *
- * @param binding Information given to an accessibility service about a client connecting to it.
*/
- public void scheduleBindInput(InputBinding binding) {
- mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::bindInput, this,
- binding));
+ public void scheduleBindInput() {
+ mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::bindInput, this));
}
- private void bindInput(InputBinding binding) {
+ private void bindInput() {
synchronized (mLock) {
// Keep records of these in case new Accessibility Services are enabled.
- mInputBinding = binding;
+ mInputBound = true;
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.requestImeApis()) {
- service.bindInputLocked(binding);
+ service.bindInputLocked();
}
}
}
@@ -4427,6 +4422,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void unbindInput() {
synchronized (mLock) {
+ mInputBound = false;
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -4440,25 +4436,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/**
* Start input for accessibility services which request ime capabilities.
*/
- public void scheduleStartInput(IBinder startInputToken, IInputContext inputContext,
+ public void scheduleStartInput(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::startInput, this,
- startInputToken, inputContext, editorInfo, restarting));
+ connection, editorInfo, restarting));
}
- private void startInput(IBinder startInputToken, IInputContext inputContext,
- EditorInfo editorInfo, boolean restarting) {
+ private void startInput(IRemoteAccessibilityInputConnection connection, EditorInfo editorInfo,
+ boolean restarting) {
synchronized (mLock) {
// Keep records of these in case new Accessibility Services are enabled.
- mStartInputToken = startInputToken;
- mInputContext = inputContext;
+ mRemoteInputConnection = connection;
mEditorInfo = editorInfo;
mRestarting = restarting;
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.requestImeApis()) {
- service.startInputLocked(startInputToken, inputContext, editorInfo, restarting);
+ service.startInputLocked(connection, editorInfo, restarting);
}
}
}
@@ -4492,13 +4487,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @param sessions Sessions to enable or disable.
* @param enabled True if enable the sessions or false if disable the sessions.
*/
- public void scheduleSetImeSessionEnabled(SparseArray sessions,
+ public void scheduleSetImeSessionEnabled(SparseArray sessions,
boolean enabled) {
mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::setImeSessionEnabled,
this, sessions, enabled));
}
- private void setImeSessionEnabled(SparseArray sessions, boolean enabled) {
+ private void setImeSessionEnabled(SparseArray sessions,
+ boolean enabled) {
synchronized (mLock) {
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/AccessibilityManagerInternal.java b/services/core/java/com/android/server/AccessibilityManagerInternal.java
index 28f6db1c800b5..6ca32af8c3cbe 100644
--- a/services/core/java/com/android/server/AccessibilityManagerInternal.java
+++ b/services/core/java/com/android/server/AccessibilityManagerInternal.java
@@ -17,28 +17,26 @@
package com.android.server;
import android.annotation.NonNull;
-import android.os.IBinder;
import android.util.ArraySet;
import android.util.SparseArray;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
/**
* Accessibility manager local system service interface.
*/
public abstract class AccessibilityManagerInternal {
/** Enable or disable the sessions. */
- public abstract void setImeSessionEnabled(SparseArray sessions,
- boolean enabled);
+ public abstract void setImeSessionEnabled(
+ SparseArray sessions, boolean enabled);
/** Unbind input for all accessibility services which require ime capabilities. */
public abstract void unbindInput();
/** Bind input for all accessibility services which require ime capabilities. */
- public abstract void bindInput(InputBinding binding);
+ public abstract void bindInput();
/**
* Request input session from all accessibility services which require ime capabilities and
@@ -47,12 +45,13 @@ public abstract class AccessibilityManagerInternal {
public abstract void createImeSession(ArraySet ignoreSet);
/** Start input for all accessibility services which require ime capabilities. */
- public abstract void startInput(IBinder startInputToken, IInputContext inputContext,
+ public abstract void startInput(
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
EditorInfo editorInfo, boolean restarting);
private static final AccessibilityManagerInternal NOP = new AccessibilityManagerInternal() {
@Override
- public void setImeSessionEnabled(SparseArray sessions,
+ public void setImeSessionEnabled(SparseArray sessions,
boolean enabled) {
}
@@ -61,7 +60,7 @@ public abstract class AccessibilityManagerInternal {
}
@Override
- public void bindInput(InputBinding binding) {
+ public void bindInput() {
}
@Override
@@ -69,7 +68,7 @@ public abstract class AccessibilityManagerInternal {
}
@Override
- public void startInput(IBinder startInputToken, IInputContext inputContext,
+ public void startInput(IRemoteAccessibilityInputConnection remoteAccessibility,
EditorInfo editorInfo, boolean restarting) {
}
};
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index a2d3588f0e68c..b978131e175c7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -24,9 +24,9 @@ import android.os.IBinder;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodInfo;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.server.LocalServices;
@@ -164,7 +164,7 @@ public abstract class InputMethodManagerInternal {
* @param session The session passed back from the accessibility service.
*/
public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IInputMethodSession session);
+ IAccessibilityInputMethodSession session);
/**
* Unbind the accessibility service with the specified accessibilityConnectionId from current
@@ -240,7 +240,7 @@ public abstract class InputMethodManagerInternal {
@Override
public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IInputMethodSession session) {
+ IAccessibilityInputMethodSession session) {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index bcebcb4c74d4d..ce8b9fabd5a08 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -155,8 +155,10 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -422,7 +424,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Id of the accessibility service.
final int mId;
- public IInputMethodSession mSession;
+ public IAccessibilityInputMethodSession mSession;
@Override
public String toString() {
@@ -434,7 +436,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
AccessibilitySessionState(ClientState client, int id,
- IInputMethodSession session) {
+ IAccessibilityInputMethodSession session) {
mClient = client;
mId = id;
mSession = session;
@@ -613,6 +615,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*/
IInputContext mCurInputContext;
+ /**
+ * The {@link IRemoteAccessibilityInputConnection} last provided by the current client.
+ */
+ @Nullable IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection;
+
/**
* The attributes last provided by the current client.
*/
@@ -2627,7 +2634,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final InputMethodInfo curInputMethodInfo = mMethodMap.get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
- final SparseArray accessibilityInputMethodSessions =
+ final SparseArray accessibilityInputMethodSessions =
createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, accessibilityInputMethodSessions,
@@ -2666,7 +2673,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
InputBindResult attachNewAccessibilityLocked(@StartInputReason int startInputReason,
boolean initial, int id) {
if (!mBoundToAccessibility) {
- AccessibilityManagerInternal.get().bindInput(mCurClient.binding);
+ AccessibilityManagerInternal.get().bindInput();
mBoundToAccessibility = true;
}
@@ -2680,14 +2687,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (startInputReason != StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY) {
final Binder startInputToken = new Binder();
setEnabledSessionForAccessibilityLocked(mCurClient.mAccessibilitySessions);
- AccessibilityManagerInternal.get().startInput(startInputToken, mCurInputContext,
+ AccessibilityManagerInternal.get().startInput(mCurRemoteAccessibilityInputConnection,
mCurAttribute, !initial /* restarting */);
}
if (accessibilitySession != null) {
final SessionState session = mCurClient.curSession;
IInputMethodSession imeSession = session == null ? null : session.session;
- final SparseArray accessibilityInputMethodSessions =
+ final SparseArray accessibilityInputMethodSessions =
createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION,
@@ -2698,9 +2705,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return null;
}
- private SparseArray createAccessibilityInputMethodSessions(
+ private SparseArray createAccessibilityInputMethodSessions(
SparseArray accessibilitySessions) {
- final SparseArray accessibilityInputMethodSessions =
+ final SparseArray accessibilityInputMethodSessions =
new SparseArray<>();
if (accessibilitySessions != null) {
for (int i = 0; i < accessibilitySessions.size(); i++) {
@@ -2722,8 +2729,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
@NonNull
private InputBindResult startInputUncheckedLocked(@NonNull ClientState cs,
- IInputContext inputContext, @NonNull EditorInfo attribute,
- @StartInputFlags int startInputFlags, @StartInputReason int startInputReason,
+ IInputContext inputContext,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
+ @StartInputReason int startInputReason,
int unverifiedTargetSdkVersion) {
// If no method is currently selected, do nothing.
final String selectedMethodId = getSelectedMethodIdLocked();
@@ -2767,6 +2776,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
advanceSequenceNumberLocked();
mCurClient = cs;
mCurInputContext = inputContext;
+ mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection;
mCurVirtualDisplayToScreenMatrix =
getVirtualDisplayToScreenMatrixLocked(cs.selfReportedDisplayId,
mDisplayIdToShowIme);
@@ -3769,10 +3779,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion) {
return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, attribute, inputContext,
- unverifiedTargetSdkVersion);
+ remoteAccessibilityInputConnection, unverifiedTargetSdkVersion);
}
@NonNull
@@ -3780,6 +3791,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
@@ -3816,7 +3828,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
client, windowToken, startInputFlags, softInputMode, windowFlags,
- attribute, inputContext, unverifiedTargetSdkVersion, userId);
+ attribute, inputContext, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion, userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3842,7 +3855,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputReason int startInputReason, IInputMethodClient client,
@NonNull IBinder windowToken, @StartInputFlags int startInputFlags,
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
- IInputContext inputContext, int unverifiedTargetSdkVersion, @UserIdInt int userId) {
+ IInputContext inputContext,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId) {
if (DEBUG) {
Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
@@ -3935,7 +3950,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ InputMethodDebug.startInputReasonToString(startInputReason));
}
if (attribute != null) {
- return startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
+ return startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
}
return new InputBindResult(
@@ -3976,8 +3992,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// UI for input.
if (isTextEditor && attribute != null
&& shouldRestoreImeVisibility(windowToken, softInputMode)) {
- res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
+ attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion);
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
@@ -4015,8 +4031,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// is more room for the target window + IME.
if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
+ startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4046,8 +4063,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, startInputFlags)) {
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
+ startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4065,8 +4083,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
unverifiedTargetSdkVersion, startInputFlags)) {
if (!sameWindowFocused) {
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
+ startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4094,7 +4113,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
}
}
- res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
@@ -4850,7 +4870,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
void setEnabledSessionForAccessibilityLocked(
SparseArray accessibilitySessions) {
// mEnabledAccessibilitySessions could the same object as accessibilitySessions.
- SparseArray disabledSessions = new SparseArray<>();
+ SparseArray disabledSessions = new SparseArray<>();
for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) {
if (!accessibilitySessions.contains(mEnabledAccessibilitySessions.keyAt(i))) {
AccessibilitySessionState sessionState = mEnabledAccessibilitySessions.valueAt(i);
@@ -4864,7 +4884,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
AccessibilityManagerInternal.get().setImeSessionEnabled(disabledSessions,
false);
}
- SparseArray enabledSessions = new SparseArray<>();
+ SparseArray enabledSessions = new SparseArray<>();
for (int i = 0; i < accessibilitySessions.size(); i++) {
if (!mEnabledAccessibilitySessions.contains(accessibilitySessions.keyAt(i))) {
AccessibilitySessionState sessionState = accessibilitySessions.valueAt(i);
@@ -5709,7 +5729,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IInputMethodSession session) {
+ IAccessibilityInputMethodSession session) {
synchronized (ImfLock.class) {
if (mCurClient != null) {
clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId);