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
This commit is contained in:
Jeff Brown 2010-07-28 15:48:59 -07:00
parent 302dd91b86
commit f67c53eee3
4 changed files with 123 additions and 48 deletions

View File

@ -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 {

View File

@ -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();

View File

@ -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*

View File

@ -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() {