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:
parent
302dd91b86
commit
f67c53eee3
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue