From d1b0a2bfe50e61241fab6a571941c207232d9212 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Sun, 26 Sep 2010 22:20:12 -0700 Subject: [PATCH] Add suuport for splitting touch events across windows. This feature is currently used to enable dragging the start and end selection handles of a TextView at the same time. Could be used for other things later. Deleted some dead code in ArrowKeyMovementMethod and CursorControllers. Change-Id: I930accd97ca1ca1917aab8a807db2c950fc7b409 --- include/ui/Input.h | 8 + include/ui/InputDispatcher.h | 122 +++-- include/ui/InputReader.h | 4 - include/utils/BitSet.h | 3 + libs/ui/InputDispatcher.cpp | 896 ++++++++++++++++++++++------------- libs/ui/InputReader.cpp | 2 +- 6 files changed, 657 insertions(+), 378 deletions(-) diff --git a/include/ui/Input.h b/include/ui/Input.h index b587e94db..21baf3259 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -40,9 +40,17 @@ enum { /* * Maximum number of pointers supported per motion event. + * Smallest number of pointers is 1. */ #define MAX_POINTERS 10 +/* + * Maximum pointer id value supported in a motion event. + * Smallest pointer id is 0. + * (This is limited by our use of BitSet32 to track pointer assignments.) + */ +#define MAX_POINTER_ID 31 + /* * Declare a concrete type for the NDK's input event forward declaration. */ diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index 96b4faedb..cc1601298 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -89,17 +90,13 @@ struct InputTarget { * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ FLAG_OUTSIDE = 0x02, - /* This flag indicates that a KeyEvent or MotionEvent is being canceled. - * In the case of a key event, it should be delivered with flag - * AKEY_EVENT_FLAG_CANCELED set. - * In the case of a motion event, it should be delivered with action - * AMOTION_EVENT_ACTION_CANCEL instead. */ - FLAG_CANCEL = 0x04, - /* This flag indicates that the target of a MotionEvent is partly or wholly * obscured by another visible window above it. The motion event should be * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - FLAG_WINDOW_IS_OBSCURED = 0x08, + FLAG_WINDOW_IS_OBSCURED = 0x04, + + /* This flag indicates that a motion event is being split across multiple windows. */ + FLAG_SPLIT = 0x08, }; // The input channel to be targeted. @@ -111,6 +108,13 @@ struct InputTarget { // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) float xOffset, yOffset; + + // The window type of the input target. + int32_t windowType; + + // The subset of pointer ids to include in motion events dispatched to this input target + // if FLAG_SPLIT is set. + BitSet32 pointerIds; }; @@ -143,7 +147,7 @@ struct InputWindow { FLAG_SHOW_WALLPAPER = 0x00100000, FLAG_TURN_SCREEN_ON = 0x00200000, FLAG_DISMISS_KEYGUARD = 0x00400000, - FLAG_IMMERSIVE = 0x00800000, + FLAG_SPLIT_TOUCH = 0x00800000, FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000, FLAG_COMPATIBLE_WINDOW = 0x20000000, FLAG_SYSTEM_ERROR = 0x40000000, @@ -276,7 +280,7 @@ public: const KeyEvent* keyEvent, uint32_t policyFlags) = 0; /* Poke user activity for an event dispatched to a window. */ - virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) = 0; + virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; /* Checks whether a given application pid/uid has permission to inject input events * into other applications. @@ -415,6 +419,16 @@ private: T* prev; }; + struct InjectionState { + mutable int32_t refCount; + + int32_t injectorPid; + int32_t injectorUid; + int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + bool injectionIsAsync; // set to true if injection is not waiting for the result + int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress + }; + struct EventEntry : Link { enum { TYPE_SENTINEL, @@ -423,21 +437,14 @@ private: TYPE_MOTION }; - int32_t refCount; + mutable int32_t refCount; int32_t type; nsecs_t eventTime; - - int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING - bool injectionIsAsync; // set to true if injection is not waiting for the result - int32_t injectorPid; // -1 if not injected - int32_t injectorUid; // -1 if not injected + InjectionState* injectionState; bool dispatchInProgress; // initially false, set to true while dispatching - int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress - inline bool isInjected() { return injectorPid >= 0; } - - void recycle(); + inline bool isInjected() { return injectionState != NULL; } }; struct ConfigurationChangedEntry : EventEntry { @@ -463,8 +470,6 @@ private: INTERCEPT_KEY_RESULT_CONTINUE, }; InterceptKeyResult interceptKeyResult; // set based on the interception result - - void recycle(); }; struct MotionSample { @@ -521,6 +526,10 @@ private: inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } + + inline bool isSplit() const { + return targetFlags & InputTarget::FLAG_SPLIT; + } }; // A command entry captures state and behavior for an action to be performed in the @@ -555,7 +564,6 @@ private: KeyEntry* keyEntry; sp inputChannel; sp inputApplicationHandle; - int32_t windowType; int32_t userActivityEventType; }; @@ -611,6 +619,7 @@ private: public: Allocator(); + InjectionState* obtainInjectionState(int32_t injectorPid, int32_t injectorUid); ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime); KeyEntry* obtainKeyEntry(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, @@ -626,6 +635,7 @@ private: int32_t targetFlags, float xOffset, float yOffset); CommandEntry* obtainCommandEntry(Command command); + void releaseInjectionState(InjectionState* injectionState); void releaseEventEntry(EventEntry* entry); void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry); void releaseKeyEntry(KeyEntry* entry); @@ -633,10 +643,13 @@ private: void releaseDispatchEntry(DispatchEntry* entry); void releaseCommandEntry(CommandEntry* entry); + void recycleKeyEntry(KeyEntry* entry); + void appendMotionSample(MotionEntry* motionEntry, nsecs_t eventTime, const PointerCoords* pointerCoords); private: + Pool mInjectionStatePool; Pool mConfigurationChangeEntryPool; Pool mKeyEntryPool; Pool mMotionEntryPool; @@ -645,6 +658,7 @@ private: Pool mCommandEntryPool; void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime); + void releaseEventEntryInjectionState(EventEntry* entry); }; /* Tracks dispatched key and motion event state so that cancelation events can be @@ -823,6 +837,7 @@ private: void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult); Condition mInjectionSyncFinishedCondition; + void incrementPendingForegroundDispatchesLocked(EventEntry* entry); void decrementPendingForegroundDispatchesLocked(EventEntry* entry); // Throttling state. @@ -858,23 +873,37 @@ private: // Dispatch state. bool mDispatchEnabled; bool mDispatchFrozen; + Vector mWindows; - Vector mWallpaperWindows; + + const InputWindow* getWindowLocked(const sp& inputChannel); // Focus tracking for keys, trackball, etc. - InputWindow* mFocusedWindow; + const InputWindow* mFocusedWindow; // Focus tracking for touch. - bool mTouchDown; - InputWindow* mTouchedWindow; // primary target for current down - bool mTouchedWindowIsObscured; // true if other windows may obscure the target - Vector mTouchedWallpaperWindows; // wallpaper targets - struct OutsideTarget { - InputWindow* window; - bool obscured; + struct TouchedWindow { + const InputWindow* window; + int32_t targetFlags; + BitSet32 pointerIds; + sp channel; }; - Vector mTempTouchedOutsideTargets; // temporary outside touch targets - Vector > mTempTouchedWallpaperChannels; // temporary wallpaper targets + struct TouchState { + bool down; + bool split; + Vector windows; + + TouchState(); + ~TouchState(); + void reset(); + void copyFrom(const TouchState& other); + void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds); + void removeOutsideTouchWindows(); + const InputWindow* getFirstForegroundWindow(); + }; + + TouchState mTouchState; + TouchState mTempTouchState; // Focused application. InputApplication* mFocusedApplication; @@ -899,8 +928,6 @@ private: // The input targets that were most recently identified for dispatch. bool mCurrentInputTargetsValid; // false while targets are being recomputed Vector mCurrentInputTargets; - int32_t mCurrentInputWindowType; - sp mCurrentInputChannel; enum InputTargetWaitCause { INPUT_TARGET_WAIT_CAUSE_NONE, @@ -915,7 +942,7 @@ private: // Finding targets for input events. void resetTargetsLocked(); - void commitTargetsLocked(const InputWindow* window); + void commitTargetsLocked(); int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const InputApplication* application, const InputWindow* window, nsecs_t* nextWakeupTime); @@ -924,19 +951,19 @@ private: nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime); void resetANRTimeoutsLocked(); - int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry, - nsecs_t* nextWakeupTime, InputWindow** outWindow); - int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry, - nsecs_t* nextWakeupTime, InputWindow** outWindow); + int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, + nsecs_t* nextWakeupTime); + int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, + nsecs_t* nextWakeupTime); - void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags); + void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, + BitSet32 pointerIds); void addMonitoringTargetsLocked(); - void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType); - bool checkInjectionPermission(const InputWindow* window, - int32_t injectorPid, int32_t injectorUid); + bool shouldPokeUserActivityForCurrentInputTargetsLocked(); + void pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType); + bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState); bool isWindowObscuredLocked(const InputWindow* window); bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window); - void releaseTouchedWindowLocked(); String8 getApplicationWindowLabelLocked(const InputApplication* application, const InputWindow* window); @@ -955,6 +982,9 @@ private: void drainOutboundQueueLocked(Connection* connection); static int handleReceiveCallback(int receiveFd, int events, void* data); + // Splitting motion events across windows. + MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); + // Dump state. void dumpDispatchStateLocked(String8& dump); void logDispatchStateLocked(); diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index 903c3c4fa..e85735a71 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -549,10 +549,6 @@ public: const int32_t* keyCodes, uint8_t* outFlags); protected: - /* Maximum pointer id value supported. - * (This is limited by our use of BitSet32 to track pointer assignments.) */ - static const uint32_t MAX_POINTER_ID = 31; - Mutex mLock; struct VirtualKey { diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index 19c8bf093..f5dbcd942 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -38,6 +38,9 @@ struct BitSet32 { // Clears the bit set. inline void clear() { value = 0; } + // Returns the number of marked bits in the set. + inline uint32_t count() const { return __builtin_popcount(value); } + // Returns true if the bit set does not contain any marked bits. inline bool isEmpty() const { return ! value; } diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 1cf7592ff..5da167675 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -69,6 +69,65 @@ static inline const char* toString(bool value) { return value ? "true" : "false"; } +static inline int32_t getMotionEventActionPointerIndex(int32_t action) { + return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) + >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; +} + +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 validateKeyEvent(int32_t action) { + if (! isValidKeyAction(action)) { + LOGE("Key event has invalid action code 0x%x", action); + return false; + } + return true; +} + +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; + } +} + +static bool validateMotionEvent(int32_t action, size_t pointerCount, + const int32_t* pointerIds) { + if (! isValidMotionAction(action)) { + LOGE("Motion event has invalid action code 0x%x", action); + return false; + } + if (pointerCount < 1 || pointerCount > MAX_POINTERS) { + LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.", + pointerCount, MAX_POINTERS); + return false; + } + for (size_t i = 0; i < pointerCount; i++) { + if (pointerIds[i] < 0 || pointerIds[i] > MAX_POINTER_ID) { + LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", + pointerIds[i], MAX_POINTER_ID); + return false; + } + } + return true; +} + // --- InputWindow --- @@ -91,7 +150,7 @@ InputDispatcher::InputDispatcher(const sp& polic mPolicy(policy), mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX), mDispatchEnabled(true), mDispatchFrozen(false), - mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL), + mFocusedWindow(NULL), mFocusedApplication(NULL), mCurrentInputTargetsValid(false), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { @@ -414,9 +473,10 @@ void InputDispatcher::releasePendingEventLocked() { } void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { - if (entry->injectionResult == INPUT_EVENT_INJECTION_PENDING) { + InjectionState* injectionState = entry->injectionState; + if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) { #if DEBUG_DISPATCH_CYCLE - LOGD("Inbound event was dropped. Setting injection result to failed."); + LOGD("Injected inbound event was dropped."); #endif setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); } @@ -424,10 +484,11 @@ void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { } bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) { - return ! entry->isInjected() - || entry->injectorUid == 0 + InjectionState* injectionState = entry->injectionState; + return ! injectionState + || injectionState->injectorUid == 0 || mPolicy->checkInjectEventsPermissionNonReentrant( - entry->injectorPid, entry->injectorUid); + injectionState->injectorPid, injectionState->injectorUid); } void InputDispatcher::resetKeyRepeatLocked() { @@ -444,7 +505,7 @@ InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked( // 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(); + mAllocator.recycleKeyEntry(entry); entry->eventTime = currentTime; entry->policyFlags = policyFlags; entry->repeatCount += 1; @@ -496,8 +557,7 @@ bool InputDispatcher::dispatchKeyLocked( if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { bool trusted; if (! dropEvent && mFocusedWindow) { - trusted = checkInjectionPermission(mFocusedWindow, - entry->injectorPid, entry->injectorUid); + trusted = checkInjectionPermission(mFocusedWindow, entry->injectionState); } else { trusted = isEventFromReliableSourceLocked(entry); } @@ -559,9 +619,8 @@ bool InputDispatcher::dispatchKeyLocked( // Identify targets. if (! mCurrentInputTargetsValid) { - InputWindow* window = NULL; - int32_t injectionResult = findFocusedWindowLocked(currentTime, - entry, nextWakeupTime, & window); + int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, + entry, nextWakeupTime); if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } @@ -572,14 +631,16 @@ bool InputDispatcher::dispatchKeyLocked( } addMonitoringTargetsLocked(); - commitTargetsLocked(window); + commitTargetsLocked(); } // Dispatch the key. dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); // Poke user activity. - pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT); + if (shouldPokeUserActivityForCurrentInputTargetsLocked()) { + pokeUserActivityLocked(entry->eventTime, POWER_MANAGER_BUTTON_EVENT); + } return true; } @@ -616,16 +677,15 @@ bool InputDispatcher::dispatchMotionLocked( // Identify targets. if (! mCurrentInputTargetsValid) { - InputWindow* window = NULL; int32_t injectionResult; if (isPointerEvent) { // Pointer event. (eg. touchscreen) - injectionResult = findTouchedWindowLocked(currentTime, - entry, nextWakeupTime, & window); + injectionResult = findTouchedWindowTargetsLocked(currentTime, + entry, nextWakeupTime); } else { // Non touch event. (eg. trackball) - injectionResult = findFocusedWindowLocked(currentTime, - entry, nextWakeupTime, & window); + injectionResult = findFocusedWindowTargetsLocked(currentTime, + entry, nextWakeupTime); } if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; @@ -637,34 +697,36 @@ bool InputDispatcher::dispatchMotionLocked( } addMonitoringTargetsLocked(); - commitTargetsLocked(window); + commitTargetsLocked(); } // 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) { + if (shouldPokeUserActivityForCurrentInputTargetsLocked()) { + int32_t eventType; + if (isPointerEvent) { + switch (entry->action) { + case AMOTION_EVENT_ACTION_DOWN: eventType = POWER_MANAGER_TOUCH_EVENT; - } else { - eventType = POWER_MANAGER_LONG_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; } - break; + } else { + eventType = POWER_MANAGER_BUTTON_EVENT; } - } else { - eventType = POWER_MANAGER_BUTTON_EVENT; + pokeUserActivityLocked(entry->eventTime, eventType); } - pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType); return true; } @@ -735,13 +797,10 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi 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; +void InputDispatcher::commitTargetsLocked() { mCurrentInputTargetsValid = true; } @@ -803,8 +862,8 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout // Give up. mInputTargetWaitTimeoutExpired = true; - // Release the touch target. - releaseTouchedWindowLocked(); + // Release the touch targets. + mTouchState.reset(); // Input state will not be realistic. Mark it out of sync. if (inputChannel.get()) { @@ -834,9 +893,8 @@ void InputDispatcher::resetANRTimeoutsLocked() { mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; } -int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry, - nsecs_t* nextWakeupTime, InputWindow** outWindow) { - *outWindow = NULL; +int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, + const EventEntry* entry, nsecs_t* nextWakeupTime) { mCurrentInputTargets.clear(); int32_t injectionResult; @@ -861,7 +919,7 @@ int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const Even } // Check permissions. - if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) { + if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; goto Failed; } @@ -888,8 +946,7 @@ int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const Even // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - *outWindow = mFocusedWindow; - addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND); + addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0)); // Done. Failed: @@ -905,15 +962,14 @@ Unresponsive: return injectionResult; } -int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry, - nsecs_t* nextWakeupTime, InputWindow** outWindow) { +int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, + const MotionEntry* entry, nsecs_t* nextWakeupTime) { enum InjectionPermission { INJECTION_PERMISSION_UNKNOWN, INJECTION_PERMISSION_GRANTED, INJECTION_PERMISSION_DENIED }; - *outWindow = NULL; mCurrentInputTargets.clear(); nsecs_t startTime = now(); @@ -945,25 +1001,33 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE; int32_t action = entry->action; + int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; // 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 */ + int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; + InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; + if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + mTempTouchState.reset(); + mTempTouchState.down = true; + } else { + mTempTouchState.copyFrom(mTouchState); + } - InputWindow* newTouchedWindow = NULL; - mTempTouchedOutsideTargets.clear(); + bool isSplit = mTempTouchState.split && mTempTouchState.down; + if (maskedAction == AMOTION_EVENT_ACTION_DOWN + || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { + /* Case 1: New splittable pointer going down. */ - 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; + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x); + int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y); + const InputWindow* newTouchedWindow = NULL; + const InputWindow* topErrorWindow = NULL; // 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); + const InputWindow* window = & mWindows.editItemAt(i); int32_t flags = window->layoutParamsFlags; if (flags & InputWindow::FLAG_SYSTEM_ERROR) { @@ -979,17 +1043,15 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti 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 (maskedAction == AMOTION_EVENT_ACTION_DOWN + && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) { + mTempTouchState.addOrUpdateWindow(window, + InputTarget::FLAG_OUTSIDE, BitSet32(0)); } } } @@ -1007,6 +1069,21 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti goto Unresponsive; } + // Figure out whether splitting will be allowed for this window. + if (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) { + // New window supports splitting. + isSplit = true; + } else if (isSplit) { + // New window does not support splitting but we have already split events. + // Assign the pointer to the first foreground window we find. + // (May be NULL which is why we put this code block before the next check.) + newTouchedWindow = mTempTouchState.getFirstForegroundWindow(); + } + int32_t targetFlags = InputTarget::FLAG_FOREGROUND; + if (isSplit) { + targetFlags |= InputTarget::FLAG_SPLIT; + } + // If we did not find a touched window then fail. if (! newTouchedWindow) { if (mFocusedApplication) { @@ -1017,140 +1094,127 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti #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); + // Update the temporary touch state. + BitSet32 pointerIds; + if (isSplit) { + uint32_t pointerId = entry->pointerIds[pointerIndex]; + pointerIds.markBit(pointerId); } + mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds); } 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; - } + /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. - if (! mTouchDown) { + if (! mTempTouchState.down) { 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) { + // Check permission to inject into all touched foreground windows and ensure there + // is at least one touched foreground window. + { + bool haveForegroundWindow = false; + for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { + const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; + if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { + haveForegroundWindow = true; + if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + injectionPermission = INJECTION_PERMISSION_DENIED; + goto Failed; + } + } + } + if (! haveForegroundWindow) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("Dropping event because there is no touched window to receive it."); + LOGD("Dropping event because there is no touched foreground 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; - } + // Permission granted to injection into all touched foreground windows. + injectionPermission = INJECTION_PERMISSION_GRANTED; + } - // 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."); + // Ensure all touched foreground windows are ready for new input. + for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { + const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; + if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { + // If the touched window is paused then keep waiting. + if (touchedWindow.window->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; + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, touchedWindow.window, nextWakeupTime); + goto Unresponsive; + } + + // If the touched window is still working on previous events then keep waiting. + if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) { +#if DEBUG_FOCUS + LOGD("Waiting because touched window still processing previous input."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, touchedWindow.window, nextWakeupTime); + goto Unresponsive; + } + } + } + + // If this is the first pointer going down and the touched window has a wallpaper + // then also add the touched wallpaper windows so they are locked in for the duration + // of the touch gesture. + if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow(); + if (foregroundWindow->hasWallpaper) { + for (size_t i = 0; i < mWindows.size(); i++) { + const InputWindow* window = & mWindows[i]; + if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) { + mTempTouchState.addOrUpdateWindow(window, 0, BitSet32(0)); + } + } + } + } + + // If a touched window has been obscured at any point during the touch gesture, set + // the appropriate flag so we remember it for the entire gesture. + for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { + TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i); + if ((touchedWindow.targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) == 0) { + if (isWindowObscuredLocked(touchedWindow.window)) { + touchedWindow.targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + } } } // 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; + for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { + const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i); + addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags, + touchedWindow.pointerIds); } + // Drop the outside touch window since we will not care about them in the next iteration. + mTempTouchState.removeOutsideTouchWindows(); + 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)) { + if (checkInjectionPermission(NULL, entry->injectionState)) { injectionPermission = INJECTION_PERMISSION_GRANTED; } else { injectionPermission = INJECTION_PERMISSION_DENIED; @@ -1159,25 +1223,41 @@ Failed: // 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! + if (maskedAction == AMOTION_EVENT_ACTION_UP + || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + // All pointers up or canceled. + mTempTouchState.reset(); + } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + // First pointer went down. + if (mTouchState.down) { LOGW("Pointer down received while already down."); - } else { - mTouchDown = true; } + } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + // One pointer went up. + if (isSplit) { + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + uint32_t pointerId = entry->pointerIds[pointerIndex]; - 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(); + for (size_t i = 0; i < mTempTouchState.windows.size(); ) { + TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i); + if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { + touchedWindow.pointerIds.clearBit(pointerId); + if (touchedWindow.pointerIds.isEmpty()) { + mTempTouchState.windows.removeAt(i); + continue; + } + } + i += 1; + } } - } else if (action == AMOTION_EVENT_ACTION_UP) { - mTouchDown = false; - releaseTouchedWindowLocked(); } + + // Save changes to touch state. + mTouchState.copyFrom(mTempTouchState); } else { - LOGW("Not updating touch focus because injection was denied."); +#if DEBUG_FOCUS + LOGD("Not updating touch focus because injection was denied."); +#endif } Unresponsive: @@ -1185,20 +1265,15 @@ Unresponsive: updateDispatchStatisticsLocked(currentTime, entry, injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS - LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d," - "timeSpendWaitingForApplication=%0.1fms", + LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " + "timeSpentWaitingForApplication=%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) { +void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, + BitSet32 pointerIds) { mCurrentInputTargets.push(); InputTarget& target = mCurrentInputTargets.editTop(); @@ -1206,6 +1281,8 @@ void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t t target.flags = targetFlags; target.xOffset = - window->frameLeft; target.yOffset = - window->frameTop; + target.windowType = window->layoutParamsType; + target.pointerIds = pointerIds; } void InputDispatcher::addMonitoringTargetsLocked() { @@ -1217,22 +1294,27 @@ void InputDispatcher::addMonitoringTargetsLocked() { target.flags = 0; target.xOffset = 0; target.yOffset = 0; + target.windowType = InputWindow::TYPE_SYSTEM_OVERLAY; } } 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); + const InjectionState* injectionState) { + if (injectionState + && injectionState->injectorUid > 0 + && (window == NULL || window->ownerUid != injectionState->injectorUid)) { + bool result = mPolicy->checkInjectEventsPermissionNonReentrant( + injectionState->injectorPid, injectionState->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(), + injectionState->injectorPid, injectionState->injectorUid, + window->inputChannel->getName().string(), window->ownerUid); } else { LOGW("Permission denied: injecting event from pid %d uid %d", - injectorPid, injectorUid); + injectionState->injectorPid, injectionState->injectorUid); } return false; } @@ -1282,12 +1364,19 @@ String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* } } -void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, - int32_t windowType, int32_t eventType) { +bool InputDispatcher::shouldPokeUserActivityForCurrentInputTargetsLocked() { + for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { + if (mCurrentInputTargets[i].windowType == InputWindow::TYPE_KEYGUARD) { + return false; + } + } + return true; +} + +void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doPokeUserActivityLockedInterruptible); commandEntry->eventTime = eventTime; - commandEntry->windowType = windowType; commandEntry->userActivityEventType = eventType; } @@ -1296,12 +1385,19 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, bool resumeWithAppendedMotionSample) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, " - "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s", + "xOffset=%f, yOffset=%f, " + "windowType=%d, pointerIds=0x%x, " + "resumeWithAppendedMotionSample=%s", connection->getInputChannelName(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, + inputTarget->windowType, inputTarget->pointerIds.value, toString(resumeWithAppendedMotionSample)); #endif + // Make sure we are never called for streaming when splitting across multiple windows. + bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT; + assert(! (resumeWithAppendedMotionSample && isSplit)); + // 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) { @@ -1310,6 +1406,23 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, return; } + // Split a motion event if needed. + if (isSplit) { + assert(eventEntry->type == EventEntry::TYPE_MOTION); + + MotionEntry* originalMotionEntry = static_cast(eventEntry); + if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { + MotionEntry* splitMotionEntry = splitMotionEvent( + originalMotionEntry, inputTarget->pointerIds); +#if DEBUG_FOCUS + LOGD("channel '%s' ~ Split motion event.", + connection->getInputChannelName()); + logOutboundMotionDetailsLocked(" ", splitMotionEntry); +#endif + eventEntry = splitMotionEntry; + } + } + // 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 @@ -1351,7 +1464,8 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // 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(eventEntry)->lastSample; + MotionEntry* motionEntry = static_cast(eventEntry); + MotionSample* appendedMotionSample = motionEntry->lastSample; status_t status = connection->inputPublisher.appendMotionSample( appendedMotionSample->eventTime, appendedMotionSample->pointerCoords); if (status == OK) { @@ -1426,7 +1540,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset); if (dispatchEntry->hasForegroundTarget()) { - eventEntry->pendingForegroundDispatches += 1; + incrementPendingForegroundDispatchesLocked(eventEntry); } // Handle the case where we could not stream a new motion sample because the consumer has @@ -1470,8 +1584,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, dispatchEntry->inProgress = true; // Update the connection's input state. - InputState::Consistency consistency = connection->inputState.trackEvent( - dispatchEntry->eventEntry); + EventEntry* eventEntry = dispatchEntry->eventEntry; + InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry); #if FILTER_INPUT_EVENTS // Filter out inconsistent sequences of input events. @@ -1497,16 +1611,13 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Publish the event. status_t status; - switch (dispatchEntry->eventEntry->type) { + switch (eventEntry->type) { case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); + KeyEntry* keyEntry = static_cast(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, @@ -1524,7 +1635,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); + MotionEntry* motionEntry = static_cast(eventEntry); // Apply target flags. int32_t action = motionEntry->action; @@ -1532,9 +1643,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, 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; } @@ -1617,7 +1725,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Record information about the newly started dispatch cycle. - connection->lastEventTime = dispatchEntry->eventEntry->eventTime; + connection->lastEventTime = eventEntry->eventTime; connection->lastDispatchTime = currentTime; // Notify other system components. @@ -1773,6 +1881,78 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data } // release lock } +InputDispatcher::MotionEntry* +InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) { + assert(pointerIds.value != 0); + + uint32_t splitPointerIndexMap[MAX_POINTERS]; + int32_t splitPointerIds[MAX_POINTERS]; + PointerCoords splitPointerCoords[MAX_POINTERS]; + + uint32_t originalPointerCount = originalMotionEntry->pointerCount; + uint32_t splitPointerCount = 0; + + for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; + originalPointerIndex++) { + int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]); + if (pointerIds.hasBit(pointerId)) { + splitPointerIndexMap[splitPointerCount] = originalPointerIndex; + splitPointerIds[splitPointerCount] = pointerId; + splitPointerCoords[splitPointerCount] = + originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]; + splitPointerCount += 1; + } + } + assert(splitPointerCount == pointerIds.count()); + + int32_t action = originalMotionEntry->action; + int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; + if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN + || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + int32_t originalPointerIndex = getMotionEventActionPointerIndex(action); + int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex]; + if (pointerIds.hasBit(pointerId)) { + if (pointerIds.count() == 1) { + // The first/last pointer went down/up. + action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN + ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } + } else { + // An unrelated pointer changed. + action = AMOTION_EVENT_ACTION_MOVE; + } + } + + MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry( + originalMotionEntry->eventTime, + originalMotionEntry->deviceId, + originalMotionEntry->source, + originalMotionEntry->policyFlags, + action, + originalMotionEntry->flags, + originalMotionEntry->metaState, + originalMotionEntry->edgeFlags, + originalMotionEntry->xPrecision, + originalMotionEntry->yPrecision, + originalMotionEntry->downTime, + splitPointerCount, splitPointerIds, splitPointerCoords); + + for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next; + originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) { + for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount; + splitPointerIndex++) { + uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex]; + splitPointerCoords[splitPointerIndex] = + originalMotionSample->pointerCoords[originalPointerIndex]; + } + + mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime, + splitPointerCoords); + } + + return splitMotionEntry; +} + void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { #if DEBUG_INBOUND_EVENT_DETAILS LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime); @@ -1800,6 +1980,9 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou eventTime, deviceId, source, policyFlags, action, flags, keyCode, scanCode, metaState, downTime); #endif + if (! validateKeyEvent(action)) { + return; + } bool needWake; { // acquire lock @@ -1839,6 +2022,9 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t pointerCoords[i].orientation); } #endif + if (! validateMotionEvent(action, pointerCount, pointerIds)) { + return; + } bool needWake; { // acquire lock @@ -1913,8 +2099,10 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; if (! dispatchEntry->inProgress - || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { - // No motion event is being dispatched. + || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION + || dispatchEntry->isSplit()) { + // No motion event is being dispatched, or it is being split across + // windows in which case we cannot stream. continue; } @@ -1972,24 +2160,24 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); - EventEntry* injectedEntry; + InjectionState* injectionState; bool needWake; { // acquire lock AutoMutex _l(mLock); - injectedEntry = createEntryFromInjectedInputEventLocked(event); + EventEntry* injectedEntry = createEntryFromInjectedInputEventLocked(event); if (! injectedEntry) { return INPUT_EVENT_INJECTION_FAILED; } - injectedEntry->refCount += 1; - injectedEntry->injectorPid = injectorPid; - injectedEntry->injectorUid = injectorUid; - + injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid); if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectedEntry->injectionIsAsync = true; + injectionState->injectionIsAsync = true; } + injectionState->refCount += 1; + injectedEntry->injectionState = injectionState; + needWake = enqueueInboundEventLocked(injectedEntry); } // release lock @@ -2005,7 +2193,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; } else { for (;;) { - injectionResult = injectedEntry->injectionResult; + injectionResult = injectionState->injectionResult; if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { break; } @@ -2025,10 +2213,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { - while (injectedEntry->pendingForegroundDispatches != 0) { + while (injectionState->pendingForegroundDispatches != 0) { #if DEBUG_INJECTION LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", - injectedEntry->pendingForegroundDispatches); + injectionState->pendingForegroundDispatches); #endif nsecs_t remainingTimeout = endTime - now(); if (remainingTimeout <= 0) { @@ -2045,7 +2233,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } } - mAllocator.releaseEventEntry(injectedEntry); + mAllocator.releaseInjectionState(injectionState); } // release lock #if DEBUG_INJECTION @@ -2058,14 +2246,15 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) { - if (entry->isInjected()) { + InjectionState* injectionState = entry->injectionState; + if (injectionState) { #if DEBUG_INJECTION LOGD("Setting input event injection result to %d. " "injectorPid=%d, injectorUid=%d", - injectionResult, entry->injectorPid, entry->injectorUid); + injectionResult, injectionState->injectorPid, injectionState->injectorUid); #endif - if (entry->injectionIsAsync) { + if (injectionState->injectionIsAsync) { // Log the outcome since the injector did not wait for the injection result. switch (injectionResult) { case INPUT_EVENT_INJECTION_SUCCEEDED: @@ -2083,41 +2272,26 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject } } - entry->injectionResult = injectionResult; + injectionState->injectionResult = injectionResult; mInjectionResultAvailableCondition.broadcast(); } } +void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) { + InjectionState* injectionState = entry->injectionState; + if (injectionState) { + injectionState->pendingForegroundDispatches += 1; + } +} + void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) { - entry->pendingForegroundDispatches -= 1; + InjectionState* injectionState = entry->injectionState; + if (injectionState) { + injectionState->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; + if (injectionState->pendingForegroundDispatches == 0) { + mInjectionSyncFinishedCondition.broadcast(); + } } } @@ -2126,9 +2300,7 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventL switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent* keyEvent = static_cast(event); - if (! isValidKeyAction(keyEvent->getAction())) { - LOGE("Dropping injected key event since it has invalid action code 0x%x", - keyEvent->getAction()); + if (! validateKeyEvent(keyEvent->getAction())) { return NULL; } @@ -2144,16 +2316,10 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventL case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent* motionEvent = static_cast(event); - if (! isValidMotionAction(motionEvent->getAction())) { - LOGE("Dropping injected motion event since it has invalid action code 0x%x.", - motionEvent->getAction()); + if (! validateMotionEvent(motionEvent->getAction(), + motionEvent->getPointerCount(), motionEvent->getPointerIds())) { 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; @@ -2182,6 +2348,16 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventL } } +const InputWindow* InputDispatcher::getWindowLocked(const sp& inputChannel) { + for (size_t i = 0; i < mWindows.size(); i++) { + const InputWindow* window = & mWindows[i]; + if (window->inputChannel == inputChannel) { + return window; + } + } + return NULL; +} + void InputDispatcher::setInputWindows(const Vector& inputWindows) { #if DEBUG_FOCUS LOGD("setInputWindows"); @@ -2189,22 +2365,8 @@ void InputDispatcher::setInputWindows(const Vector& inputWindows) { { // acquire lock AutoMutex _l(mLock); - // Clear old window pointers but remember their associated channels. + // Clear old window pointers. mFocusedWindow = NULL; - - sp 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 @@ -2213,26 +2375,23 @@ void InputDispatcher::setInputWindows(const Vector& inputWindows) { size_t numWindows = mWindows.size(); for (size_t i = 0; i < numWindows; i++) { - InputWindow* window = & mWindows.editItemAt(i); + const InputWindow* window = & mWindows.itemAt(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; + break; + } + } + + for (size_t i = 0; i < mTouchState.windows.size(); ) { + TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i); + const InputWindow* window = getWindowLocked(touchedWindow.channel); + if (window) { + touchedWindow.window = window; + i += 1; + } else { + mTouchState.windows.removeAt(i); } } - mTempTouchedWallpaperChannels.clear(); #if DEBUG_FOCUS logDispatchStateLocked(); @@ -2334,12 +2493,13 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { } dump.appendFormat(" focusedWindow: name='%s'\n", mFocusedWindow != NULL ? mFocusedWindow->name.string() : ""); - dump.appendFormat(" touchedWindow: name='%s', touchDown=%d\n", - mTouchedWindow != NULL ? mTouchedWindow->name.string() : "", - mTouchDown); - for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) { - dump.appendFormat(" touchedWallpaperWindows[%d]: name='%s'\n", - i, mTouchedWallpaperWindows[i]->name.string()); + dump.appendFormat(" touchState: down=%s, split=%s\n", toString(mTouchState.down), + toString(mTouchState.split)); + for (size_t i = 0; i < mTouchState.windows.size(); i++) { + const TouchedWindow& touchedWindow = mTouchState.windows[i]; + dump.appendFormat(" touchedWindow[%d]: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", + i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value, + touchedWindow.targetFlags); } for (size_t i = 0; i < mWindows.size(); i++) { dump.appendFormat(" windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " @@ -2594,8 +2754,7 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) { mLock.unlock(); - mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType, - commandEntry->userActivityEventType); + mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType); mLock.lock(); } @@ -2627,17 +2786,32 @@ uint32_t InputDispatcher::Queue::count() const { InputDispatcher::Allocator::Allocator() { } +InputDispatcher::InjectionState* +InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) { + InjectionState* injectionState = mInjectionStatePool.alloc(); + injectionState->refCount = 1; + injectionState->injectorPid = injectorPid; + injectionState->injectorUid = injectorUid; + injectionState->injectionIsAsync = false; + injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING; + injectionState->pendingForegroundDispatches = 0; + return injectionState; +} + 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; + entry->injectionState = NULL; +} + +void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) { + if (entry->injectionState) { + releaseInjectionState(entry->injectionState); + entry->injectionState = NULL; + } } InputDispatcher::ConfigurationChangedEntry* @@ -2720,6 +2894,15 @@ InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Co return entry; } +void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injectionState) { + injectionState->refCount -= 1; + if (injectionState->refCount == 0) { + mInjectionStatePool.free(injectionState); + } else { + assert(injectionState->refCount > 0); + } +} + void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) { switch (entry->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: @@ -2741,6 +2924,7 @@ void InputDispatcher::Allocator::releaseConfigurationChangedEntry( ConfigurationChangedEntry* entry) { entry->refCount -= 1; if (entry->refCount == 0) { + releaseEventEntryInjectionState(entry); mConfigurationChangeEntryPool.free(entry); } else { assert(entry->refCount > 0); @@ -2750,6 +2934,7 @@ void InputDispatcher::Allocator::releaseConfigurationChangedEntry( void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) { entry->refCount -= 1; if (entry->refCount == 0) { + releaseEventEntryInjectionState(entry); mKeyEntryPool.free(entry); } else { assert(entry->refCount > 0); @@ -2759,6 +2944,7 @@ void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) { void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) { entry->refCount -= 1; if (entry->refCount == 0) { + releaseEventEntryInjectionState(entry); for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) { MotionSample* next = sample->next; mMotionSamplePool.free(sample); @@ -2793,22 +2979,12 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, motionEntry->lastSample = sample; } +void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) { + releaseEventEntryInjectionState(keyEntry); -// --- 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; + keyEntry->dispatchInProgress = false; + keyEntry->syntheticRepeat = false; + keyEntry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; } @@ -3057,6 +3233,72 @@ InputDispatcher::CommandEntry::~CommandEntry() { } +// --- InputDispatcher::TouchState --- + +InputDispatcher::TouchState::TouchState() : + down(false), split(false) { +} + +InputDispatcher::TouchState::~TouchState() { +} + +void InputDispatcher::TouchState::reset() { + down = false; + split = false; + windows.clear(); +} + +void InputDispatcher::TouchState::copyFrom(const TouchState& other) { + down = other.down; + split = other.split; + windows.clear(); + windows.appendVector(other.windows); +} + +void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window, + int32_t targetFlags, BitSet32 pointerIds) { + if (targetFlags & InputTarget::FLAG_SPLIT) { + split = true; + } + + for (size_t i = 0; i < windows.size(); i++) { + TouchedWindow& touchedWindow = windows.editItemAt(i); + if (touchedWindow.window == window) { + touchedWindow.targetFlags |= targetFlags; + touchedWindow.pointerIds.value |= pointerIds.value; + return; + } + } + + windows.push(); + + TouchedWindow& touchedWindow = windows.editTop(); + touchedWindow.window = window; + touchedWindow.targetFlags = targetFlags; + touchedWindow.pointerIds = pointerIds; + touchedWindow.channel = window->inputChannel; +} + +void InputDispatcher::TouchState::removeOutsideTouchWindows() { + for (size_t i = 0 ; i < windows.size(); ) { + if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) { + windows.removeAt(i); + } else { + i += 1; + } + } +} + +const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() { + for (size_t i = 0; i < windows.size(); i++) { + if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) { + return windows[i].window; + } + } + return NULL; +} + + // --- InputDispatcherThread --- InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) : diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 783cbc427..f2b029a5e 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -3366,7 +3366,7 @@ void MultiTouchInputMapper::sync(nsecs_t when) { if (id > MAX_POINTER_ID) { #if DEBUG_POINTERS LOGD("Pointers: Ignoring driver provided pointer id %d because " - "it is larger than max supported id %d for optimizations", + "it is larger than max supported id %d", id, MAX_POINTER_ID); #endif havePointerIds = false;