Implement batching of input events on the consumer side.
To support this feature, the input dispatcher now allows input
events to be acknowledged out-of-order. As a result, the
consumer can choose to defer handling an input event from one
device (because it is building a big batch) while continuing
to handle input events from other devices.
The InputEventReceiver now sends a notification when a batch
is pending. The ViewRoot handles this notification by scheduling
a draw on the next sync. When the draw happens, the InputEventReceiver
is instructed to consume all pending batched input events, the
input event queue is fully processed (as much as possible),
and then the ViewRoot performs traversals as usual.
With these changes in place, the input dispatch latency is
consistently less than one frame as long as the application itself
isn't stalled. Input events are delivered to the application
as soon as possible and are handled as soon as possible. In practice,
it is no longer possible for an application to build up a huge
backlog of touch events.
This is part of a series of changes to improve input system pipelining.
Bug: 5963420
Change-Id: I42c01117eca78f12d66d49a736c1c122346ccd1d
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index a63b6f5..d04aa68 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1872,7 +1872,7 @@
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Publish the key event.
- status = connection->inputPublisher.publishKeyEvent(
+ status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
keyEntry->deviceId, keyEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
@@ -1916,7 +1916,7 @@
}
// Publish the motion event.
- status = connection->inputPublisher.publishMotionEvent(
+ status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
motionEntry->deviceId, motionEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
@@ -1967,10 +1967,10 @@
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, bool handled) {
+ const sp<Connection>& connection, uint32_t seq, bool handled) {
#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ finishDispatchCycle - handled=%s",
- connection->getInputChannelName(), toString(handled));
+ ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
+ connection->getInputChannelName(), seq, toString(handled));
#endif
connection->inputPublisherBlocked = false;
@@ -1981,7 +1981,7 @@
}
// Notify other system components and prepare to start the next dispatch cycle.
- onDispatchCycleFinishedLocked(currentTime, connection, handled);
+ onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
}
void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
@@ -2047,12 +2047,13 @@
bool gotOne = false;
status_t status;
for (;;) {
- bool handled = false;
- status = connection->inputPublisher.receiveFinishedSignal(&handled);
+ uint32_t seq;
+ bool handled;
+ status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
if (status) {
break;
}
- d->finishDispatchCycleLocked(currentTime, connection, handled);
+ d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
gotOne = true;
}
if (gotOne) {
@@ -3171,10 +3172,11 @@
}
void InputDispatcher::onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, bool handled) {
+ nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
commandEntry->connection = connection;
+ commandEntry->seq = seq;
commandEntry->handled = handled;
}
@@ -3268,12 +3270,13 @@
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
+ uint32_t seq = commandEntry->seq;
bool handled = commandEntry->handled;
- if (!connection->waitQueue.isEmpty()) {
- // Handle post-event policy actions.
+ // Handle post-event policy actions.
+ DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
+ if (dispatchEntry) {
bool restartEvent;
- DispatchEntry* dispatchEntry = connection->waitQueue.head;
if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
restartEvent = afterKeyEventLockedInterruptible(connection,
@@ -3290,8 +3293,8 @@
// Note that because the lock might have been released, it is possible that the
// contents of the wait queue to have been drained, so we need to double-check
// a few things.
- if (connection->waitQueue.head == dispatchEntry) {
- connection->waitQueue.dequeueAtHead();
+ if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
+ connection->waitQueue.dequeue(dispatchEntry);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.enqueueAtHead(dispatchEntry);
} else {
@@ -3635,8 +3638,11 @@
// --- InputDispatcher::DispatchEntry ---
+volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
+
InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
+ seq(nextSeq()),
eventEntry(eventEntry), targetFlags(targetFlags),
xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
resolvedAction(0), resolvedFlags(0) {
@@ -3647,6 +3653,15 @@
eventEntry->release();
}
+uint32_t InputDispatcher::DispatchEntry::nextSeq() {
+ // Sequence number 0 is reserved and will never be returned.
+ uint32_t seq;
+ do {
+ seq = android_atomic_inc(&sNextSeqAtomic);
+ } while (!seq);
+ return seq;
+}
+
// --- InputDispatcher::InputState ---
@@ -3999,11 +4014,21 @@
}
}
+InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) {
+ for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) {
+ if (entry->seq == seq) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
// --- InputDispatcher::CommandEntry ---
InputDispatcher::CommandEntry::CommandEntry(Command command) :
- command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), handled(false) {
+ command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0),
+ seq(0), handled(false) {
}
InputDispatcher::CommandEntry::~CommandEntry() {
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 1311465..03a824b 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -27,6 +27,7 @@
#include <utils/String8.h>
#include <utils/Looper.h>
#include <utils/BitSet.h>
+#include <cutils/atomic.h>
#include <stddef.h>
#include <unistd.h>
@@ -522,6 +523,8 @@
// Tracks the progress of dispatching a particular event to a particular connection.
struct DispatchEntry : Link<DispatchEntry> {
+ const uint32_t seq; // unique sequence number, never 0
+
EventEntry* eventEntry; // the event to dispatch
int32_t targetFlags;
float xOffset;
@@ -543,6 +546,11 @@
inline bool isSplit() const {
return targetFlags & InputTarget::FLAG_SPLIT;
}
+
+ private:
+ static volatile int32_t sNextSeqAtomic;
+
+ static uint32_t nextSeq();
};
// A command entry captures state and behavior for an action to be performed in the
@@ -578,6 +586,7 @@
sp<InputApplicationHandle> inputApplicationHandle;
sp<InputWindowHandle> inputWindowHandle;
int32_t userActivityEventType;
+ uint32_t seq;
bool handled;
};
@@ -797,6 +806,8 @@
inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
const char* getStatusLabel() const;
+
+ DispatchEntry* findWaitQueueEntry(uint32_t seq);
};
enum DropReason {
@@ -1000,7 +1011,7 @@
EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- bool handled);
+ uint32_t seq, bool handled);
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool notify);
void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
@@ -1034,7 +1045,7 @@
// Interesting events that we might like to log or tell the framework about.
void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, bool handled);
+ nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
void onDispatchCycleBrokenLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onANRLocked(