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

Commit 5915658c authored by Phil Weaver's avatar Phil Weaver
Browse files

Dispatch key events to multiple a11y services.

Introducing a new class to manage key event dispatch
that sends all events to all classes that request them.

Bug:22332500, 22515057
Change-Id: I8521f6257a4ac6c8f2eff14186789630d680ff11
parent f20bfb67
Loading
Loading
Loading
Loading
+18 −217
Original line number Diff line number Diff line
@@ -63,21 +63,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;
@@ -144,8 +140,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;
@@ -156,9 +150,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);

@@ -189,6 +180,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 =
@@ -752,12 +745,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);
        }
    }

@@ -909,31 +901,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--) {
@@ -1675,6 +1642,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);
@@ -1875,22 +1850,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) {
@@ -1999,8 +1958,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
@@ -2112,7 +2069,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                return false;
            }
            UserState userState = getUserStateLocked(mUserId);
            mKeyEventDispatcher.flush();
            getKeyEventDispatcher().flush(this);
            if (!mIsAutomation) {
                mContext.unbindService(this);
            } else {
@@ -2129,7 +2086,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
@@ -2700,7 +2657,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);
@@ -2809,11 +2766,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);
@@ -2834,10 +2786,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) {
@@ -2955,9 +2903,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;

            public InvocationHandler(Looper looper) {
                super(looper, null, true);
@@ -2972,21 +2918,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;

                    default: {
                        throw new IllegalArgumentException("Unknown message: " + type);
                    }
@@ -2994,140 +2929,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.