diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index 674852aeb..d3495fee5 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -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(); @@ -267,11 +282,13 @@ private: int32_t type; nsecs_t eventTime; - int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING - int32_t injectorPid; // -1 if not injected - int32_t injectorUid; // -1 if not injected + 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 { diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h index 7ebec10dd..4012c6913 100644 --- a/include/ui/InputManager.h +++ b/include/ui/InputManager.h @@ -79,13 +79,12 @@ public: virtual status_t unregisterInputChannel(const sp& 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); 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(); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index a55864be1..b53f140f6 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -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,37 +1189,59 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, { // acquire lock AutoMutex _l(mLock); - for (;;) { - injectionResult = injectedEntry->injectionResult; - if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { - break; - } + if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + } else { + for (;;) { + injectionResult = injectedEntry->injectionResult; + if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { + break; + } - nsecs_t remainingTimeout = endTime - now(); - if (remainingTimeout <= 0) { - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; - sync = false; - break; - } - - mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout); - } - - if (sync) { - while (! isFullySynchronizedLocked()) { 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; break; } - mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout); + mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout); + } + + 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; + } + + 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* diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index bf2347902..ed4f07b27 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -81,8 +81,8 @@ status_t InputManager::unregisterInputChannel(const sp& 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() {