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

Commit b76bbd8e authored by Phil Weaver's avatar Phil Weaver Committed by Android (Google) Code Review
Browse files

Merge "Dispatch key events to multiple a11y services."

parents c47132c2 5915658c
Loading
Loading
Loading
Loading
+18 −217
Original line number Diff line number Diff line
@@ -64,21 +64,17 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
import android.view.InputDevice;
import android.view.InputEventConsistencyVerifier;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerInternal;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
@@ -146,8 +142,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

    private static final int OWN_PROCESS_ID = android.os.Process.myPid();

    private static final int MAX_POOL_SIZE = 10;

    private static final int WINDOW_ID_UNKNOWN = -1;

    private static int sIdCounter = 0;
@@ -158,9 +152,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

    private final Object mLock = new Object();

    private final Pool<PendingEvent> mPendingEventPool =
            new SimplePool<>(MAX_POOL_SIZE);

    private final SimpleStringSplitter mStringColonSplitter =
            new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);

@@ -193,6 +184,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

    private boolean mHasInputFilter;

    private KeyEventDispatcher mKeyEventDispatcher;

    private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();

    private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
@@ -756,12 +749,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

    boolean notifyKeyEvent(KeyEvent event, int policyFlags) {
        synchronized (mLock) {
            KeyEvent localClone = KeyEvent.obtain(event);
            boolean handled = notifyKeyEventLocked(localClone, policyFlags, false);
            if (!handled) {
                handled = notifyKeyEventLocked(localClone, policyFlags, true);
            List<Service> boundServices = getCurrentUserStateLocked().mBoundServices;
            if (boundServices.isEmpty()) {
                return false;
            }
            return handled;
            return getKeyEventDispatcher().notifyKeyEventLocked(event, policyFlags, boundServices);
        }
    }

@@ -935,31 +927,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        return false;
    }

    private boolean notifyKeyEventLocked(KeyEvent event, int policyFlags, boolean isDefault) {
        // TODO: Now we are giving the key events to the last enabled
        //       service that can handle them Ideally, the user should
        //       make the call which service handles key events. However,
        //       only one service should handle key events to avoid user
        //       frustration when different behavior is observed from
        //       different combinations of enabled accessibility services.
        UserState state = getCurrentUserStateLocked();
        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
            Service service = state.mBoundServices.get(i);
            // Key events are handled only by services that declared
            // this capability and requested to filter key events.
            if (!service.mRequestFilterKeyEvents ||
                    (service.mAccessibilityServiceInfo.getCapabilities() & AccessibilityServiceInfo
                            .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) {
                continue;
            }
            if (service.mIsDefault == isDefault) {
                service.notifyKeyEvent(event, policyFlags);
                return true;
            }
        }
        return false;
    }

    private void notifyClearAccessibilityCacheLocked() {
        UserState state = getCurrentUserStateLocked();
        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
@@ -1754,6 +1721,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        return null;
    }

    private KeyEventDispatcher getKeyEventDispatcher() {
        if (mKeyEventDispatcher == null) {
            mKeyEventDispatcher = new KeyEventDispatcher(
                    mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock);
        }
        return mKeyEventDispatcher;
    }

    @Override
    public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
        mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
@@ -1954,22 +1929,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        }
    }

    private PendingEvent obtainPendingEventLocked(KeyEvent event, int policyFlags, int sequence) {
        PendingEvent pendingEvent = mPendingEventPool.acquire();
        if (pendingEvent == null) {
            pendingEvent = new PendingEvent();
        }
        pendingEvent.event = event;
        pendingEvent.policyFlags = policyFlags;
        pendingEvent.sequence = sequence;
        return pendingEvent;
    }

    private void recyclePendingEventLocked(PendingEvent pendingEvent) {
        pendingEvent.clear();
        mPendingEventPool.release(pendingEvent);
    }

    private int findWindowIdLocked(IBinder token) {
        final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
        if (globalIndex >= 0) {
@@ -2082,8 +2041,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        final SparseArray<AccessibilityEvent> mPendingEvents =
            new SparseArray<>();

        final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();

        boolean mWasConnectedAndDied;

        // Handler only for dispatching accessibility events since we use event
@@ -2195,7 +2152,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                return false;
            }
            UserState userState = getUserStateLocked(mUserId);
            mKeyEventDispatcher.flush();
            getKeyEventDispatcher().flush(this);
            if (!mIsAutomation) {
                mContext.unbindService(this);
            } else {
@@ -2212,7 +2169,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

        @Override
        public void setOnKeyEventResult(boolean handled, int sequence) {
            mKeyEventDispatcher.setOnKeyEventResult(handled, sequence);
            getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
        }

        @Override
@@ -2926,7 +2883,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                    return;
                }
                mWasConnectedAndDied = true;
                mKeyEventDispatcher.flush();
                getKeyEventDispatcher().flush(this);
                UserState userState = getUserStateLocked(mUserId);
                // The death recipient is unregistered in removeServiceLocked
                removeServiceLocked(this, userState);
@@ -3035,11 +2992,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                    gestureId, 0).sendToTarget();
        }

        public void notifyKeyEvent(KeyEvent event, int policyFlags) {
            mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_KEY_EVENT,
                    policyFlags, 0, event).sendToTarget();
        }

        public void notifyClearAccessibilityNodeInfoCache() {
            mInvocationHandler.sendEmptyMessage(
                    InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
@@ -3084,10 +3036,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            }
        }

        private void notifyKeyEventInternal(KeyEvent event, int policyFlags) {
            mKeyEventDispatcher.notifyKeyEvent(event, policyFlags);
        }

        private void notifyClearAccessibilityCacheInternal() {
            final IAccessibilityServiceClient listener;
            synchronized (mLock) {
@@ -3205,9 +3153,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

        private final class InvocationHandler extends Handler {
            public static final int MSG_ON_GESTURE = 1;
            public static final int MSG_ON_KEY_EVENT = 2;
            public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
            public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
            public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;

            private static final int MSG_ON_MAGNIFICATION_CHANGED = 5;

@@ -3226,21 +3172,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                        notifyGestureInternal(gestureId);
                    } break;

                    case MSG_ON_KEY_EVENT: {
                        KeyEvent event = (KeyEvent) message.obj;
                        final int policyFlags = message.arg1;
                        notifyKeyEventInternal(event, policyFlags);
                    } break;

                    case MSG_CLEAR_ACCESSIBILITY_CACHE: {
                        notifyClearAccessibilityCacheInternal();
                    } break;

                    case MSG_ON_KEY_EVENT_TIMEOUT: {
                        PendingEvent eventState = (PendingEvent) message.obj;
                        setOnKeyEventResult(false, eventState.sequence);
                    } break;

                    case MSG_ON_MAGNIFICATION_CHANGED: {
                        final SomeArgs args = (SomeArgs) message.obj;
                        final Region region = (Region) args.arg1;
@@ -3278,140 +3213,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            }
        }

        private final class KeyEventDispatcher {

            private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;

            private PendingEvent mPendingEvents;

            private final InputEventConsistencyVerifier mSentEventsVerifier =
                    InputEventConsistencyVerifier.isInstrumentationEnabled()
                            ? new InputEventConsistencyVerifier(
                                    this, 0, KeyEventDispatcher.class.getSimpleName()) : null;

            public void notifyKeyEvent(KeyEvent event, int policyFlags) {
                final PendingEvent pendingEvent;

                synchronized (mLock) {
                    pendingEvent = addPendingEventLocked(event, policyFlags);
                }

                Message message = mInvocationHandler.obtainMessage(
                        InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
                mInvocationHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);

                try {
                    // Accessibility services are exclusively not in the system
                    // process, therefore no need to clone the motion event to
                    // prevent tampering. It will be cloned in the IPC call.
                    mServiceInterface.onKeyEvent(pendingEvent.event, pendingEvent.sequence);
                } catch (RemoteException re) {
                    setOnKeyEventResult(false, pendingEvent.sequence);
                }
            }

            public void setOnKeyEventResult(boolean handled, int sequence) {
                synchronized (mLock) {
                    PendingEvent pendingEvent = removePendingEventLocked(sequence);
                    if (pendingEvent != null) {
                        mInvocationHandler.removeMessages(
                                InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
                                pendingEvent);
                        pendingEvent.handled = handled;
                        finishPendingEventLocked(pendingEvent);
                    }
                }
            }

            public void flush() {
                synchronized (mLock) {
                    cancelAllPendingEventsLocked();
                    if (mSentEventsVerifier != null) {
                        mSentEventsVerifier.reset();
                    }
                }
            }

            private PendingEvent addPendingEventLocked(KeyEvent event, int policyFlags) {
                final int sequence = event.getSequenceNumber();
                PendingEvent pendingEvent = obtainPendingEventLocked(event, policyFlags, sequence);
                pendingEvent.next = mPendingEvents;
                mPendingEvents = pendingEvent;
                return pendingEvent;
            }

            private PendingEvent removePendingEventLocked(int sequence) {
                PendingEvent previous = null;
                PendingEvent current = mPendingEvents;

                while (current != null) {
                    if (current.sequence == sequence) {
                        if (previous != null) {
                            previous.next = current.next;
                        } else {
                            mPendingEvents = current.next;
                        }
                        current.next = null;
                        return current;
                    }
                    previous = current;
                    current = current.next;
                }
                return null;
            }

            private void finishPendingEventLocked(PendingEvent pendingEvent) {
                if (!pendingEvent.handled) {
                    sendKeyEventToInputFilter(pendingEvent.event, pendingEvent.policyFlags);
                }
                // Nullify the event since we do not want it to be
                // recycled yet. It will be sent to the input filter.
                pendingEvent.event = null;
                recyclePendingEventLocked(pendingEvent);
            }

            private void sendKeyEventToInputFilter(KeyEvent event, int policyFlags) {
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Injecting event: " + event);
                }
                if (mSentEventsVerifier != null) {
                    mSentEventsVerifier.onKeyEvent(event, 0);
                }
                policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
                mMainHandler.obtainMessage(MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER,
                        policyFlags, 0, event).sendToTarget();
            }

            private void cancelAllPendingEventsLocked() {
                while (mPendingEvents != null) {
                    PendingEvent pendingEvent = removePendingEventLocked(mPendingEvents.sequence);
                    pendingEvent.handled = false;
                    mInvocationHandler.removeMessages(InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
                            pendingEvent);
                    finishPendingEventLocked(pendingEvent);
                }
            }
        }
    }

    private static final class PendingEvent {
        PendingEvent next;

        KeyEvent event;
        int policyFlags;
        int sequence;
        boolean handled;

        public void clear() {
            if (event != null) {
                event.recycle();
                event = null;
            }
            next = null;
            policyFlags = 0;
            sequence = 0;
            handled = false;
        }
    }

    final class WindowsForAccessibilityCallback implements
+285 −0

File added.

Preview size limit exceeded, changes collapsed.