Native input event dispatching.

Target identification is now fully native.
Fixed a couple of minor issues related to input injection.
Native input enabled by default, can be disabled by setting
WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH to false.

Change-Id: I7edf66ed3e987cc9306ad4743ac57a116af452ff
This commit is contained in:
Jeff Brown 2010-06-22 01:27:15 -07:00
parent a84687252b
commit 50de30a523
8 changed files with 142 additions and 46 deletions

View File

@ -87,6 +87,9 @@ enum {
// Indicates that the screen was dim when the event was received and the event
// should brighten the device.
POLICY_FLAG_BRIGHT_HERE = 0x20000000,
// Indicates that the dispatcher should call back into the policy before dispatching. */
POLICY_FLAG_INTERCEPT_DISPATCH = 0x40000000,
};
/*

View File

@ -126,21 +126,21 @@ public:
/* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
virtual nsecs_t getKeyRepeatTimeout() = 0;
/* Gets the input targets for a key event.
/* Waits for key event input targets to become available.
* If the event is being injected, injectorPid and injectorUid should specify the
* process id and used id of the injecting application, otherwise they should both
* be -1.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0;
/* Gets the input targets for a motion event.
/* Waits for motion event targets to become available.
* If the event is being injected, injectorPid and injectorUid should specify the
* process id and used id of the injecting application, otherwise they should both
* be -1.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0;
};
@ -186,6 +186,16 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
/* Preempts input dispatch in progress by making pending synchronous
* dispatches asynchronous instead. This method is generally called during a focus
* transition from one application to the next so as to enable the new application
* to start receiving input as soon as possible without having to wait for the
* old application to finish up.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual void preemptInputDispatch() = 0;
/* Registers or unregister input channels that may be used as targets for input events.
*
* These methods may be called on any thread (usually by the input manager).
@ -233,6 +243,8 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
virtual void preemptInputDispatch();
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);

View File

@ -87,6 +87,14 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
/* Preempts input dispatch in progress by making pending synchronous
* dispatches asynchronous instead. This method is generally called during a focus
* transition from one application to the next so as to enable the new application
* to start receiving input as soon as possible without having to wait for the
* old application to finish up.
*/
virtual void preemptInputDispatch() = 0;
/* Gets input device configuration. */
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
@ -130,6 +138,8 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
virtual void preemptInputDispatch();
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const;

View File

@ -361,7 +361,11 @@ public:
// The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
// passes through the dispatch pipeline.
ACTION_BRIGHT_HERE = 0x00000008
ACTION_BRIGHT_HERE = 0x00000008,
// The input dispatcher should add POLICY_FLAG_INTERCEPT_DISPATCH to the policy flags
// it passed through the dispatch pipeline.
ACTION_INTERCEPT_DISPATCH = 0x00000010
};
/* Describes a virtual key. */

View File

@ -8,25 +8,25 @@
//#define LOG_NDEBUG 0
// Log detailed debug messages about each inbound event notification to the dispatcher.
#define DEBUG_INBOUND_EVENT_DETAILS 1
#define DEBUG_INBOUND_EVENT_DETAILS 0
// Log detailed debug messages about each outbound event processed by the dispatcher.
#define DEBUG_OUTBOUND_EVENT_DETAILS 1
#define DEBUG_OUTBOUND_EVENT_DETAILS 0
// Log debug messages about batching.
#define DEBUG_BATCHING 1
#define DEBUG_BATCHING 0
// Log debug messages about the dispatch cycle.
#define DEBUG_DISPATCH_CYCLE 1
#define DEBUG_DISPATCH_CYCLE 0
// Log debug messages about registrations.
#define DEBUG_REGISTRATION 1
#define DEBUG_REGISTRATION 0
// Log debug messages about performance statistics.
#define DEBUG_PERFORMANCE_STATISTICS 1
#define DEBUG_PERFORMANCE_STATISTICS 0
// Log debug messages about input event injection.
#define DEBUG_INJECTION 1
#define DEBUG_INJECTION 0
#include <cutils/log.h>
#include <ui/InputDispatcher.h>
@ -249,9 +249,7 @@ void InputDispatcher::processKeyLockedInterruptible(
entry->downTime);
#endif
// TODO: Poke user activity.
if (entry->action == KEY_EVENT_ACTION_DOWN) {
if (entry->action == KEY_EVENT_ACTION_DOWN && ! entry->isInjected()) {
if (mKeyRepeatState.lastKeyEntry
&& mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
// We have seen two identical key downs in a row which indicates that the device
@ -277,14 +275,24 @@ void InputDispatcher::processKeyLockedInterruptible(
void InputDispatcher::processKeyRepeatLockedInterruptible(
nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
// TODO Old WindowManagerServer code sniffs the input queue for following key up
// events and drops the repeat if one is found. We should do something similar.
// One good place to do it is in notifyKey as soon as the key up enters the
// inbound event queue.
KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
// Search the inbound queue for a key up corresponding to this device.
// It doesn't make sense to generate a key repeat event if the key is already up.
for (EventEntry* queuedEntry = mInboundQueue.head.next;
queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
if (queuedEntry->type == EventEntry::TYPE_KEY) {
KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
if (queuedKeyEntry->deviceId == entry->deviceId
&& entry->action == KEY_EVENT_ACTION_UP) {
resetKeyRepeatLocked();
return;
}
}
}
// Synthesize a key repeat after the repeat timeout expired.
// We reuse the previous key entry if otherwise unreferenced.
KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
// Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
if (entry->refCount == 1) {
entry->eventTime = currentTime;
@ -366,7 +374,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
entry->downTime, entry->eventTime);
mCurrentInputTargets.clear();
int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent,
int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
entry->policyFlags, entry->injectorPid, entry->injectorUid,
mCurrentInputTargets);
@ -375,7 +383,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
setInjectionResultLocked(entry, injectionResult);
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
}
}
void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
@ -395,7 +405,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
entry->firstSample.pointerCoords);
mCurrentInputTargets.clear();
int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent,
int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
entry->policyFlags, entry->injectorPid, entry->injectorUid,
mCurrentInputTargets);
@ -404,7 +414,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
setInjectionResultLocked(entry, injectionResult);
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
}
}
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
@ -514,7 +526,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
connection->getInputChannelName());
} else if (status == status_t(FAILED_TRANSACTION)) {
LOGD("channel '%s' ~ Could not append motion sample to currently "
"dispatchedmove event because the event has already been consumed. "
"dispatched move event because the event has already been consumed. "
"(Waiting for next dispatch cycle to start.)",
connection->getInputChannelName());
} else {
@ -1253,9 +1265,37 @@ void InputDispatcher::resetKeyRepeatLocked() {
}
}
void InputDispatcher::preemptInputDispatch() {
#if DEBUG_DISPATCH_CYCLE
LOGD("preemptInputDispatch");
#endif
bool preemptedOne = false;
{ // acquire lock
AutoMutex _l(mLock);
for (size_t i = 0; i < mActiveConnections.size(); i++) {
Connection* connection = mActiveConnections[i];
if (connection->hasPendingSyncTarget()) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
connection->getInputChannelName());
#endif
connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
preemptedOne = true;
}
}
} // release lock
if (preemptedOne) {
// Wake up the poll loop so it can get a head start dispatching the next event.
mPollLoop->wake();
}
}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
LOGD("channel '%s' - Registered", inputChannel->getName().string());
LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
#endif
int receiveFd;
@ -1288,7 +1328,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan
status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
#endif
int32_t receiveFd;

View File

@ -85,6 +85,10 @@ int32_t InputManager::injectInputEvent(const InputEvent* event,
return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
}
void InputManager::preemptInputDispatch() {
mDispatcher->preemptInputDispatch();
}
void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
mReader->getCurrentInputConfiguration(outConfiguration);
}

View File

@ -11,13 +11,13 @@
#define DEBUG_RAW_EVENTS 0
// Log debug messages about touch screen filtering hacks.
#define DEBUG_HACKS 1
#define DEBUG_HACKS 0
// Log debug messages about virtual key processing.
#define DEBUG_VIRTUAL_KEYS 1
#define DEBUG_VIRTUAL_KEYS 0
// Log debug messages about pointers.
#define DEBUG_POINTERS 1
#define DEBUG_POINTERS 0
// Log debug messages about pointer assignment calculations.
#define DEBUG_POINTER_ASSIGNMENT 0
@ -630,7 +630,8 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
int32_t pressure = currentTouch.pointers[currentIndex].pressure;
if (lastTouch.idBits.hasBit(id)) {
// Pointer still down compute average.
// Pointer was down before and is still down now.
// Compute average over history trace.
uint32_t start = averagingTouchFilter.historyStart[id];
uint32_t end = averagingTouchFilter.historyEnd[id];
@ -644,11 +645,15 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
#endif
if (distance < AVERAGING_DISTANCE_LIMIT) {
// Increment end index in preparation for recording new historical data.
end += 1;
if (end > AVERAGING_HISTORY_SIZE) {
end = 0;
}
// If the end index has looped back to the start index then we have filled
// the historical trace up to the desired size so we drop the historical
// data at the start of the trace.
if (end == start) {
start += 1;
if (start > AVERAGING_HISTORY_SIZE) {
@ -656,23 +661,25 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
}
}
// Add the raw data to the historical trace.
averagingTouchFilter.historyStart[id] = start;
averagingTouchFilter.historyEnd[id] = end;
averagingTouchFilter.historyData[end].pointers[id].x = x;
averagingTouchFilter.historyData[end].pointers[id].y = y;
averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
// Average over all historical positions in the trace by total pressure.
int32_t averagedX = 0;
int32_t averagedY = 0;
int32_t totalPressure = 0;
for (;;) {
int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].x;
int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
int32_t historicalPressure = averagingTouchFilter.historyData[start]
.pointers[id].pressure;
averagedX += historicalX;
averagedY += historicalY;
averagedX += historicalX * historicalPressure;
averagedY += historicalY * historicalPressure;
totalPressure += historicalPressure;
if (start == end) {
@ -1144,12 +1151,6 @@ void InputReader::onMultiTouchScreenStateChanged(nsecs_t when,
void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
InputDevice* device) {
static const uint32_t POSITION_FIELDS =
InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X
| InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y
| InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE
| InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH;
/* Refresh display properties so we can map touch screen coords into display coords */
if (! refreshDisplayProperties()) {
@ -1167,10 +1168,19 @@ void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
in->current.down = in->accumulator.btnTouch;
}
if ((fields & POSITION_FIELDS) == POSITION_FIELDS) {
if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) {
in->current.x = in->accumulator.absX;
}
if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) {
in->current.y = in->accumulator.absY;
}
if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) {
in->current.pressure = in->accumulator.absPressure;
}
if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) {
in->current.size = in->accumulator.absToolWidth;
}
@ -1323,18 +1333,23 @@ bool InputReader::consumeVirtualKeyTouches(nsecs_t when,
void InputReader::dispatchVirtualKey(nsecs_t when,
InputDevice* device, uint32_t policyFlags,
int32_t keyEventAction, int32_t keyEventFlags) {
updateExportedVirtualKeyState();
int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode;
int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode;
nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
int32_t metaState = globalMetaState();
updateExportedVirtualKeyState();
mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags,
keyCode, scanCode, metaState, downTime);
mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
int32_t policyActions = mPolicy->interceptKey(when, device->id,
keyEventAction == KEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
}
}
void InputReader::dispatchTouches(nsecs_t when,
@ -1609,6 +1624,10 @@ bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when,
*policyFlags |= POLICY_FLAG_BRIGHT_HERE;
}
if (policyActions & InputReaderPolicyInterface::ACTION_INTERCEPT_DISPATCH) {
*policyFlags |= POLICY_FLAG_INTERCEPT_DISPATCH;
}
return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
}

View File

@ -430,10 +430,12 @@ status_t InputPublisher::appendMotionSample(
reinterpret_cast<char*>(mSharedMessage);
if (newBytesUsed > mAshmemSize) {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
"buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d",
mChannel->getName().string(),
mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
#endif
return NO_MEMORY;
}
@ -444,8 +446,10 @@ status_t InputPublisher::appendMotionSample(
if (errno == EAGAIN) {
// Only possible source of contention is the consumer having consumed (or being in the
// process of consuming) the message and left the semaphore count at 0.
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
"already been consumed.", mChannel->getName().string());
#endif
return FAILED_TRANSACTION;
} else {
LOGE("channel '%s' publisher ~ Error %d in sem_trywait.",