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);