diff options
Diffstat (limited to 'core/java/android')
11 files changed, 349 insertions, 295 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java b/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java new file mode 100644 index 000000000000..ecf449d7936a --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.accessibilityservice; + +import android.view.inputmethod.EditorInfo; + +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; + +interface AccessibilityInputMethodSession { + void finishInput(); + + void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, + int candidatesStart, int candidatesEnd); + + void invalidateInput(EditorInfo editorInfo, IRemoteAccessibilityInputConnection connection, + int sessionId); + + void setEnabled(boolean enabled); +} diff --git a/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java b/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java new file mode 100644 index 000000000000..3252ab23486f --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.accessibilityservice; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Handler; +import android.os.Looper; +import android.view.inputmethod.EditorInfo; + +import com.android.internal.inputmethod.IAccessibilityInputMethodSession; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; + +import java.util.concurrent.atomic.AtomicReference; + +final class AccessibilityInputMethodSessionWrapper extends IAccessibilityInputMethodSession.Stub { + private final Handler mHandler; + + @NonNull + private final AtomicReference<AccessibilityInputMethodSession> mSessionRef; + + AccessibilityInputMethodSessionWrapper( + @NonNull Looper looper, @NonNull AccessibilityInputMethodSession session) { + mSessionRef = new AtomicReference<>(session); + mHandler = Handler.createAsync(looper); + } + + @AnyThread + @Nullable + AccessibilityInputMethodSession getSession() { + return mSessionRef.get(); + } + + @Override + public void updateSelection(int oldSelStart, int oldSelEnd, + int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { + if (mHandler.getLooper().isCurrentThread()) { + doUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, + candidatesEnd); + } else { + mHandler.post(() -> doUpdateSelection(oldSelStart, oldSelEnd, newSelStart, + newSelEnd, candidatesStart, candidatesEnd)); + } + } + + private void doUpdateSelection(int oldSelStart, int oldSelEnd, + int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { + final AccessibilityInputMethodSession session = mSessionRef.get(); + if (session != null) { + session.updateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, + candidatesEnd); + } + } + + @Override + public void finishInput() { + if (mHandler.getLooper().isCurrentThread()) { + doFinishInput(); + } else { + mHandler.post(this::doFinishInput); + } + } + + private void doFinishInput() { + final AccessibilityInputMethodSession session = mSessionRef.get(); + if (session != null) { + session.finishInput(); + } + } + + @Override + public void finishSession() { + if (mHandler.getLooper().isCurrentThread()) { + doFinishSession(); + } else { + mHandler.post(this::doFinishSession); + } + } + + private void doFinishSession() { + mSessionRef.set(null); + } + + @Override + public void invalidateInput(EditorInfo editorInfo, + IRemoteAccessibilityInputConnection connection, int sessionId) { + if (mHandler.getLooper().isCurrentThread()) { + doInvalidateInput(editorInfo, connection, sessionId); + } else { + mHandler.post(() -> doInvalidateInput(editorInfo, connection, sessionId)); + } + } + + private void doInvalidateInput(EditorInfo editorInfo, + IRemoteAccessibilityInputConnection connection, int sessionId) { + final AccessibilityInputMethodSession session = mSessionRef.get(); + if (session != null) { + session.invalidateInput(editorInfo, connection, sessionId); + } + } +} diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 3cb04e710d3b..c17fbf19516b 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -40,8 +40,6 @@ import android.graphics.ParcelableColorSpace; import android.graphics.Region; import android.hardware.HardwareBuffer; import android.hardware.display.DisplayManager; -import android.inputmethodservice.IInputMethodSessionWrapper; -import android.inputmethodservice.RemoteInputConnection; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -68,22 +66,19 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityWindowInfo; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputBinding; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodSession; import com.android.internal.inputmethod.CancellationGroup; +import com.android.internal.inputmethod.IAccessibilityInputMethodSession; +import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; +import com.android.internal.inputmethod.RemoteAccessibilityInputConnection; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodSession; -import com.android.internal.view.IInputSessionWithIdCallback; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; @@ -639,20 +634,10 @@ public abstract class AccessibilityService extends Service { /** This is called when the system action list is changed. */ void onSystemActionsChanged(); /** This is called when an app requests ime sessions or when the service is enabled. */ - void createImeSession(IInputSessionWithIdCallback callback); - /** - * This is called when InputMethodManagerService requests to set the session enabled or - * disabled - */ - void setImeSessionEnabled(InputMethodSession session, boolean enabled); - /** This is called when an app binds input or when the service is enabled. */ - void bindInput(InputBinding binding); - /** This is called when an app unbinds input or when the service is disabled. */ - void unbindInput(); + void createImeSession(IAccessibilityInputMethodSessionCallback callback); /** This is called when an app starts input or when the service is enabled. */ - void startInput(@Nullable InputConnection inputConnection, - @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken); + void startInput(@Nullable RemoteAccessibilityInputConnection inputConnection, + @NonNull EditorInfo editorInfo, boolean restarting); } /** @@ -2740,42 +2725,20 @@ public abstract class AccessibilityService extends Service { } @Override - public void createImeSession(IInputSessionWithIdCallback callback) { + public void createImeSession(IAccessibilityInputMethodSessionCallback callback) { if (mInputMethod != null) { mInputMethod.createImeSession(callback); } } @Override - public void setImeSessionEnabled(InputMethodSession session, boolean enabled) { - if (mInputMethod != null) { - mInputMethod.setImeSessionEnabled(session, enabled); - } - } - - @Override - public void bindInput(InputBinding binding) { - if (mInputMethod != null) { - mInputMethod.bindInput(binding); - } - } - - @Override - public void unbindInput() { - if (mInputMethod != null) { - mInputMethod.unbindInput(); - } - } - - @Override - public void startInput(@Nullable InputConnection inputConnection, - @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken) { + public void startInput(@Nullable RemoteAccessibilityInputConnection connection, + @NonNull EditorInfo editorInfo, boolean restarting) { if (mInputMethod != null) { if (restarting) { - mInputMethod.restartInput(inputConnection, editorInfo); + mInputMethod.restartInput(connection, editorInfo); } else { - mInputMethod.startInput(inputConnection, editorInfo); + mInputMethod.startInput(connection, editorInfo); } } } @@ -2806,8 +2769,6 @@ public abstract class AccessibilityService extends Service { private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14; private static final int DO_CREATE_IME_SESSION = 15; private static final int DO_SET_IME_SESSION_ENABLED = 16; - private static final int DO_BIND_INPUT = 17; - private static final int DO_UNBIND_INPUT = 18; private static final int DO_START_INPUT = 19; private final HandlerCaller mCaller; @@ -2818,15 +2779,14 @@ public abstract class AccessibilityService extends Service { private int mConnectionId = AccessibilityInteractionClient.NO_ID; /** - * This is not {@null} only between {@link #bindInput(InputBinding)} and - * {@link #unbindInput()} so that {@link RemoteInputConnection} can query if - * {@link #unbindInput()} has already been called or not, mainly to avoid unnecessary - * blocking operations. + * This is not {@code null} only between {@link #bindInput()} and {@link #unbindInput()} so + * that {@link RemoteAccessibilityInputConnection} can query if {@link #unbindInput()} has + * already been called or not, mainly to avoid unnecessary blocking operations. * * <p>This field must be set and cleared only from the binder thread(s), where the system - * guarantees that {@link #bindInput(InputBinding)}, - * {@link #startInput(IBinder, IInputContext, EditorInfo, boolean)}, and - * {@link #unbindInput()} are called with the same order as the original calls + * guarantees that {@link #bindInput()}, + * {@link #startInput(IRemoteAccessibilityInputConnection, EditorInfo, boolean)}, + * and {@link #unbindInput()} are called with the same order as the original calls * in {@link com.android.server.inputmethod.InputMethodManagerService}. * See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p> */ @@ -2927,7 +2887,7 @@ public abstract class AccessibilityService extends Service { } /** This is called when an app requests ime sessions or when the service is enabled. */ - public void createImeSession(IInputSessionWithIdCallback callback) { + public void createImeSession(IAccessibilityInputMethodSessionCallback callback) { final Message message = mCaller.obtainMessageO(DO_CREATE_IME_SESSION, callback); mCaller.sendMessage(message); } @@ -2936,10 +2896,11 @@ public abstract class AccessibilityService extends Service { * This is called when InputMethodManagerService requests to set the session enabled or * disabled */ - public void setImeSessionEnabled(IInputMethodSession session, boolean enabled) { + public void setImeSessionEnabled(IAccessibilityInputMethodSession session, + boolean enabled) { try { - InputMethodSession ls = ((IInputMethodSessionWrapper) - session).getInternalInputMethodSession(); + AccessibilityInputMethodSession ls = + ((AccessibilityInputMethodSessionWrapper) session).getSession(); if (ls == null) { Log.w(LOG_TAG, "Session is already finished: " + session); return; @@ -2952,17 +2913,11 @@ public abstract class AccessibilityService extends Service { } /** This is called when an app binds input or when the service is enabled. */ - public void bindInput(InputBinding binding) { + public void bindInput() { if (mCancellationGroup != null) { Log.e(LOG_TAG, "bindInput must be paired with unbindInput."); } mCancellationGroup = new CancellationGroup(); - InputConnection ic = new RemoteInputConnection(new WeakReference<>(() -> mContext), - IInputContext.Stub.asInterface(binding.getConnectionToken()), - mCancellationGroup); - InputBinding nu = new InputBinding(ic, binding); - final Message message = mCaller.obtainMessageO(DO_BIND_INPUT, nu); - mCaller.sendMessage(message); } /** This is called when an app unbinds input or when the service is disabled. */ @@ -2974,18 +2929,17 @@ public abstract class AccessibilityService extends Service { } else { Log.e(LOG_TAG, "unbindInput must be paired with bindInput."); } - mCaller.sendMessage(mCaller.obtainMessage(DO_UNBIND_INPUT)); } /** This is called when an app starts input or when the service is enabled. */ - public void startInput(IBinder startInputToken, IInputContext inputContext, + public void startInput(IRemoteAccessibilityInputConnection connection, EditorInfo editorInfo, boolean restarting) { if (mCancellationGroup == null) { Log.e(LOG_TAG, "startInput must be called after bindInput."); mCancellationGroup = new CancellationGroup(); } - final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken, - inputContext, editorInfo, mCancellationGroup, restarting ? 1 : 0, + final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, null /* unused */, + connection, editorInfo, mCancellationGroup, restarting ? 1 : 0, 0 /* unused */); mCaller.sendMessage(message); } @@ -3157,44 +3111,33 @@ public abstract class AccessibilityService extends Service { } case DO_CREATE_IME_SESSION: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { - IInputSessionWithIdCallback callback = - (IInputSessionWithIdCallback) message.obj; + IAccessibilityInputMethodSessionCallback callback = + (IAccessibilityInputMethodSessionCallback) message.obj; mCallback.createImeSession(callback); } return; } case DO_SET_IME_SESSION_ENABLED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { - mCallback.setImeSessionEnabled((InputMethodSession) message.obj, - message.arg1 != 0); - } - return; - } - case DO_BIND_INPUT: { - if (mConnectionId != AccessibilityInteractionClient.NO_ID) { - mCallback.bindInput((InputBinding) message.obj); - } - return; - } - case DO_UNBIND_INPUT: { - if (mConnectionId != AccessibilityInteractionClient.NO_ID) { - mCallback.unbindInput(); + AccessibilityInputMethodSession session = + (AccessibilityInputMethodSession) message.obj; + session.setEnabled(message.arg1 != 0); } return; } case DO_START_INPUT: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final SomeArgs args = (SomeArgs) message.obj; - final IBinder startInputToken = (IBinder) args.arg1; - final IInputContext inputContext = (IInputContext) args.arg2; + final IRemoteAccessibilityInputConnection connection = + (IRemoteAccessibilityInputConnection) args.arg2; final EditorInfo info = (EditorInfo) args.arg3; final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4; final boolean restarting = args.argi5 == 1; - final InputConnection ic = inputContext != null - ? new RemoteInputConnection(new WeakReference<>(() -> mContext), - inputContext, cancellationGroup) : null; + final RemoteAccessibilityInputConnection ic = connection == null ? null + : new RemoteAccessibilityInputConnection( + connection, cancellationGroup); info.makeCompatible(mContext.getApplicationInfo().targetSdkVersion); - mCallback.startInput(ic, info, restarting, startInputToken); + mCallback.startInput(ic, info, restarting); args.recycle(); } return; diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 94da61f82d29..3bc61e560d8c 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -25,10 +25,9 @@ import android.accessibilityservice.MagnificationConfig; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputBinding; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodSession; -import com.android.internal.view.IInputSessionWithIdCallback; +import com.android.internal.inputmethod.IAccessibilityInputMethodSession; +import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; /** * Top-level interface to an accessibility service component. @@ -69,14 +68,14 @@ import com.android.internal.view.IInputSessionWithIdCallback; void onSystemActionsChanged(); - void createImeSession(IInputSessionWithIdCallback callback); + void createImeSession(in IAccessibilityInputMethodSessionCallback callback); - void setImeSessionEnabled(IInputMethodSession session, boolean enabled); + void setImeSessionEnabled(in IAccessibilityInputMethodSession session, boolean enabled); - void bindInput(in InputBinding binding); + void bindInput(); void unbindInput(); - void startInput(in IBinder startInputToken, in IInputContext inputContext, - in EditorInfo editorInfo, boolean restarting); + void startInput(in IRemoteAccessibilityInputConnection connection, in EditorInfo editorInfo, + boolean restarting); } diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java index 79bac9bce620..1585f99759cd 100644 --- a/core/java/android/accessibilityservice/InputMethod.java +++ b/core/java/android/accessibilityservice/InputMethod.java @@ -18,36 +18,23 @@ package android.accessibilityservice; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import android.annotation.CallbackExecutor; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.graphics.Rect; -import android.inputmethodservice.IInputMethodSessionWrapper; -import android.inputmethodservice.RemoteInputConnection; -import android.os.Bundle; import android.os.RemoteException; import android.os.Trace; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.ExtractedText; -import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSession; import android.view.inputmethod.SurroundingText; import android.view.inputmethod.TextAttribute; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputSessionWithIdCallback; - -import java.util.concurrent.Executor; +import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; +import com.android.internal.inputmethod.RemoteAccessibilityInputConnection; /** * This class provides input method APIs. Some public methods such as @@ -61,9 +48,8 @@ public class InputMethod { private static final String LOG_TAG = "A11yInputMethod"; private final AccessibilityService mService; - private InputBinding mInputBinding; private boolean mInputStarted; - private InputConnection mStartedInputConnection; + private RemoteAccessibilityInputConnection mStartedInputConnection; private EditorInfo mInputEditorInfo; /** @@ -131,9 +117,7 @@ public class InputMethod { * to perform whatever behavior you would like. */ public void onFinishInput() { - if (mStartedInputConnection != null) { - mStartedInputConnection.finishComposingText(); - } + // Intentionally empty } /** @@ -152,41 +136,26 @@ public class InputMethod { // Intentionally empty } - final void createImeSession(IInputSessionWithIdCallback callback) { - InputMethodSession session = onCreateInputMethodSessionInterface(); + final void createImeSession(IAccessibilityInputMethodSessionCallback callback) { + final AccessibilityInputMethodSessionWrapper wrapper = + new AccessibilityInputMethodSessionWrapper(mService.getMainLooper(), + new SessionImpl()); try { - IInputMethodSessionWrapper wrap = - new IInputMethodSessionWrapper(mService, session, null); - callback.sessionCreated(wrap, mService.getConnectionId()); + callback.sessionCreated(wrapper, mService.getConnectionId()); } catch (RemoteException ignored) { } } - final void setImeSessionEnabled(@NonNull InputMethodSession session, boolean enabled) { - ((InputMethodSessionForAccessibility) session).setEnabled(enabled); - } - - final void bindInput(@NonNull InputBinding binding) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.bindInput"); - mInputBinding = binding; - Log.v(LOG_TAG, "bindInput(): binding=" + binding); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - - final void unbindInput() { - Log.v(LOG_TAG, "unbindInput(): binding=" + mInputBinding); - // Unbind input is per process per display. - mInputBinding = null; - } - - final void startInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) { + final void startInput(@Nullable RemoteAccessibilityInputConnection ic, + @NonNull EditorInfo attribute) { Log.v(LOG_TAG, "startInput(): editor=" + attribute); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.startInput"); doStartInput(ic, attribute, false /* restarting */); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - final void restartInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) { + final void restartInput(@Nullable RemoteAccessibilityInputConnection ic, + @NonNull EditorInfo attribute) { Log.v(LOG_TAG, "restartInput(): editor=" + attribute); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.restartInput"); doStartInput(ic, attribute, true /* restarting */); @@ -194,7 +163,8 @@ public class InputMethod { } - final void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { + final void doStartInput(RemoteAccessibilityInputConnection ic, EditorInfo attribute, + boolean restarting) { if ((ic == null || !restarting) && mInputStarted) { doFinishInput(); if (ic == null) { @@ -220,17 +190,13 @@ public class InputMethod { mInputEditorInfo = null; } - private InputMethodSession onCreateInputMethodSessionInterface() { - return new InputMethodSessionForAccessibility(); - } - /** * This class provides the allowed list of {@link InputConnection} APIs for * accessibility services. */ public final class AccessibilityInputConnection { - private InputConnection mIc; - AccessibilityInputConnection(InputConnection ic) { + private final RemoteAccessibilityInputConnection mIc; + AccessibilityInputConnection(RemoteAccessibilityInputConnection ic) { this.mIc = ic; } @@ -249,7 +215,7 @@ public class InputMethod { * int, int)} on the current accessibility service after the batch input is over. * <strong>Editor authors</strong>, for this to happen you need to * make the changes known to the accessibility service by calling - * {@link InputMethodManager#updateSelection(View, int, int, int, int)}, + * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)}, * but be careful to wait until the batch edit is over if one is * in progress.</p> * @@ -282,7 +248,7 @@ public class InputMethod { * int,int, int)} on the current IME after the batch input is over. * <strong>Editor authors</strong>, for this to happen you need to * make the changes known to the input method by calling - * {@link InputMethodManager#updateSelection(View, int, int, int, int)}, + * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)}, * but be careful to wait until the batch edit is over if one is * in progress.</p> * @@ -367,9 +333,8 @@ public class InputMethod { * delete only half of a surrogate pair. Also take care not to * delete more characters than are in the editor, as that may have * ill effects on the application. Calling this method will cause - * the editor to call - * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, - * int, int)} on your service after the batch input is over.</p> + * the editor to call {@link InputMethod#onUpdateSelection(int, int, int, int, int, int)} + * on your service after the batch input is over.</p> * * <p><strong>Editor authors:</strong> please be careful of race * conditions in implementing this call. An IME can make a change @@ -381,7 +346,7 @@ public class InputMethod { * indices to the size of the contents to avoid crashes. Since * this changes the contents of the editor, you need to make the * changes known to the input method by calling - * {@link InputMethodManager#updateSelection(View, int, int, int, int)}, + * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)}, * but be careful to wait until the batch edit is over if one is * in progress.</p> * @@ -522,12 +487,13 @@ public class InputMethod { } /** - * Concrete implementation of InputMethodSession that provides all of the standard behavior - * for an input method session. + * Concrete implementation of {@link AccessibilityInputMethodSession} that provides all of the + * standard behavior for an A11y input method session. */ - private final class InputMethodSessionForAccessibility implements InputMethodSession { + private final class SessionImpl implements AccessibilityInputMethodSession { boolean mEnabled = true; + @Override public void setEnabled(boolean enabled) { mEnabled = enabled; } @@ -549,86 +515,15 @@ public class InputMethod { } @Override - public void viewClicked(boolean focusChanged) { - } - - @Override - public void updateCursor(@NonNull Rect newCursor) { - } - - @Override - public void displayCompletions( - @SuppressLint("ArrayReturn") @NonNull CompletionInfo[] completions) { - } - - @Override - public void updateExtractedText(int token, @NonNull ExtractedText text) { - } - - public void dispatchKeyEvent(int seq, @NonNull KeyEvent event, - @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) { - } - - @Override - public void dispatchKeyEvent(int seq, @NonNull KeyEvent event, - @NonNull EventCallback callback) { - } - - public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event, - @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) { - } - - @Override - public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event, - @NonNull EventCallback callback) { - } - - public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event, - @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) { - } - - @Override - public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event, - @NonNull EventCallback callback) { - } - - @Override - public void appPrivateCommand(@NonNull String action, @NonNull Bundle data) { - } - - @Override - public void toggleSoftInput(int showFlags, int hideFlags) { - } - - @Override - public void updateCursorAnchorInfo(@NonNull CursorAnchorInfo cursorAnchorInfo) { - } - - @Override - public void notifyImeHidden() { - } - - @Override - public void removeImeSurface() { - } - - /** - * {@inheritDoc} - */ - @Override - public void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext, - int sessionId) { - if (mStartedInputConnection instanceof RemoteInputConnection) { - final RemoteInputConnection ric = - (RemoteInputConnection) mStartedInputConnection; - if (!ric.isSameConnection(inputContext)) { - // This is not an error, and can be safely ignored. - return; - } - editorInfo.makeCompatible( - mService.getApplicationInfo().targetSdkVersion); - restartInput(new RemoteInputConnection(ric, sessionId), editorInfo); + public void invalidateInput(EditorInfo editorInfo, + IRemoteAccessibilityInputConnection connection, int sessionId) { + if (!mStartedInputConnection.isSameConnection(connection)) { + // This is not an error, and can be safely ignored. + return; } + editorInfo.makeCompatible(mService.getApplicationInfo().targetSdkVersion); + restartInput(new RemoteAccessibilityInputConnection(mStartedInputConnection, sessionId), + editorInfo); } } } diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 893dc2f6ace4..ac6759396c8f 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -64,13 +64,11 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputBinding; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodSession; import com.android.internal.annotations.GuardedBy; +import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; +import com.android.internal.inputmethod.RemoteAccessibilityInputConnection; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.internal.view.IInputSessionWithIdCallback; import libcore.io.IoUtils; @@ -1574,26 +1572,14 @@ public final class UiAutomation { } @Override - public void createImeSession(IInputSessionWithIdCallback callback) { + public void createImeSession(IAccessibilityInputMethodSessionCallback callback) { /* do nothing */ } @Override - public void setImeSessionEnabled(InputMethodSession session, boolean enabled) { - } - - @Override - public void bindInput(InputBinding binding) { - } - - @Override - public void unbindInput() { - } - - @Override - public void startInput(@Nullable InputConnection inputConnection, - @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken) { + public void startInput( + @Nullable RemoteAccessibilityInputConnection inputConnection, + @NonNull EditorInfo editorInfo, boolean restarting) { } @Override diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 75356d1ce994..eccbb403b306 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -41,9 +41,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodSession; -/** @hide */ -// TODO(b/215636776): move IInputMethodSessionWrapper to proper package -public class IInputMethodSessionWrapper extends IInputMethodSession.Stub +class IInputMethodSessionWrapper extends IInputMethodSession.Stub implements HandlerCaller.Callback { private static final String TAG = "InputMethodWrapper"; diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java index 09dbb27359b0..f44f49d7dcaf 100644 --- a/core/java/android/inputmethodservice/InputMethodServiceInternal.java +++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java @@ -32,11 +32,8 @@ import java.io.PrintWriter; * framework classes for internal use. * * <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p> - * - * @hide */ -// TODO(b/215636776): move InputMethodServiceInternal to proper package -public interface InputMethodServiceInternal { +interface InputMethodServiceInternal { /** * @return {@link Context} associated with the service. */ diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java index 5b0129ee814c..86e59e9dcf2f 100644 --- a/core/java/android/inputmethodservice/RemoteInputConnection.java +++ b/core/java/android/inputmethodservice/RemoteInputConnection.java @@ -53,11 +53,8 @@ import java.util.concurrent.CompletableFuture; * * <p>See also {@link IInputContext} for the actual {@link android.os.Binder} IPC protocols under * the hood.</p> - * - * @hide */ -// TODO(b/215636776): move RemoteInputConnection to proper package -public final class RemoteInputConnection implements InputConnection { +final class RemoteInputConnection implements InputConnection { private static final String TAG = "RemoteInputConnection"; private static final int MAX_WAIT_TIME_MILLIS = 2000; @@ -98,7 +95,7 @@ public final class RemoteInputConnection implements InputConnection { @NonNull private final CancellationGroup mCancellationGroup; - public RemoteInputConnection( + RemoteInputConnection( @NonNull WeakReference<InputMethodServiceInternal> inputMethodService, IInputContext inputContext, @NonNull CancellationGroup cancellationGroup) { mImsInternal = new InputMethodServiceInternalHolder(inputMethodService); @@ -111,7 +108,7 @@ public final class RemoteInputConnection implements InputConnection { return mInvoker.isSameConnection(inputContext); } - public RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) { + RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) { mImsInternal = original.mImsInternal; mInvoker = original.mInvoker.cloneWithSessionId(sessionId); mCancellationGroup = original.mCancellationGroup; diff --git a/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java b/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java new file mode 100644 index 000000000000..240e38b107a5 --- /dev/null +++ b/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.inputmethod.IAccessibilityInputMethodSession; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; + +final class IAccessibilityInputMethodSessionInvoker { + private static final String TAG = "IAccessibilityInputMethodSessionInvoker"; + + /** + * The actual instance of the method to make calls on it. + */ + @NonNull + private final IAccessibilityInputMethodSession mSession; + + private IAccessibilityInputMethodSessionInvoker( + @NonNull IAccessibilityInputMethodSession session) { + mSession = session; + } + + /** + * Create a {@link IAccessibilityInputMethodSessionInvoker} instance if applicable. + * + * @param session {@link IAccessibilityInputMethodSession} object to be wrapped. + * @return an instance of {@link IAccessibilityInputMethodSessionInvoker} if + * {@code inputMethodSession} is not {@code null}. {@code null} otherwise. + */ + @Nullable + public static IAccessibilityInputMethodSessionInvoker createOrNull( + @NonNull IAccessibilityInputMethodSession session) { + return session == null ? null : new IAccessibilityInputMethodSessionInvoker(session); + } + + @AnyThread + void finishInput() { + try { + mSession.finishInput(); + } catch (RemoteException e) { + Log.w(TAG, "A11yIME died", e); + } + } + + @AnyThread + void updateSelection(int oldSelStart, int oldSelEnd, int selStart, int selEnd, + int candidatesStart, int candidatesEnd) { + try { + mSession.updateSelection( + oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd); + } catch (RemoteException e) { + Log.w(TAG, "A11yIME died", e); + } + } + + @AnyThread + void invalidateInput(EditorInfo editorInfo, IRemoteAccessibilityInputConnection connection, + int sessionId) { + try { + mSession.invalidateInput(editorInfo, connection, sessionId); + } catch (RemoteException e) { + Log.w(TAG, "A11yIME died", e); + } + } +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 805f8e7551a5..84f13930e03a 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -93,6 +93,7 @@ import android.view.autofill.AutofillManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.DirectBootAwareness; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.ImeTracing; import com.android.internal.inputmethod.InputBindResult; import com.android.internal.inputmethod.InputMethodDebug; @@ -506,8 +507,8 @@ public final class InputMethodManager { */ @Nullable @GuardedBy("mH") - private final SparseArray<InputMethodSessionWrapper> mAccessibilityInputMethodSession = - new SparseArray<>(); + private final SparseArray<IAccessibilityInputMethodSessionInvoker> + mAccessibilityInputMethodSession = new SparseArray<>(); InputChannel mCurChannel; ImeInputEventSender mCurSender; @@ -669,7 +670,8 @@ public final class InputMethodManager { if (mCurrentInputMethodSession != null) { mCurrentInputMethodSession.finishInput(); } - forAccessibilitySessions(InputMethodSessionWrapper::finishInput); + forAccessibilitySessionsLocked( + IAccessibilityInputMethodSessionInvoker::finishInput); } } @@ -730,7 +732,7 @@ public final class InputMethodManager { focusedView.getWindowToken(), startInputFlags, softInputMode, windowFlags, null, - null, + null, null, mCurRootView.mContext.getApplicationInfo().targetSdkVersion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -963,13 +965,13 @@ public final class InputMethodManager { // we send a notification so that the a11y service knows the session is // registered and update the a11y service with the current cursor positions. if (res.accessibilitySessions != null) { - InputMethodSessionWrapper wrapper = - InputMethodSessionWrapper.createOrNull( + IAccessibilityInputMethodSessionInvoker invoker = + IAccessibilityInputMethodSessionInvoker.createOrNull( res.accessibilitySessions.get(id)); - if (wrapper != null) { - mAccessibilityInputMethodSession.put(id, wrapper); + if (invoker != null) { + mAccessibilityInputMethodSession.put(id, invoker); if (mServedInputConnection != null) { - wrapper.updateSelection(mInitialSelStart, mInitialSelEnd, + invoker.updateSelection(mInitialSelStart, mInitialSelEnd, mCursorSelStart, mCursorSelEnd, mCursorCandStart, mCursorCandEnd); } else { @@ -978,9 +980,7 @@ public final class InputMethodManager { // binds before or after input starts, it may wonder if it binds // after input starts, why it doesn't receive a notification of // the current cursor positions. - wrapper.updateSelection(-1, -1, - -1, -1, -1, - -1); + invoker.updateSelection(-1, -1, -1, -1, -1, -1); } } } @@ -2148,8 +2148,10 @@ public final class InputMethodManager { editorInfo.setInitialSurroundingTextInternal(textSnapshot.getSurroundingText()); mCurrentInputMethodSession.invalidateInput(editorInfo, mServedInputConnection, sessionId); - forAccessibilitySessions(wrapper -> wrapper.invalidateInput(editorInfo, - mServedInputConnection, sessionId)); + final IRemoteAccessibilityInputConnection accessibilityInputConnection = + mServedInputConnection.asIRemoteAccessibilityInputConnection(); + forAccessibilitySessionsLocked(wrapper -> wrapper.invalidateInput(editorInfo, + accessibilityInputConnection, sessionId)); return true; } } @@ -2323,6 +2325,8 @@ public final class InputMethodManager { res = mService.startInputOrWindowGainedFocus( startInputReason, mClient, windowGainingFocus, startInputFlags, softInputMode, windowFlags, tba, servedInputConnection, + servedInputConnection == null ? null + : servedInputConnection.asIRemoteAccessibilityInputConnection(), view.getContext().getApplicationInfo().targetSdkVersion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -2347,8 +2351,9 @@ public final class InputMethodManager { mAccessibilityInputMethodSession.clear(); if (res.accessibilitySessions != null) { for (int i = 0; i < res.accessibilitySessions.size(); i++) { - InputMethodSessionWrapper wrapper = InputMethodSessionWrapper.createOrNull( - res.accessibilitySessions.valueAt(i)); + IAccessibilityInputMethodSessionInvoker wrapper = + IAccessibilityInputMethodSessionInvoker.createOrNull( + res.accessibilitySessions.valueAt(i)); if (wrapper != null) { mAccessibilityInputMethodSession.append( res.accessibilitySessions.keyAt(i), wrapper); @@ -2598,7 +2603,7 @@ public final class InputMethodManager { mCursorCandEnd = candidatesEnd; mCurrentInputMethodSession.updateSelection( oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd); - forAccessibilitySessions(wrapper -> wrapper.updateSelection(oldSelStart, + forAccessibilitySessionsLocked(wrapper -> wrapper.updateSelection(oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd)); } } @@ -3658,7 +3663,8 @@ public final class InputMethodManager { } } - private void forAccessibilitySessions(Consumer<InputMethodSessionWrapper> consumer) { + private void forAccessibilitySessionsLocked( + Consumer<IAccessibilityInputMethodSessionInvoker> consumer) { for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) { consumer.accept(mAccessibilityInputMethodSession.valueAt(i)); } |
