Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8ed6af3c authored by Yabin Huang's avatar Yabin Huang
Browse files

Update accessibility framework to support multi-session IME

Change IMMI#onSessionForAccessibilityCreated() and
IMMI#unbindAccessibilityFromCurrentClient() to take a user ID.

The main motivation is to unblock our on-going project that aims to
add concurrent multi-user support in IMMS.

All the planned use cases of A11yIME remain unchanged:
1. This CL just makes it clear that A11yIME APIs are not intended
   to be used from UiAutomator and Proxy A11yService.
2. IMMS does not use user ID yet. A subsequent CL will introduce a
   user ID verification but our plan is to not break any existing
 use cases.

This is a pure mechanical refactoring (internal IMMS refactoring),
which won’t be under any feature flag.

Bug: 305829876
Test: atest WmTests && atest FrameworksServicesTests
Test: atest CtsAccessibilityTestCases && atest CtsInputMethodTestCases && atest CtsAccessibilityServiceTestCases

Change-Id: I835e2ab07cdcbe6367698675f87d5cda160a9b5b
parent 50ddd32e
Loading
Loading
Loading
Loading
+1 −33
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILIT
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
@@ -69,7 +68,6 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.Settings;
import android.util.Pair;
import android.util.Slog;
@@ -93,7 +91,6 @@ import android.window.ScreenCapture.ScreenshotHardwareBuffer;
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;
@@ -101,7 +98,6 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.WindowManagerInternal;

import java.io.FileDescriptor;
@@ -1993,20 +1989,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
        }
    }

    private void createImeSessionInternal() {
        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
        if (listener != null) {
            try {
                if (svcClientTracingEnabled()) {
                    logTraceSvcClient("createImeSession", "");
                }
                AccessibilityCallback callback = new AccessibilityCallback();
                listener.createImeSession(callback);
            } catch (RemoteException re) {
                Slog.e(LOG_TAG,
                        "Error requesting IME session from " + mService, re);
            }
        }
    protected void createImeSessionInternal() {
    }

    private void setImeSessionEnabledInternal(IAccessibilityInputMethodSession session,
@@ -2658,21 +2641,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
        }
    }

    private static final class AccessibilityCallback
            extends IAccessibilityInputMethodSessionCallback.Stub {
        @Override
        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);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    @Override
    public void attachAccessibilityOverlayToDisplay(
            int interactionId,
+26 −7
Original line number Diff line number Diff line
@@ -5378,19 +5378,37 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub

    @Override
    public void requestImeLocked(AbstractAccessibilityServiceConnection connection) {
        if (!(connection instanceof AccessibilityServiceConnection)
                || (connection instanceof ProxyAccessibilityServiceConnection)) {
            if (DEBUG) {
                Slog.d(LOG_TAG, "The connection should be a real connection but was "
                        + connection);
            }
            return;
        }
        AccessibilityServiceConnection realConnection = (AccessibilityServiceConnection) connection;
        mMainHandler.sendMessage(obtainMessage(
                AccessibilityManagerService::createSessionForConnection, this, connection));
                AccessibilityManagerService::createSessionForConnection, this, realConnection));
        mMainHandler.sendMessage(obtainMessage(
                AccessibilityManagerService::bindAndStartInputForConnection, this, connection));
                AccessibilityManagerService::bindAndStartInputForConnection, this, realConnection));
    }

    @Override
    public void unbindImeLocked(AbstractAccessibilityServiceConnection connection) {
        if (!(connection instanceof AccessibilityServiceConnection)
                || (connection instanceof ProxyAccessibilityServiceConnection)) {
            if (DEBUG) {
                Slog.d(LOG_TAG, "The connection should be a real connection but was "
                        + connection);
            }
            return;
        }
        AccessibilityServiceConnection realConnection = (AccessibilityServiceConnection) connection;
        mMainHandler.sendMessage(obtainMessage(
                AccessibilityManagerService::unbindInputForConnection, this, connection));
                AccessibilityManagerService::unbindInputForConnection, this, realConnection));
    }

    private void createSessionForConnection(AbstractAccessibilityServiceConnection connection) {
    private void createSessionForConnection(AccessibilityServiceConnection connection) {
        synchronized (mLock) {
            if (mInputSessionRequested) {
                connection.createImeSessionLocked();
@@ -5398,7 +5416,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        }
    }

    private void bindAndStartInputForConnection(AbstractAccessibilityServiceConnection connection) {
    private void bindAndStartInputForConnection(AccessibilityServiceConnection connection) {
        synchronized (mLock) {
            if (mInputBound) {
                connection.bindInputLocked();
@@ -5407,8 +5425,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        }
    }

    private void unbindInputForConnection(AbstractAccessibilityServiceConnection connection) {
        InputMethodManagerInternal.get().unbindAccessibilityFromCurrentClient(connection.mId);
    private void unbindInputForConnection(AccessibilityServiceConnection connection) {
        InputMethodManagerInternal.get()
                .unbindAccessibilityFromCurrentClient(connection.mId, connection.mUserId);
        synchronized (mLock) {
            connection.unbindInputLocked();
        }
+55 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.accessibility;

import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

@@ -27,6 +28,8 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.TouchInteractionController;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -38,12 +41,15 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.Display;
import android.view.MotionEvent;

import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -70,13 +76,38 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
     Having the reference be null when being called is a very bad sign, but we check the condition.
    */
    final WeakReference<AccessibilityUserState> mUserStateWeakReference;
    @UserIdInt
    final int mUserId;
    final Intent mIntent;
    final ActivityTaskManagerInternal mActivityTaskManagerService;

    private final Handler mMainHandler;

    AccessibilityServiceConnection(AccessibilityUserState userState, Context context,
            ComponentName componentName,
    private static final class AccessibilityInputMethodSessionCallback
            extends IAccessibilityInputMethodSessionCallback.Stub {
        @UserIdInt
        private final int mUserId;

        AccessibilityInputMethodSessionCallback(@UserIdInt int userId) {
            mUserId = userId;
        }

        @Override
        public void sessionCreated(IAccessibilityInputMethodSession session, int id) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ASC.sessionCreated");
            final long ident = Binder.clearCallingIdentity();
            try {
                InputMethodManagerInternal.get()
                        .onSessionForAccessibilityCreated(id, session, mUserId);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    AccessibilityServiceConnection(@Nullable AccessibilityUserState userState,
            Context context, ComponentName componentName,
            AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
            Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
            AccessibilityTrace trace, WindowManagerInternal windowManagerInternal,
@@ -86,6 +117,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
                securityPolicy, systemSupport, trace, windowManagerInternal, systemActionPerfomer,
                awm);
        mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
        // the user ID doesn't matter when userState is null, because it is null only when this is a
        // ProxyAccessibilityServiceConnection, for which it never creates an IME session and uses
        // the user ID.
        mUserId = userState == null ? UserHandle.USER_NULL : userState.mUserId;
        mIntent = new Intent().setComponent(mComponentName);
        mMainHandler = mainHandler;
        mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
@@ -557,6 +592,24 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
        return mRequestImeApis;
    }

    @Override
    protected void createImeSessionInternal() {
        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
        if (listener != null) {
            try {
                if (svcClientTracingEnabled()) {
                    logTraceSvcClient("createImeSession", "");
                }
                AccessibilityInputMethodSessionCallback
                        callback = new AccessibilityInputMethodSessionCallback(mUserId);
                listener.createImeSession(callback);
            } catch (RemoteException re) {
                Slog.e(LOG_TAG,
                        "Error requesting IME session from " + mService, re);
            }
        }
    }

    private void notifyMotionEventInternal(MotionEvent event) {
        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
        if (listener != null) {
+8 −4
Original line number Diff line number Diff line
@@ -171,17 +171,20 @@ public abstract class InputMethodManagerInternal {
     *
     * @param accessibilityConnectionId the connection id of the accessibility service
     * @param session                   the session passed back from the accessibility service
     * @param userId                    the user ID to be queried
     */
    public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId,
            IAccessibilityInputMethodSession session);
            IAccessibilityInputMethodSession session, @UserIdInt int userId);

    /**
     * Unbind the accessibility service with the specified accessibilityConnectionId from current
     * client.
     *
     * @param accessibilityConnectionId the connection id of the accessibility service
     * @param userId the user ID to be queried
     */
    public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId);
    public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
            @UserIdInt int userId);

    /**
     * Switch the keyboard layout in response to a keyboard shortcut.
@@ -266,11 +269,12 @@ public abstract class InputMethodManagerInternal {

                @Override
                public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
                        IAccessibilityInputMethodSession session) {
                        IAccessibilityInputMethodSession session, @UserIdInt int userId) {
                }

                @Override
                public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId) {
                public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
                        @UserIdInt int userId) {
                }

                @Override
+5 −2
Original line number Diff line number Diff line
@@ -5709,8 +5709,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

        @Override
        public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
                IAccessibilityInputMethodSession session) {
                IAccessibilityInputMethodSession session, @UserIdInt int userId) {
            synchronized (ImfLock.class) {
                // TODO(b/305829876): Implement user ID verification
                if (mCurClient != null) {
                    clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId);
                    mCurClient.mAccessibilitySessions.put(accessibilityConnectionId,
@@ -5737,8 +5738,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }

        @Override
        public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId) {
        public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
                @UserIdInt int userId) {
            synchronized (ImfLock.class) {
                // TODO(b/305829876): Implement user ID verification
                if (mCurClient != null) {
                    if (DEBUG) {
                        Slog.v(TAG, "unbindAccessibilityFromCurrentClientLocked: client="