summaryrefslogtreecommitdiff
path: root/services/input/InputDispatcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/input/InputDispatcher.cpp')
-rw-r--r--services/input/InputDispatcher.cpp156
1 files changed, 121 insertions, 35 deletions
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 89a7751a35fd..b5a4bd2da0c7 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -72,6 +72,10 @@ const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
// 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
+// Amount of time to allow for an event to be dispatched (measured since its eventTime)
+// before considering it stale and dropping it.
+const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
+
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -151,34 +155,12 @@ static bool validateMotionEvent(int32_t action, size_t pointerCount,
}
-// --- InputWindow ---
-
-bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
- return x >= touchableAreaLeft && x <= touchableAreaRight
- && y >= touchableAreaTop && y <= touchableAreaBottom;
-}
-
-bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const {
- return x >= frameLeft && x <= frameRight
- && y >= frameTop && y <= frameBottom;
-}
-
-bool InputWindow::isTrustedOverlay() const {
- return layoutParamsType == TYPE_INPUT_METHOD
- || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
- || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
-}
-
-bool InputWindow::supportsSplitTouch() const {
- return layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH;
-}
-
-
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy),
- mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
+ mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
+ mNextUnblockedEvent(NULL),
mDispatchEnabled(true), mDispatchFrozen(false),
mFocusedWindow(NULL),
mFocusedApplication(NULL),
@@ -368,6 +350,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
}
// Now we have an event to dispatch.
+ // All events are eventually dequeued and processed this way, even if we intend to drop them.
assert(mPendingEvent != NULL);
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
@@ -376,6 +359,11 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
} else if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
+
+ if (mNextUnblockedEvent == mPendingEvent) {
+ mNextUnblockedEvent = NULL;
+ }
+
switch (mPendingEvent->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
@@ -395,6 +383,13 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
dropReason = DROP_REASON_APP_SWITCH;
}
}
+ if (dropReason == DROP_REASON_NOT_DROPPED
+ && isStaleEventLocked(currentTime, typedEntry)) {
+ dropReason = DROP_REASON_STALE;
+ }
+ if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
+ dropReason = DROP_REASON_BLOCKED;
+ }
done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
&dropReason, nextWakeupTime);
break;
@@ -405,6 +400,13 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
+ if (dropReason == DROP_REASON_NOT_DROPPED
+ && isStaleEventLocked(currentTime, typedEntry)) {
+ dropReason = DROP_REASON_STALE;
+ }
+ if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
+ dropReason = DROP_REASON_BLOCKED;
+ }
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
@@ -431,6 +433,9 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
switch (entry->type) {
case EventEntry::TYPE_KEY: {
+ // Optimize app switch latency.
+ // If the application takes too long to catch up then we drop all events preceding
+ // the app switch key.
KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
@@ -448,11 +453,63 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
}
break;
}
+
+ case EventEntry::TYPE_MOTION: {
+ // Optimize case where the current application is unresponsive and the user
+ // decides to touch a window in a different application.
+ // If the application takes too long to catch up then we drop all events preceding
+ // the touch into the other window.
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
+ && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
+ && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
+ && mInputTargetWaitApplication != NULL) {
+ int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0].x);
+ int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0].y);
+ const InputWindow* touchedWindow = findTouchedWindowAtLocked(x, y);
+ if (touchedWindow
+ && touchedWindow->inputWindowHandle != NULL
+ && touchedWindow->inputWindowHandle->getInputApplicationHandle()
+ != mInputTargetWaitApplication) {
+ // User touched a different application than the one we are waiting on.
+ // Flag the event, and start pruning the input queue.
+ mNextUnblockedEvent = motionEntry;
+ needWake = true;
+ }
+ }
+ break;
+ }
}
return needWake;
}
+const InputWindow* InputDispatcher::findTouchedWindowAtLocked(int32_t x, int32_t y) {
+ // Traverse windows from front to back to find touched window.
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ const InputWindow* window = & mWindows.editItemAt(i);
+ int32_t flags = window->layoutParamsFlags;
+
+ 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)) {
+ // Found window.
+ return window;
+ }
+ }
+ }
+
+ if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
+ // Error window is on top but not visible, so touch is dropped.
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -470,6 +527,16 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR
LOGI("Dropped event because of pending overdue app switch.");
reason = "inbound event was dropped because of pending overdue app switch";
break;
+ case DROP_REASON_BLOCKED:
+ LOGI("Dropped event because the current application is not responding and the user "
+ "has started interating with a different application.");
+ reason = "inbound event was dropped because the current application is not responding "
+ "and the user has started interating with a different application";
+ break;
+ case DROP_REASON_STALE:
+ LOGI("Dropped event because it is stale.");
+ reason = "inbound event was dropped because it is stale";
+ break;
default:
assert(false);
return;
@@ -521,6 +588,10 @@ void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
#endif
}
+bool InputDispatcher::isStaleEventLocked(nsecs_t currentTime, EventEntry* entry) {
+ return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT;
+}
+
bool InputDispatcher::runCommandsLockedInterruptible() {
if (mCommandQueue.isEmpty()) {
return false;
@@ -670,7 +741,7 @@ bool InputDispatcher::dispatchKeyLocked(
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
if (mFocusedWindow) {
- commandEntry->inputChannel = mFocusedWindow->inputChannel;
+ commandEntry->inputWindowHandle = mFocusedWindow->inputWindowHandle;
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
@@ -848,6 +919,7 @@ void InputDispatcher::resetTargetsLocked() {
mCurrentInputTargetsValid = false;
mCurrentInputTargets.clear();
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+ mInputTargetWaitApplication.clear();
}
void InputDispatcher::commitTargetsLocked() {
@@ -866,6 +938,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
mInputTargetWaitTimeoutExpired = false;
+ mInputTargetWaitApplication.clear();
}
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
@@ -880,6 +953,15 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
+ mInputTargetWaitApplication.clear();
+
+ if (window && window->inputWindowHandle != NULL) {
+ mInputTargetWaitApplication =
+ window->inputWindowHandle->getInputApplicationHandle();
+ }
+ if (mInputTargetWaitApplication == NULL && application) {
+ mInputTargetWaitApplication = application->inputApplicationHandle;
+ }
}
}
@@ -2624,7 +2706,7 @@ void InputDispatcher::setFocusedApplication(const InputApplication* inputApplica
void InputDispatcher::releaseFocusedApplicationLocked() {
if (mFocusedApplication) {
mFocusedApplication = NULL;
- mFocusedApplicationStorage.handle.clear();
+ mFocusedApplicationStorage.inputApplicationHandle.clear();
}
}
@@ -2860,7 +2942,8 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
}
}
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
+ const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
toString(monitor));
@@ -2875,7 +2958,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan
return BAD_VALUE;
}
- sp<Connection> connection = new Connection(inputChannel);
+ sp<Connection> connection = new Connection(inputChannel, inputWindowHandle);
status_t status = connection->initialize();
if (status) {
LOGE("Failed to initialize input publisher for input channel '%s', status=%d",
@@ -3002,9 +3085,10 @@ void InputDispatcher::onANRLocked(
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
if (application) {
- commandEntry->inputApplicationHandle = application->handle;
+ commandEntry->inputApplicationHandle = application->inputApplicationHandle;
}
if (window) {
+ commandEntry->inputWindowHandle = window->inputWindowHandle;
commandEntry->inputChannel = window->inputChannel;
}
}
@@ -3025,7 +3109,7 @@ void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
if (connection->status != Connection::STATUS_ZOMBIE) {
mLock.unlock();
- mPolicy->notifyInputChannelBroken(connection->inputChannel);
+ mPolicy->notifyInputChannelBroken(connection->inputWindowHandle);
mLock.lock();
}
@@ -3036,7 +3120,7 @@ void InputDispatcher::doNotifyANRLockedInterruptible(
mLock.unlock();
nsecs_t newTimeout = mPolicy->notifyANR(
- commandEntry->inputApplicationHandle, commandEntry->inputChannel);
+ commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle);
mLock.lock();
@@ -3052,7 +3136,7 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
mLock.unlock();
- bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
+ bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
&event, entry->policyFlags);
mLock.lock();
@@ -3095,7 +3179,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
mLock.unlock();
- bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel,
+ bool fallback = mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
&event, keyEntry->policyFlags, &event);
mLock.lock();
@@ -3604,8 +3688,10 @@ bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& mement
// --- InputDispatcher::Connection ---
-InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
- status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
+InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
+ const sp<InputWindowHandle>& inputWindowHandle) :
+ status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
+ inputPublisher(inputChannel),
lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
}