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

Commit 0029c662 authored by Jeff Brown's avatar Jeff Brown
Browse files

Add input filter mechanism for accessibility.

This patch adds a mechanism for capturing, filtering, transforming
and injecting input events at a very low level before the input
dispatcher attempts to deliver them to applications.  At this time,
the mechanism is only intended to be used by the accessibility
system to implement built-in system-level accessibility affordances.

The accessibility input filter is currently just a stub.
It logs the input events receives and reinjects them unchanged,
except that it transforms KEYCODE_Q into KEYCODE_Z.

Currently, the accessibility input filter is installed whenever
accessibility is enabled.  We'll probably want to change that
so it only enables the input filter when a screen reader is
installed and we want touch exploration.

Change-Id: I35764fdf75522b69d09ebd78c9766eb7593c1afe
parent 6e6cd7a5
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -67,6 +67,15 @@ public abstract class InputEvent implements Parcelable {
     */
    public abstract void setSource(int source);

    /**
     * Recycles the event.
     * This method should only be used by the system since applications do not
     * expect {@link KeyEvent} objects to be recycled, although {@link MotionEvent}
     * objects are fine.  See {@link KeyEvent#recycle()} for details.
     * @hide
     */
    public abstract void recycle();

    public int describeContents() {
        return 0;
    }
+2 −0
Original line number Diff line number Diff line
@@ -81,6 +81,8 @@ public interface WindowManagerPolicy {

    public final static int FLAG_INJECTED = 0x01000000;
    public final static int FLAG_TRUSTED = 0x02000000;
    public final static int FLAG_FILTERED = 0x04000000;
    public final static int FLAG_DISABLE_KEY_REPEAT = 0x08000000;

    public final static int FLAG_WOKE_HERE = 0x10000000;
    public final static int FLAG_BRIGHT_HERE = 0x20000000;
+6 −0
Original line number Diff line number Diff line
@@ -128,6 +128,12 @@ enum {
    // input device or an application with system-wide event injection permission.
    POLICY_FLAG_TRUSTED = 0x02000000,

    // Indicates that the input event has passed through an input filter.
    POLICY_FLAG_FILTERED = 0x04000000,

    // Disables automatic key repeating behavior.
    POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000,

    /* These flags are set by the input reader policy as it intercepts each event. */

    // Indicates that the screen was off when the event was received and the event
+69 −12
Original line number Diff line number Diff line
@@ -186,7 +186,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
    mPolicy(policy),
    mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(true), mDispatchFrozen(false),
    mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false),
    mFocusedWindow(NULL),
    mFocusedApplication(NULL),
    mCurrentInputTargetsValid(false),
@@ -725,7 +725,7 @@ bool InputDispatcher::dispatchKeyLocked(
        if (entry->repeatCount == 0
                && entry->action == AKEY_EVENT_ACTION_DOWN
                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
                && !entry->isInjected()) {
                && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
            if (mKeyRepeatState.lastKeyEntry
                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
                // We have seen two identical key downs in a row which indicates that the device
@@ -2402,7 +2402,18 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t so

    bool needWake;
    { // acquire lock
        AutoMutex _l(mLock);
        mLock.lock();

        if (mInputFilterEnabled) {
            mLock.unlock();

            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        int32_t repeatCount = 0;
        KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
@@ -2410,6 +2421,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t so
                metaState, repeatCount, downTime);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
@@ -2452,7 +2464,23 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t

    bool needWake;
    { // acquire lock
        AutoMutex _l(mLock);
        mLock.lock();

        if (mInputFilterEnabled) {
            mLock.unlock();

            MotionEvent event;
            event.initialize(deviceId, source, action, flags, edgeFlags, metaState, 0, 0,
                    xPrecision, yPrecision, downTime, eventTime,
                    pointerCount, pointerIds, pointerCoords);

            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        // Attempt batching and streaming of move events.
        if (action == AMOTION_EVENT_ACTION_MOVE
@@ -2491,6 +2519,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
                LOGD("Appended motion sample onto batch for most recent "
                        "motion event for this device in the inbound queue.");
#endif
                mLock.unlock();
                return; // done!
            }

@@ -2579,6 +2608,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
                            true /*resumeWithAppendedMotionSample*/);

                    runCommandsLockedInterruptible();
                    mLock.unlock();
                    return; // done!
                }
            }
@@ -2593,6 +2623,7 @@ NoBatchingOrStreaming:;
                pointerCount, pointerIds, pointerCoords);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
@@ -2612,16 +2643,17 @@ void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t swi
}

int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
        uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
    LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
            "syncMode=%d, timeoutMillis=%d",
            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
            "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
#endif

    nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);

    uint32_t policyFlags = POLICY_FLAG_INJECTED;
    policyFlags |= POLICY_FLAG_INJECTED;
    if (hasInjectionPermission(injectorPid, injectorUid)) {
        policyFlags |= POLICY_FLAG_TRUSTED;
    }
@@ -2640,7 +2672,9 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
            policyFlags |= POLICY_FLAG_VIRTUAL;
        }

        if (!(policyFlags & POLICY_FLAG_FILTERED)) {
            mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
        }

        if (policyFlags & POLICY_FLAG_WOKE_HERE) {
            flags |= AKEY_EVENT_FLAG_WOKE_HERE;
@@ -2664,8 +2698,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
            return INPUT_EVENT_INJECTION_FAILED;
        }

        if (!(policyFlags & POLICY_FLAG_FILTERED)) {
            nsecs_t eventTime = motionEvent->getEventTime();
            mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
        }

        mLock.lock();
        const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -2780,7 +2816,8 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject
                 injectionResult, injectionState->injectorPid, injectionState->injectorUid);
#endif

        if (injectionState->injectionIsAsync) {
        if (injectionState->injectionIsAsync
                && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
            // Log the outcome since the injector did not wait for the injection result.
            switch (injectionResult) {
            case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2982,6 +3019,26 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
    }
}

void InputDispatcher::setInputFilterEnabled(bool enabled) {
#if DEBUG_FOCUS
    LOGD("setInputFilterEnabled: enabled=%d", enabled);
#endif

    { // acquire lock
        AutoMutex _l(mLock);

        if (mInputFilterEnabled == enabled) {
            return;
        }

        mInputFilterEnabled = enabled;
        resetAndDropEverythingLocked("input filter is being enabled or disabled");
    } // release lock

    // Wake up poll loop since there might be work to do to drop everything.
    mLooper->wake();
}

bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
        const sp<InputChannel>& toChannel) {
#if DEBUG_FOCUS
+21 −2
Original line number Diff line number Diff line
@@ -176,6 +176,13 @@ public:
     */
    virtual int32_t getMaxEventsPerSecond() = 0;

    /* Filters an input event.
     * Return true to dispatch the event unmodified, false to consume the event.
     * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
     * to injectInputEvent.
     */
    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;

    /* Intercepts a key event immediately before queueing it.
     * The policy can use this method as an opportunity to perform power management functions
     * and early event preprocessing such as updating policy flags.
@@ -266,7 +273,8 @@ public:
     * This method may be called on any thread (usually by the input manager).
     */
    virtual int32_t injectInputEvent(const InputEvent* event,
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
            uint32_t policyFlags) = 0;

    /* Sets the list of input windows.
     *
@@ -286,6 +294,14 @@ public:
     */
    virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;

    /* Sets whether input event filtering is enabled.
     * When enabled, incoming input events are sent to the policy's filterInputEvent
     * method instead of being dispatched.  The filter is expected to use
     * injectInputEvent to inject the events it would like to have dispatched.
     * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
     */
    virtual void setInputFilterEnabled(bool enabled) = 0;

    /* Transfers touch focus from the window associated with one channel to the
     * window associated with the other channel.
     *
@@ -345,11 +361,13 @@ public:
            int32_t switchCode, int32_t switchValue, uint32_t policyFlags) ;

    virtual int32_t injectInputEvent(const InputEvent* event,
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
            uint32_t policyFlags);

    virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
    virtual void setFocusedApplication(const InputApplication* inputApplication);
    virtual void setInputDispatchMode(bool enabled, bool frozen);
    virtual void setInputFilterEnabled(bool enabled);

    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
            const sp<InputChannel>& toChannel);
@@ -863,6 +881,7 @@ private:
    // Dispatch state.
    bool mDispatchEnabled;
    bool mDispatchFrozen;
    bool mInputFilterEnabled;

    Vector<InputWindow> mWindows;

Loading