blob: bde07f2a090bc1a79d6ec48fb4f813d9d77009fa [file] [log] [blame]
/*
* 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