replicant-frameworks_native/libs/ui/InputDispatcher.cpp
Jeff Brown 5c1ed84a2d Add support for new input sources.
Added several new coordinate values to MotionEvents to capture
touch major/minor area, tool major/minor area and orientation.

Renamed NDK input constants per convention.

Added InputDevice class in Java which will eventually provide
useful information about available input devices.

Added APIs for manufacturing new MotionEvent objects with multiple
pointers and all necessary coordinate data.

Fixed a bug in the input dispatcher where it could get stuck with
a pointer down forever.

Fixed a bug in the WindowManager where the input window list could
end up containing stale removed windows.

Fixed a bug in the WindowManager where the input channel was being
removed only after the final animation transition had taken place
which caused spurious WINDOW DIED log messages to be printed.

Change-Id: Ie55084da319b20aad29b28a0499b8dd98bb5da68
2010-07-15 18:32:33 -07:00

1712 lines
66 KiB
C++

//
// 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 == AKEYCODE_DPAD_UP
|| keyCode == AKEYCODE_DPAD_DOWN
|| keyCode == AKEYCODE_DPAD_LEFT
|| keyCode == AKEYCODE_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, source=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->source, entry->policyFlags, entry->action,
entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
entry->downTime);
#endif
if (entry->action == AKEY_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 == AKEY_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->source, 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 |= AKEY_EVENT_FLAG_LONG_PRESS;
}
mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout;
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=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->source, 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, source=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->source, 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 == AMOTION_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->source, 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->source, 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 |= AKEY_EVENT_FLAG_CANCELED;
}
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
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 = AMOTION_EVENT_ACTION_OUTSIDE;
}
if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
action = AMOTION_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->source, 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 source,
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, source=0x%x, policyFlags=0x%x, action=0x%x, "
"flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
eventTime, deviceId, source, 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, source, 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 source,
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, source=0x%x, policyFlags=0x%x, "
"action=0x%x, metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, "
"downTime=%lld",
eventTime, deviceId, source, 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 == AMOTION_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 != AMOTION_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 != AMOTION_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, source, 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 AINPUT_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->getSource(), policyFlags,
keyEvent->getAction(), keyEvent->getFlags(),
keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
keyEvent->getRepeatCount(), keyEvent->getDownTime());
return keyEntry;
}
case AINPUT_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->getSource(), 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 source, 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->source = source;
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 source, 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->source = source;
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