Loading libs/ui/InputTransport.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -27,8 +27,14 @@ namespace android { #define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1)) #define MIN_HISTORY_DEPTH 20 // Must be at least sizeof(InputMessage) + sufficient space for pointer data static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384; static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP( sizeof(InputMessage) + MIN_HISTORY_DEPTH * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)), 4096); // Signal sent by the producer to the consumer to inform it that a new message is // available to be consumed in the shared memory buffer. Loading services/input/EventHub.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -539,7 +539,24 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); #ifdef HAVE_POSIX_CLOCKS // Use the time specified in the event instead of the current time // so that downstream code can get more accurate estimates of // event dispatch latency from the time the event is enqueued onto // the evdev client buffer. // // The event's timestamp fortuitously uses the same monotonic clock // time base as the rest of Android. The kernel event device driver // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a // system call that also queries ktime_get_ts(). event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; LOGV("event time %lld, now %lld", event->when, now); #else event->when = now; #endif event->deviceId = deviceId; event->type = iev.type; event->scanCode = iev.code; Loading services/input/InputDispatcher.cpp +72 −19 Original line number Diff line number Diff line Loading @@ -76,6 +76,22 @@ const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec // before considering it stale and dropping it. const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec // Motion samples that are received within this amount of time are simply coalesced // when batched instead of being appended. This is done because some drivers update // the location of pointers one at a time instead of all at once. // For example, when there are 10 fingers down, the input dispatcher may receive 10 // samples in quick succession with only one finger's location changed in each sample. // // This value effectively imposes an upper bound on the touch sampling rate. // Touch sensors typically have a 50Hz - 200Hz sampling rate, so we expect distinct // samples to become available 5-20ms apart but individual finger reports can trickle // in over a period of 2-4ms or so. // // Empirical testing shows that a 2ms coalescing interval (500Hz) is not enough, // a 3ms coalescing interval (333Hz) works well most of the time and doesn't introduce // significant quantization noise on current hardware. const nsecs_t MOTION_SAMPLE_COALESCE_INTERVAL = 3 * 1000000LL; // 3ms, 333Hz static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); Loading Loading @@ -2385,21 +2401,15 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t continue; } if (motionEntry->action != action || motionEntry->pointerCount != pointerCount || motionEntry->isInjected()) { if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) { // Last motion event in the queue for this device and source is // not compatible for appending new samples. Stop here. goto NoBatchingOrStreaming; } // The last motion event is a move and is compatible for appending. // Do the batching magic. mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for most recent " "motion event for this device in the inbound queue."); #endif batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords, "most recent motion event for this device and source in the inbound queue"); return; // done! } Loading @@ -2413,19 +2423,15 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t && mPendingEvent->type == EventEntry::TYPE_MOTION) { MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent); if (motionEntry->deviceId == deviceId && motionEntry->source == source) { if (motionEntry->action != action || motionEntry->pointerCount != pointerCount || motionEntry->isInjected()) { // Pending event is not compatible for appending new samples. Stop here. if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) { // Pending motion event is for this device and source but it is // not compatible for appending new samples. Stop here. goto NoBatchingOrStreaming; } // The pending motion event is a move and is compatible for appending. // Do the batching magic. mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for the pending motion event."); #endif batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords, "pending motion event"); mLock.unlock(); return; // done! } Loading Loading @@ -2485,7 +2491,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for most recently dispatched " "motion event for this device in the outbound queues. " "motion event for this device and source in the outbound queues. " "Attempting to stream the motion sample."); #endif nsecs_t currentTime = now(); Loading Loading @@ -2514,6 +2520,36 @@ NoBatchingOrStreaming:; } } void InputDispatcher::batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription) { // Combine meta states. entry->metaState |= metaState; // Coalesce this sample if not enough time has elapsed since the last sample was // initially appended to the batch. MotionSample* lastSample = entry->lastSample; long interval = eventTime - lastSample->eventTimeBeforeCoalescing; if (interval <= MOTION_SAMPLE_COALESCE_INTERVAL) { uint32_t pointerCount = entry->pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { lastSample->pointerCoords[i].copyFrom(pointerCoords[i]); } lastSample->eventTime = eventTime; #if DEBUG_BATCHING LOGD("Coalesced motion into last sample of batch for %s, events were %0.3f ms apart", eventDescription, interval * 0.000001f); #endif return; } // Append the sample. mAllocator.appendMotionSample(entry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for %s, events were %0.3f ms apart", eventDescription, interval * 0.000001f); #endif } void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { #if DEBUG_INBOUND_EVENT_DETAILS Loading Loading @@ -3572,6 +3608,7 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec entry->downTime = downTime; entry->pointerCount = pointerCount; entry->firstSample.eventTime = eventTime; entry->firstSample.eventTimeBeforeCoalescing = eventTime; entry->firstSample.next = NULL; entry->lastSample = & entry->firstSample; for (uint32_t i = 0; i < pointerCount; i++) { Loading Loading @@ -3678,6 +3715,7 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, nsecs_t eventTime, const PointerCoords* pointerCoords) { MotionSample* sample = mMotionSamplePool.alloc(); sample->eventTime = eventTime; sample->eventTimeBeforeCoalescing = eventTime; uint32_t pointerCount = motionEntry->pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { sample->pointerCoords[i].copyFrom(pointerCoords[i]); Loading Loading @@ -3707,6 +3745,21 @@ uint32_t InputDispatcher::MotionEntry::countSamples() const { return count; } bool InputDispatcher::MotionEntry::canAppendSamples(int32_t action, uint32_t pointerCount, const int32_t* pointerIds) const { if (this->action != action || this->pointerCount != pointerCount || this->isInjected()) { return false; } for (uint32_t i = 0; i < pointerCount; i++) { if (this->pointerIds[i] != pointerIds[i]) { return false; } } return true; } // --- InputDispatcher::InputState --- Loading services/input/InputDispatcher.h +12 −2 Original line number Diff line number Diff line Loading @@ -375,7 +375,7 @@ private: bool dispatchInProgress; // initially false, set to true while dispatching inline bool isInjected() { return injectionState != NULL; } inline bool isInjected() const { return injectionState != NULL; } }; struct ConfigurationChangedEntry : EventEntry { Loading Loading @@ -405,7 +405,8 @@ private: struct MotionSample { MotionSample* next; nsecs_t eventTime; nsecs_t eventTime; // may be updated during coalescing nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing PointerCoords pointerCoords[MAX_POINTERS]; }; Loading @@ -427,6 +428,10 @@ private: MotionSample* lastSample; uint32_t countSamples() const; // Checks whether we can append samples, assuming the device id and source are the same. bool canAppendSamples(int32_t action, uint32_t pointerCount, const int32_t* pointerIds) const; }; // Tracks the progress of dispatching a particular event to a particular connection. Loading Loading @@ -767,6 +772,11 @@ private: void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime); // Batches a new sample onto a motion entry. // Assumes that the we have already checked that we can append samples. void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(EventEntry* entry); Loading Loading
libs/ui/InputTransport.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -27,8 +27,14 @@ namespace android { #define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1)) #define MIN_HISTORY_DEPTH 20 // Must be at least sizeof(InputMessage) + sufficient space for pointer data static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384; static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP( sizeof(InputMessage) + MIN_HISTORY_DEPTH * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)), 4096); // Signal sent by the producer to the consumer to inform it that a new message is // available to be consumed in the shared memory buffer. Loading
services/input/EventHub.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -539,7 +539,24 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); #ifdef HAVE_POSIX_CLOCKS // Use the time specified in the event instead of the current time // so that downstream code can get more accurate estimates of // event dispatch latency from the time the event is enqueued onto // the evdev client buffer. // // The event's timestamp fortuitously uses the same monotonic clock // time base as the rest of Android. The kernel event device driver // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a // system call that also queries ktime_get_ts(). event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; LOGV("event time %lld, now %lld", event->when, now); #else event->when = now; #endif event->deviceId = deviceId; event->type = iev.type; event->scanCode = iev.code; Loading
services/input/InputDispatcher.cpp +72 −19 Original line number Diff line number Diff line Loading @@ -76,6 +76,22 @@ const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec // before considering it stale and dropping it. const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec // Motion samples that are received within this amount of time are simply coalesced // when batched instead of being appended. This is done because some drivers update // the location of pointers one at a time instead of all at once. // For example, when there are 10 fingers down, the input dispatcher may receive 10 // samples in quick succession with only one finger's location changed in each sample. // // This value effectively imposes an upper bound on the touch sampling rate. // Touch sensors typically have a 50Hz - 200Hz sampling rate, so we expect distinct // samples to become available 5-20ms apart but individual finger reports can trickle // in over a period of 2-4ms or so. // // Empirical testing shows that a 2ms coalescing interval (500Hz) is not enough, // a 3ms coalescing interval (333Hz) works well most of the time and doesn't introduce // significant quantization noise on current hardware. const nsecs_t MOTION_SAMPLE_COALESCE_INTERVAL = 3 * 1000000LL; // 3ms, 333Hz static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); Loading Loading @@ -2385,21 +2401,15 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t continue; } if (motionEntry->action != action || motionEntry->pointerCount != pointerCount || motionEntry->isInjected()) { if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) { // Last motion event in the queue for this device and source is // not compatible for appending new samples. Stop here. goto NoBatchingOrStreaming; } // The last motion event is a move and is compatible for appending. // Do the batching magic. mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for most recent " "motion event for this device in the inbound queue."); #endif batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords, "most recent motion event for this device and source in the inbound queue"); return; // done! } Loading @@ -2413,19 +2423,15 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t && mPendingEvent->type == EventEntry::TYPE_MOTION) { MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent); if (motionEntry->deviceId == deviceId && motionEntry->source == source) { if (motionEntry->action != action || motionEntry->pointerCount != pointerCount || motionEntry->isInjected()) { // Pending event is not compatible for appending new samples. Stop here. if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) { // Pending motion event is for this device and source but it is // not compatible for appending new samples. Stop here. goto NoBatchingOrStreaming; } // The pending motion event is a move and is compatible for appending. // Do the batching magic. mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for the pending motion event."); #endif batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords, "pending motion event"); mLock.unlock(); return; // done! } Loading Loading @@ -2485,7 +2491,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for most recently dispatched " "motion event for this device in the outbound queues. " "motion event for this device and source in the outbound queues. " "Attempting to stream the motion sample."); #endif nsecs_t currentTime = now(); Loading Loading @@ -2514,6 +2520,36 @@ NoBatchingOrStreaming:; } } void InputDispatcher::batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription) { // Combine meta states. entry->metaState |= metaState; // Coalesce this sample if not enough time has elapsed since the last sample was // initially appended to the batch. MotionSample* lastSample = entry->lastSample; long interval = eventTime - lastSample->eventTimeBeforeCoalescing; if (interval <= MOTION_SAMPLE_COALESCE_INTERVAL) { uint32_t pointerCount = entry->pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { lastSample->pointerCoords[i].copyFrom(pointerCoords[i]); } lastSample->eventTime = eventTime; #if DEBUG_BATCHING LOGD("Coalesced motion into last sample of batch for %s, events were %0.3f ms apart", eventDescription, interval * 0.000001f); #endif return; } // Append the sample. mAllocator.appendMotionSample(entry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for %s, events were %0.3f ms apart", eventDescription, interval * 0.000001f); #endif } void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { #if DEBUG_INBOUND_EVENT_DETAILS Loading Loading @@ -3572,6 +3608,7 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec entry->downTime = downTime; entry->pointerCount = pointerCount; entry->firstSample.eventTime = eventTime; entry->firstSample.eventTimeBeforeCoalescing = eventTime; entry->firstSample.next = NULL; entry->lastSample = & entry->firstSample; for (uint32_t i = 0; i < pointerCount; i++) { Loading Loading @@ -3678,6 +3715,7 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, nsecs_t eventTime, const PointerCoords* pointerCoords) { MotionSample* sample = mMotionSamplePool.alloc(); sample->eventTime = eventTime; sample->eventTimeBeforeCoalescing = eventTime; uint32_t pointerCount = motionEntry->pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { sample->pointerCoords[i].copyFrom(pointerCoords[i]); Loading Loading @@ -3707,6 +3745,21 @@ uint32_t InputDispatcher::MotionEntry::countSamples() const { return count; } bool InputDispatcher::MotionEntry::canAppendSamples(int32_t action, uint32_t pointerCount, const int32_t* pointerIds) const { if (this->action != action || this->pointerCount != pointerCount || this->isInjected()) { return false; } for (uint32_t i = 0; i < pointerCount; i++) { if (this->pointerIds[i] != pointerIds[i]) { return false; } } return true; } // --- InputDispatcher::InputState --- Loading
services/input/InputDispatcher.h +12 −2 Original line number Diff line number Diff line Loading @@ -375,7 +375,7 @@ private: bool dispatchInProgress; // initially false, set to true while dispatching inline bool isInjected() { return injectionState != NULL; } inline bool isInjected() const { return injectionState != NULL; } }; struct ConfigurationChangedEntry : EventEntry { Loading Loading @@ -405,7 +405,8 @@ private: struct MotionSample { MotionSample* next; nsecs_t eventTime; nsecs_t eventTime; // may be updated during coalescing nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing PointerCoords pointerCoords[MAX_POINTERS]; }; Loading @@ -427,6 +428,10 @@ private: MotionSample* lastSample; uint32_t countSamples() const; // Checks whether we can append samples, assuming the device id and source are the same. bool canAppendSamples(int32_t action, uint32_t pointerCount, const int32_t* pointerIds) const; }; // Tracks the progress of dispatching a particular event to a particular connection. Loading Loading @@ -767,6 +772,11 @@ private: void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime); // Batches a new sample onto a motion entry. // Assumes that the we have already checked that we can append samples. void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(EventEntry* entry); Loading