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

Commit f67c53ee authored by Jeff Brown's avatar Jeff Brown
Browse files

DO NOT MERGE: Fix input event injection ANRs on UI thread.

Added a new asynchronous injection mode and made the existing
synchronization mechanism more robust.

Change-Id: Ia4aa04fd9b75ea2461a844c5b7933c831c1027e6
parent 302dd91b
Loading
Loading
Loading
Loading
+33 −12
Original line number Diff line number Diff line
@@ -55,6 +55,22 @@ enum {
    INPUT_EVENT_INJECTION_TIMED_OUT = 3
};

/*
 * Constants used to determine the input event injection synchronization mode.
 */
enum {
    /* Injection is asynchronous and is assumed always to be successful. */
    INPUT_EVENT_INJECTION_SYNC_NONE = 0,

    /* Waits for previous events to be dispatched so that the input dispatcher can determine
     * whether input event injection willbe permitted based on the current input focus.
     * Does not wait for the input event to finish processing. */
    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,

    /* Waits for the input event to be completely processed. */
    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
};


/*
 * An input target specifies how an input event is to be dispatched to a particular window
@@ -176,15 +192,14 @@ public:
            float xPrecision, float yPrecision, nsecs_t downTime) = 0;

    /* Injects an input event and optionally waits for sync.
     * This method may block even if sync is false because it must wait for previous events
     * to be dispatched before it can determine whether input event injection will be
     * permitted based on the current input focus.
     * The synchronization mode determines whether the method blocks while waiting for
     * input injection to proceed.
     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
     *
     * 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, bool sync, int32_t timeoutMillis) = 0;
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;

    /* Preempts input dispatch in progress by making pending synchronous
     * dispatches asynchronous instead.  This method is generally called during a focus
@@ -241,7 +256,7 @@ public:
            float xPrecision, float yPrecision, nsecs_t downTime);

    virtual int32_t injectInputEvent(const InputEvent* event,
            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);

    virtual void preemptInputDispatch();

@@ -268,10 +283,12 @@ private:
        nsecs_t eventTime;

        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
        bool    injectionIsAsync; // set to true if injection is not waiting for the result
        int32_t injectorPid;      // -1 if not injected
        int32_t injectorUid;      // -1 if not injected

        bool dispatchInProgress; // initially false, set to true while dispatching
        int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress

        inline bool isInjected() { return injectorPid >= 0; }
    };
@@ -340,6 +357,10 @@ private:
        //   headMotionSample will be initialized to tailMotionSample and tailMotionSample
        //   will be set to NULL.
        MotionSample* tailMotionSample;

        inline bool isSyncTarget() {
            return targetFlags & InputTarget::FLAG_SYNC;
        }
    };

    // A command entry captures state and behavior for an action to be performed in the
@@ -497,8 +518,7 @@ private:
        // Since there can only ever be at most one such target at a time, if there is one,
        // it must be at the tail because nothing else can be enqueued after it.
        inline bool hasPendingSyncTarget() {
            return ! outboundQueue.isEmpty()
                    && (outboundQueue.tail.prev->targetFlags & InputTarget::FLAG_SYNC);
            return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget();
        }

        // Gets the time since the current event was originally obtained from the input driver.
@@ -559,11 +579,12 @@ private:

    // Event injection and synchronization.
    Condition mInjectionResultAvailableCondition;
    Condition mFullySynchronizedCondition;
    bool isFullySynchronizedLocked();
    EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
    void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);

    Condition mInjectionSyncFinishedCondition;
    void decrementPendingSyncDispatchesLocked(EventEntry* entry);

    // Key repeat tracking.
    // XXX Move this up to the input reader instead.
    struct KeyRepeatState {
+4 −5
Original line number Diff line number Diff line
@@ -79,13 +79,12 @@ public:
    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;

    /* Injects an input event and optionally waits for sync.
     * This method may block even if sync is false because it must wait for previous events
     * to be dispatched before it can determine whether input event injection will be
     * permitted based on the current input focus.
     * The synchronization mode determines whether the method blocks while waiting for
     * input injection to proceed.
     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
     */
    virtual int32_t injectInputEvent(const InputEvent* event,
            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;

    /* Preempts input dispatch in progress by making pending synchronous
     * dispatches asynchronous instead.  This method is generally called during a focus
@@ -142,7 +141,7 @@ public:
    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);

    virtual int32_t injectInputEvent(const InputEvent* event,
            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);

    virtual void preemptInputDispatch();

+84 −29
Original line number Diff line number Diff line
@@ -184,11 +184,6 @@ void InputDispatcher::dispatchOnce() {

        // Run any deferred commands.
        skipPoll |= runCommandsLockedInterruptible();

        // Wake up synchronization waiters, if needed.
        if (isFullySynchronizedLocked()) {
            mFullySynchronizedCondition.broadcast();
        }
    } // release lock

    // If we dispatched anything, don't poll just now.  Wait for the next iteration.
@@ -560,6 +555,10 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
    dispatchEntry->headMotionSample = NULL;
    dispatchEntry->tailMotionSample = NULL;

    if (dispatchEntry->isSyncTarget()) {
        eventEntry->pendingSyncDispatches += 1;
    }

    // Handle the case where we could not stream a new motion sample because the consumer has
    // already consumed the motion event (otherwise the corresponding dispatch entry would
    // still be in the outbound queue for this connection).  We set the head motion sample
@@ -789,6 +788,9 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
            }
            // Finished.
            connection->outboundQueue.dequeueAtHead();
            if (dispatchEntry->isSyncTarget()) {
                decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
            }
            mAllocator.releaseDispatchEntry(dispatchEntry);
        } else {
            // If the head is not in progress, then we must have already dequeued the in
@@ -854,6 +856,9 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
    if (! connection->outboundQueue.isEmpty()) {
        do {
            DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
            if (dispatchEntry->isSyncTarget()) {
                decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
            }
            mAllocator.releaseDispatchEntry(dispatchEntry);
        } while (! connection->outboundQueue.isEmpty());

@@ -1097,7 +1102,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
                    Connection* connection = mActiveConnections.itemAt(i);
                    if (! connection->outboundQueue.isEmpty()) {
                        DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
                        if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) {
                        if (dispatchEntry->isSyncTarget()) {
                            if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
                                goto NoBatchingOrStreaming;
                            }
@@ -1148,11 +1153,11 @@ NoBatchingOrStreaming:;
}

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

    nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
@@ -1167,6 +1172,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
        injectedEntry->injectorPid = injectorPid;
        injectedEntry->injectorUid = injectorUid;

        if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
            injectedEntry->injectionIsAsync = true;
        }

        wasEmpty = mInboundQueue.isEmpty();
        mInboundQueue.enqueueAtTail(injectedEntry);

@@ -1180,6 +1189,9 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
    { // acquire lock
        AutoMutex _l(mLock);

        if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
            injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
        } else {
            for (;;) {
                injectionResult = injectedEntry->injectionResult;
                if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
@@ -1188,29 +1200,48 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,

                nsecs_t remainingTimeout = endTime - now();
                if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
                    LOGD("injectInputEvent - Timed out waiting for injection result "
                            "to become available.");
#endif
                    injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
                sync = false;
                    break;
                }

                mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
            }

        if (sync) {
            while (! isFullySynchronizedLocked()) {
            if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
                    && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
                while (injectedEntry->pendingSyncDispatches != 0) {
#if DEBUG_INJECTION
                    LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
                            injectedEntry->pendingSyncDispatches);
#endif
                    nsecs_t remainingTimeout = endTime - now();
                    if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
                    LOGD("injectInputEvent - Timed out waiting for pending synchronous "
                            "dispatches to finish.");
#endif
                        injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
                        break;
                    }

                mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout);
                    mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
                }
            }
        }

        mAllocator.releaseEventEntry(injectedEntry);
    } // release lock

#if DEBUG_INJECTION
    LOGD("injectInputEvent - Finished with result %d.  "
            "injectorPid=%d, injectorUid=%d",
            injectionResult, injectorPid, injectorUid);
#endif

    return injectionResult;
}

@@ -1222,13 +1253,35 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject
                 injectionResult, entry->injectorPid, entry->injectorUid);
#endif

        if (entry->injectionIsAsync) {
            // Log the outcome since the injector did not wait for the injection result.
            switch (injectionResult) {
            case INPUT_EVENT_INJECTION_SUCCEEDED:
                LOGV("Asynchronous input event injection succeeded.");
                break;
            case INPUT_EVENT_INJECTION_FAILED:
                LOGW("Asynchronous input event injection failed.");
                break;
            case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
                LOGW("Asynchronous input event injection permission denied.");
                break;
            case INPUT_EVENT_INJECTION_TIMED_OUT:
                LOGW("Asynchronous input event injection timed out.");
                break;
            }
        }

        entry->injectionResult = injectionResult;
        mInjectionResultAvailableCondition.broadcast();
    }
}

bool InputDispatcher::isFullySynchronizedLocked() {
    return mInboundQueue.isEmpty() && mActiveConnections.isEmpty();
void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
    entry->pendingSyncDispatches -= 1;

    if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
        mInjectionSyncFinishedCondition.broadcast();
    }
}

InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
@@ -1498,8 +1551,10 @@ void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t
    entry->dispatchInProgress = false;
    entry->eventTime = eventTime;
    entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
    entry->injectionIsAsync = false;
    entry->injectorPid = -1;
    entry->injectorUid = -1;
    entry->pendingSyncDispatches = 0;
}

InputDispatcher::ConfigurationChangedEntry*
+2 −2
Original line number Diff line number Diff line
@@ -81,8 +81,8 @@ status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChann
}

int32_t InputManager::injectInputEvent(const InputEvent* event,
        int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
}

void InputManager::preemptInputDispatch() {