replicant-frameworks_native/libs/ui/InputDispatcher.cpp
Jeff Brown d8816c3c4c Fix app switch latency optimization.
This optimization was broken due to recent changes in how ANRs are handled.

Change-Id: Ic99248a12755fadac8d4893e7d305b773e038d3d
2010-09-16 16:41:46 -07:00

3075 lines
114 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
// Log debug messages about input event throttling.
#define DEBUG_THROTTLING 0
// Log debug messages about input focus tracking.
#define DEBUG_FOCUS 0
// Log debug messages about the app switch latency optimization.
#define DEBUG_APP_SWITCH 0
#include <cutils/log.h>
#include <ui/InputDispatcher.h>
#include <ui/PowerManager.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
namespace android {
// Delay between reporting long touch events to the power manager.
const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
// Amount of time to allow for all pending events to be processed when an app switch
// key is on the way. This is used to preempt input dispatch and drop input events
// when an application takes too long to respond and the user has pressed an app switch key.
const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
// --- InputWindow ---
bool InputWindow::visibleFrameIntersects(const InputWindow* other) const {
return visibleFrameRight > other->visibleFrameLeft
&& visibleFrameLeft < other->visibleFrameRight
&& visibleFrameBottom > other->visibleFrameTop
&& visibleFrameTop < other->visibleFrameBottom;
}
bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
return x >= touchableAreaLeft && x <= touchableAreaRight
&& y >= touchableAreaTop && y <= touchableAreaBottom;
}
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy),
mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
mDispatchEnabled(true), mDispatchFrozen(false),
mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
mFocusedApplication(NULL),
mCurrentInputTargetsValid(false),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mLooper = new Looper(false);
mInboundQueue.headSentinel.refCount = -1;
mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN;
mInboundQueue.tailSentinel.refCount = -1;
mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL;
mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX;
mKeyRepeatState.lastKeyEntry = NULL;
int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
mThrottleState.lastDeviceId = -1;
#if DEBUG_THROTTLING
mThrottleState.originalSampleCount = 0;
LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
#endif
}
InputDispatcher::~InputDispatcher() {
{ // acquire lock
AutoMutex _l(mLock);
resetKeyRepeatLocked();
releasePendingEventLocked();
drainInboundQueueLocked();
}
while (mConnectionsByReceiveFd.size() != 0) {
unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
}
}
void InputDispatcher::dispatchOnce() {
nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int32_t timeoutMillis;
if (nextWakeupTime > currentTime) {
uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
timeout = (timeout + 999999LL) / 1000000LL;
timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
} else {
timeoutMillis = 0;
}
mLooper->pollOnce(timeoutMillis);
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
nsecs_t 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.
if (keyRepeatTimeout < 0) {
resetKeyRepeatLocked();
}
// If dispatching is disabled, drop all events in the queue.
if (! mDispatchEnabled) {
if (mPendingEvent || ! mInboundQueue.isEmpty()) {
LOGI("Dropping pending events because input dispatch is disabled.");
releasePendingEventLocked();
drainInboundQueueLocked();
}
return;
}
// If dispatching is frozen, do not process timeouts or try to deliver any new events.
if (mDispatchFrozen) {
#if DEBUG_FOCUS
LOGD("Dispatch frozen. Waiting some more.");
#endif
return;
}
// Optimize latency of app switches.
// Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
// been pressed. When it expires, we preempt dispatch and drop all other pending events.
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
if (mAppSwitchDueTime < *nextWakeupTime) {
*nextWakeupTime = mAppSwitchDueTime;
}
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
// Synthesize a key repeat if appropriate.
if (mKeyRepeatState.lastKeyEntry) {
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay);
} else {
if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
*nextWakeupTime = mKeyRepeatState.nextRepeatTime;
}
}
}
if (! mPendingEvent) {
return;
}
} else {
// Inbound queue has at least one entry.
EventEntry* entry = mInboundQueue.headSentinel.next;
// Throttle the entry if it is a move event and there are no
// other events behind it in the queue. Due to movement batching, additional
// samples may be appended to this event by the time the throttling timeout
// expires.
// TODO Make this smarter and consider throttling per device independently.
if (entry->type == EventEntry::TYPE_MOTION) {
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
int32_t deviceId = motionEntry->deviceId;
uint32_t source = motionEntry->source;
if (! isAppSwitchDue
&& motionEntry->next == & mInboundQueue.tailSentinel // exactly one event
&& motionEntry->action == AMOTION_EVENT_ACTION_MOVE
&& deviceId == mThrottleState.lastDeviceId
&& source == mThrottleState.lastSource) {
nsecs_t nextTime = mThrottleState.lastEventTime
+ mThrottleState.minTimeBetweenEvents;
if (currentTime < nextTime) {
// Throttle it!
#if DEBUG_THROTTLING
LOGD("Throttling - Delaying motion event for "
"device 0x%x, source 0x%08x by up to %0.3fms.",
deviceId, source, (nextTime - currentTime) * 0.000001);
#endif
if (nextTime < *nextWakeupTime) {
*nextWakeupTime = nextTime;
}
if (mThrottleState.originalSampleCount == 0) {
mThrottleState.originalSampleCount =
motionEntry->countSamples();
}
return;
}
}
#if DEBUG_THROTTLING
if (mThrottleState.originalSampleCount != 0) {
uint32_t count = motionEntry->countSamples();
LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
count - mThrottleState.originalSampleCount,
mThrottleState.originalSampleCount, count);
mThrottleState.originalSampleCount = 0;
}
#endif
mThrottleState.lastEventTime = entry->eventTime < currentTime
? entry->eventTime : currentTime;
mThrottleState.lastDeviceId = deviceId;
mThrottleState.lastSource = source;
}
mInboundQueue.dequeue(entry);
mPendingEvent = entry;
}
}
// Now we have an event to dispatch.
assert(mPendingEvent != NULL);
bool done = false;
switch (mPendingEvent->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
break;
}
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
bool appSwitchKey = isAppSwitchKey(typedEntry->keyCode);
bool dropEvent = isAppSwitchDue && ! appSwitchKey;
done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout, dropEvent,
nextWakeupTime);
if (done) {
if (dropEvent) {
LOGI("Dropped key because of pending overdue app switch.");
} else if (appSwitchKey) {
resetPendingAppSwitchLocked(true);
}
}
break;
}
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
bool dropEvent = isAppSwitchDue;
done = dispatchMotionLocked(currentTime, typedEntry, dropEvent, nextWakeupTime);
if (done) {
if (dropEvent) {
LOGI("Dropped motion because of pending overdue app switch.");
}
}
break;
}
default:
assert(false);
break;
}
if (done) {
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
switch (entry->type) {
case EventEntry::TYPE_KEY:
needWake |= detectPendingAppSwitchLocked(static_cast<KeyEntry*>(entry));
break;
}
return needWake;
}
bool InputDispatcher::isAppSwitchKey(int32_t keyCode) {
return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
}
bool InputDispatcher::isAppSwitchPendingLocked() {
return mAppSwitchDueTime != LONG_LONG_MAX;
}
bool InputDispatcher::detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry) {
if (inboundKeyEntry->action == AKEY_EVENT_ACTION_UP
&& ! (inboundKeyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
&& isAppSwitchKey(inboundKeyEntry->keyCode)
&& isEventFromReliableSourceLocked(inboundKeyEntry)) {
#if DEBUG_APP_SWITCH
LOGD("App switch is pending!");
#endif
mAppSwitchDueTime = inboundKeyEntry->eventTime + APP_SWITCH_TIMEOUT;
return true; // need wake
}
return false;
}
void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
mAppSwitchDueTime = LONG_LONG_MAX;
#if DEBUG_APP_SWITCH
if (handled) {
LOGD("App switch has arrived.");
} else {
LOGD("App switch was abandoned.");
}
#endif
}
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::drainInboundQueueLocked() {
while (! mInboundQueue.isEmpty()) {
EventEntry* entry = mInboundQueue.dequeueAtHead();
releaseInboundEventLocked(entry);
}
}
void InputDispatcher::releasePendingEventLocked() {
if (mPendingEvent) {
releaseInboundEventLocked(mPendingEvent);
mPendingEvent = NULL;
}
}
void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
if (entry->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
#if DEBUG_DISPATCH_CYCLE
LOGD("Inbound event was dropped. Setting injection result to failed.");
#endif
setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
}
mAllocator.releaseEventEntry(entry);
}
bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
return ! entry->isInjected()
|| entry->injectorUid == 0
|| mPolicy->checkInjectEventsPermissionNonReentrant(
entry->injectorPid, entry->injectorUid);
}
void InputDispatcher::resetKeyRepeatLocked() {
if (mKeyRepeatState.lastKeyEntry) {
mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
mKeyRepeatState.lastKeyEntry = NULL;
}
}
InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(
nsecs_t currentTime, nsecs_t keyRepeatDelay) {
KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
// Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
if (entry->refCount == 1) {
entry->recycle();
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;
}
entry->syntheticRepeat = true;
// Increment reference count since we keep a reference to the event in
// mKeyRepeatState.lastKeyEntry in addition to the one we return.
entry->refCount += 1;
if (entry->repeatCount == 1) {
entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
}
mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
return entry;
}
bool InputDispatcher::dispatchConfigurationChangedLocked(
nsecs_t currentTime, ConfigurationChangedEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
#endif
// Reset key repeating in case a keyboard device was added or removed or something.
resetKeyRepeatLocked();
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyConfigurationChangedInterruptible);
commandEntry->eventTime = entry->eventTime;
return true;
}
bool InputDispatcher::dispatchKeyLocked(
nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
bool dropEvent, nsecs_t* nextWakeupTime) {
// Give the policy a chance to intercept the key.
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
bool trusted;
if (! dropEvent && mFocusedWindow) {
trusted = checkInjectionPermission(mFocusedWindow,
entry->injectorPid, entry->injectorUid);
} else {
trusted = isEventFromReliableSourceLocked(entry);
}
if (trusted) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
if (! dropEvent && mFocusedWindow) {
commandEntry->inputChannel = mFocusedWindow->inputChannel;
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
} else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
resetTargetsLocked();
setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_SUCCEEDED);
return true;
}
// Clean up if dropping the event.
if (dropEvent) {
resetTargetsLocked();
setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
return true;
}
// Preprocessing.
if (! entry->dispatchInProgress) {
logOutboundKeyDetailsLocked("dispatchKey - ", entry);
if (entry->repeatCount == 0
&& 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 if (! entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
entry->dispatchInProgress = true;
resetTargetsLocked();
}
// Identify targets.
if (! mCurrentInputTargetsValid) {
InputWindow* window = NULL;
int32_t injectionResult = findFocusedWindowLocked(currentTime,
entry, nextWakeupTime, & window);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
setInjectionResultLocked(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true;
}
addMonitoringTargetsLocked();
commitTargetsLocked(window);
}
// Dispatch the key.
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
// Poke user activity.
pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
return true;
}
void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("%seventTime=%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",
prefix,
entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
entry->downTime);
#endif
}
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, bool dropEvent, nsecs_t* nextWakeupTime) {
// Clean up if dropping the event.
if (dropEvent) {
resetTargetsLocked();
setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
return true;
}
// Preprocessing.
if (! entry->dispatchInProgress) {
logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
entry->dispatchInProgress = true;
resetTargetsLocked();
}
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
// Identify targets.
if (! mCurrentInputTargetsValid) {
InputWindow* window = NULL;
int32_t injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
injectionResult = findTouchedWindowLocked(currentTime,
entry, nextWakeupTime, & window);
} else {
// Non touch event. (eg. trackball)
injectionResult = findFocusedWindowLocked(currentTime,
entry, nextWakeupTime, & window);
}
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
setInjectionResultLocked(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true;
}
addMonitoringTargetsLocked();
commitTargetsLocked(window);
}
// Dispatch the motion.
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
// Poke user activity.
int32_t eventType;
if (isPointerEvent) {
switch (entry->action) {
case AMOTION_EVENT_ACTION_DOWN:
eventType = POWER_MANAGER_TOUCH_EVENT;
break;
case AMOTION_EVENT_ACTION_UP:
eventType = POWER_MANAGER_TOUCH_UP_EVENT;
break;
default:
if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
eventType = POWER_MANAGER_TOUCH_EVENT;
} else {
eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
}
break;
}
} else {
eventType = POWER_MANAGER_BUTTON_EVENT;
}
pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
return true;
}
void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
"action=0x%x, flags=0x%x, "
"metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
prefix,
entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
entry->action, entry->flags,
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;
const 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, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
"orientation=%f",
i, entry->pointerIds[i],
sample->pointerCoords[i].x, sample->pointerCoords[i].y,
sample->pointerCoords[i].pressure, sample->pointerCoords[i].size,
sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor,
sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor,
sample->pointerCoords[i].orientation);
}
// 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
}
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
#if DEBUG_DISPATCH_CYCLE
LOGD("dispatchEventToCurrentInputTargets - "
"resumeWithAppendedMotionSample=%s",
toString(resumeWithAppendedMotionSample));
#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 = getConnectionIndexLocked(inputTarget.inputChannel);
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::resetTargetsLocked() {
mCurrentInputTargetsValid = false;
mCurrentInputTargets.clear();
mCurrentInputChannel.clear();
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
}
void InputDispatcher::commitTargetsLocked(const InputWindow* window) {
mCurrentInputWindowType = window->layoutParamsType;
mCurrentInputChannel = window->inputChannel;
mCurrentInputTargetsValid = true;
}
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry, const InputApplication* application, const InputWindow* window,
nsecs_t* nextWakeupTime) {
if (application == NULL && window == NULL) {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
#if DEBUG_FOCUS
LOGD("Waiting for system to become ready for input.");
#endif
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
mInputTargetWaitTimeoutExpired = false;
}
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
#if DEBUG_FOCUS
LOGD("Waiting for application to become ready for input: %s",
getApplicationWindowLabelLocked(application, window).string());
#endif
nsecs_t timeout = window ? window->dispatchingTimeout :
application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
}
}
if (mInputTargetWaitTimeoutExpired) {
return INPUT_EVENT_INJECTION_TIMED_OUT;
}
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
*nextWakeupTime = LONG_LONG_MIN;
return INPUT_EVENT_INJECTION_PENDING;
} else {
// Force poll loop to wake up when timeout is due.
if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
*nextWakeupTime = mInputTargetWaitTimeoutTime;
}
return INPUT_EVENT_INJECTION_PENDING;
}
}
void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
const sp<InputChannel>& inputChannel) {
if (newTimeout > 0) {
// Extend the timeout.
mInputTargetWaitTimeoutTime = now() + newTimeout;
} else {
// Give up.
mInputTargetWaitTimeoutExpired = true;
// Release the touch target.
releaseTouchedWindowLocked();
// Input state will not be realistic. Mark it out of sync.
if (inputChannel.get()) {
ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
connection->inputState.setOutOfSync();
}
}
}
}
nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
nsecs_t currentTime) {
if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
return currentTime - mInputTargetWaitStartTime;
}
return 0;
}
void InputDispatcher::resetANRTimeoutsLocked() {
#if DEBUG_FOCUS
LOGD("Resetting ANR timeouts.");
#endif
// Reset input target wait timeout.
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
}
int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
nsecs_t* nextWakeupTime, InputWindow** outWindow) {
*outWindow = NULL;
mCurrentInputTargets.clear();
int32_t injectionResult;
// If there is no currently focused window and no focused application
// then drop the event.
if (! mFocusedWindow) {
if (mFocusedApplication) {
#if DEBUG_FOCUS
LOGD("Waiting because there is no focused window but there is a "
"focused application that may eventually add a window: %s.",
getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
goto Unresponsive;
}
LOGI("Dropping event because there is no focused window or focused application.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
// Check permissions.
if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
goto Failed;
}
// If the currently focused window is paused then keep waiting.
if (mFocusedWindow->paused) {
#if DEBUG_FOCUS
LOGD("Waiting because focused window is paused.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, mFocusedWindow, nextWakeupTime);
goto Unresponsive;
}
// If the currently focused window is still working on previous events then keep waiting.
if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {
#if DEBUG_FOCUS
LOGD("Waiting because focused window still processing previous input.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, mFocusedWindow, nextWakeupTime);
goto Unresponsive;
}
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
*outWindow = mFocusedWindow;
addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
// Done.
Failed:
Unresponsive:
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatisticsLocked(currentTime, entry,
injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
LOGD("findFocusedWindow finished: injectionResult=%d, "
"timeSpendWaitingForApplication=%0.1fms",
injectionResult, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
nsecs_t* nextWakeupTime, InputWindow** outWindow) {
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
INJECTION_PERMISSION_GRANTED,
INJECTION_PERMISSION_DENIED
};
*outWindow = NULL;
mCurrentInputTargets.clear();
nsecs_t startTime = now();
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
//
// FIXME In the original code, screenWasOff could never be set to true.
// The reason is that the POLICY_FLAG_WOKE_HERE
// and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
// EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
// actually enqueued using the policyFlags that appeared in the final EV_SYN
// events upon which no preprocessing took place. So policyFlags was always 0.
// In the new native input dispatcher we're a bit more careful about event
// preprocessing so the touches we receive can actually have non-zero policyFlags.
// Unfortunately we obtain undesirable behavior.
//
// Here's what happens:
//
// When the device dims in anticipation of going to sleep, touches
// in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
// the device to brighten and reset the user activity timer.
// Touches on other windows (such as the launcher window)
// are dropped. Then after a moment, the device goes to sleep. Oops.
//
// Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
// instead of POLICY_FLAG_WOKE_HERE...
//
bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
int32_t action = entry->action;
// Update the touch state as needed based on the properties of the touch event.
int32_t injectionResult;
InjectionPermission injectionPermission;
if (action == AMOTION_EVENT_ACTION_DOWN) {
/* Case 1: ACTION_DOWN */
InputWindow* newTouchedWindow = NULL;
mTempTouchedOutsideTargets.clear();
int32_t x = int32_t(entry->firstSample.pointerCoords[0].x);
int32_t y = int32_t(entry->firstSample.pointerCoords[0].y);
InputWindow* topErrorWindow = NULL;
bool obscured = false;
// Traverse windows from front to back to find touched window and outside targets.
size_t numWindows = mWindows.size();
for (size_t i = 0; i < numWindows; i++) {
InputWindow* window = & mWindows.editItemAt(i);
int32_t flags = window->layoutParamsFlags;
if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
if (! topErrorWindow) {
topErrorWindow = window;
}
}
if (window->visible) {
if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
| InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
newTouchedWindow = window;
obscured = isWindowObscuredLocked(window);
}
break; // found touched window, exit window loop
}
}
if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) {
OutsideTarget outsideTarget;
outsideTarget.window = window;
outsideTarget.obscured = isWindowObscuredLocked(window);
mTempTouchedOutsideTargets.push(outsideTarget);
}
}
}
// If there is an error window but it is not taking focus (typically because
// it is invisible) then wait for it. Any other focused window may in
// fact be in ANR state.
if (topErrorWindow && newTouchedWindow != topErrorWindow) {
#if DEBUG_FOCUS
LOGD("Waiting because system error window is pending.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
NULL, NULL, nextWakeupTime);
injectionPermission = INJECTION_PERMISSION_UNKNOWN;
goto Unresponsive;
}
// If we did not find a touched window then fail.
if (! newTouchedWindow) {
if (mFocusedApplication) {
#if DEBUG_FOCUS
LOGD("Waiting because there is no touched window but there is a "
"focused application that may eventually add a new window: %s.",
getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
injectionPermission = INJECTION_PERMISSION_UNKNOWN;
goto Unresponsive;
}
LOGI("Dropping event because there is no touched window or focused application.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
injectionPermission = INJECTION_PERMISSION_UNKNOWN;
goto Failed;
}
// Check permissions.
if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
goto Failed;
}
// If the touched window is paused then keep waiting.
if (newTouchedWindow->paused) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("Waiting because touched window is paused.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
NULL, newTouchedWindow, nextWakeupTime);
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Unresponsive;
}
// If the touched window is still working on previous events then keep waiting.
if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
#if DEBUG_FOCUS
LOGD("Waiting because touched window still processing previous input.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
NULL, newTouchedWindow, nextWakeupTime);
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Unresponsive;
}
// Success! Update the touch dispatch state for real.
releaseTouchedWindowLocked();
mTouchedWindow = newTouchedWindow;
mTouchedWindowIsObscured = obscured;
if (newTouchedWindow->hasWallpaper) {
mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
}
} else {
/* Case 2: Everything but ACTION_DOWN */
// Check permissions.
if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
goto Failed;
}
// If the pointer is not currently down, then ignore the event.
if (! mTouchDown) {
LOGI("Dropping event because the pointer is not down.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Failed;
}
// If there is no currently touched window then fail.
if (! mTouchedWindow) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("Dropping event because there is no touched window to receive it.");
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Failed;
}
// If the touched window is paused then keep waiting.
if (mTouchedWindow->paused) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("Waiting because touched window is paused.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
NULL, mTouchedWindow, nextWakeupTime);
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Unresponsive;
}
// If the touched window is still working on previous events then keep waiting.
if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
#if DEBUG_FOCUS
LOGD("Waiting because touched window still processing previous input.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
NULL, mTouchedWindow, nextWakeupTime);
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Unresponsive;
}
}
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
injectionPermission = INJECTION_PERMISSION_GRANTED;
{
size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
for (size_t i = 0; i < numWallpaperWindows; i++) {
addWindowTargetLocked(mTouchedWallpaperWindows[i],
InputTarget::FLAG_WINDOW_IS_OBSCURED);
}
size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
for (size_t i = 0; i < numOutsideTargets; i++) {
const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
if (outsideTarget.obscured) {
outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
}
mTempTouchedOutsideTargets.clear();
int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
if (mTouchedWindowIsObscured) {
targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
addWindowTargetLocked(mTouchedWindow, targetFlags);
*outWindow = mTouchedWindow;
}
Failed:
// Check injection permission once and for all.
if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
entry->injectorPid, entry->injectorUid)) {
injectionPermission = INJECTION_PERMISSION_GRANTED;
} else {
injectionPermission = INJECTION_PERMISSION_DENIED;
}
}
// Update final pieces of touch state if the injector had permission.
if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
if (action == AMOTION_EVENT_ACTION_DOWN) {
if (mTouchDown) {
// This is weird. We got a down but we thought it was already down!
LOGW("Pointer down received while already down.");
} else {
mTouchDown = true;
}
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
// Since we failed to identify a target for this touch down, we may still
// be holding on to an earlier target from a previous touch down. Release it.
releaseTouchedWindowLocked();
}
} else if (action == AMOTION_EVENT_ACTION_UP) {
mTouchDown = false;
releaseTouchedWindowLocked();
}
} else {
LOGW("Not updating touch focus because injection was denied.");
}
Unresponsive:
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatisticsLocked(currentTime, entry,
injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
"timeSpendWaitingForApplication=%0.1fms",
injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
void InputDispatcher::releaseTouchedWindowLocked() {
mTouchedWindow = NULL;
mTouchedWindowIsObscured = false;
mTouchedWallpaperWindows.clear();
}
void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
mCurrentInputTargets.push();
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = window->inputChannel;
target.flags = targetFlags;
target.xOffset = - window->frameLeft;
target.yOffset = - window->frameTop;
}
void InputDispatcher::addMonitoringTargetsLocked() {
for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
mCurrentInputTargets.push();
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = mMonitoringChannels[i];
target.flags = 0;
target.xOffset = 0;
target.yOffset = 0;
}
}
bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
int32_t injectorPid, int32_t injectorUid) {
if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
if (! result) {
if (window) {
LOGW("Permission denied: injecting event from pid %d uid %d to window "
"with input channel %s owned by uid %d",
injectorPid, injectorUid, window->inputChannel->getName().string(),
window->ownerUid);
} else {
LOGW("Permission denied: injecting event from pid %d uid %d",
injectorPid, injectorUid);
}
return false;
}
}
return true;
}
bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
size_t numWindows = mWindows.size();
for (size_t i = 0; i < numWindows; i++) {
const InputWindow* other = & mWindows.itemAt(i);
if (other == window) {
break;
}
if (other->visible && window->visibleFrameIntersects(other)) {
return true;
}
}
return false;
}
bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) {
ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
return connection->outboundQueue.isEmpty();
} else {
return true;
}
}
String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application,
const InputWindow* window) {
if (application) {
if (window) {
String8 label(application->name);
label.append(" - ");
label.append(window->name);
return label;
} else {
return application->name;
}
} else if (window) {
return window->name;
} else {
return String8("<unknown application or window>");
}
}
void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
int32_t windowType, int32_t eventType) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doPokeUserActivityLockedInterruptible);
commandEntry->eventTime = eventTime;
commandEntry->windowType = windowType;
commandEntry->userActivityEventType = eventType;
}
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, "
"xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
connection->getInputChannelName(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
toString(resumeWithAppendedMotionSample));
#endif
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
if (connection->status != Connection::STATUS_NORMAL) {
LOGW("channel '%s' ~ Dropping event because the channel status is %s",
connection->getInputChannelName(), 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;
}
}
// Bring the input state back in line with reality in case it drifted off during an ANR.
if (connection->inputState.isOutOfSync()) {
mTempCancelationEvents.clear();
connection->inputState.synthesizeCancelationEvents(& mAllocator, mTempCancelationEvents);
connection->inputState.resetOutOfSync();
if (! mTempCancelationEvents.isEmpty()) {
LOGI("channel '%s' ~ Generated %d cancelation events to bring channel back in sync "
"with reality.",
connection->getInputChannelName(), mTempCancelationEvents.size());
for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
switch (cancelationEventEntry->type) {
case EventEntry::TYPE_KEY:
logOutboundKeyDetailsLocked(" ",
static_cast<KeyEntry*>(cancelationEventEntry));
break;
case EventEntry::TYPE_MOTION:
logOutboundMotionDetailsLocked(" ",
static_cast<MotionEntry*>(cancelationEventEntry));
break;
}
DispatchEntry* cancelationDispatchEntry =
mAllocator.obtainDispatchEntry(cancelationEventEntry,
0, inputTarget->xOffset, inputTarget->yOffset); // increments ref
connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
mAllocator.releaseEventEntry(cancelationEventEntry);
}
}
}
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
if (dispatchEntry->hasForegroundTarget()) {
eventEntry->pendingForegroundDispatches += 1;
}
// 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.headSentinel.next;
assert(! dispatchEntry->inProgress);
// Mark the dispatch entry as in progress.
dispatchEntry->inProgress = true;
// Update the connection's input state.
InputState::Consistency consistency = connection->inputState.trackEvent(
dispatchEntry->eventEntry);
#if FILTER_INPUT_EVENTS
// Filter out inconsistent sequences of input events.
// The input system may drop or inject events in a way that could violate implicit
// invariants on input state and potentially cause an application to crash
// or think that a key or pointer is stuck down. Technically we make no guarantees
// of consistency but it would be nice to improve on this where possible.
// XXX: This code is a proof of concept only. Not ready for prime time.
if (consistency == InputState::TOLERABLE) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's "
"current input state but that is likely to be tolerated by the application.",
connection->getInputChannelName());
#endif
} else if (consistency == InputState::BROKEN) {
LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's "
"current input state and that is likely to cause the application to crash.",
connection->getInputChannelName());
startNextDispatchCycleLocked(currentTime, connection);
return;
}
#endif
// 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;
int32_t flags = motionEntry->flags;
if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
action = AMOTION_EVENT_ACTION_OUTSIDE;
}
if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
action = AMOTION_EVENT_ACTION_CANCEL;
}
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
}
// 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;
}
// Set the X and Y offset depending on the input source.
float xOffset, yOffset;
if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
xOffset = dispatchEntry->xOffset;
yOffset = dispatchEntry->yOffset;
} else {
xOffset = 0.0f;
yOffset = 0.0f;
}
// Publish the motion event and the first motion sample.
status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
xOffset, 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.
connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
connection->lastDispatchTime = currentTime;
// 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;
}
// Notify other system components.
onDispatchCycleFinishedLocked(currentTime, connection);
// 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;
}
startNextDispatchCycleLocked(currentTime, connection);
}
void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
// Start the next dispatch cycle for this connection.
while (! connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.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();
if (dispatchEntry->hasForegroundTarget()) {
decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
}
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.
// 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::abortDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, bool broken) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
connection->getInputChannelName(), toString(broken));
#endif
// Input state will no longer be realistic.
connection->inputState.setOutOfSync();
// Clear the outbound queue.
drainOutboundQueueLocked(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_BROKEN;
// Notify other system components.
onDispatchCycleBrokenLocked(currentTime, connection);
}
}
}
void InputDispatcher::drainOutboundQueueLocked(Connection* connection) {
while (! connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
if (dispatchEntry->hasForegroundTarget()) {
decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
}
mAllocator.releaseDispatchEntry(dispatchEntry);
}
deactivateConnectionLocked(connection);
}
int 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 0; // remove the callback
}
nsecs_t currentTime = now();
sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
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 0; // remove the callback
}
if (! (events & ALOOPER_EVENT_INPUT)) {
LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
"events=0x%x", connection->getInputChannelName(), events);
return 1;
}
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 0; // remove the callback
}
d->finishDispatchCycleLocked(currentTime, connection);
d->runCommandsLockedInterruptible();
return 1;
} // release lock
}
void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
#if DEBUG_INBOUND_EVENT_DETAILS
LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
#endif
bool needWake;
{ // acquire lock
AutoMutex _l(mLock);
ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
needWake = enqueueInboundEventLocked(newEntry);
} // release lock
if (needWake) {
mLooper->wake();
}
}
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 needWake;
{ // acquire lock
AutoMutex _l(mLock);
int32_t repeatCount = 0;
KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
deviceId, source, policyFlags, action, flags, keyCode, scanCode,
metaState, repeatCount, downTime);
needWake = enqueueInboundEventLocked(newEntry);
} // release lock
if (needWake) {
mLooper->wake();
}
}
void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
uint32_t policyFlags, int32_t action, int32_t flags, 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, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, "
"xPrecision=%f, yPrecision=%f, downTime=%lld",
eventTime, deviceId, source, policyFlags, action, flags, 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, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
"orientation=%f",
i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y,
pointerCoords[i].pressure, pointerCoords[i].size,
pointerCoords[i].touchMajor, pointerCoords[i].touchMinor,
pointerCoords[i].toolMajor, pointerCoords[i].toolMinor,
pointerCoords[i].orientation);
}
#endif
bool needWake;
{ // 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.tailSentinel.prev;
entry != & mInboundQueue.headSentinel; 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
return; // done!
}
// STREAMING CASE
//
// There is no pending motion event (of any kind) for this device in the inbound queue.
// Search the outbound queue for the current foreground targets to find a dispatched
// motion event that is still in progress. If found, then, appen the new sample to
// that event and push it out to all current targets. The logic in
// prepareDispatchCycleLocked takes care of the case where some targets may
// already have consumed the motion event by starting a new dispatch cycle if needed.
if (mCurrentInputTargetsValid) {
for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets[i];
if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) {
// Skip non-foreground targets. We only want to stream if there is at
// least one foreground target whose dispatch is still in progress.
continue;
}
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex < 0) {
// Connection must no longer be valid.
continue;
}
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
if (connection->outboundQueue.isEmpty()) {
// This foreground target has an empty outbound queue.
continue;
}
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
if (! dispatchEntry->inProgress
|| dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
// No motion event is being dispatched.
continue;
}
MotionEntry* motionEntry = static_cast<MotionEntry*>(
dispatchEntry->eventEntry);
if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE
|| motionEntry->deviceId != deviceId
|| motionEntry->pointerCount != pointerCount
|| motionEntry->isInjected()) {
// The motion event is not compatible with this move.
continue;
}
// Hurray! This foreground target is currently dispatching a move event
// that we can stream onto. Append the motion sample and resume dispatch.
mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
#if DEBUG_BATCHING
LOGD("Appended motion sample onto batch for most recently dispatched "
"motion event for this device in the outbound queues. "
"Attempting to stream the motion sample.");
#endif
nsecs_t currentTime = now();
dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry,
true /*resumeWithAppendedMotionSample*/);
runCommandsLockedInterruptible();
return; // done!
}
}
NoBatchingOrStreaming:;
}
// Just enqueue a new motion event.
MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
xPrecision, yPrecision, downTime,
pointerCount, pointerIds, pointerCoords);
needWake = enqueueInboundEventLocked(newEntry);
} // release lock
if (needWake) {
mLooper->wake();
}
}
int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
#if DEBUG_INBOUND_EVENT_DETAILS
LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
"syncMode=%d, timeoutMillis=%d",
event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
#endif
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
EventEntry* injectedEntry;
bool needWake;
{ // acquire lock
AutoMutex _l(mLock);
injectedEntry = createEntryFromInjectedInputEventLocked(event);
if (! injectedEntry) {
return INPUT_EVENT_INJECTION_FAILED;
}
injectedEntry->refCount += 1;
injectedEntry->injectorPid = injectorPid;
injectedEntry->injectorUid = injectorUid;
if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
injectedEntry->injectionIsAsync = true;
}
needWake = enqueueInboundEventLocked(injectedEntry);
} // release lock
if (needWake) {
mLooper->wake();
}
int32_t injectionResult;
{ // acquire lock
AutoMutex _l(mLock);
if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
} else {
for (;;) {
injectionResult = injectedEntry->injectionResult;
if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
break;
}
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
LOGD("injectInputEvent - Timed out waiting for injection result "
"to become available.");
#endif
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
break;
}
mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
}
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
&& syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
while (injectedEntry->pendingForegroundDispatches != 0) {
#if DEBUG_INJECTION
LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
injectedEntry->pendingForegroundDispatches);
#endif
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
LOGD("injectInputEvent - Timed out waiting for pending foreground "
"dispatches to finish.");
#endif
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
break;
}
mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
}
}
}
mAllocator.releaseEventEntry(injectedEntry);
} // release lock
#if DEBUG_INJECTION
LOGD("injectInputEvent - Finished with result %d. "
"injectorPid=%d, injectorUid=%d",
injectionResult, injectorPid, injectorUid);
#endif
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
if (entry->injectionIsAsync) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
case INPUT_EVENT_INJECTION_SUCCEEDED:
LOGV("Asynchronous input event injection succeeded.");
break;
case INPUT_EVENT_INJECTION_FAILED:
LOGW("Asynchronous input event injection failed.");
break;
case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
LOGW("Asynchronous input event injection permission denied.");
break;
case INPUT_EVENT_INJECTION_TIMED_OUT:
LOGW("Asynchronous input event injection timed out.");
break;
}
}
entry->injectionResult = injectionResult;
mInjectionResultAvailableCondition.broadcast();
}
}
void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
entry->pendingForegroundDispatches -= 1;
if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
mInjectionSyncFinishedCondition.broadcast();
}
}
static bool isValidKeyAction(int32_t action) {
switch (action) {
case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP:
return true;
default:
return false;
}
}
static bool isValidMotionAction(int32_t action) {
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_MOVE:
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP:
case AMOTION_EVENT_ACTION_OUTSIDE:
return true;
default:
return false;
}
}
InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventLocked(
const InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
if (! isValidKeyAction(keyEvent->getAction())) {
LOGE("Dropping injected key event since it has invalid action code 0x%x",
keyEvent->getAction());
return NULL;
}
uint32_t policyFlags = POLICY_FLAG_INJECTED;
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);
if (! isValidMotionAction(motionEvent->getAction())) {
LOGE("Dropping injected motion event since it has invalid action code 0x%x.",
motionEvent->getAction());
return NULL;
}
if (motionEvent->getPointerCount() == 0
|| motionEvent->getPointerCount() > MAX_POINTERS) {
LOGE("Dropping injected motion event since it has an invalid pointer count %d.",
motionEvent->getPointerCount());
}
uint32_t policyFlags = POLICY_FLAG_INJECTED;
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->getFlags(),
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::setInputWindows(const Vector<InputWindow>& inputWindows) {
#if DEBUG_FOCUS
LOGD("setInputWindows");
#endif
{ // acquire lock
AutoMutex _l(mLock);
// Clear old window pointers but remember their associated channels.
mFocusedWindow = NULL;
sp<InputChannel> touchedWindowChannel;
if (mTouchedWindow) {
touchedWindowChannel = mTouchedWindow->inputChannel;
mTouchedWindow = NULL;
}
size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
if (numTouchedWallpapers != 0) {
for (size_t i = 0; i < numTouchedWallpapers; i++) {
mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
}
mTouchedWallpaperWindows.clear();
}
mWallpaperWindows.clear();
mWindows.clear();
// Loop over new windows and rebuild the necessary window pointers for
// tracking focus and touch.
mWindows.appendVector(inputWindows);
size_t numWindows = mWindows.size();
for (size_t i = 0; i < numWindows; i++) {
InputWindow* window = & mWindows.editItemAt(i);
if (window->hasFocus) {
mFocusedWindow = window;
}
if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
mWallpaperWindows.push(window);
for (size_t j = 0; j < numTouchedWallpapers; j++) {
if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
mTouchedWallpaperWindows.push(window);
}
}
}
if (window->inputChannel == touchedWindowChannel) {
mTouchedWindow = window;
}
}
mTempTouchedWallpaperChannels.clear();
#if DEBUG_FOCUS
logDispatchStateLocked();
#endif
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
#if DEBUG_FOCUS
LOGD("setFocusedApplication");
#endif
{ // acquire lock
AutoMutex _l(mLock);
releaseFocusedApplicationLocked();
if (inputApplication) {
mFocusedApplicationStorage = *inputApplication;
mFocusedApplication = & mFocusedApplicationStorage;
}
#if DEBUG_FOCUS
logDispatchStateLocked();
#endif
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
void InputDispatcher::releaseFocusedApplicationLocked() {
if (mFocusedApplication) {
mFocusedApplication = NULL;
mFocusedApplicationStorage.handle.clear();
}
}
void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
#if DEBUG_FOCUS
LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
#endif
bool changed;
{ // acquire lock
AutoMutex _l(mLock);
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && ! frozen) {
resetANRTimeoutsLocked();
}
mDispatchEnabled = enabled;
mDispatchFrozen = frozen;
changed = true;
} else {
changed = false;
}
#if DEBUG_FOCUS
logDispatchStateLocked();
#endif
} // release lock
if (changed) {
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
}
void InputDispatcher::logDispatchStateLocked() {
String8 dump;
dumpDispatchStateLocked(dump);
char* text = dump.lockBuffer(dump.size());
char* start = text;
while (*start != '\0') {
char* end = strchr(start, '\n');
if (*end == '\n') {
*(end++) = '\0';
}
LOGD("%s", start);
start = end;
}
}
void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled);
dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen);
if (mFocusedApplication) {
dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
mFocusedApplication->name.string(),
mFocusedApplication->dispatchingTimeout / 1000000.0);
} else {
dump.append(" focusedApplication: <null>\n");
}
dump.appendFormat(" focusedWindow: name='%s'\n",
mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
dump.appendFormat(" touchedWindow: name='%s', touchDown=%d\n",
mTouchedWindow != NULL ? mTouchedWindow->name.string() : "<null>",
mTouchDown);
for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
dump.appendFormat(" touchedWallpaperWindows[%d]: name='%s'\n",
i, mTouchedWallpaperWindows[i]->name.string());
}
for (size_t i = 0; i < mWindows.size(); i++) {
dump.appendFormat(" windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
"visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
"frame=[%d,%d][%d,%d], "
"visibleFrame=[%d,%d][%d,%d], "
"touchableArea=[%d,%d][%d,%d], "
"ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
i, mWindows[i].name.string(),
toString(mWindows[i].paused),
toString(mWindows[i].hasFocus),
toString(mWindows[i].hasWallpaper),
toString(mWindows[i].visible),
toString(mWindows[i].canReceiveKeys),
mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
mWindows[i].layer,
mWindows[i].frameLeft, mWindows[i].frameTop,
mWindows[i].frameRight, mWindows[i].frameBottom,
mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
mWindows[i].ownerPid, mWindows[i].ownerUid,
mWindows[i].dispatchingTimeout / 1000000.0);
}
for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
const sp<InputChannel>& channel = mMonitoringChannels[i];
dump.appendFormat(" monitoringChannel[%d]: '%s'\n",
i, channel->getName().string());
}
dump.appendFormat(" inboundQueue: length=%u", mInboundQueue.count());
for (size_t i = 0; i < mActiveConnections.size(); i++) {
const Connection* connection = mActiveConnections[i];
dump.appendFormat(" activeConnection[%d]: '%s', status=%s, outboundQueueLength=%u"
"inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
i, connection->getInputChannelName(), connection->getStatusLabel(),
connection->outboundQueue.count(),
toString(connection->inputState.isNeutral()),
toString(connection->inputState.isOutOfSync()));
}
if (isAppSwitchPendingLocked()) {
dump.appendFormat(" appSwitch: pending, due in %01.1fms\n",
(mAppSwitchDueTime - now()) / 1000000.0);
} else {
dump.append(" appSwitch: not pending\n");
}
}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
#if DEBUG_REGISTRATION
LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
toString(monitor));
#endif
{ // acquire lock
AutoMutex _l(mLock);
if (getConnectionIndexLocked(inputChannel) >= 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;
}
int32_t receiveFd = inputChannel->getReceivePipeFd();
mConnectionsByReceiveFd.add(receiveFd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
runCommandsLockedInterruptible();
} // release lock
return OK;
}
status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
#endif
{ // acquire lock
AutoMutex _l(mLock);
ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
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;
for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
if (mMonitoringChannels[i] == inputChannel) {
mMonitoringChannels.removeAt(i);
break;
}
}
mLooper->removeFd(inputChannel->getReceivePipeFd());
nsecs_t currentTime = now();
abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
runCommandsLockedInterruptible();
} // release lock
// Wake the poll loop because removing the connection may have changed the current
// synchronization state.
mLooper->wake();
return OK;
}
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
if (connection->inputChannel.get() == inputChannel.get()) {
return connectionIndex;
}
}
return -1;
}
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) {
}
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::onANRLocked(
nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
nsecs_t eventTime, nsecs_t waitStartTime) {
LOGI("Application is not responding: %s. "
"%01.1fms since event, %01.1fms since wait started",
getApplicationWindowLabelLocked(application, window).string(),
(currentTime - eventTime) / 1000000.0,
(currentTime - waitStartTime) / 1000000.0);
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
if (application) {
commandEntry->inputApplicationHandle = application->handle;
}
if (window) {
commandEntry->inputChannel = window->inputChannel;
}
}
void InputDispatcher::doNotifyConfigurationChangedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
mLock.lock();
}
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::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
nsecs_t newTimeout = mPolicy->notifyANR(
commandEntry->inputApplicationHandle, commandEntry->inputChannel);
mLock.lock();
resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
entry->downTime, entry->eventTime);
mLock.unlock();
bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
& mReusableKeyEvent, entry->policyFlags);
mLock.lock();
entry->interceptKeyResult = consumed
? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
: KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
mAllocator.releaseKeyEntry(entry);
}
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
commandEntry->userActivityEventType);
mLock.lock();
}
void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
// TODO Write some statistics about how long we spend waiting.
}
void InputDispatcher::dump(String8& dump) {
dumpDispatchStateLocked(dump);
}
// --- InputDispatcher::Queue ---
template <typename T>
uint32_t InputDispatcher::Queue<T>::count() const {
uint32_t result = 0;
for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) {
result += 1;
}
return result;
}
// --- 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->injectionIsAsync = false;
entry->injectorPid = -1;
entry->injectorUid = -1;
entry->pendingForegroundDispatches = 0;
}
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;
entry->syntheticRepeat = false;
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
return entry;
}
InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags,
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->flags = flags;
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,
int32_t targetFlags, float xOffset, float yOffset) {
DispatchEntry* entry = mDispatchEntryPool.alloc();
entry->eventEntry = eventEntry;
eventEntry->refCount += 1;
entry->targetFlags = targetFlags;
entry->xOffset = xOffset;
entry->yOffset = yOffset;
entry->inProgress = false;
entry->headMotionSample = NULL;
entry->tailMotionSample = NULL;
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::EventEntry ---
void InputDispatcher::EventEntry::recycle() {
injectionResult = INPUT_EVENT_INJECTION_PENDING;
dispatchInProgress = false;
pendingForegroundDispatches = 0;
}
// --- InputDispatcher::KeyEntry ---
void InputDispatcher::KeyEntry::recycle() {
EventEntry::recycle();
syntheticRepeat = false;
interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
}
// --- InputDispatcher::MotionEntry ---
uint32_t InputDispatcher::MotionEntry::countSamples() const {
uint32_t count = 1;
for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
count += 1;
}
return count;
}
// --- InputDispatcher::InputState ---
InputDispatcher::InputState::InputState() :
mIsOutOfSync(false) {
}
InputDispatcher::InputState::~InputState() {
}
bool InputDispatcher::InputState::isNeutral() const {
return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
}
bool InputDispatcher::InputState::isOutOfSync() const {
return mIsOutOfSync;
}
void InputDispatcher::InputState::setOutOfSync() {
if (! isNeutral()) {
mIsOutOfSync = true;
}
}
void InputDispatcher::InputState::resetOutOfSync() {
mIsOutOfSync = false;
}
InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent(
const EventEntry* entry) {
switch (entry->type) {
case EventEntry::TYPE_KEY:
return trackKey(static_cast<const KeyEntry*>(entry));
case EventEntry::TYPE_MOTION:
return trackMotion(static_cast<const MotionEntry*>(entry));
default:
return CONSISTENT;
}
}
InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey(
const KeyEntry* entry) {
int32_t action = entry->action;
for (size_t i = 0; i < mKeyMementos.size(); i++) {
KeyMemento& memento = mKeyMementos.editItemAt(i);
if (memento.deviceId == entry->deviceId
&& memento.source == entry->source
&& memento.keyCode == entry->keyCode
&& memento.scanCode == entry->scanCode) {
switch (action) {
case AKEY_EVENT_ACTION_UP:
mKeyMementos.removeAt(i);
if (isNeutral()) {
mIsOutOfSync = false;
}
return CONSISTENT;
case AKEY_EVENT_ACTION_DOWN:
return TOLERABLE;
default:
return BROKEN;
}
}
}
switch (action) {
case AKEY_EVENT_ACTION_DOWN: {
mKeyMementos.push();
KeyMemento& memento = mKeyMementos.editTop();
memento.deviceId = entry->deviceId;
memento.source = entry->source;
memento.keyCode = entry->keyCode;
memento.scanCode = entry->scanCode;
memento.downTime = entry->downTime;
return CONSISTENT;
}
default:
return BROKEN;
}
}
InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion(
const MotionEntry* entry) {
int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK;
for (size_t i = 0; i < mMotionMementos.size(); i++) {
MotionMemento& memento = mMotionMementos.editItemAt(i);
if (memento.deviceId == entry->deviceId
&& memento.source == entry->source) {
switch (action) {
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL:
mMotionMementos.removeAt(i);
if (isNeutral()) {
mIsOutOfSync = false;
}
return CONSISTENT;
case AMOTION_EVENT_ACTION_DOWN:
return TOLERABLE;
case AMOTION_EVENT_ACTION_POINTER_DOWN:
if (entry->pointerCount == memento.pointerCount + 1) {
memento.setPointers(entry);
return CONSISTENT;
}
return BROKEN;
case AMOTION_EVENT_ACTION_POINTER_UP:
if (entry->pointerCount == memento.pointerCount - 1) {
memento.setPointers(entry);
return CONSISTENT;
}
return BROKEN;
case AMOTION_EVENT_ACTION_MOVE:
if (entry->pointerCount == memento.pointerCount) {
return CONSISTENT;
}
return BROKEN;
default:
return BROKEN;
}
}
}
switch (action) {
case AMOTION_EVENT_ACTION_DOWN: {
mMotionMementos.push();
MotionMemento& memento = mMotionMementos.editTop();
memento.deviceId = entry->deviceId;
memento.source = entry->source;
memento.xPrecision = entry->xPrecision;
memento.yPrecision = entry->yPrecision;
memento.downTime = entry->downTime;
memento.setPointers(entry);
return CONSISTENT;
}
default:
return BROKEN;
}
}
void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
pointerCount = entry->pointerCount;
for (uint32_t i = 0; i < entry->pointerCount; i++) {
pointerIds[i] = entry->pointerIds[i];
pointerCoords[i] = entry->lastSample->pointerCoords[i];
}
}
void InputDispatcher::InputState::synthesizeCancelationEvents(
Allocator* allocator, Vector<EventEntry*>& outEvents) const {
for (size_t i = 0; i < mKeyMementos.size(); i++) {
const KeyMemento& memento = mKeyMementos.itemAt(i);
outEvents.push(allocator->obtainKeyEntry(now(),
memento.deviceId, memento.source, 0,
AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED,
memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
}
for (size_t i = 0; i < mMotionMementos.size(); i++) {
const MotionMemento& memento = mMotionMementos.itemAt(i);
outEvents.push(allocator->obtainMotionEntry(now(),
memento.deviceId, memento.source, 0,
AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
memento.xPrecision, memento.yPrecision, memento.downTime,
memento.pointerCount, memento.pointerIds, memento.pointerCoords));
}
}
void InputDispatcher::InputState::clear() {
mKeyMementos.clear();
mMotionMementos.clear();
mIsOutOfSync = false;
}
// --- InputDispatcher::Connection ---
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
}
InputDispatcher::Connection::~Connection() {
}
status_t InputDispatcher::Connection::initialize() {
return inputPublisher.initialize();
}
const char* InputDispatcher::Connection::getStatusLabel() const {
switch (status) {
case STATUS_NORMAL:
return "NORMAL";
case STATUS_BROKEN:
return "BROKEN";
case STATUS_ZOMBIE:
return "ZOMBIE";
default:
return "UNKNOWN";
}
}
InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
const EventEntry* eventEntry) const {
for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev;
dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) {
if (dispatchEntry->eventEntry == eventEntry) {
return dispatchEntry;
}
}
return NULL;
}
// --- InputDispatcher::CommandEntry ---
InputDispatcher::CommandEntry::CommandEntry() :
keyEntry(NULL) {
}
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