| // | 
 | // Copyright 2010 The Android Open Source Project | 
 | // | 
 | // The input dispatcher. | 
 | // | 
 | #define LOG_TAG "InputDispatcher" | 
 |  | 
 | //#define LOG_NDEBUG 0 | 
 |  | 
 | // Log detailed debug messages about each inbound event notification to the dispatcher. | 
 | #define DEBUG_INBOUND_EVENT_DETAILS 0 | 
 |  | 
 | // Log detailed debug messages about each outbound event processed by the dispatcher. | 
 | #define DEBUG_OUTBOUND_EVENT_DETAILS 0 | 
 |  | 
 | // Log debug messages about batching. | 
 | #define DEBUG_BATCHING 0 | 
 |  | 
 | // Log debug messages about the dispatch cycle. | 
 | #define DEBUG_DISPATCH_CYCLE 0 | 
 |  | 
 | // Log debug messages about registrations. | 
 | #define DEBUG_REGISTRATION 0 | 
 |  | 
 | // Log debug messages about performance statistics. | 
 | #define DEBUG_PERFORMANCE_STATISTICS 0 | 
 |  | 
 | // Log debug messages about input event injection. | 
 | #define DEBUG_INJECTION 0 | 
 |  | 
 | #include <cutils/log.h> | 
 | #include <ui/InputDispatcher.h> | 
 |  | 
 | #include <stddef.h> | 
 | #include <unistd.h> | 
 | #include <errno.h> | 
 | #include <limits.h> | 
 |  | 
 | namespace android { | 
 |  | 
 | // TODO, this needs to be somewhere else, perhaps in the policy | 
 | static inline bool isMovementKey(int32_t keyCode) { | 
 |     return keyCode == KEYCODE_DPAD_UP | 
 |             || keyCode == KEYCODE_DPAD_DOWN | 
 |             || keyCode == KEYCODE_DPAD_LEFT | 
 |             || keyCode == KEYCODE_DPAD_RIGHT; | 
 | } | 
 |  | 
 | static inline nsecs_t now() { | 
 |     return systemTime(SYSTEM_TIME_MONOTONIC); | 
 | } | 
 |  | 
 | // --- InputDispatcher --- | 
 |  | 
 | InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : | 
 |     mPolicy(policy) { | 
 |     mPollLoop = new PollLoop(false); | 
 |  | 
 |     mInboundQueue.head.refCount = -1; | 
 |     mInboundQueue.head.type = EventEntry::TYPE_SENTINEL; | 
 |     mInboundQueue.head.eventTime = LONG_LONG_MIN; | 
 |  | 
 |     mInboundQueue.tail.refCount = -1; | 
 |     mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL; | 
 |     mInboundQueue.tail.eventTime = LONG_LONG_MAX; | 
 |  | 
 |     mKeyRepeatState.lastKeyEntry = NULL; | 
 |  | 
 |     mCurrentInputTargetsValid = false; | 
 | } | 
 |  | 
 | InputDispatcher::~InputDispatcher() { | 
 |     resetKeyRepeatLocked(); | 
 |  | 
 |     while (mConnectionsByReceiveFd.size() != 0) { | 
 |         unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); | 
 |     } | 
 |  | 
 |     for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) { | 
 |         EventEntry* next = entry->next; | 
 |         mAllocator.releaseEventEntry(next); | 
 |         entry = next; | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::dispatchOnce() { | 
 |     nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); | 
 |  | 
 |     bool skipPoll = false; | 
 |     nsecs_t currentTime; | 
 |     nsecs_t nextWakeupTime = LONG_LONG_MAX; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |         currentTime = now(); | 
 |  | 
 |         // Reset the key repeat timer whenever we disallow key events, even if the next event | 
 |         // is not a key.  This is to ensure that we abort a key repeat if the device is just coming | 
 |         // out of sleep. | 
 |         // XXX we should handle resetting input state coming out of sleep more generally elsewhere | 
 |         if (keyRepeatTimeout < 0) { | 
 |             resetKeyRepeatLocked(); | 
 |         } | 
 |  | 
 |         // Detect and process timeouts for all connections and determine if there are any | 
 |         // synchronous event dispatches pending.  This step is entirely non-interruptible. | 
 |         bool hasPendingSyncTarget = false; | 
 |         size_t activeConnectionCount = mActiveConnections.size(); | 
 |         for (size_t i = 0; i < activeConnectionCount; i++) { | 
 |             Connection* connection = mActiveConnections.itemAt(i); | 
 |  | 
 |             if (connection->hasPendingSyncTarget()) { | 
 |                 hasPendingSyncTarget = true; | 
 |             } | 
 |  | 
 |             nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime; | 
 |             if (connectionTimeoutTime <= currentTime) { | 
 |                 mTimedOutConnections.add(connection); | 
 |             } else if (connectionTimeoutTime < nextWakeupTime) { | 
 |                 nextWakeupTime = connectionTimeoutTime; | 
 |             } | 
 |         } | 
 |  | 
 |         size_t timedOutConnectionCount = mTimedOutConnections.size(); | 
 |         for (size_t i = 0; i < timedOutConnectionCount; i++) { | 
 |             Connection* connection = mTimedOutConnections.itemAt(i); | 
 |             timeoutDispatchCycleLocked(currentTime, connection); | 
 |             skipPoll = true; | 
 |         } | 
 |         mTimedOutConnections.clear(); | 
 |  | 
 |         // If we don't have a pending sync target, then we can begin delivering a new event. | 
 |         // (Otherwise we wait for dispatch to complete for that target.) | 
 |         if (! hasPendingSyncTarget) { | 
 |             if (mInboundQueue.isEmpty()) { | 
 |                 if (mKeyRepeatState.lastKeyEntry) { | 
 |                     if (currentTime >= mKeyRepeatState.nextRepeatTime) { | 
 |                         processKeyRepeatLockedInterruptible(currentTime, keyRepeatTimeout); | 
 |                         skipPoll = true; | 
 |                     } else { | 
 |                         if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) { | 
 |                             nextWakeupTime = mKeyRepeatState.nextRepeatTime; | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } else { | 
 |                 // Inbound queue has at least one entry. | 
 |                 // Start processing it 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 = | 
 |                             static_cast<ConfigurationChangedEntry*>(entry); | 
 |                     processConfigurationChangedLockedInterruptible(currentTime, typedEntry); | 
 |                     break; | 
 |                 } | 
 |  | 
 |                 case EventEntry::TYPE_KEY: { | 
 |                     KeyEntry* typedEntry = static_cast<KeyEntry*>(entry); | 
 |                     processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout); | 
 |                     break; | 
 |                 } | 
 |  | 
 |                 case EventEntry::TYPE_MOTION: { | 
 |                     MotionEntry* typedEntry = static_cast<MotionEntry*>(entry); | 
 |                     processMotionLockedInterruptible(currentTime, typedEntry); | 
 |                     break; | 
 |                 } | 
 |  | 
 |                 default: | 
 |                     assert(false); | 
 |                     break; | 
 |                 } | 
 |  | 
 |                 // Dequeue and release the event entry that we just processed. | 
 |                 mInboundQueue.dequeue(entry); | 
 |                 mAllocator.releaseEventEntry(entry); | 
 |                 skipPoll = true; | 
 |             } | 
 |         } | 
 |  | 
 |         // 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. | 
 |     // Contents may have shifted during flight. | 
 |     if (skipPoll) { | 
 |         return; | 
 |     } | 
 |  | 
 |     // Wait for callback or timeout or wake. | 
 |     nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime); | 
 |     int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; | 
 |     mPollLoop->pollOnce(timeoutMillis); | 
 | } | 
 |  | 
 | bool InputDispatcher::runCommandsLockedInterruptible() { | 
 |     if (mCommandQueue.isEmpty()) { | 
 |         return false; | 
 |     } | 
 |  | 
 |     do { | 
 |         CommandEntry* commandEntry = mCommandQueue.dequeueAtHead(); | 
 |  | 
 |         Command command = commandEntry->command; | 
 |         (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible' | 
 |  | 
 |         commandEntry->connection.clear(); | 
 |         mAllocator.releaseCommandEntry(commandEntry); | 
 |     } while (! mCommandQueue.isEmpty()); | 
 |     return true; | 
 | } | 
 |  | 
 | InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { | 
 |     CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command); | 
 |     mCommandQueue.enqueueAtTail(commandEntry); | 
 |     return commandEntry; | 
 | } | 
 |  | 
 | void InputDispatcher::processConfigurationChangedLockedInterruptible( | 
 |         nsecs_t currentTime, ConfigurationChangedEntry* entry) { | 
 | #if DEBUG_OUTBOUND_EVENT_DETAILS | 
 |     LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime); | 
 | #endif | 
 |  | 
 |     // Reset key repeating in case a keyboard device was added or removed or something. | 
 |     resetKeyRepeatLocked(); | 
 |  | 
 |     mLock.unlock(); | 
 |  | 
 |     mPolicy->notifyConfigurationChanged(entry->eventTime); | 
 |  | 
 |     mLock.lock(); | 
 | } | 
 |  | 
 | void InputDispatcher::processKeyLockedInterruptible( | 
 |         nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) { | 
 | #if DEBUG_OUTBOUND_EVENT_DETAILS | 
 |     LOGD("processKey - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, " | 
 |             "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", | 
 |             entry->eventTime, entry->deviceId, entry->nature, entry->policyFlags, entry->action, | 
 |             entry->flags, entry->keyCode, entry->scanCode, entry->metaState, | 
 |             entry->downTime); | 
 | #endif | 
 |  | 
 |     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 | 
 |             // driver is automatically generating key repeats itself.  We take note of the | 
 |             // repeat here, but we disable our own next key repeat timer since it is clear that | 
 |             // we will not need to synthesize key repeats ourselves. | 
 |             entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; | 
 |             resetKeyRepeatLocked(); | 
 |             mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves | 
 |         } else { | 
 |             // Not a repeat.  Save key down state in case we do see a repeat later. | 
 |             resetKeyRepeatLocked(); | 
 |             mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; | 
 |         } | 
 |         mKeyRepeatState.lastKeyEntry = entry; | 
 |         entry->refCount += 1; | 
 |     } else { | 
 |         resetKeyRepeatLocked(); | 
 |     } | 
 |  | 
 |     identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); | 
 | } | 
 |  | 
 | void InputDispatcher::processKeyRepeatLockedInterruptible( | 
 |         nsecs_t currentTime, nsecs_t keyRepeatTimeout) { | 
 |     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<KeyEntry*>(queuedEntry); | 
 |             if (queuedKeyEntry->deviceId == entry->deviceId | 
 |                     && entry->action == KEY_EVENT_ACTION_UP) { | 
 |                 resetKeyRepeatLocked(); | 
 |                 return; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     // Synthesize a key repeat after the repeat timeout expired. | 
 |     // 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; | 
 |         entry->policyFlags = policyFlags; | 
 |         entry->repeatCount += 1; | 
 |     } else { | 
 |         KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime, | 
 |                 entry->deviceId, entry->nature, policyFlags, | 
 |                 entry->action, entry->flags, entry->keyCode, entry->scanCode, | 
 |                 entry->metaState, entry->repeatCount + 1, entry->downTime); | 
 |  | 
 |         mKeyRepeatState.lastKeyEntry = newEntry; | 
 |         mAllocator.releaseKeyEntry(entry); | 
 |  | 
 |         entry = newEntry; | 
 |     } | 
 |  | 
 |     if (entry->repeatCount == 1) { | 
 |         entry->flags |= KEY_EVENT_FLAG_LONG_PRESS; | 
 |     } | 
 |  | 
 |     mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout; | 
 |  | 
 | #if DEBUG_OUTBOUND_EVENT_DETAILS | 
 |     LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, " | 
 |             "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, " | 
 |             "repeatCount=%d, downTime=%lld", | 
 |             entry->eventTime, entry->deviceId, entry->nature, entry->policyFlags, | 
 |             entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, | 
 |             entry->repeatCount, entry->downTime); | 
 | #endif | 
 |  | 
 |     identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); | 
 | } | 
 |  | 
 | void InputDispatcher::processMotionLockedInterruptible( | 
 |         nsecs_t currentTime, MotionEntry* entry) { | 
 | #if DEBUG_OUTBOUND_EVENT_DETAILS | 
 |     LOGD("processMotion - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, " | 
 |             "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld", | 
 |             entry->eventTime, entry->deviceId, entry->nature, entry->policyFlags, entry->action, | 
 |             entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, | 
 |             entry->downTime); | 
 |  | 
 |     // Print the most recent sample that we have available, this may change due to batching. | 
 |     size_t sampleCount = 1; | 
 |     MotionSample* sample = & entry->firstSample; | 
 |     for (; sample->next != NULL; sample = sample->next) { | 
 |         sampleCount += 1; | 
 |     } | 
 |     for (uint32_t i = 0; i < entry->pointerCount; i++) { | 
 |         LOGD("  Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f", | 
 |                 i, entry->pointerIds[i], | 
 |                 sample->pointerCoords[i].x, | 
 |                 sample->pointerCoords[i].y, | 
 |                 sample->pointerCoords[i].pressure, | 
 |                 sample->pointerCoords[i].size); | 
 |     } | 
 |  | 
 |     // Keep in mind that due to batching, it is possible for the number of samples actually | 
 |     // dispatched to change before the application finally consumed them. | 
 |     if (entry->action == MOTION_EVENT_ACTION_MOVE) { | 
 |         LOGD("  ... Total movement samples currently batched %d ...", sampleCount); | 
 |     } | 
 | #endif | 
 |  | 
 |     identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry); | 
 | } | 
 |  | 
 | void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( | 
 |         nsecs_t currentTime, KeyEntry* entry) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("identifyInputTargetsAndDispatchKey"); | 
 | #endif | 
 |  | 
 |     entry->dispatchInProgress = true; | 
 |     mCurrentInputTargetsValid = false; | 
 |     mLock.unlock(); | 
 |  | 
 |     mReusableKeyEvent.initialize(entry->deviceId, entry->nature, entry->action, entry->flags, | 
 |             entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, | 
 |             entry->downTime, entry->eventTime); | 
 |  | 
 |     mCurrentInputTargets.clear(); | 
 |     int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent, | 
 |             entry->policyFlags, entry->injectorPid, entry->injectorUid, | 
 |             mCurrentInputTargets); | 
 |  | 
 |     mLock.lock(); | 
 |     mCurrentInputTargetsValid = true; | 
 |  | 
 |     setInjectionResultLocked(entry, injectionResult); | 
 |  | 
 |     if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { | 
 |         dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( | 
 |         nsecs_t currentTime, MotionEntry* entry) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("identifyInputTargetsAndDispatchMotion"); | 
 | #endif | 
 |  | 
 |     entry->dispatchInProgress = true; | 
 |     mCurrentInputTargetsValid = false; | 
 |     mLock.unlock(); | 
 |  | 
 |     mReusableMotionEvent.initialize(entry->deviceId, entry->nature, entry->action, | 
 |             entry->edgeFlags, entry->metaState, | 
 |             0, 0, entry->xPrecision, entry->yPrecision, | 
 |             entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds, | 
 |             entry->firstSample.pointerCoords); | 
 |  | 
 |     mCurrentInputTargets.clear(); | 
 |     int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent, | 
 |             entry->policyFlags, entry->injectorPid, entry->injectorUid, | 
 |             mCurrentInputTargets); | 
 |  | 
 |     mLock.lock(); | 
 |     mCurrentInputTargetsValid = true; | 
 |  | 
 |     setInjectionResultLocked(entry, injectionResult); | 
 |  | 
 |     if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { | 
 |         dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, | 
 |         EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("dispatchEventToCurrentInputTargets - " | 
 |             "resumeWithAppendedMotionSample=%s", | 
 |             resumeWithAppendedMotionSample ? "true" : "false"); | 
 | #endif | 
 |  | 
 |     assert(eventEntry->dispatchInProgress); // should already have been set to true | 
 |  | 
 |     for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { | 
 |         const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); | 
 |  | 
 |         ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey( | 
 |                 inputTarget.inputChannel->getReceivePipeFd()); | 
 |         if (connectionIndex >= 0) { | 
 |             sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); | 
 |             prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, | 
 |                     resumeWithAppendedMotionSample); | 
 |         } else { | 
 |             LOGW("Framework requested delivery of an input event to channel '%s' but it " | 
 |                     "is not registered with the input dispatcher.", | 
 |                     inputTarget.inputChannel->getName().string()); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, | 
 |         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, | 
 |         bool resumeWithAppendedMotionSample) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, " | 
 |             "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s", | 
 |             connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout, | 
 |             inputTarget->xOffset, inputTarget->yOffset, | 
 |             resumeWithAppendedMotionSample ? "true" : "false"); | 
 | #endif | 
 |  | 
 |     // Skip this event if the connection status is not normal. | 
 |     // We don't want to queue outbound events at all if the connection is broken or | 
 |     // not responding. | 
 |     if (connection->status != Connection::STATUS_NORMAL) { | 
 |         LOGV("channel '%s' ~ Dropping event because the channel status is %s", | 
 |                 connection->getStatusLabel()); | 
 |         return; | 
 |     } | 
 |  | 
 |     // Resume the dispatch cycle with a freshly appended motion sample. | 
 |     // First we check that the last dispatch entry in the outbound queue is for the same | 
 |     // motion event to which we appended the motion sample.  If we find such a dispatch | 
 |     // entry, and if it is currently in progress then we try to stream the new sample. | 
 |     bool wasEmpty = connection->outboundQueue.isEmpty(); | 
 |  | 
 |     if (! wasEmpty && resumeWithAppendedMotionSample) { | 
 |         DispatchEntry* motionEventDispatchEntry = | 
 |                 connection->findQueuedDispatchEntryForEvent(eventEntry); | 
 |         if (motionEventDispatchEntry) { | 
 |             // If the dispatch entry is not in progress, then we must be busy dispatching an | 
 |             // earlier event.  Not a problem, the motion event is on the outbound queue and will | 
 |             // be dispatched later. | 
 |             if (! motionEventDispatchEntry->inProgress) { | 
 | #if DEBUG_BATCHING | 
 |                 LOGD("channel '%s' ~ Not streaming because the motion event has " | 
 |                         "not yet been dispatched.  " | 
 |                         "(Waiting for earlier events to be consumed.)", | 
 |                         connection->getInputChannelName()); | 
 | #endif | 
 |                 return; | 
 |             } | 
 |  | 
 |             // If the dispatch entry is in progress but it already has a tail of pending | 
 |             // motion samples, then it must mean that the shared memory buffer filled up. | 
 |             // Not a problem, when this dispatch cycle is finished, we will eventually start | 
 |             // a new dispatch cycle to process the tail and that tail includes the newly | 
 |             // appended motion sample. | 
 |             if (motionEventDispatchEntry->tailMotionSample) { | 
 | #if DEBUG_BATCHING | 
 |                 LOGD("channel '%s' ~ Not streaming because no new samples can " | 
 |                         "be appended to the motion event in this dispatch cycle.  " | 
 |                         "(Waiting for next dispatch cycle to start.)", | 
 |                         connection->getInputChannelName()); | 
 | #endif | 
 |                 return; | 
 |             } | 
 |  | 
 |             // The dispatch entry is in progress and is still potentially open for streaming. | 
 |             // Try to stream the new motion sample.  This might fail if the consumer has already | 
 |             // consumed the motion event (or if the channel is broken). | 
 |             MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample; | 
 |             status_t status = connection->inputPublisher.appendMotionSample( | 
 |                     appendedMotionSample->eventTime, appendedMotionSample->pointerCoords); | 
 |             if (status == OK) { | 
 | #if DEBUG_BATCHING | 
 |                 LOGD("channel '%s' ~ Successfully streamed new motion sample.", | 
 |                         connection->getInputChannelName()); | 
 | #endif | 
 |                 return; | 
 |             } | 
 |  | 
 | #if DEBUG_BATCHING | 
 |             if (status == NO_MEMORY) { | 
 |                 LOGD("channel '%s' ~ Could not append motion sample to currently " | 
 |                         "dispatched move event because the shared memory buffer is full.  " | 
 |                         "(Waiting for next dispatch cycle to start.)", | 
 |                         connection->getInputChannelName()); | 
 |             } else if (status == status_t(FAILED_TRANSACTION)) { | 
 |                 LOGD("channel '%s' ~ Could not append motion sample to currently " | 
 |                         "dispatched move event because the event has already been consumed.  " | 
 |                         "(Waiting for next dispatch cycle to start.)", | 
 |                         connection->getInputChannelName()); | 
 |             } else { | 
 |                 LOGD("channel '%s' ~ Could not append motion sample to currently " | 
 |                         "dispatched move event due to an error, status=%d.  " | 
 |                         "(Waiting for next dispatch cycle to start.)", | 
 |                         connection->getInputChannelName(), status); | 
 |             } | 
 | #endif | 
 |             // Failed to stream.  Start a new tail of pending motion samples to dispatch | 
 |             // in the next cycle. | 
 |             motionEventDispatchEntry->tailMotionSample = appendedMotionSample; | 
 |             return; | 
 |         } | 
 |     } | 
 |  | 
 |     // This is a new event. | 
 |     // Enqueue a new dispatch entry onto the outbound queue for this connection. | 
 |     DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry); // increments ref | 
 |     dispatchEntry->targetFlags = inputTarget->flags; | 
 |     dispatchEntry->xOffset = inputTarget->xOffset; | 
 |     dispatchEntry->yOffset = inputTarget->yOffset; | 
 |     dispatchEntry->timeout = inputTarget->timeout; | 
 |     dispatchEntry->inProgress = false; | 
 |     dispatchEntry->headMotionSample = NULL; | 
 |     dispatchEntry->tailMotionSample = NULL; | 
 |  | 
 |     // 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 | 
 |     // to the list starting with the newly appended motion sample. | 
 |     if (resumeWithAppendedMotionSample) { | 
 | #if DEBUG_BATCHING | 
 |         LOGD("channel '%s' ~ Preparing a new dispatch cycle for additional motion samples " | 
 |                 "that cannot be streamed because the motion event has already been consumed.", | 
 |                 connection->getInputChannelName()); | 
 | #endif | 
 |         MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample; | 
 |         dispatchEntry->headMotionSample = appendedMotionSample; | 
 |     } | 
 |  | 
 |     // Enqueue the dispatch entry. | 
 |     connection->outboundQueue.enqueueAtTail(dispatchEntry); | 
 |  | 
 |     // If the outbound queue was previously empty, start the dispatch cycle going. | 
 |     if (wasEmpty) { | 
 |         activateConnectionLocked(connection.get()); | 
 |         startDispatchCycleLocked(currentTime, connection); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, | 
 |         const sp<Connection>& connection) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("channel '%s' ~ startDispatchCycle", | 
 |             connection->getInputChannelName()); | 
 | #endif | 
 |  | 
 |     assert(connection->status == Connection::STATUS_NORMAL); | 
 |     assert(! connection->outboundQueue.isEmpty()); | 
 |  | 
 |     DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; | 
 |     assert(! dispatchEntry->inProgress); | 
 |  | 
 |     // TODO throttle successive ACTION_MOVE motion events for the same device | 
 |     //      possible implementation could set a brief poll timeout here and resume starting the | 
 |     //      dispatch cycle when elapsed | 
 |  | 
 |     // Publish the event. | 
 |     status_t status; | 
 |     switch (dispatchEntry->eventEntry->type) { | 
 |     case EventEntry::TYPE_KEY: { | 
 |         KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); | 
 |  | 
 |         // Apply target flags. | 
 |         int32_t action = keyEntry->action; | 
 |         int32_t flags = keyEntry->flags; | 
 |         if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) { | 
 |             flags |= KEY_EVENT_FLAG_CANCELED; | 
 |         } | 
 |  | 
 |         // Publish the key event. | 
 |         status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->nature, | 
 |                 action, flags, keyEntry->keyCode, keyEntry->scanCode, | 
 |                 keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, | 
 |                 keyEntry->eventTime); | 
 |  | 
 |         if (status) { | 
 |             LOGE("channel '%s' ~ Could not publish key event, " | 
 |                     "status=%d", connection->getInputChannelName(), status); | 
 |             abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |             return; | 
 |         } | 
 |         break; | 
 |     } | 
 |  | 
 |     case EventEntry::TYPE_MOTION: { | 
 |         MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry); | 
 |  | 
 |         // Apply target flags. | 
 |         int32_t action = motionEntry->action; | 
 |         if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) { | 
 |             action = MOTION_EVENT_ACTION_OUTSIDE; | 
 |         } | 
 |         if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) { | 
 |             action = MOTION_EVENT_ACTION_CANCEL; | 
 |         } | 
 |  | 
 |         // If headMotionSample is non-NULL, then it points to the first new sample that we | 
 |         // were unable to dispatch during the previous cycle so we resume dispatching from | 
 |         // that point in the list of motion samples. | 
 |         // Otherwise, we just start from the first sample of the motion event. | 
 |         MotionSample* firstMotionSample = dispatchEntry->headMotionSample; | 
 |         if (! firstMotionSample) { | 
 |             firstMotionSample = & motionEntry->firstSample; | 
 |         } | 
 |  | 
 |         // Publish the motion event and the first motion sample. | 
 |         status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId, | 
 |                 motionEntry->nature, action, motionEntry->edgeFlags, motionEntry->metaState, | 
 |                 dispatchEntry->xOffset, dispatchEntry->yOffset, | 
 |                 motionEntry->xPrecision, motionEntry->yPrecision, | 
 |                 motionEntry->downTime, firstMotionSample->eventTime, | 
 |                 motionEntry->pointerCount, motionEntry->pointerIds, | 
 |                 firstMotionSample->pointerCoords); | 
 |  | 
 |         if (status) { | 
 |             LOGE("channel '%s' ~ Could not publish motion event, " | 
 |                     "status=%d", connection->getInputChannelName(), status); | 
 |             abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |             return; | 
 |         } | 
 |  | 
 |         // Append additional motion samples. | 
 |         MotionSample* nextMotionSample = firstMotionSample->next; | 
 |         for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { | 
 |             status = connection->inputPublisher.appendMotionSample( | 
 |                     nextMotionSample->eventTime, nextMotionSample->pointerCoords); | 
 |             if (status == NO_MEMORY) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |                     LOGD("channel '%s' ~ Shared memory buffer full.  Some motion samples will " | 
 |                             "be sent in the next dispatch cycle.", | 
 |                             connection->getInputChannelName()); | 
 | #endif | 
 |                 break; | 
 |             } | 
 |             if (status != OK) { | 
 |                 LOGE("channel '%s' ~ Could not append motion sample " | 
 |                         "for a reason other than out of memory, status=%d", | 
 |                         connection->getInputChannelName(), status); | 
 |                 abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |                 return; | 
 |             } | 
 |         } | 
 |  | 
 |         // Remember the next motion sample that we could not dispatch, in case we ran out | 
 |         // of space in the shared memory buffer. | 
 |         dispatchEntry->tailMotionSample = nextMotionSample; | 
 |         break; | 
 |     } | 
 |  | 
 |     default: { | 
 |         assert(false); | 
 |     } | 
 |     } | 
 |  | 
 |     // Send the dispatch signal. | 
 |     status = connection->inputPublisher.sendDispatchSignal(); | 
 |     if (status) { | 
 |         LOGE("channel '%s' ~ Could not send dispatch signal, status=%d", | 
 |                 connection->getInputChannelName(), status); | 
 |         abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |         return; | 
 |     } | 
 |  | 
 |     // Record information about the newly started dispatch cycle. | 
 |     dispatchEntry->inProgress = true; | 
 |  | 
 |     connection->lastEventTime = dispatchEntry->eventEntry->eventTime; | 
 |     connection->lastDispatchTime = currentTime; | 
 |  | 
 |     nsecs_t timeout = dispatchEntry->timeout; | 
 |     connection->setNextTimeoutTime(currentTime, timeout); | 
 |  | 
 |     // Notify other system components. | 
 |     onDispatchCycleStartedLocked(currentTime, connection); | 
 | } | 
 |  | 
 | void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, | 
 |         const sp<Connection>& connection) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, " | 
 |             "%01.1fms since dispatch", | 
 |             connection->getInputChannelName(), | 
 |             connection->getEventLatencyMillis(currentTime), | 
 |             connection->getDispatchLatencyMillis(currentTime)); | 
 | #endif | 
 |  | 
 |     if (connection->status == Connection::STATUS_BROKEN | 
 |             || connection->status == Connection::STATUS_ZOMBIE) { | 
 |         return; | 
 |     } | 
 |  | 
 |     // Clear the pending timeout. | 
 |     connection->nextTimeoutTime = LONG_LONG_MAX; | 
 |  | 
 |     if (connection->status == Connection::STATUS_NOT_RESPONDING) { | 
 |         // Recovering from an ANR. | 
 |         connection->status = Connection::STATUS_NORMAL; | 
 |  | 
 |         // Notify other system components. | 
 |         onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/); | 
 |     } else { | 
 |         // Normal finish.  Not much to do here. | 
 |  | 
 |         // Notify other system components. | 
 |         onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/); | 
 |     } | 
 |  | 
 |     // Reset the publisher since the event has been consumed. | 
 |     // We do this now so that the publisher can release some of its internal resources | 
 |     // while waiting for the next dispatch cycle to begin. | 
 |     status_t status = connection->inputPublisher.reset(); | 
 |     if (status) { | 
 |         LOGE("channel '%s' ~ Could not reset publisher, status=%d", | 
 |                 connection->getInputChannelName(), status); | 
 |         abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |         return; | 
 |     } | 
 |  | 
 |     // Start the next dispatch cycle for this connection. | 
 |     while (! connection->outboundQueue.isEmpty()) { | 
 |         DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; | 
 |         if (dispatchEntry->inProgress) { | 
 |              // Finish or resume current event in progress. | 
 |             if (dispatchEntry->tailMotionSample) { | 
 |                 // We have a tail of undispatched motion samples. | 
 |                 // Reuse the same DispatchEntry and start a new cycle. | 
 |                 dispatchEntry->inProgress = false; | 
 |                 dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample; | 
 |                 dispatchEntry->tailMotionSample = NULL; | 
 |                 startDispatchCycleLocked(currentTime, connection); | 
 |                 return; | 
 |             } | 
 |             // Finished. | 
 |             connection->outboundQueue.dequeueAtHead(); | 
 |             mAllocator.releaseDispatchEntry(dispatchEntry); | 
 |         } else { | 
 |             // If the head is not in progress, then we must have already dequeued the in | 
 |             // progress event, which means we actually aborted it (due to ANR). | 
 |             // So just start the next event for this connection. | 
 |             startDispatchCycleLocked(currentTime, connection); | 
 |             return; | 
 |         } | 
 |     } | 
 |  | 
 |     // Outbound queue is empty, deactivate the connection. | 
 |     deactivateConnectionLocked(connection.get()); | 
 | } | 
 |  | 
 | void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, | 
 |         const sp<Connection>& connection) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("channel '%s' ~ timeoutDispatchCycle", | 
 |             connection->getInputChannelName()); | 
 | #endif | 
 |  | 
 |     if (connection->status != Connection::STATUS_NORMAL) { | 
 |         return; | 
 |     } | 
 |  | 
 |     // Enter the not responding state. | 
 |     connection->status = Connection::STATUS_NOT_RESPONDING; | 
 |     connection->lastANRTime = currentTime; | 
 |  | 
 |     // Notify other system components. | 
 |     // This enqueues a command which will eventually either call | 
 |     // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked. | 
 |     onDispatchCycleANRLocked(currentTime, connection); | 
 | } | 
 |  | 
 | void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, | 
 |         const sp<Connection>& connection, nsecs_t newTimeout) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked", | 
 |             connection->getInputChannelName()); | 
 | #endif | 
 |  | 
 |     if (connection->status != Connection::STATUS_NOT_RESPONDING) { | 
 |         return; | 
 |     } | 
 |  | 
 |     // Resume normal dispatch. | 
 |     connection->status = Connection::STATUS_NORMAL; | 
 |     connection->setNextTimeoutTime(currentTime, newTimeout); | 
 | } | 
 |  | 
 | void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, | 
 |         const sp<Connection>& connection, bool broken) { | 
 | #if DEBUG_DISPATCH_CYCLE | 
 |     LOGD("channel '%s' ~ abortDispatchCycle - broken=%s", | 
 |             connection->getInputChannelName(), broken ? "true" : "false"); | 
 | #endif | 
 |  | 
 |     // Clear the pending timeout. | 
 |     connection->nextTimeoutTime = LONG_LONG_MAX; | 
 |  | 
 |     // Clear the outbound queue. | 
 |     if (! connection->outboundQueue.isEmpty()) { | 
 |         do { | 
 |             DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); | 
 |             mAllocator.releaseDispatchEntry(dispatchEntry); | 
 |         } while (! connection->outboundQueue.isEmpty()); | 
 |  | 
 |         deactivateConnectionLocked(connection.get()); | 
 |     } | 
 |  | 
 |     // Handle the case where the connection appears to be unrecoverably broken. | 
 |     // Ignore already broken or zombie connections. | 
 |     if (broken) { | 
 |         if (connection->status == Connection::STATUS_NORMAL | 
 |                 || connection->status == Connection::STATUS_NOT_RESPONDING) { | 
 |             connection->status = Connection::STATUS_BROKEN; | 
 |  | 
 |             // Notify other system components. | 
 |             onDispatchCycleBrokenLocked(currentTime, connection); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { | 
 |     InputDispatcher* d = static_cast<InputDispatcher*>(data); | 
 |  | 
 |     { // acquire lock | 
 |         AutoMutex _l(d->mLock); | 
 |  | 
 |         ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd); | 
 |         if (connectionIndex < 0) { | 
 |             LOGE("Received spurious receive callback for unknown input channel.  " | 
 |                     "fd=%d, events=0x%x", receiveFd, events); | 
 |             return false; // remove the callback | 
 |         } | 
 |  | 
 |         nsecs_t currentTime = now(); | 
 |  | 
 |         sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); | 
 |         if (events & (POLLERR | POLLHUP | POLLNVAL)) { | 
 |             LOGE("channel '%s' ~ Consumer closed input channel or an error occurred.  " | 
 |                     "events=0x%x", connection->getInputChannelName(), events); | 
 |             d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |             d->runCommandsLockedInterruptible(); | 
 |             return false; // remove the callback | 
 |         } | 
 |  | 
 |         if (! (events & POLLIN)) { | 
 |             LOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  " | 
 |                     "events=0x%x", connection->getInputChannelName(), events); | 
 |             return true; | 
 |         } | 
 |  | 
 |         status_t status = connection->inputPublisher.receiveFinishedSignal(); | 
 |         if (status) { | 
 |             LOGE("channel '%s' ~ Failed to receive finished signal.  status=%d", | 
 |                     connection->getInputChannelName(), status); | 
 |             d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |             d->runCommandsLockedInterruptible(); | 
 |             return false; // remove the callback | 
 |         } | 
 |  | 
 |         d->finishDispatchCycleLocked(currentTime, connection); | 
 |         d->runCommandsLockedInterruptible(); | 
 |         return true; | 
 |     } // release lock | 
 | } | 
 |  | 
 | void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { | 
 | #if DEBUG_INBOUND_EVENT_DETAILS | 
 |     LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime); | 
 | #endif | 
 |  | 
 |     bool wasEmpty; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime); | 
 |  | 
 |         wasEmpty = mInboundQueue.isEmpty(); | 
 |         mInboundQueue.enqueueAtTail(newEntry); | 
 |     } // release lock | 
 |  | 
 |     if (wasEmpty) { | 
 |         mPollLoop->wake(); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) { | 
 | #if DEBUG_INBOUND_EVENT_DETAILS | 
 |     LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime); | 
 | #endif | 
 |  | 
 |     // Remove movement keys from the queue from most recent to least recent, stopping at the | 
 |     // first non-movement key. | 
 |     // TODO: Include a detailed description of why we do this... | 
 |  | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) { | 
 |             EventEntry* prev = entry->prev; | 
 |  | 
 |             if (entry->type == EventEntry::TYPE_KEY) { | 
 |                 KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); | 
 |                 if (isMovementKey(keyEntry->keyCode)) { | 
 |                     LOGV("Dropping movement key during app switch: keyCode=%d, action=%d", | 
 |                             keyEntry->keyCode, keyEntry->action); | 
 |                     mInboundQueue.dequeue(keyEntry); | 
 |  | 
 |                     setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); | 
 |  | 
 |                     mAllocator.releaseKeyEntry(keyEntry); | 
 |                 } else { | 
 |                     // stop at last non-movement key | 
 |                     break; | 
 |                 } | 
 |             } | 
 |  | 
 |             entry = prev; | 
 |         } | 
 |     } // release lock | 
 | } | 
 |  | 
 | void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature, | 
 |         uint32_t policyFlags, int32_t action, int32_t flags, | 
 |         int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { | 
 | #if DEBUG_INBOUND_EVENT_DETAILS | 
 |     LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, " | 
 |             "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", | 
 |             eventTime, deviceId, nature, policyFlags, action, flags, | 
 |             keyCode, scanCode, metaState, downTime); | 
 | #endif | 
 |  | 
 |     bool wasEmpty; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         int32_t repeatCount = 0; | 
 |         KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime, | 
 |                 deviceId, nature, policyFlags, action, flags, keyCode, scanCode, | 
 |                 metaState, repeatCount, downTime); | 
 |  | 
 |         wasEmpty = mInboundQueue.isEmpty(); | 
 |         mInboundQueue.enqueueAtTail(newEntry); | 
 |     } // release lock | 
 |  | 
 |     if (wasEmpty) { | 
 |         mPollLoop->wake(); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t nature, | 
 |         uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags, | 
 |         uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, | 
 |         float xPrecision, float yPrecision, nsecs_t downTime) { | 
 | #if DEBUG_INBOUND_EVENT_DETAILS | 
 |     LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, " | 
 |             "action=0x%x, metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, " | 
 |             "downTime=%lld", | 
 |             eventTime, deviceId, nature, policyFlags, action, metaState, edgeFlags, | 
 |             xPrecision, yPrecision, downTime); | 
 |     for (uint32_t i = 0; i < pointerCount; i++) { | 
 |         LOGD("  Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f", | 
 |                 i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y, | 
 |                 pointerCoords[i].pressure, pointerCoords[i].size); | 
 |     } | 
 | #endif | 
 |  | 
 |     bool wasEmpty; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         // Attempt batching and streaming of move events. | 
 |         if (action == MOTION_EVENT_ACTION_MOVE) { | 
 |             // BATCHING CASE | 
 |             // | 
 |             // Try to append a move sample to the tail of the inbound queue for this device. | 
 |             // Give up if we encounter a non-move motion event for this device since that | 
 |             // means we cannot append any new samples until a new motion event has started. | 
 |             for (EventEntry* entry = mInboundQueue.tail.prev; | 
 |                     entry != & mInboundQueue.head; entry = entry->prev) { | 
 |                 if (entry->type != EventEntry::TYPE_MOTION) { | 
 |                     // Keep looking for motion events. | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); | 
 |                 if (motionEntry->deviceId != deviceId) { | 
 |                     // Keep looking for this device. | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 if (motionEntry->action != MOTION_EVENT_ACTION_MOVE | 
 |                         || motionEntry->pointerCount != pointerCount | 
 |                         || motionEntry->isInjected()) { | 
 |                     // Last motion event in the queue for this device 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 | 
 |  | 
 |                 // Sanity check for special case because dispatch is interruptible. | 
 |                 // The dispatch logic is partially interruptible and releases its lock while | 
 |                 // identifying targets.  However, as soon as the targets have been identified, | 
 |                 // the dispatcher proceeds to write a dispatch entry into all relevant outbound | 
 |                 // queues and then promptly removes the motion entry from the queue. | 
 |                 // | 
 |                 // Consequently, we should never observe the case where the inbound queue contains | 
 |                 // an in-progress motion entry unless the current input targets are invalid | 
 |                 // (currently being computed).  Check for this! | 
 |                 assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid)); | 
 |  | 
 |                 return; // done! | 
 |             } | 
 |  | 
 |             // STREAMING CASE | 
 |             // | 
 |             // There is no pending motion event (of any kind) for this device in the inbound queue. | 
 |             // Search the outbound queues for a synchronously dispatched motion event for this | 
 |             // device.  If found, then we append the new sample to that event and then try to | 
 |             // push it out to all current targets.  It is possible that some targets will already | 
 |             // have consumed the motion event.  This case is automatically handled by the | 
 |             // logic in prepareDispatchCycleLocked by tracking where resumption takes place. | 
 |             // | 
 |             // The reason we look for a synchronously dispatched motion event is because we | 
 |             // want to be sure that no other motion events have been dispatched since the move. | 
 |             // It's also convenient because it means that the input targets are still valid. | 
 |             // This code could be improved to support streaming of asynchronously dispatched | 
 |             // motion events (which might be significantly more efficient) but it may become | 
 |             // a little more complicated as a result. | 
 |             // | 
 |             // Note: This code crucially depends on the invariant that an outbound queue always | 
 |             //       contains at most one synchronous event and it is always last (but it might | 
 |             //       not be first!). | 
 |             if (mCurrentInputTargetsValid) { | 
 |                 for (size_t i = 0; i < mActiveConnections.size(); i++) { | 
 |                     Connection* connection = mActiveConnections.itemAt(i); | 
 |                     if (! connection->outboundQueue.isEmpty()) { | 
 |                         DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev; | 
 |                         if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) { | 
 |                             if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { | 
 |                                 goto NoBatchingOrStreaming; | 
 |                             } | 
 |  | 
 |                             MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>( | 
 |                                     dispatchEntry->eventEntry); | 
 |                             if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE | 
 |                                     || syncedMotionEntry->deviceId != deviceId | 
 |                                     || syncedMotionEntry->pointerCount != pointerCount | 
 |                                     || syncedMotionEntry->isInjected()) { | 
 |                                 goto NoBatchingOrStreaming; | 
 |                             } | 
 |  | 
 |                             // Found synced move entry.  Append sample and resume dispatch. | 
 |                             mAllocator.appendMotionSample(syncedMotionEntry, eventTime, | 
 |                                     pointerCoords); | 
 |     #if DEBUG_BATCHING | 
 |                             LOGD("Appended motion sample onto batch for most recent synchronously " | 
 |                                     "dispatched motion event for this device in the outbound queues."); | 
 |     #endif | 
 |                             nsecs_t currentTime = now(); | 
 |                             dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry, | 
 |                                     true /*resumeWithAppendedMotionSample*/); | 
 |  | 
 |                             runCommandsLockedInterruptible(); | 
 |                             return; // done! | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 | NoBatchingOrStreaming:; | 
 |         } | 
 |  | 
 |         // Just enqueue a new motion event. | 
 |         MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime, | 
 |                 deviceId, nature, policyFlags, action, metaState, edgeFlags, | 
 |                 xPrecision, yPrecision, downTime, | 
 |                 pointerCount, pointerIds, pointerCoords); | 
 |  | 
 |         wasEmpty = mInboundQueue.isEmpty(); | 
 |         mInboundQueue.enqueueAtTail(newEntry); | 
 |     } // release lock | 
 |  | 
 |     if (wasEmpty) { | 
 |         mPollLoop->wake(); | 
 |     } | 
 | } | 
 |  | 
 | int32_t InputDispatcher::injectInputEvent(const InputEvent* event, | 
 |         int32_t injectorPid, int32_t injectorUid, bool sync, 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); | 
 | #endif | 
 |  | 
 |     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); | 
 |  | 
 |     EventEntry* injectedEntry; | 
 |     bool wasEmpty; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         injectedEntry = createEntryFromInputEventLocked(event); | 
 |         injectedEntry->refCount += 1; | 
 |         injectedEntry->injectorPid = injectorPid; | 
 |         injectedEntry->injectorUid = injectorUid; | 
 |  | 
 |         wasEmpty = mInboundQueue.isEmpty(); | 
 |         mInboundQueue.enqueueAtTail(injectedEntry); | 
 |  | 
 |     } // release lock | 
 |  | 
 |     if (wasEmpty) { | 
 |         mPollLoop->wake(); | 
 |     } | 
 |  | 
 |     int32_t injectionResult; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         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) { | 
 |                     injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; | 
 |                     break; | 
 |                 } | 
 |  | 
 |                 mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout); | 
 |             } | 
 |         } | 
 |  | 
 |         mAllocator.releaseEventEntry(injectedEntry); | 
 |     } // release lock | 
 |  | 
 |     return injectionResult; | 
 | } | 
 |  | 
 | void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) { | 
 |     if (entry->isInjected()) { | 
 | #if DEBUG_INJECTION | 
 |         LOGD("Setting input event injection result to %d.  " | 
 |                 "injectorPid=%d, injectorUid=%d", | 
 |                  injectionResult, entry->injectorPid, entry->injectorUid); | 
 | #endif | 
 |  | 
 |         entry->injectionResult = injectionResult; | 
 |         mInjectionResultAvailableCondition.broadcast(); | 
 |     } | 
 | } | 
 |  | 
 | bool InputDispatcher::isFullySynchronizedLocked() { | 
 |     return mInboundQueue.isEmpty() && mActiveConnections.isEmpty(); | 
 | } | 
 |  | 
 | InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( | 
 |         const InputEvent* event) { | 
 |     switch (event->getType()) { | 
 |     case INPUT_EVENT_TYPE_KEY: { | 
 |         const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); | 
 |         uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events | 
 |  | 
 |         KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(), | 
 |                 keyEvent->getDeviceId(), keyEvent->getNature(), policyFlags, | 
 |                 keyEvent->getAction(), keyEvent->getFlags(), | 
 |                 keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), | 
 |                 keyEvent->getRepeatCount(), keyEvent->getDownTime()); | 
 |         return keyEntry; | 
 |     } | 
 |  | 
 |     case INPUT_EVENT_TYPE_MOTION: { | 
 |         const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); | 
 |         uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events | 
 |  | 
 |         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); | 
 |         const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); | 
 |         size_t pointerCount = motionEvent->getPointerCount(); | 
 |  | 
 |         MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes, | 
 |                 motionEvent->getDeviceId(), motionEvent->getNature(), policyFlags, | 
 |                 motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(), | 
 |                 motionEvent->getXPrecision(), motionEvent->getYPrecision(), | 
 |                 motionEvent->getDownTime(), uint32_t(pointerCount), | 
 |                 motionEvent->getPointerIds(), samplePointerCoords); | 
 |         for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { | 
 |             sampleEventTimes += 1; | 
 |             samplePointerCoords += pointerCount; | 
 |             mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords); | 
 |         } | 
 |         return motionEntry; | 
 |     } | 
 |  | 
 |     default: | 
 |         assert(false); | 
 |         return NULL; | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::resetKeyRepeatLocked() { | 
 |     if (mKeyRepeatState.lastKeyEntry) { | 
 |         mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); | 
 |         mKeyRepeatState.lastKeyEntry = NULL; | 
 |     } | 
 | } | 
 |  | 
 | 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>& inputChannel) { | 
 | #if DEBUG_REGISTRATION | 
 |     LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string()); | 
 | #endif | 
 |  | 
 |     int receiveFd; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         receiveFd = inputChannel->getReceivePipeFd(); | 
 |         if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) { | 
 |             LOGW("Attempted to register already registered input channel '%s'", | 
 |                     inputChannel->getName().string()); | 
 |             return BAD_VALUE; | 
 |         } | 
 |  | 
 |         sp<Connection> connection = new Connection(inputChannel); | 
 |         status_t status = connection->initialize(); | 
 |         if (status) { | 
 |             LOGE("Failed to initialize input publisher for input channel '%s', status=%d", | 
 |                     inputChannel->getName().string(), status); | 
 |             return status; | 
 |         } | 
 |  | 
 |         mConnectionsByReceiveFd.add(receiveFd, connection); | 
 |  | 
 |         runCommandsLockedInterruptible(); | 
 |     } // release lock | 
 |  | 
 |     mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); | 
 |     return OK; | 
 | } | 
 |  | 
 | status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) { | 
 | #if DEBUG_REGISTRATION | 
 |     LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string()); | 
 | #endif | 
 |  | 
 |     int32_t receiveFd; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         receiveFd = inputChannel->getReceivePipeFd(); | 
 |         ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd); | 
 |         if (connectionIndex < 0) { | 
 |             LOGW("Attempted to unregister already unregistered input channel '%s'", | 
 |                     inputChannel->getName().string()); | 
 |             return BAD_VALUE; | 
 |         } | 
 |  | 
 |         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); | 
 |         mConnectionsByReceiveFd.removeItemsAt(connectionIndex); | 
 |  | 
 |         connection->status = Connection::STATUS_ZOMBIE; | 
 |  | 
 |         nsecs_t currentTime = now(); | 
 |         abortDispatchCycleLocked(currentTime, connection, true /*broken*/); | 
 |  | 
 |         runCommandsLockedInterruptible(); | 
 |     } // release lock | 
 |  | 
 |     mPollLoop->removeCallback(receiveFd); | 
 |  | 
 |     // Wake the poll loop because removing the connection may have changed the current | 
 |     // synchronization state. | 
 |     mPollLoop->wake(); | 
 |     return OK; | 
 | } | 
 |  | 
 | void InputDispatcher::activateConnectionLocked(Connection* connection) { | 
 |     for (size_t i = 0; i < mActiveConnections.size(); i++) { | 
 |         if (mActiveConnections.itemAt(i) == connection) { | 
 |             return; | 
 |         } | 
 |     } | 
 |     mActiveConnections.add(connection); | 
 | } | 
 |  | 
 | void InputDispatcher::deactivateConnectionLocked(Connection* connection) { | 
 |     for (size_t i = 0; i < mActiveConnections.size(); i++) { | 
 |         if (mActiveConnections.itemAt(i) == connection) { | 
 |             mActiveConnections.removeAt(i); | 
 |             return; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::onDispatchCycleStartedLocked( | 
 |         nsecs_t currentTime, const sp<Connection>& connection) { | 
 | } | 
 |  | 
 | void InputDispatcher::onDispatchCycleFinishedLocked( | 
 |         nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) { | 
 |     if (recoveredFromANR) { | 
 |         LOGI("channel '%s' ~ Recovered from ANR.  %01.1fms since event, " | 
 |                 "%01.1fms since dispatch, %01.1fms since ANR", | 
 |                 connection->getInputChannelName(), | 
 |                 connection->getEventLatencyMillis(currentTime), | 
 |                 connection->getDispatchLatencyMillis(currentTime), | 
 |                 connection->getANRLatencyMillis(currentTime)); | 
 |  | 
 |         CommandEntry* commandEntry = postCommandLocked( | 
 |                 & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible); | 
 |         commandEntry->connection = connection; | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::onDispatchCycleANRLocked( | 
 |         nsecs_t currentTime, const sp<Connection>& connection) { | 
 |     LOGI("channel '%s' ~ Not responding!  %01.1fms since event, %01.1fms since dispatch", | 
 |             connection->getInputChannelName(), | 
 |             connection->getEventLatencyMillis(currentTime), | 
 |             connection->getDispatchLatencyMillis(currentTime)); | 
 |  | 
 |     CommandEntry* commandEntry = postCommandLocked( | 
 |             & InputDispatcher::doNotifyInputChannelANRLockedInterruptible); | 
 |     commandEntry->connection = connection; | 
 | } | 
 |  | 
 | void InputDispatcher::onDispatchCycleBrokenLocked( | 
 |         nsecs_t currentTime, const sp<Connection>& connection) { | 
 |     LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", | 
 |             connection->getInputChannelName()); | 
 |  | 
 |     CommandEntry* commandEntry = postCommandLocked( | 
 |             & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); | 
 |     commandEntry->connection = connection; | 
 | } | 
 |  | 
 | void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( | 
 |         CommandEntry* commandEntry) { | 
 |     sp<Connection> connection = commandEntry->connection; | 
 |  | 
 |     if (connection->status != Connection::STATUS_ZOMBIE) { | 
 |         mLock.unlock(); | 
 |  | 
 |         mPolicy->notifyInputChannelBroken(connection->inputChannel); | 
 |  | 
 |         mLock.lock(); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::doNotifyInputChannelANRLockedInterruptible( | 
 |         CommandEntry* commandEntry) { | 
 |     sp<Connection> connection = commandEntry->connection; | 
 |  | 
 |     if (connection->status != Connection::STATUS_ZOMBIE) { | 
 |         mLock.unlock(); | 
 |  | 
 |         nsecs_t newTimeout; | 
 |         bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout); | 
 |  | 
 |         mLock.lock(); | 
 |  | 
 |         nsecs_t currentTime = now(); | 
 |         if (resume) { | 
 |             resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); | 
 |         } else { | 
 |             abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( | 
 |         CommandEntry* commandEntry) { | 
 |     sp<Connection> connection = commandEntry->connection; | 
 |  | 
 |     if (connection->status != Connection::STATUS_ZOMBIE) { | 
 |         mLock.unlock(); | 
 |  | 
 |         mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel); | 
 |  | 
 |         mLock.lock(); | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | // --- InputDispatcher::Allocator --- | 
 |  | 
 | InputDispatcher::Allocator::Allocator() { | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type, | 
 |         nsecs_t eventTime) { | 
 |     entry->type = type; | 
 |     entry->refCount = 1; | 
 |     entry->dispatchInProgress = false; | 
 |     entry->eventTime = eventTime; | 
 |     entry->injectionResult = INPUT_EVENT_INJECTION_PENDING; | 
 |     entry->injectorPid = -1; | 
 |     entry->injectorUid = -1; | 
 | } | 
 |  | 
 | InputDispatcher::ConfigurationChangedEntry* | 
 | InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) { | 
 |     ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc(); | 
 |     initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime); | 
 |     return entry; | 
 | } | 
 |  | 
 | InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime, | 
 |         int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, | 
 |         int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, | 
 |         int32_t repeatCount, nsecs_t downTime) { | 
 |     KeyEntry* entry = mKeyEntryPool.alloc(); | 
 |     initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime); | 
 |  | 
 |     entry->deviceId = deviceId; | 
 |     entry->nature = nature; | 
 |     entry->policyFlags = policyFlags; | 
 |     entry->action = action; | 
 |     entry->flags = flags; | 
 |     entry->keyCode = keyCode; | 
 |     entry->scanCode = scanCode; | 
 |     entry->metaState = metaState; | 
 |     entry->repeatCount = repeatCount; | 
 |     entry->downTime = downTime; | 
 |     return entry; | 
 | } | 
 |  | 
 | InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime, | 
 |         int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, | 
 |         int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision, | 
 |         nsecs_t downTime, uint32_t pointerCount, | 
 |         const int32_t* pointerIds, const PointerCoords* pointerCoords) { | 
 |     MotionEntry* entry = mMotionEntryPool.alloc(); | 
 |     initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime); | 
 |  | 
 |     entry->eventTime = eventTime; | 
 |     entry->deviceId = deviceId; | 
 |     entry->nature = nature; | 
 |     entry->policyFlags = policyFlags; | 
 |     entry->action = action; | 
 |     entry->metaState = metaState; | 
 |     entry->edgeFlags = edgeFlags; | 
 |     entry->xPrecision = xPrecision; | 
 |     entry->yPrecision = yPrecision; | 
 |     entry->downTime = downTime; | 
 |     entry->pointerCount = pointerCount; | 
 |     entry->firstSample.eventTime = eventTime; | 
 |     entry->firstSample.next = NULL; | 
 |     entry->lastSample = & entry->firstSample; | 
 |     for (uint32_t i = 0; i < pointerCount; i++) { | 
 |         entry->pointerIds[i] = pointerIds[i]; | 
 |         entry->firstSample.pointerCoords[i] = pointerCoords[i]; | 
 |     } | 
 |     return entry; | 
 | } | 
 |  | 
 | InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( | 
 |         EventEntry* eventEntry) { | 
 |     DispatchEntry* entry = mDispatchEntryPool.alloc(); | 
 |     entry->eventEntry = eventEntry; | 
 |     eventEntry->refCount += 1; | 
 |     return entry; | 
 | } | 
 |  | 
 | InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) { | 
 |     CommandEntry* entry = mCommandEntryPool.alloc(); | 
 |     entry->command = command; | 
 |     return entry; | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) { | 
 |     switch (entry->type) { | 
 |     case EventEntry::TYPE_CONFIGURATION_CHANGED: | 
 |         releaseConfigurationChangedEntry(static_cast<ConfigurationChangedEntry*>(entry)); | 
 |         break; | 
 |     case EventEntry::TYPE_KEY: | 
 |         releaseKeyEntry(static_cast<KeyEntry*>(entry)); | 
 |         break; | 
 |     case EventEntry::TYPE_MOTION: | 
 |         releaseMotionEntry(static_cast<MotionEntry*>(entry)); | 
 |         break; | 
 |     default: | 
 |         assert(false); | 
 |         break; | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::releaseConfigurationChangedEntry( | 
 |         ConfigurationChangedEntry* entry) { | 
 |     entry->refCount -= 1; | 
 |     if (entry->refCount == 0) { | 
 |         mConfigurationChangeEntryPool.free(entry); | 
 |     } else { | 
 |         assert(entry->refCount > 0); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) { | 
 |     entry->refCount -= 1; | 
 |     if (entry->refCount == 0) { | 
 |         mKeyEntryPool.free(entry); | 
 |     } else { | 
 |         assert(entry->refCount > 0); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) { | 
 |     entry->refCount -= 1; | 
 |     if (entry->refCount == 0) { | 
 |         for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) { | 
 |             MotionSample* next = sample->next; | 
 |             mMotionSamplePool.free(sample); | 
 |             sample = next; | 
 |         } | 
 |         mMotionEntryPool.free(entry); | 
 |     } else { | 
 |         assert(entry->refCount > 0); | 
 |     } | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) { | 
 |     releaseEventEntry(entry->eventEntry); | 
 |     mDispatchEntryPool.free(entry); | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) { | 
 |     mCommandEntryPool.free(entry); | 
 | } | 
 |  | 
 | void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, | 
 |         nsecs_t eventTime, const PointerCoords* pointerCoords) { | 
 |     MotionSample* sample = mMotionSamplePool.alloc(); | 
 |     sample->eventTime = eventTime; | 
 |     uint32_t pointerCount = motionEntry->pointerCount; | 
 |     for (uint32_t i = 0; i < pointerCount; i++) { | 
 |         sample->pointerCoords[i] = pointerCoords[i]; | 
 |     } | 
 |  | 
 |     sample->next = NULL; | 
 |     motionEntry->lastSample->next = sample; | 
 |     motionEntry->lastSample = sample; | 
 | } | 
 |  | 
 | // --- InputDispatcher::Connection --- | 
 |  | 
 | InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : | 
 |         status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel), | 
 |         nextTimeoutTime(LONG_LONG_MAX), | 
 |         lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX), | 
 |         lastANRTime(LONG_LONG_MAX) { | 
 | } | 
 |  | 
 | InputDispatcher::Connection::~Connection() { | 
 | } | 
 |  | 
 | status_t InputDispatcher::Connection::initialize() { | 
 |     return inputPublisher.initialize(); | 
 | } | 
 |  | 
 | void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) { | 
 |     nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX; | 
 | } | 
 |  | 
 | const char* InputDispatcher::Connection::getStatusLabel() const { | 
 |     switch (status) { | 
 |     case STATUS_NORMAL: | 
 |         return "NORMAL"; | 
 |  | 
 |     case STATUS_BROKEN: | 
 |         return "BROKEN"; | 
 |  | 
 |     case STATUS_NOT_RESPONDING: | 
 |         return "NOT_RESPONDING"; | 
 |  | 
 |     case STATUS_ZOMBIE: | 
 |         return "ZOMBIE"; | 
 |  | 
 |     default: | 
 |         return "UNKNOWN"; | 
 |     } | 
 | } | 
 |  | 
 | InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent( | 
 |         const EventEntry* eventEntry) const { | 
 |     for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev; | 
 |             dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) { | 
 |         if (dispatchEntry->eventEntry == eventEntry) { | 
 |             return dispatchEntry; | 
 |         } | 
 |     } | 
 |     return NULL; | 
 | } | 
 |  | 
 | // --- InputDispatcher::CommandEntry --- | 
 |  | 
 | InputDispatcher::CommandEntry::CommandEntry() { | 
 | } | 
 |  | 
 | InputDispatcher::CommandEntry::~CommandEntry() { | 
 | } | 
 |  | 
 |  | 
 | // --- InputDispatcherThread --- | 
 |  | 
 | InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) : | 
 |         Thread(/*canCallJava*/ true), mDispatcher(dispatcher) { | 
 | } | 
 |  | 
 | InputDispatcherThread::~InputDispatcherThread() { | 
 | } | 
 |  | 
 | bool InputDispatcherThread::threadLoop() { | 
 |     mDispatcher->dispatchOnce(); | 
 |     return true; | 
 | } | 
 |  | 
 | } // namespace android |