diff options
-rw-r--r-- | include/ui/InputDispatcher.h | 507 | ||||
-rw-r--r-- | include/ui/InputManager.h | 71 | ||||
-rw-r--r-- | include/ui/InputReader.h | 16 | ||||
-rw-r--r-- | include/ui/PowerManager.h | 37 | ||||
-rw-r--r-- | include/utils/PollLoop.h | 38 | ||||
-rw-r--r-- | libs/ui/InputDispatcher.cpp | 2162 | ||||
-rw-r--r-- | libs/ui/InputManager.cpp | 48 | ||||
-rw-r--r-- | libs/ui/InputReader.cpp | 165 | ||||
-rw-r--r-- | libs/utils/PollLoop.cpp | 8 |
9 files changed, 2394 insertions, 658 deletions
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index f00f2db..a06208a 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -30,6 +30,7 @@ #include <stddef.h> #include <unistd.h> +#include <limits.h> namespace android { @@ -108,9 +109,12 @@ struct InputTarget { // Flags for the input target. int32_t flags; - // The timeout for event delivery to this target in nanoseconds. Or -1 if none. + // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely. nsecs_t timeout; + // The time already spent waiting for this target in nanoseconds, or 0 if none. + nsecs_t timeSpentWaitingForApplication; + // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) float xOffset, yOffset; @@ -118,6 +122,122 @@ struct InputTarget { /* + * An input window describes the bounds of a window that can receive input. + */ +struct InputWindow { + // Window flags from WindowManager.LayoutParams + enum { + FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, + FLAG_DIM_BEHIND = 0x00000002, + FLAG_BLUR_BEHIND = 0x00000004, + FLAG_NOT_FOCUSABLE = 0x00000008, + FLAG_NOT_TOUCHABLE = 0x00000010, + FLAG_NOT_TOUCH_MODAL = 0x00000020, + FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, + FLAG_KEEP_SCREEN_ON = 0x00000080, + FLAG_LAYOUT_IN_SCREEN = 0x00000100, + FLAG_LAYOUT_NO_LIMITS = 0x00000200, + FLAG_FULLSCREEN = 0x00000400, + FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, + FLAG_DITHER = 0x00001000, + FLAG_SECURE = 0x00002000, + FLAG_SCALED = 0x00004000, + FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, + FLAG_LAYOUT_INSET_DECOR = 0x00010000, + FLAG_ALT_FOCUSABLE_IM = 0x00020000, + FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, + FLAG_SHOW_WHEN_LOCKED = 0x00080000, + FLAG_SHOW_WALLPAPER = 0x00100000, + FLAG_TURN_SCREEN_ON = 0x00200000, + FLAG_DISMISS_KEYGUARD = 0x00400000, + FLAG_IMMERSIVE = 0x00800000, + FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000, + FLAG_COMPATIBLE_WINDOW = 0x20000000, + FLAG_SYSTEM_ERROR = 0x40000000, + }; + + // Window types from WindowManager.LayoutParams + enum { + FIRST_APPLICATION_WINDOW = 1, + TYPE_BASE_APPLICATION = 1, + TYPE_APPLICATION = 2, + TYPE_APPLICATION_STARTING = 3, + LAST_APPLICATION_WINDOW = 99, + FIRST_SUB_WINDOW = 1000, + TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, + TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1, + TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2, + TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3, + TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4, + LAST_SUB_WINDOW = 1999, + FIRST_SYSTEM_WINDOW = 2000, + TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, + TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1, + TYPE_PHONE = FIRST_SYSTEM_WINDOW+2, + TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3, + TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4, + TYPE_TOAST = FIRST_SYSTEM_WINDOW+5, + TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6, + TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7, + TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8, + TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9, + TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10, + TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11, + TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12, + TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13, + TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14, + LAST_SYSTEM_WINDOW = 2999, + }; + + sp<InputChannel> inputChannel; + int32_t layoutParamsFlags; + int32_t layoutParamsType; + nsecs_t dispatchingTimeout; + int32_t frameLeft; + int32_t frameTop; + int32_t frameRight; + int32_t frameBottom; + int32_t visibleFrameLeft; + int32_t visibleFrameTop; + int32_t visibleFrameRight; + int32_t visibleFrameBottom; + int32_t touchableAreaLeft; + int32_t touchableAreaTop; + int32_t touchableAreaRight; + int32_t touchableAreaBottom; + bool visible; + bool hasFocus; + bool hasWallpaper; + bool paused; + int32_t ownerPid; + int32_t ownerUid; + + bool visibleFrameIntersects(const InputWindow* other) const; + bool touchableAreaContainsPoint(int32_t x, int32_t y) const; +}; + + +/* + * A private handle type used by the input manager to track the window. + */ +class InputApplicationHandle : public RefBase { +protected: + InputApplicationHandle() { } + virtual ~InputApplicationHandle() { } +}; + + +/* + * An input application describes properties of an application that can receive input. + */ +struct InputApplication { + String8 name; + nsecs_t dispatchingTimeout; + sp<InputApplicationHandle> handle; +}; + + +/* * Input dispatcher policy interface. * * The input reader policy is used by the input reader to interact with the Window Manager @@ -135,14 +255,16 @@ public: /* Notifies the system that a configuration change has occurred. */ virtual void notifyConfigurationChanged(nsecs_t when) = 0; + /* Notifies the system that an application is not responding. + * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ + virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0; + /* Notifies the system that an input channel is unrecoverably broken. */ virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0; /* Notifies the system that an input channel is not responding. - * Returns true and a new timeout value if the dispatcher should keep waiting. - * Otherwise returns false. */ - virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel, - nsecs_t& outNewTimeout) = 0; + * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ + virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0; /* Notifies the system that an input channel recovered from ANR. */ virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0; @@ -153,29 +275,27 @@ public: /* Gets the key repeat inter-key delay. */ virtual nsecs_t getKeyRepeatDelay() = 0; - /* 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 waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - int32_t injectorPid, int32_t injectorUid, - Vector<InputTarget>& outTargets) = 0; - - /* 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 waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - int32_t injectorPid, int32_t injectorUid, - Vector<InputTarget>& outTargets) = 0; - /* Gets the maximum suggested event delivery rate per second. * This value is used to throttle motion event movement actions on a per-device * basis. It is not intended to be a hard limit. */ virtual int32_t getMaxEventsPerSecond() = 0; + + /* Allows the policy a chance to intercept a key before dispatching. */ + virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, + 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; + + /* Checks whether a given application pid/uid has permission to inject input events + * into other applications. + * + * This method is special in that its implementation promises to be non-reentrant and + * is safe to call while holding other locks. (Most other methods make no such guarantees!) + */ + virtual bool checkInjectEventsPermissionNonReentrant( + int32_t injectorPid, int32_t injectorUid) = 0; }; @@ -187,6 +307,11 @@ protected: virtual ~InputDispatcherInterface() { } public: + /* Dumps the state of the input dispatcher. + * + * This method may be called on any thread (usually by the input manager). */ + virtual void dump(String8& dump) = 0; + /* Runs a single iteration of the dispatch loop. * Nominally processes one queued event, a timeout, or a response from an input consumer. * @@ -199,7 +324,6 @@ public: * These methods should only be called on the input reader thread. */ virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0; - virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0; virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; @@ -219,6 +343,24 @@ public: virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0; + /* Sets the list of input windows. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputWindows(const Vector<InputWindow>& inputWindows) = 0; + + /* Sets the focused application. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedApplication(const InputApplication* inputApplication) = 0; + + /* Sets the input dispatching mode. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputDispatchMode(bool enabled, bool frozen) = 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 @@ -230,10 +372,11 @@ public: virtual void preemptInputDispatch() = 0; /* Registers or unregister input channels that may be used as targets for input events. + * If monitor is true, the channel will receive a copy of all input events. * * These methods may be called on any thread (usually by the input manager). */ - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0; + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) = 0; virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; }; @@ -261,10 +404,11 @@ protected: public: explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); + virtual void dump(String8& dump); + virtual void dispatchOnce(); virtual void notifyConfigurationChanged(nsecs_t eventTime); - virtual void notifyAppSwitchComing(nsecs_t eventTime); virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime); @@ -277,9 +421,12 @@ public: virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis); + virtual void setInputWindows(const Vector<InputWindow>& inputWindows); + virtual void setFocusedApplication(const InputApplication* inputApplication); + virtual void setInputDispatchMode(bool enabled, bool frozen); virtual void preemptInputDispatch(); - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel); + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor); virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); private: @@ -310,6 +457,8 @@ private: int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress inline bool isInjected() { return injectorPid >= 0; } + + void recycle(); }; struct ConfigurationChangedEntry : EventEntry { @@ -326,6 +475,17 @@ private: int32_t metaState; int32_t repeatCount; nsecs_t downTime; + + bool syntheticRepeat; // set to true for synthetic key repeats + + enum InterceptKeyResult { + INTERCEPT_KEY_RESULT_UNKNOWN, + INTERCEPT_KEY_RESULT_SKIP, + INTERCEPT_KEY_RESULT_CONTINUE, + }; + InterceptKeyResult interceptKeyResult; // set based on the interception result + + void recycle(); }; struct MotionSample { @@ -380,9 +540,13 @@ private: // will be set to NULL. MotionSample* tailMotionSample; - inline bool isSyncTarget() { + inline bool isSyncTarget() const { return targetFlags & InputTarget::FLAG_SYNC; } + + inline void preemptSyncTarget() { + targetFlags &= ~ InputTarget::FLAG_SYNC; + } }; // A command entry captures state and behavior for an action to be performed in the @@ -413,37 +577,43 @@ private: // parameters for the command (usage varies by command) sp<Connection> connection; + nsecs_t eventTime; + KeyEntry* keyEntry; + sp<InputChannel> inputChannel; + sp<InputApplicationHandle> inputApplicationHandle; + int32_t windowType; + int32_t userActivityEventType; }; // Generic queue implementation. template <typename T> struct Queue { - T head; - T tail; + T headSentinel; + T tailSentinel; inline Queue() { - head.prev = NULL; - head.next = & tail; - tail.prev = & head; - tail.next = NULL; + headSentinel.prev = NULL; + headSentinel.next = & tailSentinel; + tailSentinel.prev = & headSentinel; + tailSentinel.next = NULL; } - inline bool isEmpty() { - return head.next == & tail; + inline bool isEmpty() const { + return headSentinel.next == & tailSentinel; } inline void enqueueAtTail(T* entry) { - T* last = tail.prev; + T* last = tailSentinel.prev; last->next = entry; entry->prev = last; - entry->next = & tail; - tail.prev = entry; + entry->next = & tailSentinel; + tailSentinel.prev = entry; } inline void enqueueAtHead(T* entry) { - T* first = head.next; - head.next = entry; - entry->prev = & head; + T* first = headSentinel.next; + headSentinel.next = entry; + entry->prev = & headSentinel; entry->next = first; first->prev = entry; } @@ -454,7 +624,7 @@ private: } inline T* dequeueAtHead() { - T* first = head.next; + T* first = headSentinel.next; dequeue(first); return first; } @@ -476,7 +646,8 @@ private: float xPrecision, float yPrecision, nsecs_t downTime, uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords); - DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry); + DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry, + int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout); CommandEntry* obtainCommandEntry(Command command); void releaseEventEntry(EventEntry* entry); @@ -500,6 +671,85 @@ private: void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime); }; + /* Tracks dispatched key and motion event state so that cancelation events can be + * synthesized when events are dropped. */ + class InputState { + public: + // Specifies whether a given event will violate input state consistency. + enum Consistency { + // The event is consistent with the current input state. + CONSISTENT, + // The event is inconsistent with the current input state but applications + // will tolerate it. eg. Down followed by another down. + TOLERABLE, + // The event is inconsistent with the current input state and will probably + // cause applications to crash. eg. Up without prior down, move with + // unexpected number of pointers. + BROKEN + }; + + InputState(); + ~InputState(); + + // Returns true if there is no state to be canceled. + bool isNeutral() const; + + // Returns true if the input state believes it is out of sync. + bool isOutOfSync() const; + + // Sets the input state to be out of sync if it is not neutral. + void setOutOfSync(); + + // Resets the input state out of sync flag. + void resetOutOfSync(); + + // Records tracking information for an event that has just been published. + // Returns whether the event is consistent with the current input state. + Consistency trackEvent(const EventEntry* entry); + + // Records tracking information for a key event that has just been published. + // Returns whether the event is consistent with the current input state. + Consistency trackKey(const KeyEntry* entry); + + // Records tracking information for a motion event that has just been published. + // Returns whether the event is consistent with the current input state. + Consistency trackMotion(const MotionEntry* entry); + + // Synthesizes cancelation events for the current state. + void synthesizeCancelationEvents(Allocator* allocator, + Vector<EventEntry*>& outEvents) const; + + // Clears the current state. + void clear(); + + private: + bool mIsOutOfSync; + + struct KeyMemento { + int32_t deviceId; + int32_t source; + int32_t keyCode; + int32_t scanCode; + nsecs_t downTime; + }; + + struct MotionMemento { + int32_t deviceId; + int32_t source; + float xPrecision; + float yPrecision; + nsecs_t downTime; + uint32_t pointerCount; + int32_t pointerIds[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + + void setPointers(const MotionEntry* entry); + }; + + Vector<KeyMemento> mKeyMementos; + Vector<MotionMemento> mMotionMementos; + }; + /* Manages the dispatch state associated with a single input channel. */ class Connection : public RefBase { protected: @@ -520,6 +770,7 @@ private: Status status; sp<InputChannel> inputChannel; InputPublisher inputPublisher; + InputState inputState; Queue<DispatchEntry> outboundQueue; nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none) @@ -540,28 +791,34 @@ private: // Determine whether this connection has a pending synchronous dispatch target. // Since there can only ever be at most one such target at a time, if there is one, // it must be at the tail because nothing else can be enqueued after it. - inline bool hasPendingSyncTarget() { - return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget(); + inline bool hasPendingSyncTarget() const { + return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget(); + } + + // Assuming there is a pending sync target, make it async. + inline void preemptSyncTarget() { + outboundQueue.tailSentinel.prev->preemptSyncTarget(); } // Gets the time since the current event was originally obtained from the input driver. - inline double getEventLatencyMillis(nsecs_t currentTime) { + inline double getEventLatencyMillis(nsecs_t currentTime) const { return (currentTime - lastEventTime) / 1000000.0; } // Gets the time since the current event entered the outbound dispatch queue. - inline double getDispatchLatencyMillis(nsecs_t currentTime) { + inline double getDispatchLatencyMillis(nsecs_t currentTime) const { return (currentTime - lastDispatchTime) / 1000000.0; } // Gets the time since the current event ANR was declared, if applicable. - inline double getANRLatencyMillis(nsecs_t currentTime) { + inline double getANRLatencyMillis(nsecs_t currentTime) const { return (currentTime - lastANRTime) / 1000000.0; } status_t initialize(); void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout); + void resetTimeout(nsecs_t currentTime); }; sp<InputDispatcherPolicyInterface> mPolicy; @@ -571,9 +828,26 @@ private: Allocator mAllocator; sp<PollLoop> mPollLoop; + EventEntry* mPendingEvent; Queue<EventEntry> mInboundQueue; Queue<CommandEntry> mCommandQueue; + Vector<EventEntry*> mTempCancelationEvents; + + void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay, + nsecs_t* nextWakeupTime); + + // Enqueues an inbound event. Returns true if mPollLoop->wake() should be called. + bool enqueueInboundEventLocked(EventEntry* entry); + + // App switch latency optimization. + nsecs_t mAppSwitchDueTime; + + static bool isAppSwitchKey(int32_t keyCode); + bool isAppSwitchPendingLocked(); + bool detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry); + void resetPendingAppSwitchLocked(bool handled); + // All registered connections mapped by receive pipe file descriptor. KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd; @@ -591,20 +865,15 @@ private: // the duration. Vector<Connection*> mTimedOutConnections; - // Preallocated key and motion event objects used only to ask the input dispatcher policy - // for the targets of an event that is to be dispatched. - KeyEvent mReusableKeyEvent; - MotionEvent mReusableMotionEvent; + // Input channels that will receive a copy of all input events. + Vector<sp<InputChannel> > mMonitoringChannels; - // The input targets that were most recently identified for dispatch. - // If there is a synchronous event dispatch in progress, the current input targets will - // remain unchanged until the dispatch has completed or been aborted. - Vector<InputTarget> mCurrentInputTargets; - bool mCurrentInputTargetsValid; // false while targets are being recomputed + // Preallocated key event object used for policy inquiries. + KeyEvent mReusableKeyEvent; // Event injection and synchronization. Condition mInjectionResultAvailableCondition; - EventEntry* createEntryFromInputEventLocked(const InputEvent* event); + EventEntry* createEntryFromInjectedInputEventLocked(const InputEvent* event); void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult); Condition mInjectionSyncFinishedCondition; @@ -622,36 +891,108 @@ private: } mThrottleState; // Key repeat tracking. - // XXX Move this up to the input reader instead. struct KeyRepeatState { KeyEntry* lastKeyEntry; // or null if no repeat nsecs_t nextRepeatTime; } mKeyRepeatState; void resetKeyRepeatLocked(); + KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime, nsecs_t keyRepeatTimeout); // Deferred command processing. bool runCommandsLockedInterruptible(); CommandEntry* postCommandLocked(Command command); - // Process events that have just been dequeued from the head of the input queue. - void processConfigurationChangedLockedInterruptible( + // Inbound event processing. + void drainInboundQueueLocked(); + void releasePendingEventLocked(bool wasDropped); + void releaseInboundEventLocked(EventEntry* entry, bool wasDropped); + bool isEventFromReliableSourceLocked(EventEntry* entry); + + // Dispatch state. + bool mDispatchEnabled; + bool mDispatchFrozen; + Vector<InputWindow> mWindows; + Vector<InputWindow*> mWallpaperWindows; + + // Focus tracking for keys, trackball, etc. + 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<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets + struct OutsideTarget { + InputWindow* window; + bool obscured; + }; + Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets + Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets + + // Focused application. + InputApplication* mFocusedApplication; + InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication + void releaseFocusedApplicationLocked(); + + // Dispatch inbound events. + bool dispatchConfigurationChangedLocked( nsecs_t currentTime, ConfigurationChangedEntry* entry); - void processKeyLockedInterruptible( - nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout); - void processKeyRepeatLockedInterruptible( - nsecs_t currentTime, nsecs_t keyRepeatTimeout); - void processMotionLockedInterruptible( - nsecs_t currentTime, MotionEntry* entry); - - // Identify input targets for an event and dispatch to them. - void identifyInputTargetsAndDispatchKeyLockedInterruptible( - nsecs_t currentTime, KeyEntry* entry); - void identifyInputTargetsAndDispatchMotionLockedInterruptible( - nsecs_t currentTime, MotionEntry* entry); + bool dispatchKeyLocked( + nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout, + nsecs_t* nextWakeupTime); + bool dispatchMotionLocked( + nsecs_t currentTime, MotionEntry* entry, + nsecs_t* nextWakeupTime); void dispatchEventToCurrentInputTargetsLocked( nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample); + void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry); + void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry); + + // The input targets that were most recently identified for dispatch. + // If there is a synchronous event dispatch in progress, the current input targets will + // remain unchanged until the dispatch has completed or been aborted. + bool mCurrentInputTargetsValid; // false while targets are being recomputed + Vector<InputTarget> mCurrentInputTargets; + int32_t mCurrentInputWindowType; + sp<InputChannel> mCurrentInputChannel; + + enum InputTargetWaitCause { + INPUT_TARGET_WAIT_CAUSE_NONE, + INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, + INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, + }; + + InputTargetWaitCause mInputTargetWaitCause; + nsecs_t mInputTargetWaitStartTime; + nsecs_t mInputTargetWaitTimeoutTime; + bool mInputTargetWaitTimeoutExpired; + + // Finding targets for input events. + void startFindingTargetsLocked(); + void finishFindingTargetsLocked(const InputWindow* window); + int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, + const InputApplication* application, const InputWindow* window, + nsecs_t* nextWakeupTime); + void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout); + nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(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); + + void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, + nsecs_t timeSpentWaitingForApplication); + 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 isWindowObscuredLocked(const InputWindow* window); + void releaseTouchedWindowLocked(); + // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work // with the mutex held makes it easier to ensure that connection invariants are maintained. @@ -659,15 +1000,25 @@ private: void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, bool resumeWithAppendedMotionSample); - void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, + nsecs_t timeSpentWaitingForApplication); void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, nsecs_t newTimeout); void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool broken); + void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain); static bool handleReceiveCallback(int receiveFd, int events, void* data); + // Preempting input dispatch. + bool preemptInputDispatchInnerLocked(); + + // Dump state. + void dumpDispatchStateLocked(String8& dump); + void logDispatchStateLocked(); + // Add or remove a connection to the mActiveConnections vector. void activateConnectionLocked(Connection* connection); void deactivateConnectionLocked(Connection* connection); @@ -683,9 +1034,13 @@ private: nsecs_t currentTime, const sp<Connection>& connection); // Outbound policy interactions. + void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry); void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry); void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry); + void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry); + void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry); + void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry); }; /* Enqueues and dispatches input events, endlessly. */ @@ -702,4 +1057,4 @@ private: } // namespace android -#endif // _UI_INPUT_DISPATCHER_PRIV_H +#endif // _UI_INPUT_DISPATCHER_H diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h index 4012c69..568568b 100644 --- a/include/ui/InputManager.h +++ b/include/ui/InputManager.h @@ -72,51 +72,11 @@ public: /* Stops the input manager threads and waits for them to exit. */ virtual status_t stop() = 0; - /* Registers an input channel prior to using it as the target of an event. */ - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0; - - /* Unregisters an input channel. */ - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; - - /* Injects an input event and optionally waits for sync. - * The synchronization mode determines whether the method blocks while waiting for - * input injection to proceed. - * Returns one of the INPUT_EVENT_INJECTION_XXX constants. - */ - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, 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) = 0; - - /* Gets information about the specified input device. - * Returns OK if the device information was obtained or NAME_NOT_FOUND if there - * was no such device. - */ - virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0; - - /* Gets the list of all registered device ids. */ - virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0; - - /* Queries current input state. */ - virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode) = 0; - virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode) = 0; - virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, - int32_t sw) = 0; - - /* Determines whether physical keys exist for the given framework-domain key codes. */ - virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0; + /* Gets the input reader. */ + virtual sp<InputReaderInterface> getReader() = 0; + + /* Gets the input dispatcher. */ + virtual sp<InputDispatcherInterface> getDispatcher() = 0; }; class InputManager : public InputManagerInterface { @@ -137,25 +97,8 @@ public: virtual status_t start(); virtual status_t stop(); - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel); - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); - - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis); - - virtual void preemptInputDispatch(); - - virtual void getInputConfiguration(InputConfiguration* outConfiguration); - virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo); - virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds); - virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode); - virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode); - virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, - int32_t sw); - virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + virtual sp<InputReaderInterface> getReader(); + virtual sp<InputDispatcherInterface> getDispatcher(); private: sp<InputReaderInterface> mReader; diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index 7a089a4..903c3c4 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -95,10 +95,6 @@ public: // The input dispatcher should dispatch the input to the application. ACTION_DISPATCH = 0x00000001, - - // The input dispatcher should perform special filtering in preparation for - // a pending app switch. - ACTION_APP_SWITCH_COMING = 0x00000002, }; /* Gets information about the display with the specified id. @@ -168,6 +164,11 @@ protected: virtual ~InputReaderInterface() { } public: + /* Dumps the state of the input reader. + * + * This method may be called on any thread (usually by the input manager). */ + virtual void dump(String8& dump) = 0; + /* Runs a single iteration of the processing loop. * Nominally reads and processes one incoming message from the EventHub. * @@ -240,6 +241,8 @@ public: const sp<InputDispatcherInterface>& dispatcher); virtual ~InputReader(); + virtual void dump(String8& dump); + virtual void loopOnce(); virtual void getInputConfiguration(InputConfiguration* outConfiguration); @@ -305,6 +308,9 @@ private: GetStateFunc getStateFunc); bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + + // dump state + void dumpDeviceInfo(String8& dump); }; @@ -759,9 +765,11 @@ protected: } mLocked; virtual void configureParameters(); + virtual void logParameters(); virtual void configureRawAxes(); virtual void logRawAxes(); virtual bool configureSurfaceLocked(); + virtual void logMotionRangesLocked(); virtual void configureVirtualKeysLocked(); virtual void parseCalibration(); virtual void resolveCalibration(); diff --git a/include/ui/PowerManager.h b/include/ui/PowerManager.h new file mode 100644 index 0000000..5434b4f --- /dev/null +++ b/include/ui/PowerManager.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_POWER_MANAGER_H +#define _UI_POWER_MANAGER_H + + +namespace android { + +enum { + POWER_MANAGER_OTHER_EVENT = 0, + POWER_MANAGER_CHEEK_EVENT = 1, + POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either + // up events or LONG_TOUCH events. + POWER_MANAGER_LONG_TOUCH_EVENT = 3, + POWER_MANAGER_TOUCH_UP_EVENT = 4, + POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events. + + POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code. +}; + +} // namespace android + +#endif // _UI_POWER_MANAGER_H diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index bc616eb..c2dfe5d 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -172,22 +172,36 @@ private: void* data; }; - const bool mAllowNonCallbacks; - + const bool mAllowNonCallbacks; // immutable + + int mWakeReadPipeFd; // immutable + int mWakeWritePipeFd; // immutable + + // The lock guards state used to track whether there is a poll() in progress and whether + // there are any other threads waiting in wakeAndLock(). The condition variables + // are used to transfer control among these threads such that all waiters are + // serviced before a new poll can begin. + // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake + // until mPolling becomes false, then decrements mWaiters again. + // The poll() method blocks on mResume until mWaiters becomes 0, then sets + // mPolling to true, blocks until the poll completes, then resets mPolling to false + // and signals mResume if there are waiters. Mutex mLock; - bool mPolling; - uint32_t mWaiters; - Condition mAwake; - Condition mResume; - - int mWakeReadPipeFd; - int mWakeWritePipeFd; - + bool mPolling; // guarded by mLock + uint32_t mWaiters; // guarded by mLock + Condition mAwake; // guarded by mLock + Condition mResume; // guarded by mLock + + // The next two vectors are only mutated when mPolling is false since they must + // not be changed while the poll() system call is in progress. To mutate these + // vectors, the poll() must first be awoken then the lock acquired. Vector<struct pollfd> mRequestedFds; Vector<RequestedCallback> mRequestedCallbacks; - Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce - Vector<PendingCallback> mPendingFds; // used privately by pollOnce + // This state is only used privately by pollOnce and does not require a lock since + // it runs on a single thread. + Vector<PendingCallback> mPendingCallbacks; + Vector<PendingCallback> mPendingFds; size_t mPendingFdsPos; void openWakePipe(); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index df232d4..b8a26b0 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -31,8 +31,15 @@ // Log debug messages about input event throttling. #define DEBUG_THROTTLING 0 +// Log debug messages about input focus tracking. +#define DEBUG_FOCUS 0 + +// Log debug messages about the app switch latency optimization. +#define DEBUG_APP_SWITCH 0 + #include <cutils/log.h> #include <ui/InputDispatcher.h> +#include <ui/PowerManager.h> #include <stddef.h> #include <unistd.h> @@ -41,31 +48,62 @@ namespace android { -// TODO, this needs to be somewhere else, perhaps in the policy -static inline bool isMovementKey(int32_t keyCode) { - return keyCode == AKEYCODE_DPAD_UP - || keyCode == AKEYCODE_DPAD_DOWN - || keyCode == AKEYCODE_DPAD_LEFT - || keyCode == AKEYCODE_DPAD_RIGHT; -} +// Delay between reporting long touch events to the power manager. +const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms + +// Default input dispatching timeout if there is no focused application or paused window +// from which to determine an appropriate dispatching timeout. +const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec + +// Amount of time to allow for all pending events to be processed when an app switch +// key is on the way. This is used to preempt input dispatch and drop input events +// when an application takes too long to respond and the user has pressed an app switch key. +const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec + static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + + +// --- InputWindow --- + +bool InputWindow::visibleFrameIntersects(const InputWindow* other) const { + return visibleFrameRight > other->visibleFrameLeft + && visibleFrameLeft < other->visibleFrameRight + && visibleFrameBottom > other->visibleFrameTop + && visibleFrameTop < other->visibleFrameBottom; +} + +bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const { + return x >= touchableAreaLeft && x <= touchableAreaRight + && y >= touchableAreaTop && y <= touchableAreaBottom; +} + + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : - mPolicy(policy) { + mPolicy(policy), + mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX), + mDispatchEnabled(true), mDispatchFrozen(false), + mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL), + mFocusedApplication(NULL), + mCurrentInputTargetsValid(false), + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mPollLoop = new PollLoop(false); - mInboundQueue.head.refCount = -1; - mInboundQueue.head.type = EventEntry::TYPE_SENTINEL; - mInboundQueue.head.eventTime = LONG_LONG_MIN; + mInboundQueue.headSentinel.refCount = -1; + mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL; + mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN; - mInboundQueue.tail.refCount = -1; - mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL; - mInboundQueue.tail.eventTime = LONG_LONG_MAX; + mInboundQueue.tailSentinel.refCount = -1; + mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL; + mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX; mKeyRepeatState.lastKeyEntry = NULL; @@ -77,21 +115,19 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mThrottleState.originalSampleCount = 0; LOGD("Throttling - Max events per second = %d", maxEventsPerSecond); #endif - - mCurrentInputTargetsValid = false; } InputDispatcher::~InputDispatcher() { - resetKeyRepeatLocked(); + { // acquire lock + AutoMutex _l(mLock); - while (mConnectionsByReceiveFd.size() != 0) { - unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); + resetKeyRepeatLocked(); + releasePendingEventLocked(true); + drainInboundQueueLocked(); } - for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) { - EventEntry* next = entry->next; - mAllocator.releaseEventEntry(next); - entry = next; + while (mConnectionsByReceiveFd.size() != 0) { + unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); } } @@ -99,167 +135,282 @@ void InputDispatcher::dispatchOnce() { nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay(); - bool skipPoll = false; - nsecs_t currentTime; nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); - currentTime = now(); + dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime); - // Reset the key repeat timer whenever we disallow key events, even if the next event - // is not a key. This is to ensure that we abort a key repeat if the device is just coming - // out of sleep. - // XXX we should handle resetting input state coming out of sleep more generally elsewhere - if (keyRepeatTimeout < 0) { - resetKeyRepeatLocked(); + if (runCommandsLockedInterruptible()) { + nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately } + } // release lock - // Detect and process timeouts for all connections and determine if there are any - // synchronous event dispatches pending. This step is entirely non-interruptible. - bool hasPendingSyncTarget = false; - size_t activeConnectionCount = mActiveConnections.size(); - for (size_t i = 0; i < activeConnectionCount; i++) { - Connection* connection = mActiveConnections.itemAt(i); + // Wait for callback or timeout or wake. (make sure we round up, not down) + nsecs_t currentTime = now(); + int32_t timeoutMillis; + if (nextWakeupTime > currentTime) { + uint64_t timeout = uint64_t(nextWakeupTime - currentTime); + timeout = (timeout + 999999LL) / 1000000LL; + timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout); + } else { + timeoutMillis = 0; + } - if (connection->hasPendingSyncTarget()) { - hasPendingSyncTarget = true; - } + mPollLoop->pollOnce(timeoutMillis); +} + +void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, + nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) { + nsecs_t currentTime = now(); + + // Reset the key repeat timer whenever we disallow key events, even if the next event + // is not a key. This is to ensure that we abort a key repeat if the device is just coming + // out of sleep. + if (keyRepeatTimeout < 0) { + resetKeyRepeatLocked(); + } + + // If dispatching is disabled, drop all events in the queue. + if (! mDispatchEnabled) { + if (mPendingEvent || ! mInboundQueue.isEmpty()) { + LOGI("Dropping pending events because input dispatch is disabled."); + releasePendingEventLocked(true); + drainInboundQueueLocked(); + } + return; + } + + // If dispatching is frozen, do not process timeouts or try to deliver any new events. + if (mDispatchFrozen) { +#if DEBUG_FOCUS + LOGD("Dispatch frozen. Waiting some more."); +#endif + return; + } - nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; - if (connectionTimeoutTime <= currentTime) { - mTimedOutConnections.add(connection); - } else if (connectionTimeoutTime < nextWakeupTime) { - nextWakeupTime = connectionTimeoutTime; + // Optimize latency of app switches. + // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has + // been pressed. When it expires, we preempt dispatch and drop all other pending events. + bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; + if (mAppSwitchDueTime < *nextWakeupTime) { + *nextWakeupTime = mAppSwitchDueTime; + } + + // Detect and process timeouts for all connections and determine if there are any + // synchronous event dispatches pending. This step is entirely non-interruptible. + bool havePendingSyncTarget = false; + size_t activeConnectionCount = mActiveConnections.size(); + for (size_t i = 0; i < activeConnectionCount; i++) { + Connection* connection = mActiveConnections.itemAt(i); + + if (connection->hasPendingSyncTarget()) { + if (isAppSwitchDue) { + connection->preemptSyncTarget(); + } else { + havePendingSyncTarget = true; } } - size_t timedOutConnectionCount = mTimedOutConnections.size(); - for (size_t i = 0; i < timedOutConnectionCount; i++) { - Connection* connection = mTimedOutConnections.itemAt(i); - timeoutDispatchCycleLocked(currentTime, connection); - skipPoll = true; - } - mTimedOutConnections.clear(); - - // If we don't have a pending sync target, then we can begin delivering a new event. - // (Otherwise we wait for dispatch to complete for that target.) - if (! hasPendingSyncTarget) { - if (mInboundQueue.isEmpty()) { - if (mKeyRepeatState.lastKeyEntry) { - if (currentTime >= mKeyRepeatState.nextRepeatTime) { - processKeyRepeatLockedInterruptible(currentTime, keyRepeatDelay); - skipPoll = true; - } else { - if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) { - nextWakeupTime = mKeyRepeatState.nextRepeatTime; - } + nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; + if (connectionTimeoutTime <= currentTime) { + mTimedOutConnections.add(connection); + } else if (connectionTimeoutTime < *nextWakeupTime) { + *nextWakeupTime = connectionTimeoutTime; + } + } + + size_t timedOutConnectionCount = mTimedOutConnections.size(); + for (size_t i = 0; i < timedOutConnectionCount; i++) { + Connection* connection = mTimedOutConnections.itemAt(i); + timeoutDispatchCycleLocked(currentTime, connection); + *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + } + mTimedOutConnections.clear(); + + // If we have a pending synchronous target, skip dispatch. + if (havePendingSyncTarget) { + return; + } + + // Ready to start a new event. + // If we don't already have a pending event, go grab one. + if (! mPendingEvent) { + if (mInboundQueue.isEmpty()) { + if (isAppSwitchDue) { + // The inbound queue is empty so the app switch key we were waiting + // for will never arrive. Stop waiting for it. + resetPendingAppSwitchLocked(false); + isAppSwitchDue = false; + } + + // Synthesize a key repeat if appropriate. + if (mKeyRepeatState.lastKeyEntry) { + if (currentTime >= mKeyRepeatState.nextRepeatTime) { + mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay); + } else { + if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { + *nextWakeupTime = mKeyRepeatState.nextRepeatTime; } } - } else { - // Inbound queue has at least one entry. - EventEntry* entry = mInboundQueue.head.next; - - // Consider throttling the entry if it is a move event and there are no - // other events behind it in the queue. Due to movement batching, additional - // samples may be appended to this event by the time the throttling timeout - // expires. - // TODO Make this smarter and consider throttling per device independently. - if (entry->type == EventEntry::TYPE_MOTION) { - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - int32_t deviceId = motionEntry->deviceId; - uint32_t source = motionEntry->source; - if (motionEntry->next == & mInboundQueue.tail - && motionEntry->action == AMOTION_EVENT_ACTION_MOVE - && deviceId == mThrottleState.lastDeviceId - && source == mThrottleState.lastSource) { - nsecs_t nextTime = mThrottleState.lastEventTime - + mThrottleState.minTimeBetweenEvents; - if (currentTime < nextTime) { - // Throttle it! + } + if (! mPendingEvent) { + return; + } + } else { + // Inbound queue has at least one entry. + EventEntry* entry = mInboundQueue.headSentinel.next; + + // Throttle the entry if it is a move event and there are no + // other events behind it in the queue. Due to movement batching, additional + // samples may be appended to this event by the time the throttling timeout + // expires. + // TODO Make this smarter and consider throttling per device independently. + if (entry->type == EventEntry::TYPE_MOTION) { + MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); + int32_t deviceId = motionEntry->deviceId; + uint32_t source = motionEntry->source; + if (! isAppSwitchDue + && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event + && motionEntry->action == AMOTION_EVENT_ACTION_MOVE + && deviceId == mThrottleState.lastDeviceId + && source == mThrottleState.lastSource) { + nsecs_t nextTime = mThrottleState.lastEventTime + + mThrottleState.minTimeBetweenEvents; + if (currentTime < nextTime) { + // Throttle it! #if DEBUG_THROTTLING - LOGD("Throttling - Delaying motion event for " - "device 0x%x, source 0x%08x by up to %0.3fms.", - deviceId, source, (nextTime - currentTime) * 0.000001); + LOGD("Throttling - Delaying motion event for " + "device 0x%x, source 0x%08x by up to %0.3fms.", + deviceId, source, (nextTime - currentTime) * 0.000001); #endif - if (nextTime < nextWakeupTime) { - nextWakeupTime = nextTime; - } - if (mThrottleState.originalSampleCount == 0) { - mThrottleState.originalSampleCount = - motionEntry->countSamples(); - } - goto Throttle; + if (nextTime < *nextWakeupTime) { + *nextWakeupTime = nextTime; + } + if (mThrottleState.originalSampleCount == 0) { + mThrottleState.originalSampleCount = + motionEntry->countSamples(); } + return; } + } #if DEBUG_THROTTLING - if (mThrottleState.originalSampleCount != 0) { - uint32_t count = motionEntry->countSamples(); - LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", - count - mThrottleState.originalSampleCount, - mThrottleState.originalSampleCount, count); - mThrottleState.originalSampleCount = 0; - } + if (mThrottleState.originalSampleCount != 0) { + uint32_t count = motionEntry->countSamples(); + LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", + count - mThrottleState.originalSampleCount, + mThrottleState.originalSampleCount, count); + mThrottleState.originalSampleCount = 0; + } #endif - mThrottleState.lastEventTime = entry->eventTime < currentTime - ? entry->eventTime : currentTime; - mThrottleState.lastDeviceId = deviceId; - mThrottleState.lastSource = source; - } + mThrottleState.lastEventTime = entry->eventTime < currentTime + ? entry->eventTime : currentTime; + mThrottleState.lastDeviceId = deviceId; + mThrottleState.lastSource = source; + } - // Start processing the entry but leave it on the queue until later so that the - // input reader can keep appending samples onto a motion event between the - // time we started processing it and the time we finally enqueue dispatch - // entries for it. - switch (entry->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: { - ConfigurationChangedEntry* typedEntry = - static_cast<ConfigurationChangedEntry*>(entry); - processConfigurationChangedLockedInterruptible(currentTime, typedEntry); - break; - } + mInboundQueue.dequeue(entry); + mPendingEvent = entry; + } + } - case EventEntry::TYPE_KEY: { - KeyEntry* typedEntry = static_cast<KeyEntry*>(entry); - processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout); - break; - } + // Now we have an event to dispatch. + assert(mPendingEvent != NULL); + bool wasDispatched = false; + bool wasDropped = false; + switch (mPendingEvent->type) { + case EventEntry::TYPE_CONFIGURATION_CHANGED: { + ConfigurationChangedEntry* typedEntry = + static_cast<ConfigurationChangedEntry*>(mPendingEvent); + wasDispatched = dispatchConfigurationChangedLocked(currentTime, typedEntry); + break; + } - case EventEntry::TYPE_MOTION: { - MotionEntry* typedEntry = static_cast<MotionEntry*>(entry); - processMotionLockedInterruptible(currentTime, typedEntry); - break; - } + case EventEntry::TYPE_KEY: { + KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); + if (isAppSwitchPendingLocked()) { + if (isAppSwitchKey(typedEntry->keyCode)) { + resetPendingAppSwitchLocked(true); + } else if (isAppSwitchDue) { + LOGI("Dropping key because of pending overdue app switch."); + wasDropped = true; + break; + } + } + wasDispatched = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout, + nextWakeupTime); + break; + } - default: - assert(false); - break; - } + case EventEntry::TYPE_MOTION: { + MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); + if (isAppSwitchDue) { + LOGI("Dropping motion because of pending overdue app switch."); + wasDropped = true; + break; + } + wasDispatched = dispatchMotionLocked(currentTime, typedEntry, nextWakeupTime); + break; + } - // Dequeue and release the event entry that we just processed. - mInboundQueue.dequeue(entry); - mAllocator.releaseEventEntry(entry); - skipPoll = true; + default: + assert(false); + wasDropped = true; + break; + } - Throttle: ; - } - } + if (wasDispatched || wasDropped) { + releasePendingEventLocked(wasDropped); + *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + } +} - // Run any deferred commands. - skipPoll |= runCommandsLockedInterruptible(); - } // release lock +bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { + bool needWake = mInboundQueue.isEmpty(); + mInboundQueue.enqueueAtTail(entry); - // If we dispatched anything, don't poll just now. Wait for the next iteration. - // Contents may have shifted during flight. - if (skipPoll) { - return; + switch (entry->type) { + case EventEntry::TYPE_KEY: + needWake |= detectPendingAppSwitchLocked(static_cast<KeyEntry*>(entry)); + break; } - // Wait for callback or timeout or wake. (make sure we round up, not down) - nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL; - int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; - mPollLoop->pollOnce(timeoutMillis); + return needWake; +} + +bool InputDispatcher::isAppSwitchKey(int32_t keyCode) { + return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL; +} + +bool InputDispatcher::isAppSwitchPendingLocked() { + return mAppSwitchDueTime != LONG_LONG_MAX; +} + +bool InputDispatcher::detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry) { + if (inboundKeyEntry->action == AKEY_EVENT_ACTION_UP + && ! (inboundKeyEntry->flags & AKEY_EVENT_FLAG_CANCELED) + && isAppSwitchKey(inboundKeyEntry->keyCode) + && isEventFromReliableSourceLocked(inboundKeyEntry)) { +#if DEBUG_APP_SWITCH + LOGD("App switch is pending!"); +#endif + mAppSwitchDueTime = inboundKeyEntry->eventTime + APP_SWITCH_TIMEOUT; + return true; // need wake + } + return false; +} + +void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { + mAppSwitchDueTime = LONG_LONG_MAX; + +#if DEBUG_APP_SWITCH + if (handled) { + LOGD("App switch has arrived."); + } else { + LOGD("App switch was abandoned."); + } +#endif } bool InputDispatcher::runCommandsLockedInterruptible() { @@ -285,78 +436,52 @@ InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command comman return commandEntry; } -void InputDispatcher::processConfigurationChangedLockedInterruptible( - nsecs_t currentTime, ConfigurationChangedEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime); -#endif - - // Reset key repeating in case a keyboard device was added or removed or something. - resetKeyRepeatLocked(); - - mLock.unlock(); - - mPolicy->notifyConfigurationChanged(entry->eventTime); +void InputDispatcher::drainInboundQueueLocked() { + while (! mInboundQueue.isEmpty()) { + EventEntry* entry = mInboundQueue.dequeueAtHead(); + releaseInboundEventLocked(entry, true /*wasDropped*/); + } +} - mLock.lock(); +void InputDispatcher::releasePendingEventLocked(bool wasDropped) { + if (mPendingEvent) { + releaseInboundEventLocked(mPendingEvent, wasDropped); + mPendingEvent = NULL; + } } -void InputDispatcher::processKeyLockedInterruptible( - nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " - "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", - entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, - entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->downTime); +void InputDispatcher::releaseInboundEventLocked(EventEntry* entry, bool wasDropped) { + if (wasDropped) { +#if DEBUG_DISPATCH_CYCLE + LOGD("Pending event was dropped."); #endif - - if (entry->action == AKEY_EVENT_ACTION_DOWN && ! entry->isInjected()) { - if (mKeyRepeatState.lastKeyEntry - && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { - // We have seen two identical key downs in a row which indicates that the device - // driver is automatically generating key repeats itself. We take note of the - // repeat here, but we disable our own next key repeat timer since it is clear that - // we will not need to synthesize key repeats ourselves. - entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves - } else { - // Not a repeat. Save key down state in case we do see a repeat later. - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; - } - mKeyRepeatState.lastKeyEntry = entry; - entry->refCount += 1; - } else { - resetKeyRepeatLocked(); + setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); } + mAllocator.releaseEventEntry(entry); +} - identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); +bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) { + return ! entry->isInjected() + || entry->injectorUid == 0 + || mPolicy->checkInjectEventsPermissionNonReentrant( + entry->injectorPid, entry->injectorUid); } -void InputDispatcher::processKeyRepeatLockedInterruptible( +void InputDispatcher::resetKeyRepeatLocked() { + if (mKeyRepeatState.lastKeyEntry) { + mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); + mKeyRepeatState.lastKeyEntry = NULL; + } +} + +InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked( nsecs_t currentTime, nsecs_t keyRepeatDelay) { KeyEntry* entry = mKeyRepeatState.lastKeyEntry; - // Search the inbound queue for a key up corresponding to this device. - // It doesn't make sense to generate a key repeat event if the key is already up. - for (EventEntry* queuedEntry = mInboundQueue.head.next; - queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) { - if (queuedEntry->type == EventEntry::TYPE_KEY) { - KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry); - if (queuedKeyEntry->deviceId == entry->deviceId - && entry->action == AKEY_EVENT_ACTION_UP) { - resetKeyRepeatLocked(); - return; - } - } - } - - // Synthesize a key repeat. // Reuse the repeated key entry if it is otherwise unreferenced. uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK; if (entry->refCount == 1) { + entry->recycle(); entry->eventTime = currentTime; entry->policyFlags = policyFlags; entry->repeatCount += 1; @@ -371,31 +496,194 @@ void InputDispatcher::processKeyRepeatLockedInterruptible( entry = newEntry; } + entry->syntheticRepeat = true; + + // Increment reference count since we keep a reference to the event in + // mKeyRepeatState.lastKeyEntry in addition to the one we return. + entry->refCount += 1; if (entry->repeatCount == 1) { entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; } mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay; + return entry; +} + +bool InputDispatcher::dispatchConfigurationChangedLocked( + nsecs_t currentTime, ConfigurationChangedEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime); +#endif + + // Reset key repeating in case a keyboard device was added or removed or something. + resetKeyRepeatLocked(); + + // Enqueue a command to run outside the lock to tell the policy that the configuration changed. + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyConfigurationChangedInterruptible); + commandEntry->eventTime = entry->eventTime; + return true; +} +bool InputDispatcher::dispatchKeyLocked( + nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout, + nsecs_t* nextWakeupTime) { + // Preprocessing. + if (! entry->dispatchInProgress) { + logOutboundKeyDetailsLocked("dispatchKey - ", entry); + + if (entry->repeatCount == 0 + && entry->action == AKEY_EVENT_ACTION_DOWN + && ! entry->isInjected()) { + if (mKeyRepeatState.lastKeyEntry + && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { + // We have seen two identical key downs in a row which indicates that the device + // driver is automatically generating key repeats itself. We take note of the + // repeat here, but we disable our own next key repeat timer since it is clear that + // we will not need to synthesize key repeats ourselves. + entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves + } else { + // Not a repeat. Save key down state in case we do see a repeat later. + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; + } + mKeyRepeatState.lastKeyEntry = entry; + entry->refCount += 1; + } else if (! entry->syntheticRepeat) { + resetKeyRepeatLocked(); + } + + entry->dispatchInProgress = true; + startFindingTargetsLocked(); + } + + // Identify targets. + if (! mCurrentInputTargetsValid) { + InputWindow* window = NULL; + int32_t injectionResult = findFocusedWindowLocked(currentTime, + entry, nextWakeupTime, & window); + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; + } + + setInjectionResultLocked(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + return true; + } + + addMonitoringTargetsLocked(); + finishFindingTargetsLocked(window); + } + + // Give the policy a chance to intercept the key. + if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); + commandEntry->inputChannel = mCurrentInputChannel; + commandEntry->keyEntry = entry; + entry->refCount += 1; + return false; // wait for the command to run + } + if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { + return true; + } + + // Dispatch the key. + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + + // Poke user activity. + pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT); + return true; +} + +void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, " - "repeatCount=%d, downTime=%lld", + "downTime=%lld", + prefix, entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->repeatCount, entry->downTime); + entry->downTime); #endif +} + +bool InputDispatcher::dispatchMotionLocked( + nsecs_t currentTime, MotionEntry* entry, nsecs_t* nextWakeupTime) { + // Preprocessing. + if (! entry->dispatchInProgress) { + logOutboundMotionDetailsLocked("dispatchMotion - ", entry); + + entry->dispatchInProgress = true; + startFindingTargetsLocked(); + } + + bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; + + // Identify targets. + if (! mCurrentInputTargetsValid) { + InputWindow* window = NULL; + int32_t injectionResult; + if (isPointerEvent) { + // Pointer event. (eg. touchscreen) + injectionResult = findTouchedWindowLocked(currentTime, + entry, nextWakeupTime, & window); + } else { + // Non touch event. (eg. trackball) + injectionResult = findFocusedWindowLocked(currentTime, + entry, nextWakeupTime, & window); + } + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; + } + + setInjectionResultLocked(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + return true; + } - identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); + addMonitoringTargetsLocked(); + finishFindingTargetsLocked(window); + } + + // Dispatch the motion. + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + + // Poke user activity. + int32_t eventType; + if (isPointerEvent) { + switch (entry->action) { + case AMOTION_EVENT_ACTION_DOWN: + eventType = POWER_MANAGER_TOUCH_EVENT; + break; + case AMOTION_EVENT_ACTION_UP: + eventType = POWER_MANAGER_TOUCH_UP_EVENT; + break; + default: + if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) { + eventType = POWER_MANAGER_TOUCH_EVENT; + } else { + eventType = POWER_MANAGER_LONG_TOUCH_EVENT; + } + break; + } + } else { + eventType = POWER_MANAGER_BUTTON_EVENT; + } + pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType); + return true; } -void InputDispatcher::processMotionLockedInterruptible( - nsecs_t currentTime, MotionEntry* entry) { + +void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " "action=0x%x, flags=0x%x, " "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld", + prefix, entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, entry->flags, entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, @@ -403,7 +691,7 @@ void InputDispatcher::processMotionLockedInterruptible( // Print the most recent sample that we have available, this may change due to batching. size_t sampleCount = 1; - MotionSample* sample = & entry->firstSample; + const MotionSample* sample = & entry->firstSample; for (; sample->next != NULL; sample = sample->next) { sampleCount += 1; } @@ -425,94 +713,541 @@ void InputDispatcher::processMotionLockedInterruptible( LOGD(" ... Total movement samples currently batched %d ...", sampleCount); } #endif - - identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry); } -void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( - nsecs_t currentTime, KeyEntry* entry) { +void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, + EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { #if DEBUG_DISPATCH_CYCLE - LOGD("identifyInputTargetsAndDispatchKey"); + LOGD("dispatchEventToCurrentInputTargets - " + "resumeWithAppendedMotionSample=%s", + toString(resumeWithAppendedMotionSample)); #endif - entry->dispatchInProgress = true; - mCurrentInputTargetsValid = false; - mLock.unlock(); + assert(eventEntry->dispatchInProgress); // should already have been set to true - mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, - entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, - entry->downTime, entry->eventTime); + for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { + const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); + ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel); + if (connectionIndex >= 0) { + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, + resumeWithAppendedMotionSample); + } else { + LOGW("Framework requested delivery of an input event to channel '%s' but it " + "is not registered with the input dispatcher.", + inputTarget.inputChannel->getName().string()); + } + } +} + +void InputDispatcher::startFindingTargetsLocked() { + mCurrentInputTargetsValid = false; mCurrentInputTargets.clear(); - int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent, - entry->policyFlags, entry->injectorPid, entry->injectorUid, - mCurrentInputTargets); + mCurrentInputChannel.clear(); + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; +} - mLock.lock(); +void InputDispatcher::finishFindingTargetsLocked(const InputWindow* window) { + mCurrentInputWindowType = window->layoutParamsType; + mCurrentInputChannel = window->inputChannel; mCurrentInputTargetsValid = true; +} + +int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, + const EventEntry* entry, const InputApplication* application, const InputWindow* window, + nsecs_t* nextWakeupTime) { + if (application == NULL && window == NULL) { + if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { +#if DEBUG_FOCUS + LOGD("Waiting for system to become ready for input."); +#endif + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; + mInputTargetWaitStartTime = currentTime; + mInputTargetWaitTimeoutTime = LONG_LONG_MAX; + mInputTargetWaitTimeoutExpired = false; + } + } else { + if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { +#if DEBUG_FOCUS + LOGD("Waiting for application to become ready for input: name=%s, window=%s", + application ? application->name.string() : "<unknown>", + window ? window->inputChannel->getName().string() : "<unknown>"); +#endif + nsecs_t timeout = window ? window->dispatchingTimeout : + application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT; + + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; + mInputTargetWaitStartTime = currentTime; + mInputTargetWaitTimeoutTime = currentTime + timeout; + mInputTargetWaitTimeoutExpired = false; + } + } + + if (mInputTargetWaitTimeoutExpired) { + return INPUT_EVENT_INJECTION_TIMED_OUT; + } + + if (currentTime >= mInputTargetWaitTimeoutTime) { + LOGI("Application is not ready for input: name=%s, window=%s," + "%01.1fms since event, %01.1fms since wait started", + application ? application->name.string() : "<unknown>", + window ? window->inputChannel->getName().string() : "<unknown>", + (currentTime - entry->eventTime) / 1000000.0, + (currentTime - mInputTargetWaitStartTime) / 1000000.0); + + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible); + if (application) { + commandEntry->inputApplicationHandle = application->handle; + } + if (window) { + commandEntry->inputChannel = window->inputChannel; + } + + // Force poll loop to wake up immediately on next iteration once we get the + // ANR response back from the policy. + *nextWakeupTime = LONG_LONG_MIN; + return INPUT_EVENT_INJECTION_PENDING; + } else { + // Force poll loop to wake up when timeout is due. + if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { + *nextWakeupTime = mInputTargetWaitTimeoutTime; + } + return INPUT_EVENT_INJECTION_PENDING; + } +} - setInjectionResultLocked(entry, injectionResult); +void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) { + if (newTimeout > 0) { + // Extend the timeout. + mInputTargetWaitTimeoutTime = now() + newTimeout; + } else { + // Give up. + mInputTargetWaitTimeoutExpired = true; + } +} - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); +nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked( + nsecs_t currentTime) { + if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { + return currentTime - mInputTargetWaitStartTime; } + return 0; } -void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( - nsecs_t currentTime, MotionEntry* entry) { -#if DEBUG_DISPATCH_CYCLE - LOGD("identifyInputTargetsAndDispatchMotion"); +void InputDispatcher::resetANRTimeoutsLocked() { +#if DEBUG_FOCUS + LOGD("Resetting ANR timeouts."); #endif - entry->dispatchInProgress = true; - mCurrentInputTargetsValid = false; - mLock.unlock(); + // Reset timeouts for all active connections. + nsecs_t currentTime = now(); + for (size_t i = 0; i < mActiveConnections.size(); i++) { + Connection* connection = mActiveConnections[i]; + connection->resetTimeout(currentTime); + } - mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, - entry->edgeFlags, entry->metaState, - 0, 0, entry->xPrecision, entry->yPrecision, - entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds, - entry->firstSample.pointerCoords); + // Reset input target wait timeout. + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; +} +int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry, + nsecs_t* nextWakeupTime, InputWindow** outWindow) { + *outWindow = NULL; mCurrentInputTargets.clear(); - int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent, - entry->policyFlags, entry->injectorPid, entry->injectorUid, - mCurrentInputTargets); - mLock.lock(); - mCurrentInputTargetsValid = true; + int32_t injectionResult; - setInjectionResultLocked(entry, injectionResult); + // If there is no currently focused window and no focused application + // then drop the event. + if (! mFocusedWindow) { + if (mFocusedApplication) { +#if DEBUG_FOCUS + LOGD("Waiting because there is no focused window but there is a " + "focused application that may eventually add a window: '%s'.", + mFocusedApplication->name.string()); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplication, NULL, nextWakeupTime); + goto Unresponsive; + } - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + LOGI("Dropping event because there is no focused window or focused application."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; } + + // Check permissions. + if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + goto Failed; + } + + // If the currently focused window is paused then keep waiting. + if (mFocusedWindow->paused) { +#if DEBUG_FOCUS + LOGD("Waiting because focused window is paused."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplication, mFocusedWindow, nextWakeupTime); + goto Unresponsive; + } + + // Success! Output targets. + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + *outWindow = mFocusedWindow; + addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC, + getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime)); + + // Done. +Failed: +Unresponsive: +#if DEBUG_FOCUS + LOGD("findFocusedWindow finished: injectionResult=%d", + injectionResult); + logDispatchStateLocked(); +#endif + return injectionResult; } -void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, - EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { -#if DEBUG_DISPATCH_CYCLE - LOGD("dispatchEventToCurrentInputTargets - " - "resumeWithAppendedMotionSample=%s", - resumeWithAppendedMotionSample ? "true" : "false"); +int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry, + nsecs_t* nextWakeupTime, InputWindow** outWindow) { + enum InjectionPermission { + INJECTION_PERMISSION_UNKNOWN, + INJECTION_PERMISSION_GRANTED, + INJECTION_PERMISSION_DENIED + }; + + *outWindow = NULL; + mCurrentInputTargets.clear(); + + nsecs_t startTime = now(); + + // For security reasons, we defer updating the touch state until we are sure that + // event injection will be allowed. + // + // FIXME In the original code, screenWasOff could never be set to true. + // The reason is that the POLICY_FLAG_WOKE_HERE + // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw + // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was + // actually enqueued using the policyFlags that appeared in the final EV_SYN + // events upon which no preprocessing took place. So policyFlags was always 0. + // In the new native input dispatcher we're a bit more careful about event + // preprocessing so the touches we receive can actually have non-zero policyFlags. + // Unfortunately we obtain undesirable behavior. + // + // Here's what happens: + // + // When the device dims in anticipation of going to sleep, touches + // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause + // the device to brighten and reset the user activity timer. + // Touches on other windows (such as the launcher window) + // are dropped. Then after a moment, the device goes to sleep. Oops. + // + // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE + // instead of POLICY_FLAG_WOKE_HERE... + // + bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE; + + int32_t action = entry->action; + + // Update the touch state as needed based on the properties of the touch event. + int32_t injectionResult; + InjectionPermission injectionPermission; + if (action == AMOTION_EVENT_ACTION_DOWN) { + /* Case 1: ACTION_DOWN */ + + InputWindow* newTouchedWindow = NULL; + mTempTouchedOutsideTargets.clear(); + + int32_t x = int32_t(entry->firstSample.pointerCoords[0].x); + int32_t y = int32_t(entry->firstSample.pointerCoords[0].y); + InputWindow* topErrorWindow = NULL; + bool obscured = false; + + // Traverse windows from front to back to find touched window and outside targets. + size_t numWindows = mWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + InputWindow* window = & mWindows.editItemAt(i); + int32_t flags = window->layoutParamsFlags; + + if (flags & InputWindow::FLAG_SYSTEM_ERROR) { + if (! topErrorWindow) { + topErrorWindow = window; + } + } + + if (window->visible) { + if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) { + bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE + | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0; + if (isTouchModal || window->touchableAreaContainsPoint(x, y)) { + if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) { + newTouchedWindow = window; + obscured = isWindowObscuredLocked(window); + } + break; // found touched window, exit window loop + } + } + + if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) { + OutsideTarget outsideTarget; + outsideTarget.window = window; + outsideTarget.obscured = isWindowObscuredLocked(window); + mTempTouchedOutsideTargets.push(outsideTarget); + } + } + } + + // If there is an error window but it is not taking focus (typically because + // it is invisible) then wait for it. Any other focused window may in + // fact be in ANR state. + if (topErrorWindow && newTouchedWindow != topErrorWindow) { +#if DEBUG_FOCUS + LOGD("Waiting because system error window is pending."); #endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, NULL, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_UNKNOWN; + goto Unresponsive; + } - assert(eventEntry->dispatchInProgress); // should already have been set to true + // If we did not find a touched window then fail. + if (! newTouchedWindow) { + if (mFocusedApplication) { +#if DEBUG_FOCUS + LOGD("Waiting because there is no touched window but there is a " + "focused application that may eventually add a new window: '%s'.", + mFocusedApplication->name.string()); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplication, NULL, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_UNKNOWN; + goto Unresponsive; + } - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); + LOGI("Dropping event because there is no touched window or focused application."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionPermission = INJECTION_PERMISSION_UNKNOWN; + goto Failed; + } - ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, - resumeWithAppendedMotionSample); + // 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; + } + + // Success! Update the touch dispatch state for real. + releaseTouchedWindowLocked(); + + mTouchedWindow = newTouchedWindow; + mTouchedWindowIsObscured = obscured; + + if (newTouchedWindow->hasWallpaper) { + mTouchedWallpaperWindows.appendVector(mWallpaperWindows); + } + } else { + /* Case 2: Everything but ACTION_DOWN */ + + // Check permissions. + if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + injectionPermission = INJECTION_PERMISSION_DENIED; + goto Failed; + } + + // If the pointer is not currently down, then ignore the event. + if (! mTouchDown) { + LOGI("Dropping event because the pointer is not down."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Failed; + } + + // If there is no currently touched window then fail. + if (! mTouchedWindow) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("Dropping event because there is no touched window to receive it."); +#endif + injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Failed; + } + + // If the touched window is paused then keep waiting. + if (mTouchedWindow->paused) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("Waiting because touched window is paused."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, mTouchedWindow, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Unresponsive; + } + } + + // 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, 0); + } + + 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, 0); + } + mTempTouchedOutsideTargets.clear(); + + int32_t targetFlags = InputTarget::FLAG_SYNC; + if (mTouchedWindowIsObscured) { + targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + } + addWindowTargetLocked(mTouchedWindow, targetFlags, + getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime)); + *outWindow = mTouchedWindow; + } + +Failed: + // Check injection permission once and for all. + if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { + if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow, + entry->injectorPid, entry->injectorUid)) { + injectionPermission = INJECTION_PERMISSION_GRANTED; } else { - LOGW("Framework requested delivery of an input event to channel '%s' but it " - "is not registered with the input dispatcher.", - inputTarget.inputChannel->getName().string()); + injectionPermission = INJECTION_PERMISSION_DENIED; } } + + // Update final pieces of touch state if the injector had permission. + if (injectionPermission == INJECTION_PERMISSION_GRANTED) { + if (action == AMOTION_EVENT_ACTION_DOWN) { + if (mTouchDown) { + // This is weird. We got a down but we thought it was already down! + LOGW("Pointer down received while already down."); + } else { + mTouchDown = true; + } + + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + // Since we failed to identify a target for this touch down, we may still + // be holding on to an earlier target from a previous touch down. Release it. + releaseTouchedWindowLocked(); + } + } else if (action == AMOTION_EVENT_ACTION_UP) { + mTouchDown = false; + releaseTouchedWindowLocked(); + } + } else { + LOGW("Not updating touch focus because injection was denied."); + } + +Unresponsive: +#if DEBUG_FOCUS + LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d", + injectionResult, injectionPermission); + logDispatchStateLocked(); +#endif + return injectionResult; +} + +void InputDispatcher::releaseTouchedWindowLocked() { + mTouchedWindow = NULL; + mTouchedWindowIsObscured = false; + mTouchedWallpaperWindows.clear(); +} + +void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, + nsecs_t timeSpentWaitingForApplication) { + mCurrentInputTargets.push(); + + InputTarget& target = mCurrentInputTargets.editTop(); + target.inputChannel = window->inputChannel; + target.flags = targetFlags; + target.timeout = window->dispatchingTimeout; + target.timeSpentWaitingForApplication = timeSpentWaitingForApplication; + target.xOffset = - window->frameLeft; + target.yOffset = - window->frameTop; +} + +void InputDispatcher::addMonitoringTargetsLocked() { + for (size_t i = 0; i < mMonitoringChannels.size(); i++) { + mCurrentInputTargets.push(); + + InputTarget& target = mCurrentInputTargets.editTop(); + target.inputChannel = mMonitoringChannels[i]; + target.flags = 0; + target.timeout = -1; + target.timeSpentWaitingForApplication = 0; + target.xOffset = 0; + target.yOffset = 0; + } +} + +bool InputDispatcher::checkInjectionPermission(const InputWindow* window, + int32_t injectorPid, int32_t injectorUid) { + if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) { + bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); + if (! result) { + if (window) { + LOGW("Permission denied: injecting event from pid %d uid %d to window " + "with input channel %s owned by uid %d", + injectorPid, injectorUid, window->inputChannel->getName().string(), + window->ownerUid); + } else { + LOGW("Permission denied: injecting event from pid %d uid %d", + injectorPid, injectorUid); + } + return false; + } + } + return true; +} + +bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) { + size_t numWindows = mWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + const InputWindow* other = & mWindows.itemAt(i); + if (other == window) { + break; + } + if (other->visible && window->visibleFrameIntersects(other)) { + return true; + } + } + return false; +} + +void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, + int32_t windowType, int32_t eventType) { + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doPokeUserActivityLockedInterruptible); + commandEntry->eventTime = eventTime; + commandEntry->windowType = windowType; + commandEntry->userActivityEventType = eventType; } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, @@ -523,15 +1258,21 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s", connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout, inputTarget->xOffset, inputTarget->yOffset, - resumeWithAppendedMotionSample ? "true" : "false"); + toString(resumeWithAppendedMotionSample)); #endif // Skip this event if the connection status is not normal. - // We don't want to queue outbound events at all if the connection is broken or + // We don't want to enqueue additional outbound events if the connection is broken or // not responding. if (connection->status != Connection::STATUS_NORMAL) { - LOGV("channel '%s' ~ Dropping event because the channel status is %s", - connection->getStatusLabel()); + LOGW("channel '%s' ~ Dropping event because the channel status is %s", + connection->getInputChannelName(), connection->getStatusLabel()); + + // If the connection is not responding but the user is poking the application anyways, + // retrigger the original timeout. + if (connection->status == Connection::STATUS_NOT_RESPONDING) { + timeoutDispatchCycleLocked(currentTime, connection); + } return; } @@ -612,17 +1353,45 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } } + // Bring the input state back in line with reality in case it drifted off during an ANR. + if (connection->inputState.isOutOfSync()) { + mTempCancelationEvents.clear(); + connection->inputState.synthesizeCancelationEvents(& mAllocator, mTempCancelationEvents); + connection->inputState.resetOutOfSync(); + + if (! mTempCancelationEvents.isEmpty()) { + LOGI("channel '%s' ~ Generated %d cancelation events to bring channel back in sync " + "with reality.", + connection->getInputChannelName(), mTempCancelationEvents.size()); + + for (size_t i = 0; i < mTempCancelationEvents.size(); i++) { + EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i); + switch (cancelationEventEntry->type) { + case EventEntry::TYPE_KEY: + logOutboundKeyDetailsLocked(" ", + static_cast<KeyEntry*>(cancelationEventEntry)); + break; + case EventEntry::TYPE_MOTION: + logOutboundMotionDetailsLocked(" ", + static_cast<MotionEntry*>(cancelationEventEntry)); + break; + } + + DispatchEntry* cancelationDispatchEntry = + mAllocator.obtainDispatchEntry(cancelationEventEntry, + 0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout); + connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); + + mAllocator.releaseEventEntry(cancelationEventEntry); + } + } + } + // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. - DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry); // increments ref - dispatchEntry->targetFlags = inputTarget->flags; - dispatchEntry->xOffset = inputTarget->xOffset; - dispatchEntry->yOffset = inputTarget->yOffset; - dispatchEntry->timeout = inputTarget->timeout; - dispatchEntry->inProgress = false; - dispatchEntry->headMotionSample = NULL; - dispatchEntry->tailMotionSample = NULL; - + DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref + inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, + inputTarget->timeout); if (dispatchEntry->isSyncTarget()) { eventEntry->pendingSyncDispatches += 1; } @@ -647,12 +1416,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty) { activateConnectionLocked(connection.get()); - startDispatchCycleLocked(currentTime, connection); + startDispatchCycleLocked(currentTime, connection, + inputTarget->timeSpentWaitingForApplication); } } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { + const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName()); @@ -661,12 +1431,37 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, assert(connection->status == Connection::STATUS_NORMAL); assert(! connection->outboundQueue.isEmpty()); - DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; + DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; assert(! dispatchEntry->inProgress); - // TODO throttle successive ACTION_MOVE motion events for the same device - // possible implementation could set a brief poll timeout here and resume starting the - // dispatch cycle when elapsed + // Mark the dispatch entry as in progress. + dispatchEntry->inProgress = true; + + // Update the connection's input state. + InputState::Consistency consistency = connection->inputState.trackEvent( + dispatchEntry->eventEntry); + +#if FILTER_INPUT_EVENTS + // Filter out inconsistent sequences of input events. + // The input system may drop or inject events in a way that could violate implicit + // invariants on input state and potentially cause an application to crash + // or think that a key or pointer is stuck down. Technically we make no guarantees + // of consistency but it would be nice to improve on this where possible. + // XXX: This code is a proof of concept only. Not ready for prime time. + if (consistency == InputState::TOLERABLE) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's " + "current input state but that is likely to be tolerated by the application.", + connection->getInputChannelName()); +#endif + } else if (consistency == InputState::BROKEN) { + LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's " + "current input state and that is likely to cause the application to crash.", + connection->getInputChannelName()); + startNextDispatchCycleLocked(currentTime, connection); + return; + } +#endif // Publish the event. status_t status; @@ -790,12 +1585,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Record information about the newly started dispatch cycle. - dispatchEntry->inProgress = true; - connection->lastEventTime = dispatchEntry->eventEntry->eventTime; connection->lastDispatchTime = currentTime; - nsecs_t timeout = dispatchEntry->timeout; + nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication; connection->setNextTimeoutTime(currentTime, timeout); // Notify other system components. @@ -844,9 +1637,14 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, return; } + startNextDispatchCycleLocked(currentTime, connection); +} + +void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { // Start the next dispatch cycle for this connection. while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; + DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; if (dispatchEntry->inProgress) { // Finish or resume current event in progress. if (dispatchEntry->tailMotionSample) { @@ -855,7 +1653,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, dispatchEntry->inProgress = false; dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample; dispatchEntry->tailMotionSample = NULL; - startDispatchCycleLocked(currentTime, connection); + startDispatchCycleLocked(currentTime, connection, 0); return; } // Finished. @@ -868,7 +1666,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, // If the head is not in progress, then we must have already dequeued the in // progress event, which means we actually aborted it (due to ANR). // So just start the next event for this connection. - startDispatchCycleLocked(currentTime, connection); + startDispatchCycleLocked(currentTime, connection, 0); return; } } @@ -884,58 +1682,74 @@ void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName()); #endif - if (connection->status != Connection::STATUS_NORMAL) { + if (connection->status == Connection::STATUS_NORMAL) { + // Enter the not responding state. + connection->status = Connection::STATUS_NOT_RESPONDING; + connection->lastANRTime = currentTime; + } else if (connection->status != Connection::STATUS_NOT_RESPONDING) { + // Connection is broken or dead. return; } - // Enter the not responding state. - connection->status = Connection::STATUS_NOT_RESPONDING; - connection->lastANRTime = currentTime; - // Notify other system components. - // This enqueues a command which will eventually either call - // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked. + // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked. onDispatchCycleANRLocked(currentTime, connection); } void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, nsecs_t newTimeout) { #if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked", - connection->getInputChannelName()); + LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld", + connection->getInputChannelName(), newTimeout); #endif if (connection->status != Connection::STATUS_NOT_RESPONDING) { return; } - // Resume normal dispatch. - connection->status = Connection::STATUS_NORMAL; - connection->setNextTimeoutTime(currentTime, newTimeout); + if (newTimeout > 0) { + // The system has decided to give the application some more time. + // Keep waiting synchronously and resume normal dispatch. + connection->status = Connection::STATUS_NORMAL; + connection->setNextTimeoutTime(currentTime, newTimeout); + } else { + // The system is about to throw up an ANR dialog and has requested that we abort dispatch. + // Reset the timeout. + connection->nextTimeoutTime = LONG_LONG_MAX; + + // Input state will no longer be realistic. + connection->inputState.setOutOfSync(); + + if (! connection->outboundQueue.isEmpty()) { + // Make the current pending dispatch asynchronous (if it isn't already) so that + // subsequent events can be delivered to the ANR dialog or to another application. + DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next; + currentDispatchEntry->preemptSyncTarget(); + + // Drain all but the first entry in the outbound queue. We keep the first entry + // since that is the one that dispatch is stuck on. We throw away the others + // so that we don't spam the application with stale messages if it eventually + // wakes up and recovers from the ANR. + drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next); + } + } } void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool broken) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ abortDispatchCycle - broken=%s", - connection->getInputChannelName(), broken ? "true" : "false"); + connection->getInputChannelName(), toString(broken)); #endif // Clear the pending timeout. connection->nextTimeoutTime = LONG_LONG_MAX; - // Clear the outbound queue. - if (! connection->outboundQueue.isEmpty()) { - do { - DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->isSyncTarget()) { - decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); - } - mAllocator.releaseDispatchEntry(dispatchEntry); - } while (! connection->outboundQueue.isEmpty()); + // Input state will no longer be realistic. + connection->inputState.setOutOfSync(); - deactivateConnectionLocked(connection.get()); - } + // Clear the outbound queue. + drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next); // Handle the case where the connection appears to be unrecoverably broken. // Ignore already broken or zombie connections. @@ -950,6 +1764,26 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, } } +void InputDispatcher::drainOutboundQueueLocked(Connection* connection, + DispatchEntry* firstDispatchEntryToDrain) { + for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain; + dispatchEntry != & connection->outboundQueue.tailSentinel;) { + DispatchEntry* next = dispatchEntry->next; + connection->outboundQueue.dequeue(dispatchEntry); + + if (dispatchEntry->isSyncTarget()) { + decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); + } + mAllocator.releaseDispatchEntry(dispatchEntry); + + dispatchEntry = next; + } + + if (connection->outboundQueue.isEmpty()) { + deactivateConnectionLocked(connection); + } +} + bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { InputDispatcher* d = static_cast<InputDispatcher*>(data); @@ -1000,57 +1834,19 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime); #endif - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime); - - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(newEntry); + needWake = enqueueInboundEventLocked(newEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } } -void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime); -#endif - - // Remove movement keys from the queue from most recent to least recent, stopping at the - // first non-movement key. - // TODO: Include a detailed description of why we do this... - - { // acquire lock - AutoMutex _l(mLock); - - for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) { - EventEntry* prev = entry->prev; - - if (entry->type == EventEntry::TYPE_KEY) { - KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); - if (isMovementKey(keyEntry->keyCode)) { - LOGV("Dropping movement key during app switch: keyCode=%d, action=%d", - keyEntry->keyCode, keyEntry->action); - mInboundQueue.dequeue(keyEntry); - - setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); - - mAllocator.releaseKeyEntry(keyEntry); - } else { - // stop at last non-movement key - break; - } - } - - entry = prev; - } - } // release lock -} - void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { @@ -1061,7 +1857,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou keyCode, scanCode, metaState, downTime); #endif - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); @@ -1070,11 +1866,10 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou deviceId, source, policyFlags, action, flags, keyCode, scanCode, metaState, repeatCount, downTime); - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(newEntry); + needWake = enqueueInboundEventLocked(newEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } } @@ -1101,7 +1896,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t } #endif - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); @@ -1112,8 +1907,8 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t // Try to append a move sample to the tail of the inbound queue for this device. // Give up if we encounter a non-move motion event for this device since that // means we cannot append any new samples until a new motion event has started. - for (EventEntry* entry = mInboundQueue.tail.prev; - entry != & mInboundQueue.head; entry = entry->prev) { + for (EventEntry* entry = mInboundQueue.tailSentinel.prev; + entry != & mInboundQueue.headSentinel; entry = entry->prev) { if (entry->type != EventEntry::TYPE_MOTION) { // Keep looking for motion events. continue; @@ -1140,18 +1935,6 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t LOGD("Appended motion sample onto batch for most recent " "motion event for this device in the inbound queue."); #endif - - // Sanity check for special case because dispatch is interruptible. - // The dispatch logic is partially interruptible and releases its lock while - // identifying targets. However, as soon as the targets have been identified, - // the dispatcher proceeds to write a dispatch entry into all relevant outbound - // queues and then promptly removes the motion entry from the queue. - // - // Consequently, we should never observe the case where the inbound queue contains - // an in-progress motion entry unless the current input targets are invalid - // (currently being computed). Check for this! - assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid)); - return; // done! } @@ -1178,7 +1961,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t for (size_t i = 0; i < mActiveConnections.size(); i++) { Connection* connection = mActiveConnections.itemAt(i); if (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev; + DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev; if (dispatchEntry->isSyncTarget()) { if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { goto NoBatchingOrStreaming; @@ -1220,11 +2003,10 @@ NoBatchingOrStreaming:; xPrecision, yPrecision, downTime, pointerCount, pointerIds, pointerCoords); - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(newEntry); + needWake = enqueueInboundEventLocked(newEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } } @@ -1240,11 +2022,15 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); EventEntry* injectedEntry; - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); - injectedEntry = createEntryFromInputEventLocked(event); + injectedEntry = createEntryFromInjectedInputEventLocked(event); + if (! injectedEntry) { + return INPUT_EVENT_INJECTION_FAILED; + } + injectedEntry->refCount += 1; injectedEntry->injectorPid = injectorPid; injectedEntry->injectorUid = injectorUid; @@ -1253,12 +2039,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, injectedEntry->injectionIsAsync = true; } - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(injectedEntry); - + needWake = enqueueInboundEventLocked(injectedEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } @@ -1361,11 +2145,42 @@ void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) { } } -InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( +static bool isValidKeyAction(int32_t action) { + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + case AKEY_EVENT_ACTION_UP: + return true; + default: + return false; + } +} + +static bool isValidMotionAction(int32_t action) { + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_OUTSIDE: + return true; + default: + return false; + } +} + +InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventLocked( const InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); + if (! isValidKeyAction(keyEvent->getAction())) { + LOGE("Dropping injected key event since it has invalid action code 0x%x", + keyEvent->getAction()); + return NULL; + } + uint32_t policyFlags = POLICY_FLAG_INJECTED; KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(), @@ -1378,6 +2193,17 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); + if (! isValidMotionAction(motionEvent->getAction())) { + LOGE("Dropping injected motion event since it has invalid action code 0x%x.", + motionEvent->getAction()); + return NULL; + } + if (motionEvent->getPointerCount() == 0 + || motionEvent->getPointerCount() > MAX_POINTERS) { + LOGE("Dropping injected motion event since it has an invalid pointer count %d.", + motionEvent->getPointerCount()); + } + uint32_t policyFlags = POLICY_FLAG_INJECTED; const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); @@ -1405,33 +2231,143 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( } } -void InputDispatcher::resetKeyRepeatLocked() { - if (mKeyRepeatState.lastKeyEntry) { - mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); - mKeyRepeatState.lastKeyEntry = NULL; +void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { +#if DEBUG_FOCUS + LOGD("setInputWindows"); +#endif + { // acquire lock + AutoMutex _l(mLock); + + sp<InputChannel> touchedWindowChannel; + if (mTouchedWindow) { + touchedWindowChannel = mTouchedWindow->inputChannel; + mTouchedWindow = NULL; + } + size_t numTouchedWallpapers = mTouchedWallpaperWindows.size(); + if (numTouchedWallpapers != 0) { + for (size_t i = 0; i < numTouchedWallpapers; i++) { + mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel); + } + mTouchedWallpaperWindows.clear(); + } + + bool hadFocusedWindow = mFocusedWindow != NULL; + + mFocusedWindow = NULL; + mWallpaperWindows.clear(); + + mWindows.clear(); + mWindows.appendVector(inputWindows); + + size_t numWindows = mWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + InputWindow* window = & mWindows.editItemAt(i); + if (window->hasFocus) { + mFocusedWindow = window; + } + + if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) { + mWallpaperWindows.push(window); + + for (size_t j = 0; j < numTouchedWallpapers; j++) { + if (window->inputChannel == mTempTouchedWallpaperChannels[i]) { + mTouchedWallpaperWindows.push(window); + } + } + } + + if (window->inputChannel == touchedWindowChannel) { + mTouchedWindow = window; + } + } + + mTempTouchedWallpaperChannels.clear(); + + if ((hadFocusedWindow && ! mFocusedWindow) + || (mFocusedWindow && ! mFocusedWindow->visible)) { + preemptInputDispatchInnerLocked(); + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mPollLoop->wake(); +} + +void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) { +#if DEBUG_FOCUS + LOGD("setFocusedApplication"); +#endif + { // acquire lock + AutoMutex _l(mLock); + + releaseFocusedApplicationLocked(); + + if (inputApplication) { + mFocusedApplicationStorage = *inputApplication; + mFocusedApplication = & mFocusedApplicationStorage; + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mPollLoop->wake(); +} + +void InputDispatcher::releaseFocusedApplicationLocked() { + if (mFocusedApplication) { + mFocusedApplication = NULL; + mFocusedApplicationStorage.handle.clear(); } } -void InputDispatcher::preemptInputDispatch() { -#if DEBUG_DISPATCH_CYCLE - LOGD("preemptInputDispatch"); +void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { +#if DEBUG_FOCUS + LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); #endif - bool preemptedOne = false; + bool changed; { // 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; + if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { + if (mDispatchFrozen && ! frozen) { + resetANRTimeoutsLocked(); } + + mDispatchEnabled = enabled; + mDispatchFrozen = frozen; + changed = true; + } else { + changed = false; } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + if (changed) { + // Wake up poll loop since it may need to make new input dispatching choices. + mPollLoop->wake(); + } +} + +void InputDispatcher::preemptInputDispatch() { +#if DEBUG_FOCUS + LOGD("preemptInputDispatch"); +#endif + + bool preemptedOne; + { // acquire lock + AutoMutex _l(mLock); + preemptedOne = preemptInputDispatchInnerLocked(); } // release lock if (preemptedOne) { @@ -1440,9 +2376,99 @@ void InputDispatcher::preemptInputDispatch() { } } -status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) { +bool InputDispatcher::preemptInputDispatchInnerLocked() { + bool preemptedOne = false; + 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->preemptSyncTarget(); + preemptedOne = true; + } + } + return preemptedOne; +} + +void InputDispatcher::logDispatchStateLocked() { + String8 dump; + dumpDispatchStateLocked(dump); + LOGD("%s", dump.string()); +} + +void InputDispatcher::dumpDispatchStateLocked(String8& dump) { + dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled); + dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen); + + if (mFocusedApplication) { + dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n", + mFocusedApplication->name.string(), + mFocusedApplication->dispatchingTimeout / 1000000.0); + } else { + dump.append(" focusedApplication: <null>\n"); + } + dump.appendFormat(" focusedWindow: '%s'\n", + mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>"); + dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n", + mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>", + mTouchDown); + for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) { + dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n", + i, mTouchedWallpaperWindows[i]->inputChannel->getName().string()); + } + for (size_t i = 0; i < mWindows.size(); i++) { + dump.appendFormat(" windows[%d]: '%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " + "visible=%s, flags=0x%08x, type=0x%08x, " + "frame=[%d,%d][%d,%d], " + "visibleFrame=[%d,%d][%d,%d], " + "touchableArea=[%d,%d][%d,%d], " + "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + i, mWindows[i].inputChannel->getName().string(), + toString(mWindows[i].paused), + toString(mWindows[i].hasFocus), + toString(mWindows[i].hasWallpaper), + toString(mWindows[i].visible), + mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType, + mWindows[i].frameLeft, mWindows[i].frameTop, + mWindows[i].frameRight, mWindows[i].frameBottom, + mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop, + mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom, + mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop, + mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom, + mWindows[i].ownerPid, mWindows[i].ownerUid, + mWindows[i].dispatchingTimeout / 1000000.0); + } + + for (size_t i = 0; i < mMonitoringChannels.size(); i++) { + const sp<InputChannel>& channel = mMonitoringChannels[i]; + dump.appendFormat(" monitoringChannel[%d]: '%s'\n", + i, channel->getName().string()); + } + + for (size_t i = 0; i < mActiveConnections.size(); i++) { + const Connection* connection = mActiveConnections[i]; + dump.appendFormat(" activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, " + "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n", + i, connection->getInputChannelName(), connection->getStatusLabel(), + toString(connection->hasPendingSyncTarget()), + toString(connection->inputState.isNeutral()), + toString(connection->inputState.isOutOfSync())); + } + + if (isAppSwitchPendingLocked()) { + dump.appendFormat(" appSwitch: pending, due in %01.1fms\n", + (mAppSwitchDueTime - now()) / 1000000.0); + } else { + dump.append(" appSwitch: not pending\n"); + } +} + +status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) { #if DEBUG_REGISTRATION - LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string()); + LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(), + toString(monitor)); #endif { // acquire lock @@ -1465,6 +2491,10 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan int32_t receiveFd = inputChannel->getReceivePipeFd(); mConnectionsByReceiveFd.add(receiveFd, connection); + if (monitor) { + mMonitoringChannels.push(inputChannel); + } + mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); runCommandsLockedInterruptible(); @@ -1492,6 +2522,13 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh connection->status = Connection::STATUS_ZOMBIE; + for (size_t i = 0; i < mMonitoringChannels.size(); i++) { + if (mMonitoringChannels[i] == inputChannel) { + mMonitoringChannels.removeAt(i); + break; + } + } + mPollLoop->removeCallback(inputChannel->getReceivePipeFd()); nsecs_t currentTime = now(); @@ -1578,6 +2615,15 @@ void InputDispatcher::onDispatchCycleBrokenLocked( commandEntry->connection = connection; } +void InputDispatcher::doNotifyConfigurationChangedInterruptible( + CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyConfigurationChanged(commandEntry->eventTime); + + mLock.lock(); +} + void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( CommandEntry* commandEntry) { sp<Connection> connection = commandEntry->connection; @@ -1598,17 +2644,12 @@ void InputDispatcher::doNotifyInputChannelANRLockedInterruptible( if (connection->status != Connection::STATUS_ZOMBIE) { mLock.unlock(); - nsecs_t newTimeout; - bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout); + nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel); mLock.lock(); nsecs_t currentTime = now(); - if (resume) { - resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); - } else { - abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/); - } + resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); } } @@ -1625,6 +2666,57 @@ void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( } } +void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( + CommandEntry* commandEntry) { + KeyEntry* entry = commandEntry->keyEntry; + mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, + entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, + entry->downTime, entry->eventTime); + + mLock.unlock(); + + bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel, + & mReusableKeyEvent, entry->policyFlags); + + mLock.lock(); + + entry->interceptKeyResult = consumed + ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP + : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + mAllocator.releaseKeyEntry(entry); +} + +void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType, + commandEntry->userActivityEventType); + + mLock.lock(); +} + +void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible( + CommandEntry* commandEntry) { + mLock.unlock(); + + nsecs_t newTimeout; + if (commandEntry->inputChannel.get()) { + newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel); + } else if (commandEntry->inputApplicationHandle.get()) { + newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle); + } else { + newTimeout = 0; + } + + mLock.lock(); + + resumeAfterTargetsNotReadyTimeoutLocked(newTimeout); +} + +void InputDispatcher::dump(String8& dump) { + dumpDispatchStateLocked(dump); +} + // --- InputDispatcher::Allocator --- @@ -1668,6 +2760,8 @@ InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t ev entry->metaState = metaState; entry->repeatCount = repeatCount; entry->downTime = downTime; + entry->syntheticRepeat = false; + entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; return entry; } @@ -1702,10 +2796,18 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec } InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( - EventEntry* eventEntry) { + EventEntry* eventEntry, + int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) { DispatchEntry* entry = mDispatchEntryPool.alloc(); entry->eventEntry = eventEntry; eventEntry->refCount += 1; + entry->targetFlags = targetFlags; + entry->xOffset = xOffset; + entry->yOffset = yOffset; + entry->timeout = timeout; + entry->inProgress = false; + entry->headMotionSample = NULL; + entry->tailMotionSample = NULL; return entry; } @@ -1788,6 +2890,25 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, motionEntry->lastSample = sample; } + +// --- InputDispatcher::EventEntry --- + +void InputDispatcher::EventEntry::recycle() { + injectionResult = INPUT_EVENT_INJECTION_PENDING; + dispatchInProgress = false; + pendingSyncDispatches = 0; +} + + +// --- InputDispatcher::KeyEntry --- + +void InputDispatcher::KeyEntry::recycle() { + EventEntry::recycle(); + syntheticRepeat = false; + interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN; +} + + // --- InputDispatcher::MotionEntry --- uint32_t InputDispatcher::MotionEntry::countSamples() const { @@ -1798,6 +2919,189 @@ uint32_t InputDispatcher::MotionEntry::countSamples() const { return count; } + +// --- InputDispatcher::InputState --- + +InputDispatcher::InputState::InputState() : + mIsOutOfSync(false) { +} + +InputDispatcher::InputState::~InputState() { +} + +bool InputDispatcher::InputState::isNeutral() const { + return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); +} + +bool InputDispatcher::InputState::isOutOfSync() const { + return mIsOutOfSync; +} + +void InputDispatcher::InputState::setOutOfSync() { + if (! isNeutral()) { + mIsOutOfSync = true; + } +} + +void InputDispatcher::InputState::resetOutOfSync() { + mIsOutOfSync = false; +} + +InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent( + const EventEntry* entry) { + switch (entry->type) { + case EventEntry::TYPE_KEY: + return trackKey(static_cast<const KeyEntry*>(entry)); + + case EventEntry::TYPE_MOTION: + return trackMotion(static_cast<const MotionEntry*>(entry)); + + default: + return CONSISTENT; + } +} + +InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey( + const KeyEntry* entry) { + int32_t action = entry->action; + for (size_t i = 0; i < mKeyMementos.size(); i++) { + KeyMemento& memento = mKeyMementos.editItemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source + && memento.keyCode == entry->keyCode + && memento.scanCode == entry->scanCode) { + switch (action) { + case AKEY_EVENT_ACTION_UP: + mKeyMementos.removeAt(i); + if (isNeutral()) { + mIsOutOfSync = false; + } + return CONSISTENT; + + case AKEY_EVENT_ACTION_DOWN: + return TOLERABLE; + + default: + return BROKEN; + } + } + } + + switch (action) { + case AKEY_EVENT_ACTION_DOWN: { + mKeyMementos.push(); + KeyMemento& memento = mKeyMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.keyCode = entry->keyCode; + memento.scanCode = entry->scanCode; + memento.downTime = entry->downTime; + return CONSISTENT; + } + + default: + return BROKEN; + } +} + +InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion( + const MotionEntry* entry) { + int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK; + for (size_t i = 0; i < mMotionMementos.size(); i++) { + MotionMemento& memento = mMotionMementos.editItemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source) { + switch (action) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: + mMotionMementos.removeAt(i); + if (isNeutral()) { + mIsOutOfSync = false; + } + return CONSISTENT; + + case AMOTION_EVENT_ACTION_DOWN: + return TOLERABLE; + + case AMOTION_EVENT_ACTION_POINTER_DOWN: + if (entry->pointerCount == memento.pointerCount + 1) { + memento.setPointers(entry); + return CONSISTENT; + } + return BROKEN; + + case AMOTION_EVENT_ACTION_POINTER_UP: + if (entry->pointerCount == memento.pointerCount - 1) { + memento.setPointers(entry); + return CONSISTENT; + } + return BROKEN; + + case AMOTION_EVENT_ACTION_MOVE: + if (entry->pointerCount == memento.pointerCount) { + return CONSISTENT; + } + return BROKEN; + + default: + return BROKEN; + } + } + } + + switch (action) { + case AMOTION_EVENT_ACTION_DOWN: { + mMotionMementos.push(); + MotionMemento& memento = mMotionMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.xPrecision = entry->xPrecision; + memento.yPrecision = entry->yPrecision; + memento.downTime = entry->downTime; + memento.setPointers(entry); + return CONSISTENT; + } + + default: + return BROKEN; + } +} + +void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { + pointerCount = entry->pointerCount; + for (uint32_t i = 0; i < entry->pointerCount; i++) { + pointerIds[i] = entry->pointerIds[i]; + pointerCoords[i] = entry->lastSample->pointerCoords[i]; + } +} + +void InputDispatcher::InputState::synthesizeCancelationEvents( + Allocator* allocator, Vector<EventEntry*>& outEvents) const { + for (size_t i = 0; i < mKeyMementos.size(); i++) { + const KeyMemento& memento = mKeyMementos.itemAt(i); + outEvents.push(allocator->obtainKeyEntry(now(), + memento.deviceId, memento.source, 0, + AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED, + memento.keyCode, memento.scanCode, 0, 0, memento.downTime)); + } + + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos.itemAt(i); + outEvents.push(allocator->obtainMotionEntry(now(), + memento.deviceId, memento.source, 0, + AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0, + memento.xPrecision, memento.yPrecision, memento.downTime, + memento.pointerCount, memento.pointerIds, memento.pointerCoords)); + } +} + +void InputDispatcher::InputState::clear() { + mKeyMementos.clear(); + mMotionMementos.clear(); + mIsOutOfSync = false; +} + + // --- InputDispatcher::Connection --- InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : @@ -1818,6 +3122,14 @@ void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_ nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX; } +void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) { + if (outboundQueue.isEmpty()) { + nextTimeoutTime = LONG_LONG_MAX; + } else { + setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout); + } +} + const char* InputDispatcher::Connection::getStatusLabel() const { switch (status) { case STATUS_NORMAL: @@ -1839,8 +3151,8 @@ const char* InputDispatcher::Connection::getStatusLabel() const { InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent( const EventEntry* eventEntry) const { - for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev; - dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) { + for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev; + dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) { if (dispatchEntry->eventEntry == eventEntry) { return dispatchEntry; } @@ -1848,9 +3160,11 @@ InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchE return NULL; } + // --- InputDispatcher::CommandEntry --- -InputDispatcher::CommandEntry::CommandEntry() { +InputDispatcher::CommandEntry::CommandEntry() : + keyEntry(NULL) { } InputDispatcher::CommandEntry::~CommandEntry() { diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index ed4f07b..09fce38 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -72,52 +72,12 @@ status_t InputManager::stop() { return OK; } -status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) { - return mDispatcher->registerInputChannel(inputChannel); +sp<InputReaderInterface> InputManager::getReader() { + return mReader; } -status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) { - return mDispatcher->unregisterInputChannel(inputChannel); -} - -int32_t InputManager::injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { - return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); -} - -void InputManager::preemptInputDispatch() { - mDispatcher->preemptInputDispatch(); -} - -void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) { - mReader->getInputConfiguration(outConfiguration); -} - -status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { - return mReader->getInputDeviceInfo(deviceId, outDeviceInfo); -} - -void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { - mReader->getInputDeviceIds(outDeviceIds); -} - -int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode) { - return mReader->getScanCodeState(deviceId, sourceMask, scanCode); -} - -int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode) { - return mReader->getKeyCodeState(deviceId, sourceMask, keyCode); -} - -int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) { - return mReader->getSwitchState(deviceId, sourceMask, sw); -} - -bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags); +sp<InputDispatcherInterface> InputManager::getDispatcher() { + return mDispatcher; } } // namespace android diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index d57b38c..88084c0 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -573,6 +573,60 @@ bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, s } // release device registy reader lock } +void InputReader::dump(String8& dump) { + dumpDeviceInfo(dump); +} + +static void dumpMotionRange(String8& dump, + const char* name, const InputDeviceInfo::MotionRange* range) { + if (range) { + dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n", + name, range->min, range->max, range->flat, range->fuzz); + } +} + +#define DUMP_MOTION_RANGE(range) \ + dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range)); + +void InputReader::dumpDeviceInfo(String8& dump) { + Vector<int32_t> deviceIds; + getInputDeviceIds(deviceIds); + + InputDeviceInfo deviceInfo; + for (size_t i = 0; i < deviceIds.size(); i++) { + int32_t deviceId = deviceIds[i]; + + status_t result = getInputDeviceInfo(deviceId, & deviceInfo); + if (result == NAME_NOT_FOUND) { + continue; + } else if (result != OK) { + dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n", + result); + continue; + } + + dump.appendFormat(" Device %d: '%s'\n", + deviceInfo.getId(), deviceInfo.getName().string()); + dump.appendFormat(" sources = 0x%08x\n", + deviceInfo.getSources()); + dump.appendFormat(" keyboardType = %d\n", + deviceInfo.getKeyboardType()); + + dump.append(" motion ranges:\n"); + DUMP_MOTION_RANGE(X); + DUMP_MOTION_RANGE(Y); + DUMP_MOTION_RANGE(PRESSURE); + DUMP_MOTION_RANGE(SIZE); + DUMP_MOTION_RANGE(TOUCH_MAJOR); + DUMP_MOTION_RANGE(TOUCH_MINOR); + DUMP_MOTION_RANGE(TOOL_MAJOR); + DUMP_MOTION_RANGE(TOOL_MINOR); + DUMP_MOTION_RANGE(ORIENTATION); + } +} + +#undef DUMP_MOTION_RANGE + // --- InputReaderThread --- @@ -740,10 +794,6 @@ int32_t InputMapper::getMetaState() { } bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) { - if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { - getDispatcher()->notifyAppSwitchComing(when); - } - return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; } @@ -1249,20 +1299,12 @@ void TouchInputMapper::initializeLocked() { mLocked.orientedRanges.haveOrientation = false; } -static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) { - if (axis.valid) { - LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); - } else { - LOGI(INDENT "Raw %s axis: unknown range", name); - } -} - void TouchInputMapper::configure() { InputMapper::configure(); // Configure basic parameters. configureParameters(); + logParameters(); // Configure absolute axis information. configureRawAxes(); @@ -1287,6 +1329,18 @@ void TouchInputMapper::configureParameters() { mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); } +void TouchInputMapper::logParameters() { + if (mParameters.useBadTouchFilter) { + LOGI(INDENT "Bad touch filter enabled."); + } + if (mParameters.useAveragingTouchFilter) { + LOGI(INDENT "Averaging touch filter enabled."); + } + if (mParameters.useJumpyTouchFilter) { + LOGI(INDENT "Jumpy touch filter enabled."); + } +} + void TouchInputMapper::configureRawAxes() { mRawAxes.x.clear(); mRawAxes.y.clear(); @@ -1298,6 +1352,15 @@ void TouchInputMapper::configureRawAxes() { mRawAxes.orientation.clear(); } +static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) { + if (axis.valid) { + LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d", + name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); + } else { + LOGI(INDENT "Raw %s axis: unknown range", name); + } +} + void TouchInputMapper::logRawAxes() { logAxisInfo(mRawAxes.x, "x"); logAxisInfo(mRawAxes.y, "y"); @@ -1331,8 +1394,10 @@ bool TouchInputMapper::configureSurfaceLocked() { bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height; if (sizeChanged) { - LOGI("Device configured: id=0x%x, name=%s (display size was changed)", + LOGI("Device reconfigured (display size changed): id=0x%x, name=%s", getDeviceId(), getDeviceName().string()); + LOGI(INDENT "Width: %dpx", width); + LOGI(INDENT "Height: %dpx", height); mLocked.surfaceWidth = width; mLocked.surfaceHeight = height; @@ -1500,9 +1565,41 @@ bool TouchInputMapper::configureSurfaceLocked() { mLocked.orientedRanges.y.fuzz = orientedYScale; } + if (sizeChanged) { + logMotionRangesLocked(); + } + return true; } +static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) { + if (range) { + LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f", + name, range->min, range->max, range->flat, range->fuzz); + } else { + LOGI(INDENT "Output %s range: unsupported", name); + } +} + +void TouchInputMapper::logMotionRangesLocked() { + logMotionRangeInfo(& mLocked.orientedRanges.x, "x"); + logMotionRangeInfo(& mLocked.orientedRanges.y, "y"); + logMotionRangeInfo(mLocked.orientedRanges.havePressure + ? & mLocked.orientedRanges.pressure : NULL, "pressure"); + logMotionRangeInfo(mLocked.orientedRanges.haveSize + ? & mLocked.orientedRanges.size : NULL, "size"); + logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea + ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor"); + logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea + ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor"); + logMotionRangeInfo(mLocked.orientedRanges.haveToolArea + ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor"); + logMotionRangeInfo(mLocked.orientedRanges.haveToolArea + ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor"); + logMotionRangeInfo(mLocked.orientedRanges.haveOrientation + ? & mLocked.orientedRanges.orientation : NULL, "orientation"); +} + void TouchInputMapper::configureVirtualKeysLocked() { assert(mRawAxes.x.valid && mRawAxes.y.valid); @@ -1768,16 +1865,18 @@ void TouchInputMapper::resolveCalibration() { } void TouchInputMapper::logCalibration() { + LOGI(INDENT "Calibration:"); + // Touch Area switch (mCalibration.touchAreaCalibration) { case Calibration::TOUCH_AREA_CALIBRATION_NONE: - LOGI(INDENT " touch.touchArea.calibration: none"); + LOGI(INDENT INDENT "touch.touchArea.calibration: none"); break; case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC: - LOGI(INDENT " touch.touchArea.calibration: geometric"); + LOGI(INDENT INDENT "touch.touchArea.calibration: geometric"); break; case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE: - LOGI(INDENT " touch.touchArea.calibration: pressure"); + LOGI(INDENT INDENT "touch.touchArea.calibration: pressure"); break; default: assert(false); @@ -1786,40 +1885,40 @@ void TouchInputMapper::logCalibration() { // Tool Area switch (mCalibration.toolAreaCalibration) { case Calibration::TOOL_AREA_CALIBRATION_NONE: - LOGI(INDENT " touch.toolArea.calibration: none"); + LOGI(INDENT INDENT "touch.toolArea.calibration: none"); break; case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC: - LOGI(INDENT " touch.toolArea.calibration: geometric"); + LOGI(INDENT INDENT "touch.toolArea.calibration: geometric"); break; case Calibration::TOOL_AREA_CALIBRATION_LINEAR: - LOGI(INDENT " touch.toolArea.calibration: linear"); + LOGI(INDENT INDENT "touch.toolArea.calibration: linear"); break; default: assert(false); } if (mCalibration.haveToolAreaLinearScale) { - LOGI(INDENT " touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale); + LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale); } if (mCalibration.haveToolAreaLinearBias) { - LOGI(INDENT " touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias); + LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias); } if (mCalibration.haveToolAreaIsSummed) { - LOGI(INDENT " touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed); + LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed); } // Pressure switch (mCalibration.pressureCalibration) { case Calibration::PRESSURE_CALIBRATION_NONE: - LOGI(INDENT " touch.pressure.calibration: none"); + LOGI(INDENT INDENT "touch.pressure.calibration: none"); break; case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - LOGI(INDENT " touch.pressure.calibration: physical"); + LOGI(INDENT INDENT "touch.pressure.calibration: physical"); break; case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - LOGI(INDENT " touch.pressure.calibration: amplitude"); + LOGI(INDENT INDENT "touch.pressure.calibration: amplitude"); break; default: assert(false); @@ -1827,10 +1926,10 @@ void TouchInputMapper::logCalibration() { switch (mCalibration.pressureSource) { case Calibration::PRESSURE_SOURCE_PRESSURE: - LOGI(INDENT " touch.pressure.source: pressure"); + LOGI(INDENT INDENT "touch.pressure.source: pressure"); break; case Calibration::PRESSURE_SOURCE_TOUCH: - LOGI(INDENT " touch.pressure.source: touch"); + LOGI(INDENT INDENT "touch.pressure.source: touch"); break; case Calibration::PRESSURE_SOURCE_DEFAULT: break; @@ -1839,16 +1938,16 @@ void TouchInputMapper::logCalibration() { } if (mCalibration.havePressureScale) { - LOGI(INDENT " touch.pressure.scale: %f", mCalibration.pressureScale); + LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale); } // Size switch (mCalibration.sizeCalibration) { case Calibration::SIZE_CALIBRATION_NONE: - LOGI(INDENT " touch.size.calibration: none"); + LOGI(INDENT INDENT "touch.size.calibration: none"); break; case Calibration::SIZE_CALIBRATION_NORMALIZED: - LOGI(INDENT " touch.size.calibration: normalized"); + LOGI(INDENT INDENT "touch.size.calibration: normalized"); break; default: assert(false); @@ -1857,10 +1956,10 @@ void TouchInputMapper::logCalibration() { // Orientation switch (mCalibration.orientationCalibration) { case Calibration::ORIENTATION_CALIBRATION_NONE: - LOGI(INDENT " touch.orientation.calibration: none"); + LOGI(INDENT INDENT "touch.orientation.calibration: none"); break; case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - LOGI(INDENT " touch.orientation.calibration: interpolated"); + LOGI(INDENT INDENT "touch.orientation.calibration: interpolated"); break; default: assert(false); diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 6d3eeee..fe76cd0 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -119,7 +119,8 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { if (outData != NULL) *outData = pending.data; return pending.ident; } - + + // Wait for wakeAndLock() waiters to run then set mPolling to true. mLock.lock(); while (mWaiters != 0) { mResume.wait(mLock); @@ -127,6 +128,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { mPolling = true; mLock.unlock(); + // Poll. int32_t result; size_t requestedCount = mRequestedFds.size(); @@ -168,6 +170,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { } #endif + // Process the poll results. mPendingCallbacks.clear(); mPendingFds.clear(); mPendingFdsPos = 0; @@ -218,6 +221,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { } Done: + // Set mPolling to false and wake up the wakeAndLock() waiters. mLock.lock(); mPolling = false; if (mWaiters != 0) { @@ -357,11 +361,13 @@ ssize_t PollLoop::getRequestIndexLocked(int fd) { void PollLoop::wakeAndLock() { mLock.lock(); + mWaiters += 1; while (mPolling) { wake(); mAwake.wait(mLock); } + mWaiters -= 1; if (mWaiters == 0) { mResume.signal(); |