| /* |
| * 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 <ui/Input.h> |
| #include <ui/InputDispatchPolicy.h> |
| #include <ui/InputTransport.h> |
| #include <utils/KeyedVector.h> |
| #include <utils/Vector.h> |
| #include <utils/threads.h> |
| #include <utils/Timers.h> |
| #include <utils/RefBase.h> |
| #include <utils/String8.h> |
| #include <utils/PollLoop.h> |
| #include <utils/Pool.h> |
| |
| #include <stddef.h> |
| #include <unistd.h> |
| |
| |
| 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>& inputChannel) = 0; |
| virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; |
| }; |
| |
| /* Dispatches events. */ |
| class InputDispatcher : public InputDispatcherInterface { |
| protected: |
| virtual ~InputDispatcher(); |
| |
| public: |
| explicit InputDispatcher(const sp<InputDispatchPolicyInterface>& 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>& inputChannel); |
| virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); |
| |
| private: |
| template <typename T> |
| struct Link { |
| T* next; |
| T* prev; |
| }; |
| |
| struct EventEntry : Link<EventEntry> { |
| 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<DispatchEntry> { |
| 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 <typename T> |
| 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<ConfigurationChangedEntry> mConfigurationChangeEntryPool; |
| Pool<KeyEntry> mKeyEntryPool; |
| Pool<MotionEntry> mMotionEntryPool; |
| Pool<MotionSample> mMotionSamplePool; |
| Pool<DispatchEntry> 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> inputChannel; |
| InputPublisher inputPublisher; |
| Queue<DispatchEntry> 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>& 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<InputDispatchPolicyInterface> mPolicy; |
| |
| Mutex mLock; |
| |
| Queue<EventEntry> mInboundQueue; |
| Allocator mAllocator; |
| |
| sp<PollLoop> mPollLoop; |
| |
| // 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 |
| // 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<InputTarget> 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<InputDispatcherInterface>& dispatcher); |
| ~InputDispatcherThread(); |
| |
| private: |
| virtual bool threadLoop(); |
| |
| sp<InputDispatcherInterface> mDispatcher; |
| }; |
| |
| } // namespace android |
| |
| #endif // _UI_INPUT_DISPATCHER_PRIV_H |