Merge "Let A11yIME use its own IPC definitions" into tm-dev

This commit is contained in:
Yohei Yukawa
2022-04-20 17:42:55 +00:00
committed by Android (Google) Code Review
24 changed files with 1203 additions and 414 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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";

View File

@@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}
oneway interface IAccessibilityInputMethodSessionCallback {
void sessionCreated(IAccessibilityInputMethodSession session, int id);
}

View File

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

View File

@@ -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<SurroundingText> getSurroundingText(int beforeLength, int afterLength,
int flags) {
final AndroidFuture<SurroundingText> 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<Integer>} that can be used to retrieve the invocation
* result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
public AndroidFuture<Integer> getCursorCapsMode(int reqModes) {
final AndroidFuture<Integer> 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) {
}
}
}

View File

@@ -186,7 +186,7 @@ public final class InputBindResult implements Parcelable {
/**
* The accessibility services.
*/
public SparseArray<IInputMethodSession> accessibilitySessions;
public SparseArray<IAccessibilityInputMethodSession> 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<IInputMethodSession> accessibilitySessions,
IInputMethodSession method,
SparseArray<IAccessibilityInputMethodSession> 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--;
}

View File

@@ -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.
*
* <p>This needs to be public to be referenced from {@link android.app.UiAutomation}.</p>
*/
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.
*
* <p>This is expected to be signaled immediately when the IME process receives
* {@link IInputMethod#unbindInput()}.</p>
*/
@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<SurroundingText> 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<Integer> 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);
}
}

View File

@@ -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}.</p>
*
* <p>{@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}.</p>
*/
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.

View File

@@ -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,

View File

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

View File

@@ -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<IInputMethodSession> sessions,
public void setImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> 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<IInputMethodSession> sessions,
public void scheduleSetImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> sessions,
boolean enabled) {
mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::setImeSessionEnabled,
this, sessions, enabled));
}
private void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) {
private void setImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> sessions,
boolean enabled) {
synchronized (mLock) {
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {

View File

@@ -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<IInputMethodSession> sessions,
boolean enabled);
public abstract void setImeSessionEnabled(
SparseArray<IAccessibilityInputMethodSession> 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<Integer> 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<IInputMethodSession> sessions,
public void setImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> 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) {
}
};

View File

@@ -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

View File

@@ -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<IInputMethodSession> accessibilityInputMethodSessions =
final SparseArray<IAccessibilityInputMethodSession> 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<IInputMethodSession> accessibilityInputMethodSessions =
final SparseArray<IAccessibilityInputMethodSession> 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<IInputMethodSession> createAccessibilityInputMethodSessions(
private SparseArray<IAccessibilityInputMethodSession> createAccessibilityInputMethodSessions(
SparseArray<AccessibilitySessionState> accessibilitySessions) {
final SparseArray<IInputMethodSession> accessibilityInputMethodSessions =
final SparseArray<IAccessibilityInputMethodSession> 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<AccessibilitySessionState> accessibilitySessions) {
// mEnabledAccessibilitySessions could the same object as accessibilitySessions.
SparseArray<IInputMethodSession> disabledSessions = new SparseArray<>();
SparseArray<IAccessibilityInputMethodSession> 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<IInputMethodSession> enabledSessions = new SparseArray<>();
SparseArray<IAccessibilityInputMethodSession> 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);