/* * 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_INPUT_DISPATCHER_H #define _UI_INPUT_DISPATCHER_H #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { /* Notifies the system about input events generated by the input reader. * The dispatcher is expected to be mostly asynchronous. */ class InputDispatcherInterface : public virtual RefBase { protected: InputDispatcherInterface() { } virtual ~InputDispatcherInterface() { } public: /* Runs a single iteration of the dispatch loop. * Nominally processes one queued event, a timeout, or a response from an input consumer. * * This method should only be called on the input dispatcher thread. */ 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 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, int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags, uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime) = 0; /* Registers or unregister input channels that may be used as targets for input events. * * These methods may be called on any thread (usually by the input manager). */ virtual status_t registerInputChannel(const sp& inputChannel) = 0; virtual status_t unregisterInputChannel(const sp& inputChannel) = 0; }; /* Dispatches events. */ class InputDispatcher : public InputDispatcherInterface { protected: virtual ~InputDispatcher(); public: explicit InputDispatcher(const sp& 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 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, int32_t scanCode, int32_t metaState, nsecs_t downTime); virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags, uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime); virtual status_t registerInputChannel(const sp& inputChannel); virtual status_t unregisterInputChannel(const sp& inputChannel); private: template struct Link { T* next; T* prev; }; struct EventEntry : Link { enum { TYPE_SENTINEL, TYPE_CONFIGURATION_CHANGED, TYPE_KEY, TYPE_MOTION }; int32_t refCount; int32_t type; nsecs_t eventTime; }; struct ConfigurationChangedEntry : EventEntry { int32_t touchScreenConfig; int32_t keyboardConfig; int32_t navigationConfig; }; struct KeyEntry : EventEntry { int32_t deviceId; int32_t nature; uint32_t policyFlags; int32_t action; int32_t flags; int32_t keyCode; int32_t scanCode; int32_t metaState; int32_t repeatCount; nsecs_t downTime; }; struct MotionSample { MotionSample* next; nsecs_t eventTime; PointerCoords pointerCoords[MAX_POINTERS]; }; struct MotionEntry : EventEntry { int32_t deviceId; int32_t nature; uint32_t policyFlags; int32_t action; int32_t metaState; int32_t edgeFlags; float xPrecision; float yPrecision; nsecs_t downTime; uint32_t pointerCount; int32_t pointerIds[MAX_POINTERS]; // Linked list of motion samples associated with this motion event. MotionSample firstSample; MotionSample* lastSample; }; struct DispatchEntry : Link { EventEntry* eventEntry; // the event to dispatch int32_t targetFlags; float xOffset; float yOffset; nsecs_t timeout; // True if dispatch has started. bool inProgress; // For motion events: // Pointer to the first motion sample to dispatch in this cycle. // Usually NULL to indicate that the list of motion samples begins at // MotionEntry::firstSample. Otherwise, some samples were dispatched in a previous // cycle and this pointer indicates the location of the first remainining sample // to dispatch during the current cycle. MotionSample* headMotionSample; // Pointer to a motion sample to dispatch in the next cycle if the dispatcher was // unable to send all motion samples during this cycle. On the next cycle, // headMotionSample will be initialized to tailMotionSample and tailMotionSample // will be set to NULL. MotionSample* tailMotionSample; }; template struct Queue { T head; T tail; inline Queue() { head.prev = NULL; head.next = & tail; tail.prev = & head; tail.next = NULL; } inline bool isEmpty() { return head.next == & tail; } inline void enqueueAtTail(T* entry) { T* last = tail.prev; last->next = entry; entry->prev = last; entry->next = & tail; tail.prev = entry; } inline void enqueueAtHead(T* entry) { T* first = head.next; head.next = entry; entry->prev = & head; entry->next = first; first->prev = entry; } inline void dequeue(T* entry) { entry->prev->next = entry->next; entry->next->prev = entry->prev; } inline T* dequeueAtHead() { T* first = head.next; dequeue(first); return first; } }; /* Allocates queue entries and performs reference counting as needed. */ class Allocator { public: Allocator(); ConfigurationChangedEntry* obtainConfigurationChangedEntry(); KeyEntry* obtainKeyEntry(); MotionEntry* obtainMotionEntry(); DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry); void releaseEventEntry(EventEntry* entry); void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry); void releaseKeyEntry(KeyEntry* entry); void releaseMotionEntry(MotionEntry* entry); void releaseDispatchEntry(DispatchEntry* entry); void appendMotionSample(MotionEntry* motionEntry, nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords); void freeMotionSample(MotionSample* sample); void freeMotionSampleList(MotionSample* head); private: Pool mConfigurationChangeEntryPool; Pool mKeyEntryPool; Pool mMotionEntryPool; Pool mMotionSamplePool; Pool mDispatchEntryPool; }; /* Manages the dispatch state associated with a single input channel. */ class Connection : public RefBase { protected: virtual ~Connection(); public: enum Status { // Everything is peachy. STATUS_NORMAL, // An unrecoverable communication error has occurred. STATUS_BROKEN, // The client is not responding. STATUS_NOT_RESPONDING, // The input channel has been unregistered. STATUS_ZOMBIE }; Status status; sp inputChannel; InputPublisher inputPublisher; Queue outboundQueue; nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none) nsecs_t lastEventTime; // the time when the event was originally captured nsecs_t lastDispatchTime; // the time when the last event was dispatched nsecs_t lastANRTime; // the time when the last ANR was recorded explicit Connection(const sp& inputChannel); inline const char* getInputChannelName() { return inputChannel->getName().string(); } // Finds a DispatchEntry in the outbound queue associated with the specified event. // Returns NULL if not found. DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const; // 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->targetFlags & InputTarget::FLAG_SYNC); } // Gets the time since the current event was originally obtained from the input driver. inline double getEventLatencyMillis(nsecs_t currentTime) { return (currentTime - lastEventTime) / 1000000.0; } // Gets the time since the current event entered the outbound dispatch queue. inline double getDispatchLatencyMillis(nsecs_t currentTime) { return (currentTime - lastDispatchTime) / 1000000.0; } // Gets the time since the current event ANR was declared, if applicable. inline double getANRLatencyMillis(nsecs_t currentTime) { return (currentTime - lastANRTime) / 1000000.0; } status_t initialize(); }; sp mPolicy; Mutex mLock; Queue mInboundQueue; Allocator mAllocator; sp mPollLoop; // All registered connections mapped by receive pipe file descriptor. KeyedVector > mConnectionsByReceiveFd; // Active connections are connections that have a non-empty outbound queue. Vector mActiveConnections; // Pool of key and motion event objects used only to ask the input dispatch policy // for the targets of an event that is to be dispatched. KeyEvent mReusableKeyEvent; MotionEvent mReusableMotionEvent; // 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 mCurrentInputTargets; // 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(); // 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); // 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); // Manage the dispatch cycle for a single connection. void prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection, EventEntry* eventEntry, const InputTarget* inputTarget, bool resumeWithAppendedMotionSample); void startDispatchCycleLocked(nsecs_t currentTime, Connection* connection); void finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection); bool timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection); bool abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection, bool broken); static bool handleReceiveCallback(int receiveFd, int events, void* data); // Add or remove a connection to the mActiveConnections vector. void activateConnectionLocked(Connection* connection); void deactivateConnectionLocked(Connection* connection); // 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); }; /* Enqueues and dispatches input events, endlessly. */ class InputDispatcherThread : public Thread { public: explicit InputDispatcherThread(const sp& dispatcher); ~InputDispatcherThread(); private: virtual bool threadLoop(); sp mDispatcher; }; } // namespace android #endif // _UI_INPUT_DISPATCHER_PRIV_H