summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/accessibilityservice/AccessibilityInputMethodSession.java33
-rw-r--r--core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java116
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java133
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl17
-rw-r--r--core/java/android/accessibilityservice/InputMethod.java175
-rw-r--r--core/java/android/app/UiAutomation.java26
-rw-r--r--core/java/android/inputmethodservice/IInputMethodSessionWrapper.java4
-rw-r--r--core/java/android/inputmethodservice/InputMethodServiceInternal.java5
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java9
-rw-r--r--core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java84
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java42
11 files changed, 349 insertions, 295 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java b/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java
new file mode 100644
index 000000000000..ecf449d7936a
--- /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 000000000000..3252ab23486f
--- /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<AccessibilityInputMethodSession> 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 3cb04e710d3b..c17fbf19516b 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.
*
* <p>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.</p>
*/
@@ -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 94da61f82d29..3bc61e560d8c 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 79bac9bce620..1585f99759cd 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.
* <strong>Editor authors</strong>, 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.</p>
*
@@ -282,7 +248,7 @@ public class InputMethod {
* int,int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, 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.</p>
*
@@ -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.</p>
+ * the editor to call {@link InputMethod#onUpdateSelection(int, int, int, int, int, int)}
+ * on your service after the batch input is over.</p>
*
* <p><strong>Editor authors:</strong> 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.</p>
*
@@ -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 893dc2f6ace4..ac6759396c8f 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 75356d1ce994..eccbb403b306 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 09dbb27359b0..f44f49d7dcaf 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.
*
* <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p>
- *
- * @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 5b0129ee814c..86e59e9dcf2f 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -53,11 +53,8 @@ import java.util.concurrent.CompletableFuture;
*
* <p>See also {@link IInputContext} for the actual {@link android.os.Binder} IPC protocols under
* the hood.</p>
- *
- * @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<InputMethodServiceInternal> 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 000000000000..240e38b107a5
--- /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 805f8e7551a5..84f13930e03a 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<InputMethodSessionWrapper> mAccessibilityInputMethodSession =
- new SparseArray<>();
+ private final SparseArray<IAccessibilityInputMethodSessionInvoker>
+ 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<InputMethodSessionWrapper> consumer) {
+ private void forAccessibilitySessionsLocked(
+ Consumer<IAccessibilityInputMethodSessionInvoker> consumer) {
for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) {
consumer.accept(mAccessibilityInputMethodSession.valueAt(i));
}