Merge "Add support for throttling motion events." into gingerbread
This commit is contained in:
commit
6b5e70b701
@ -159,6 +159,12 @@ public:
|
||||
virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
|
||||
int32_t injectorPid, int32_t injectorUid,
|
||||
Vector<InputTarget>& outTargets) = 0;
|
||||
|
||||
/* Gets the maximum suggested event delivery rate per second.
|
||||
* This value is used to throttle motion event movement actions on a per-device
|
||||
* basis. It is not intended to be a hard limit.
|
||||
*/
|
||||
virtual int32_t getMaxEventsPerSecond() = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -332,6 +338,8 @@ private:
|
||||
// Linked list of motion samples associated with this motion event.
|
||||
MotionSample firstSample;
|
||||
MotionSample* lastSample;
|
||||
|
||||
uint32_t countSamples() const;
|
||||
};
|
||||
|
||||
// Tracks the progress of dispatching a particular event to a particular connection.
|
||||
@ -587,6 +595,17 @@ private:
|
||||
Condition mInjectionSyncFinishedCondition;
|
||||
void decrementPendingSyncDispatchesLocked(EventEntry* entry);
|
||||
|
||||
// Throttling state.
|
||||
struct ThrottleState {
|
||||
nsecs_t minTimeBetweenEvents;
|
||||
|
||||
nsecs_t lastEventTime;
|
||||
int32_t lastDeviceId;
|
||||
uint32_t lastSource;
|
||||
|
||||
uint32_t originalSampleCount; // only collected during debugging
|
||||
} mThrottleState;
|
||||
|
||||
// Key repeat tracking.
|
||||
// XXX Move this up to the input reader instead.
|
||||
struct KeyRepeatState {
|
||||
|
@ -28,6 +28,9 @@
|
||||
// Log debug messages about input event injection.
|
||||
#define DEBUG_INJECTION 0
|
||||
|
||||
// Log debug messages about input event throttling.
|
||||
#define DEBUG_THROTTLING 0
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <ui/InputDispatcher.h>
|
||||
|
||||
@ -66,6 +69,15 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
|
||||
|
||||
mKeyRepeatState.lastKeyEntry = NULL;
|
||||
|
||||
int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
|
||||
mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
|
||||
mThrottleState.lastDeviceId = -1;
|
||||
|
||||
#if DEBUG_THROTTLING
|
||||
mThrottleState.originalSampleCount = 0;
|
||||
LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
|
||||
#endif
|
||||
|
||||
mCurrentInputTargetsValid = false;
|
||||
}
|
||||
|
||||
@ -144,12 +156,60 @@ void InputDispatcher::dispatchOnce() {
|
||||
}
|
||||
} else {
|
||||
// Inbound queue has at least one entry.
|
||||
// Start processing it but leave it on the queue until later so that the
|
||||
EventEntry* entry = mInboundQueue.head.next;
|
||||
|
||||
// Consider throttling the entry if it is a move event and there are no
|
||||
// other events behind it in the queue. Due to movement batching, additional
|
||||
// samples may be appended to this event by the time the throttling timeout
|
||||
// expires.
|
||||
// TODO Make this smarter and consider throttling per device independently.
|
||||
if (entry->type == EventEntry::TYPE_MOTION) {
|
||||
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
|
||||
int32_t deviceId = motionEntry->deviceId;
|
||||
uint32_t source = motionEntry->source;
|
||||
if (motionEntry->next == & mInboundQueue.tail
|
||||
&& motionEntry->action == AMOTION_EVENT_ACTION_MOVE
|
||||
&& deviceId == mThrottleState.lastDeviceId
|
||||
&& source == mThrottleState.lastSource) {
|
||||
nsecs_t nextTime = mThrottleState.lastEventTime
|
||||
+ mThrottleState.minTimeBetweenEvents;
|
||||
if (currentTime < nextTime) {
|
||||
// Throttle it!
|
||||
#if DEBUG_THROTTLING
|
||||
LOGD("Throttling - Delaying motion event for "
|
||||
"device 0x%x, source 0x%08x by up to %0.3fms.",
|
||||
deviceId, source, (nextTime - currentTime) * 0.000001);
|
||||
#endif
|
||||
if (nextTime < nextWakeupTime) {
|
||||
nextWakeupTime = nextTime;
|
||||
}
|
||||
if (mThrottleState.originalSampleCount == 0) {
|
||||
mThrottleState.originalSampleCount =
|
||||
motionEntry->countSamples();
|
||||
}
|
||||
goto Throttle;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_THROTTLING
|
||||
if (mThrottleState.originalSampleCount != 0) {
|
||||
uint32_t count = motionEntry->countSamples();
|
||||
LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
|
||||
count - mThrottleState.originalSampleCount,
|
||||
mThrottleState.originalSampleCount, count);
|
||||
mThrottleState.originalSampleCount = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
mThrottleState.lastEventTime = currentTime;
|
||||
mThrottleState.lastDeviceId = deviceId;
|
||||
mThrottleState.lastSource = source;
|
||||
}
|
||||
|
||||
// Start processing the entry but leave it on the queue until later so that the
|
||||
// input reader can keep appending samples onto a motion event between the
|
||||
// time we started processing it and the time we finally enqueue dispatch
|
||||
// entries for it.
|
||||
EventEntry* entry = mInboundQueue.head.next;
|
||||
|
||||
switch (entry->type) {
|
||||
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
|
||||
ConfigurationChangedEntry* typedEntry =
|
||||
@ -179,6 +239,8 @@ void InputDispatcher::dispatchOnce() {
|
||||
mInboundQueue.dequeue(entry);
|
||||
mAllocator.releaseEventEntry(entry);
|
||||
skipPoll = true;
|
||||
|
||||
Throttle: ;
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,8 +254,8 @@ void InputDispatcher::dispatchOnce() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for callback or timeout or wake.
|
||||
nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
|
||||
// Wait for callback or timeout or wake. (make sure we round up, not down)
|
||||
nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
|
||||
int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
|
||||
mPollLoop->pollOnce(timeoutMillis);
|
||||
}
|
||||
@ -1708,6 +1770,16 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
|
||||
motionEntry->lastSample = sample;
|
||||
}
|
||||
|
||||
// --- InputDispatcher::MotionEntry ---
|
||||
|
||||
uint32_t InputDispatcher::MotionEntry::countSamples() const {
|
||||
uint32_t count = 1;
|
||||
for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// --- InputDispatcher::Connection ---
|
||||
|
||||
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
|
||||
|
Loading…
Reference in New Issue
Block a user