diff options
Diffstat (limited to 'include/ui/InputDispatcher.h')
-rw-r--r-- | include/ui/InputDispatcher.h | 208 |
1 files changed, 174 insertions, 34 deletions
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index bde07f2..80a20c9 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -18,7 +18,6 @@ #define _UI_INPUT_DISPATCHER_H #include <ui/Input.h> -#include <ui/InputDispatchPolicy.h> #include <ui/InputTransport.h> #include <utils/KeyedVector.h> #include <utils/Vector.h> @@ -35,6 +34,82 @@ namespace android { +/* + * An input target specifies how an input event is to be dispatched to a particular window + * including the window's input channel, control flags, a timeout, and an X / Y offset to + * be added to input event coordinates to compensate for the absolute position of the + * window area. + */ +struct InputTarget { + enum { + /* This flag indicates that subsequent event delivery should be held until the + * current event is delivered to this target or a timeout occurs. */ + FLAG_SYNC = 0x01, + + /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of + * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */ + FLAG_OUTSIDE = 0x02, + + /* This flag indicates that a KeyEvent or MotionEvent is being canceled. + * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set. + * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */ + FLAG_CANCEL = 0x04 + }; + + // The input channel to be targeted. + sp<InputChannel> inputChannel; + + // Flags for the input target. + int32_t flags; + + // The timeout for event delivery to this target in nanoseconds. Or -1 if none. + nsecs_t timeout; + + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset, yOffset; +}; + +/* + * Input dispatcher policy interface. + * + * The input reader policy is used by the input reader to interact with the Window Manager + * and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class InputDispatcherPolicyInterface : public virtual RefBase { +protected: + InputDispatcherPolicyInterface() { } + virtual ~InputDispatcherPolicyInterface() { } + +public: + /* Notifies the system that a configuration change has occurred. */ + virtual void notifyConfigurationChanged(nsecs_t when) = 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. */ + virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0; + + /* Notifies the system that an input channel recovered from ANR. */ + virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0; + + /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */ + virtual nsecs_t getKeyRepeatTimeout() = 0; + + /* Gets the input targets for a key event. */ + virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + Vector<InputTarget>& outTargets) = 0; + + /* Gets the input targets for a motion event. */ + virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + Vector<InputTarget>& outTargets) = 0; +}; + + /* Notifies the system about input events generated by the input reader. * The dispatcher is expected to be mostly asynchronous. */ class InputDispatcherInterface : public virtual RefBase { @@ -51,14 +126,10 @@ public: virtual void dispatchOnce() = 0; /* Notifies the dispatcher about new events. - * The dispatcher will process most of these events asynchronously although some - * policy processing may occur synchronously. * * These methods should only be called on the input reader thread. */ - virtual void notifyConfigurationChanged(nsecs_t eventTime, - int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0; - virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen) = 0; + 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 nature, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, @@ -76,19 +147,33 @@ public: virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; }; -/* Dispatches events. */ +/* Dispatches events to input targets. Some functions of the input dispatcher, such as + * identifying input targets, are controlled by a separate policy object. + * + * IMPORTANT INVARIANT: + * Because the policy can potentially block or cause re-entrance into the input dispatcher, + * the input dispatcher never calls into the policy while holding its internal locks. + * The implementation is also carefully designed to recover from scenarios such as an + * input channel becoming unregistered while identifying input targets or processing timeouts. + * + * Methods marked 'Locked' must be called with the lock acquired. + * + * Methods marked 'LockedInterruptible' must be called with the lock acquired but + * may during the course of their execution release the lock, call into the policy, and + * then reacquire the lock. The caller is responsible for recovering gracefully. + * + * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. + */ class InputDispatcher : public InputDispatcherInterface { protected: virtual ~InputDispatcher(); public: - explicit InputDispatcher(const sp<InputDispatchPolicyInterface>& policy); + explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); virtual void dispatchOnce(); - virtual void notifyConfigurationChanged(nsecs_t eventTime, - int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig); - virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen); + virtual void notifyConfigurationChanged(nsecs_t eventTime); virtual void notifyAppSwitchComing(nsecs_t eventTime); virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, @@ -119,12 +204,11 @@ private: int32_t refCount; int32_t type; nsecs_t eventTime; + + bool dispatchInProgress; // initially false, set to true while dispatching }; struct ConfigurationChangedEntry : EventEntry { - int32_t touchScreenConfig; - int32_t keyboardConfig; - int32_t navigationConfig; }; struct KeyEntry : EventEntry { @@ -165,6 +249,7 @@ private: MotionSample* lastSample; }; + // Tracks the progress of dispatching a particular event to a particular connection. struct DispatchEntry : Link<DispatchEntry> { EventEntry* eventEntry; // the event to dispatch int32_t targetFlags; @@ -189,6 +274,36 @@ private: MotionSample* tailMotionSample; }; + // A command entry captures state and behavior for an action to be performed in the + // dispatch loop after the initial processing has taken place. It is essentially + // a kind of continuation used to postpone sensitive policy interactions to a point + // in the dispatch loop where it is safe to release the lock (generally after finishing + // the critical parts of the dispatch cycle). + // + // The special thing about commands is that they can voluntarily release and reacquire + // the dispatcher lock at will. Initially when the command starts running, the + // dispatcher lock is held. However, if the command needs to call into the policy to + // do some work, it can release the lock, do the work, then reacquire the lock again + // before returning. + // + // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch + // never calls into the policy while holding its lock. + // + // Commands are implicitly 'LockedInterruptible'. + struct CommandEntry; + typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); + + struct CommandEntry : Link<CommandEntry> { + CommandEntry(); + ~CommandEntry(); + + Command command; + + // parameters for the command (usage varies by command) + sp<InputChannel> inputChannel; + }; + + // Generic queue implementation. template <typename T> struct Queue { T head; @@ -242,17 +357,17 @@ private: KeyEntry* obtainKeyEntry(); MotionEntry* obtainMotionEntry(); DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry); + CommandEntry* obtainCommandEntry(Command command); void releaseEventEntry(EventEntry* entry); void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry); void releaseKeyEntry(KeyEntry* entry); void releaseMotionEntry(MotionEntry* entry); void releaseDispatchEntry(DispatchEntry* entry); + void releaseCommandEntry(CommandEntry* entry); void appendMotionSample(MotionEntry* motionEntry, nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords); - void freeMotionSample(MotionSample* sample); - void freeMotionSampleList(MotionSample* head); private: Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool; @@ -260,6 +375,7 @@ private: Pool<MotionEntry> mMotionEntryPool; Pool<MotionSample> mMotionSamplePool; Pool<DispatchEntry> mDispatchEntryPool; + Pool<CommandEntry> mCommandEntryPool; }; /* Manages the dispatch state associated with a single input channel. */ @@ -291,7 +407,9 @@ private: explicit Connection(const sp<InputChannel>& inputChannel); - inline const char* getInputChannelName() { return inputChannel->getName().string(); } + inline const char* getInputChannelName() const { return inputChannel->getName().string(); } + + const char* getStatusLabel() const; // Finds a DispatchEntry in the outbound queue associated with the specified event. // Returns NULL if not found. @@ -323,22 +441,23 @@ private: status_t initialize(); }; - sp<InputDispatchPolicyInterface> mPolicy; + sp<InputDispatcherPolicyInterface> mPolicy; Mutex mLock; - Queue<EventEntry> mInboundQueue; Allocator mAllocator; - sp<PollLoop> mPollLoop; + Queue<EventEntry> mInboundQueue; + Queue<CommandEntry> mCommandQueue; + // All registered connections mapped by receive pipe file descriptor. KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd; // Active connections are connections that have a non-empty outbound queue. Vector<Connection*> mActiveConnections; - // Pool of key and motion event objects used only to ask the input dispatch policy + // 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; @@ -347,6 +466,7 @@ private: // 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 // Key repeat tracking. // XXX Move this up to the input reader instead. @@ -357,17 +477,27 @@ private: void resetKeyRepeatLocked(); + // Deferred command processing. + bool runCommandsLockedInterruptible(); + CommandEntry* postCommandLocked(Command command); + // Process events that have just been dequeued from the head of the input queue. - void processConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry); - void processKeyLocked(nsecs_t currentTime, KeyEntry* entry); - void processKeyRepeatLocked(nsecs_t currentTime); - void processMotionLocked(nsecs_t currentTime, MotionEntry* entry); + void processConfigurationChangedLockedInterruptible( + 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 identifyInputTargetsAndDispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry); - void identifyInputTargetsAndDispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry); - void dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, EventEntry* entry, - bool resumeWithAppendedMotionSample); + void identifyInputTargetsAndDispatchKeyLockedInterruptible( + nsecs_t currentTime, KeyEntry* entry); + void identifyInputTargetsAndDispatchMotionLockedInterruptible( + nsecs_t currentTime, MotionEntry* entry); + void dispatchEventToCurrentInputTargetsLocked( + nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample); // Manage the dispatch cycle for a single connection. void prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection, @@ -384,12 +514,22 @@ private: void activateConnectionLocked(Connection* connection); void deactivateConnectionLocked(Connection* connection); + // Outbound policy interactions. + void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry); + // Interesting events that we might like to log or tell the framework about. - void onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection); - void onDispatchCycleFinishedLocked(nsecs_t currentTime, Connection* connection, - bool recoveredFromANR); - void onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection); - void onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection); + void onDispatchCycleStartedLocked( + nsecs_t currentTime, Connection* connection); + void onDispatchCycleFinishedLocked( + nsecs_t currentTime, Connection* connection, bool recoveredFromANR); + void onDispatchCycleANRLocked( + nsecs_t currentTime, Connection* connection); + void onDispatchCycleBrokenLocked( + nsecs_t currentTime, Connection* connection); + + void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); + void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry); + void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry); }; /* Enqueues and dispatches input events, endlessly. */ |