From 50de30a5230dd15326f8bcbb4beaf617bca265e2 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 22 Jun 2010 01:27:15 -0700 Subject: [PATCH] Native input event dispatching. Target identification is now fully native. Fixed a couple of minor issues related to input injection. Native input enabled by default, can be disabled by setting WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH to false. Change-Id: I7edf66ed3e987cc9306ad4743ac57a116af452ff --- include/ui/Input.h | 3 ++ include/ui/InputDispatcher.h | 20 +++++++-- include/ui/InputManager.h | 10 +++++ include/ui/InputReader.h | 6 ++- libs/ui/InputDispatcher.cpp | 86 ++++++++++++++++++++++++++---------- libs/ui/InputManager.cpp | 4 ++ libs/ui/InputReader.cpp | 55 +++++++++++++++-------- libs/ui/InputTransport.cpp | 4 ++ 8 files changed, 142 insertions(+), 46 deletions(-) diff --git a/include/ui/Input.h b/include/ui/Input.h index 32f85b3b6..57b292bbc 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -87,6 +87,9 @@ enum { // Indicates that the screen was dim when the event was received and the event // should brighten the device. POLICY_FLAG_BRIGHT_HERE = 0x20000000, + + // Indicates that the dispatcher should call back into the policy before dispatching. */ + POLICY_FLAG_INTERCEPT_DISPATCH = 0x40000000, }; /* diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index 511ad20cf..eb8f820f0 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -126,21 +126,21 @@ public: /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */ virtual nsecs_t getKeyRepeatTimeout() = 0; - /* Gets the input targets for a key event. + /* Waits for key event input targets to become available. * If the event is being injected, injectorPid and injectorUid should specify the * process id and used id of the injecting application, otherwise they should both * be -1. * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */ - virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets) = 0; - /* Gets the input targets for a motion event. + /* Waits for motion event targets to become available. * If the event is being injected, injectorPid and injectorUid should specify the * process id and used id of the injecting application, otherwise they should both * be -1. * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */ - virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets) = 0; }; @@ -186,6 +186,16 @@ public: virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0; + /* Preempts input dispatch in progress by making pending synchronous + * dispatches asynchronous instead. This method is generally called during a focus + * transition from one application to the next so as to enable the new application + * to start receiving input as soon as possible without having to wait for the + * old application to finish up. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void preemptInputDispatch() = 0; + /* Registers or unregister input channels that may be used as targets for input events. * * These methods may be called on any thread (usually by the input manager). @@ -233,6 +243,8 @@ public: virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis); + virtual void preemptInputDispatch(); + virtual status_t registerInputChannel(const sp& inputChannel); virtual status_t unregisterInputChannel(const sp& inputChannel); diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h index 7509dd205..e75523811 100644 --- a/include/ui/InputManager.h +++ b/include/ui/InputManager.h @@ -87,6 +87,14 @@ public: virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0; + /* Preempts input dispatch in progress by making pending synchronous + * dispatches asynchronous instead. This method is generally called during a focus + * transition from one application to the next so as to enable the new application + * to start receiving input as soon as possible without having to wait for the + * old application to finish up. + */ + virtual void preemptInputDispatch() = 0; + /* Gets input device configuration. */ virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0; @@ -130,6 +138,8 @@ public: virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis); + virtual void preemptInputDispatch(); + virtual void getInputConfiguration(InputConfiguration* outConfiguration) const; virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses, int32_t scanCode) const; diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index d76b8fed0..20935604e 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -361,7 +361,11 @@ public: // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it // passes through the dispatch pipeline. - ACTION_BRIGHT_HERE = 0x00000008 + ACTION_BRIGHT_HERE = 0x00000008, + + // The input dispatcher should add POLICY_FLAG_INTERCEPT_DISPATCH to the policy flags + // it passed through the dispatch pipeline. + ACTION_INTERCEPT_DISPATCH = 0x00000010 }; /* Describes a virtual key. */ diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index b3103a47c..0fc29b24f 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -8,25 +8,25 @@ //#define LOG_NDEBUG 0 // Log detailed debug messages about each inbound event notification to the dispatcher. -#define DEBUG_INBOUND_EVENT_DETAILS 1 +#define DEBUG_INBOUND_EVENT_DETAILS 0 // Log detailed debug messages about each outbound event processed by the dispatcher. -#define DEBUG_OUTBOUND_EVENT_DETAILS 1 +#define DEBUG_OUTBOUND_EVENT_DETAILS 0 // Log debug messages about batching. -#define DEBUG_BATCHING 1 +#define DEBUG_BATCHING 0 // Log debug messages about the dispatch cycle. -#define DEBUG_DISPATCH_CYCLE 1 +#define DEBUG_DISPATCH_CYCLE 0 // Log debug messages about registrations. -#define DEBUG_REGISTRATION 1 +#define DEBUG_REGISTRATION 0 // Log debug messages about performance statistics. -#define DEBUG_PERFORMANCE_STATISTICS 1 +#define DEBUG_PERFORMANCE_STATISTICS 0 // Log debug messages about input event injection. -#define DEBUG_INJECTION 1 +#define DEBUG_INJECTION 0 #include #include @@ -249,9 +249,7 @@ void InputDispatcher::processKeyLockedInterruptible( entry->downTime); #endif - // TODO: Poke user activity. - - if (entry->action == KEY_EVENT_ACTION_DOWN) { + if (entry->action == KEY_EVENT_ACTION_DOWN && ! entry->isInjected()) { if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { // We have seen two identical key downs in a row which indicates that the device @@ -277,14 +275,24 @@ void InputDispatcher::processKeyLockedInterruptible( void InputDispatcher::processKeyRepeatLockedInterruptible( nsecs_t currentTime, nsecs_t keyRepeatTimeout) { - // TODO Old WindowManagerServer code sniffs the input queue for following key up - // events and drops the repeat if one is found. We should do something similar. - // One good place to do it is in notifyKey as soon as the key up enters the - // inbound event queue. + KeyEntry* entry = mKeyRepeatState.lastKeyEntry; + + // Search the inbound queue for a key up corresponding to this device. + // It doesn't make sense to generate a key repeat event if the key is already up. + for (EventEntry* queuedEntry = mInboundQueue.head.next; + queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) { + if (queuedEntry->type == EventEntry::TYPE_KEY) { + KeyEntry* queuedKeyEntry = static_cast(queuedEntry); + if (queuedKeyEntry->deviceId == entry->deviceId + && entry->action == KEY_EVENT_ACTION_UP) { + resetKeyRepeatLocked(); + return; + } + } + } // Synthesize a key repeat after the repeat timeout expired. - // We reuse the previous key entry if otherwise unreferenced. - KeyEntry* entry = mKeyRepeatState.lastKeyEntry; + // Reuse the repeated key entry if it is otherwise unreferenced. uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK; if (entry->refCount == 1) { entry->eventTime = currentTime; @@ -366,7 +374,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( entry->downTime, entry->eventTime); mCurrentInputTargets.clear(); - int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent, + int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent, entry->policyFlags, entry->injectorPid, entry->injectorUid, mCurrentInputTargets); @@ -375,7 +383,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( setInjectionResultLocked(entry, injectionResult); - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + } } void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( @@ -395,7 +405,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( entry->firstSample.pointerCoords); mCurrentInputTargets.clear(); - int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent, + int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent, entry->policyFlags, entry->injectorPid, entry->injectorUid, mCurrentInputTargets); @@ -404,7 +414,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( setInjectionResultLocked(entry, injectionResult); - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + } } void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, @@ -514,7 +526,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName()); } else if (status == status_t(FAILED_TRANSACTION)) { LOGD("channel '%s' ~ Could not append motion sample to currently " - "dispatchedmove event because the event has already been consumed. " + "dispatched move event because the event has already been consumed. " "(Waiting for next dispatch cycle to start.)", connection->getInputChannelName()); } else { @@ -1253,9 +1265,37 @@ void InputDispatcher::resetKeyRepeatLocked() { } } +void InputDispatcher::preemptInputDispatch() { +#if DEBUG_DISPATCH_CYCLE + LOGD("preemptInputDispatch"); +#endif + + bool preemptedOne = false; + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = 0; i < mActiveConnections.size(); i++) { + Connection* connection = mActiveConnections[i]; + if (connection->hasPendingSyncTarget()) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Preempted pending synchronous dispatch", + connection->getInputChannelName()); +#endif + connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC; + preemptedOne = true; + } + } + } // release lock + + if (preemptedOne) { + // Wake up the poll loop so it can get a head start dispatching the next event. + mPollLoop->wake(); + } +} + status_t InputDispatcher::registerInputChannel(const sp& inputChannel) { #if DEBUG_REGISTRATION - LOGD("channel '%s' - Registered", inputChannel->getName().string()); + LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string()); #endif int receiveFd; @@ -1288,7 +1328,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan status_t InputDispatcher::unregisterInputChannel(const sp& inputChannel) { #if DEBUG_REGISTRATION - LOGD("channel '%s' - Unregistered", inputChannel->getName().string()); + LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string()); #endif int32_t receiveFd; diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index 32c58b4fa..e1d15a4ba 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -85,6 +85,10 @@ int32_t InputManager::injectInputEvent(const InputEvent* event, return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis); } +void InputManager::preemptInputDispatch() { + mDispatcher->preemptInputDispatch(); +} + void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const { mReader->getCurrentInputConfiguration(outConfiguration); } diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 1824054e0..8087f84bc 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -11,13 +11,13 @@ #define DEBUG_RAW_EVENTS 0 // Log debug messages about touch screen filtering hacks. -#define DEBUG_HACKS 1 +#define DEBUG_HACKS 0 // Log debug messages about virtual key processing. -#define DEBUG_VIRTUAL_KEYS 1 +#define DEBUG_VIRTUAL_KEYS 0 // Log debug messages about pointers. -#define DEBUG_POINTERS 1 +#define DEBUG_POINTERS 0 // Log debug messages about pointer assignment calculations. #define DEBUG_POINTER_ASSIGNMENT 0 @@ -630,7 +630,8 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() { int32_t pressure = currentTouch.pointers[currentIndex].pressure; if (lastTouch.idBits.hasBit(id)) { - // Pointer still down compute average. + // Pointer was down before and is still down now. + // Compute average over history trace. uint32_t start = averagingTouchFilter.historyStart[id]; uint32_t end = averagingTouchFilter.historyEnd[id]; @@ -644,11 +645,15 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() { #endif if (distance < AVERAGING_DISTANCE_LIMIT) { + // Increment end index in preparation for recording new historical data. end += 1; if (end > AVERAGING_HISTORY_SIZE) { end = 0; } + // If the end index has looped back to the start index then we have filled + // the historical trace up to the desired size so we drop the historical + // data at the start of the trace. if (end == start) { start += 1; if (start > AVERAGING_HISTORY_SIZE) { @@ -656,23 +661,25 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() { } } + // Add the raw data to the historical trace. averagingTouchFilter.historyStart[id] = start; averagingTouchFilter.historyEnd[id] = end; averagingTouchFilter.historyData[end].pointers[id].x = x; averagingTouchFilter.historyData[end].pointers[id].y = y; averagingTouchFilter.historyData[end].pointers[id].pressure = pressure; + // Average over all historical positions in the trace by total pressure. int32_t averagedX = 0; int32_t averagedY = 0; int32_t totalPressure = 0; for (;;) { int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x; - int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].x; + int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y; int32_t historicalPressure = averagingTouchFilter.historyData[start] .pointers[id].pressure; - averagedX += historicalX; - averagedY += historicalY; + averagedX += historicalX * historicalPressure; + averagedY += historicalY * historicalPressure; totalPressure += historicalPressure; if (start == end) { @@ -1144,12 +1151,6 @@ void InputReader::onMultiTouchScreenStateChanged(nsecs_t when, void InputReader::onSingleTouchScreenStateChanged(nsecs_t when, InputDevice* device) { - static const uint32_t POSITION_FIELDS = - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X - | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y - | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE - | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH; - /* Refresh display properties so we can map touch screen coords into display coords */ if (! refreshDisplayProperties()) { @@ -1167,10 +1168,19 @@ void InputReader::onSingleTouchScreenStateChanged(nsecs_t when, in->current.down = in->accumulator.btnTouch; } - if ((fields & POSITION_FIELDS) == POSITION_FIELDS) { + if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) { in->current.x = in->accumulator.absX; + } + + if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) { in->current.y = in->accumulator.absY; + } + + if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) { in->current.pressure = in->accumulator.absPressure; + } + + if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) { in->current.size = in->accumulator.absToolWidth; } @@ -1323,18 +1333,23 @@ bool InputReader::consumeVirtualKeyTouches(nsecs_t when, void InputReader::dispatchVirtualKey(nsecs_t when, InputDevice* device, uint32_t policyFlags, int32_t keyEventAction, int32_t keyEventFlags) { + updateExportedVirtualKeyState(); + int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode; int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode; nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime; int32_t metaState = globalMetaState(); - updateExportedVirtualKeyState(); - mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); - mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags, - keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); + int32_t policyActions = mPolicy->interceptKey(when, device->id, + keyEventAction == KEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags); + + if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { + mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags, + keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); + } } void InputReader::dispatchTouches(nsecs_t when, @@ -1609,6 +1624,10 @@ bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when, *policyFlags |= POLICY_FLAG_BRIGHT_HERE; } + if (policyActions & InputReaderPolicyInterface::ACTION_INTERCEPT_DISPATCH) { + *policyFlags |= POLICY_FLAG_INTERCEPT_DISPATCH; + } + return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; } diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index b2842d0ae..f56537ab7 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -430,10 +430,12 @@ status_t InputPublisher::appendMotionSample( reinterpret_cast(mSharedMessage); if (newBytesUsed > mAshmemSize) { +#if DEBUG_TRANSPORT_ACTIONS LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory " "buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d", mChannel->getName().string(), mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount); +#endif return NO_MEMORY; } @@ -444,8 +446,10 @@ status_t InputPublisher::appendMotionSample( if (errno == EAGAIN) { // Only possible source of contention is the consumer having consumed (or being in the // process of consuming) the message and left the semaphore count at 0. +#if DEBUG_TRANSPORT_ACTIONS LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has " "already been consumed.", mChannel->getName().string()); +#endif return FAILED_TRANSACTION; } else { LOGE("channel '%s' publisher ~ Error %d in sem_trywait.",