michael@0: /* michael@0: * Copyright (C) 2010 The Android Open Source Project michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #ifndef _UI_INPUT_DISPATCHER_H michael@0: #define _UI_INPUT_DISPATCHER_H michael@0: michael@0: #include "Input.h" michael@0: #include "InputTransport.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "InputWindow.h" michael@0: #include "InputApplication.h" michael@0: #include "InputListener.h" michael@0: michael@0: michael@0: namespace android { michael@0: michael@0: /* michael@0: * Constants used to report the outcome of input event injection. michael@0: */ michael@0: enum { michael@0: /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ michael@0: INPUT_EVENT_INJECTION_PENDING = -1, michael@0: michael@0: /* Injection succeeded. */ michael@0: INPUT_EVENT_INJECTION_SUCCEEDED = 0, michael@0: michael@0: /* Injection failed because the injector did not have permission to inject michael@0: * into the application with input focus. */ michael@0: INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, michael@0: michael@0: /* Injection failed because there were no available input targets. */ michael@0: INPUT_EVENT_INJECTION_FAILED = 2, michael@0: michael@0: /* Injection failed due to a timeout. */ michael@0: INPUT_EVENT_INJECTION_TIMED_OUT = 3 michael@0: }; michael@0: michael@0: /* michael@0: * Constants used to determine the input event injection synchronization mode. michael@0: */ michael@0: enum { michael@0: /* Injection is asynchronous and is assumed always to be successful. */ michael@0: INPUT_EVENT_INJECTION_SYNC_NONE = 0, michael@0: michael@0: /* Waits for previous events to be dispatched so that the input dispatcher can determine michael@0: * whether input event injection willbe permitted based on the current input focus. michael@0: * Does not wait for the input event to finish processing. */ michael@0: INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, michael@0: michael@0: /* Waits for the input event to be completely processed. */ michael@0: INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * An input target specifies how an input event is to be dispatched to a particular window michael@0: * including the window's input channel, control flags, a timeout, and an X / Y offset to michael@0: * be added to input event coordinates to compensate for the absolute position of the michael@0: * window area. michael@0: */ michael@0: struct InputTarget { michael@0: enum { michael@0: /* This flag indicates that the event is being delivered to a foreground application. */ michael@0: FLAG_FOREGROUND = 1 << 0, michael@0: michael@0: /* This flag indicates that the target of a MotionEvent is partly or wholly michael@0: * obscured by another visible window above it. The motion event should be michael@0: * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ michael@0: FLAG_WINDOW_IS_OBSCURED = 1 << 1, michael@0: michael@0: /* This flag indicates that a motion event is being split across multiple windows. */ michael@0: FLAG_SPLIT = 1 << 2, michael@0: michael@0: /* This flag indicates that the pointer coordinates dispatched to the application michael@0: * will be zeroed out to avoid revealing information to an application. This is michael@0: * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing michael@0: * the same UID from watching all touches. */ michael@0: FLAG_ZERO_COORDS = 1 << 3, michael@0: michael@0: /* This flag indicates that the event should be sent as is. michael@0: * Should always be set unless the event is to be transmuted. */ michael@0: FLAG_DISPATCH_AS_IS = 1 << 8, michael@0: michael@0: /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside michael@0: * of the area of this target and so should instead be delivered as an michael@0: * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ michael@0: FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, michael@0: michael@0: /* This flag indicates that a hover sequence is starting in the given window. michael@0: * The event is transmuted into ACTION_HOVER_ENTER. */ michael@0: FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, michael@0: michael@0: /* This flag indicates that a hover event happened outside of a window which handled michael@0: * previous hover events, signifying the end of the current hover sequence for that michael@0: * window. michael@0: * The event is transmuted into ACTION_HOVER_ENTER. */ michael@0: FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, michael@0: michael@0: /* This flag indicates that the event should be canceled. michael@0: * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips michael@0: * outside of a window. */ michael@0: FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, michael@0: michael@0: /* This flag indicates that the event should be dispatched as an initial down. michael@0: * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips michael@0: * into a new window. */ michael@0: FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, michael@0: michael@0: /* Mask for all dispatch modes. */ michael@0: FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS michael@0: | FLAG_DISPATCH_AS_OUTSIDE michael@0: | FLAG_DISPATCH_AS_HOVER_ENTER michael@0: | FLAG_DISPATCH_AS_HOVER_EXIT michael@0: | FLAG_DISPATCH_AS_SLIPPERY_EXIT michael@0: | FLAG_DISPATCH_AS_SLIPPERY_ENTER, michael@0: }; michael@0: michael@0: // The input channel to be targeted. michael@0: sp inputChannel; michael@0: michael@0: // Flags for the input target. michael@0: int32_t flags; michael@0: michael@0: // The x and y offset to add to a MotionEvent as it is delivered. michael@0: // (ignored for KeyEvents) michael@0: float xOffset, yOffset; michael@0: michael@0: // Scaling factor to apply to MotionEvent as it is delivered. michael@0: // (ignored for KeyEvents) michael@0: float scaleFactor; michael@0: michael@0: // The subset of pointer ids to include in motion events dispatched to this input target michael@0: // if FLAG_SPLIT is set. michael@0: BitSet32 pointerIds; michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * Input dispatcher configuration. michael@0: * michael@0: * Specifies various options that modify the behavior of the input dispatcher. michael@0: * The values provided here are merely defaults. The actual values will come from ViewConfiguration michael@0: * and are passed into the dispatcher during initialization. michael@0: */ michael@0: struct InputDispatcherConfiguration { michael@0: // The key repeat initial timeout. michael@0: nsecs_t keyRepeatTimeout; michael@0: michael@0: // The key repeat inter-key delay. michael@0: nsecs_t keyRepeatDelay; michael@0: michael@0: InputDispatcherConfiguration() : michael@0: keyRepeatTimeout(500 * 1000000LL), michael@0: keyRepeatDelay(50 * 1000000LL) { } michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * Input dispatcher policy interface. michael@0: * michael@0: * The input reader policy is used by the input reader to interact with the Window Manager michael@0: * and other system components. michael@0: * michael@0: * The actual implementation is partially supported by callbacks into the DVM michael@0: * via JNI. This interface is also mocked in the unit tests. michael@0: */ michael@0: class InputDispatcherPolicyInterface : public virtual RefBase { michael@0: protected: michael@0: InputDispatcherPolicyInterface() { } michael@0: virtual ~InputDispatcherPolicyInterface() { } michael@0: michael@0: public: michael@0: /* Notifies the system that a configuration change has occurred. */ michael@0: virtual void notifyConfigurationChanged(nsecs_t when) = 0; michael@0: michael@0: /* Notifies the system that an application is not responding. michael@0: * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ michael@0: virtual nsecs_t notifyANR(const sp& inputApplicationHandle, michael@0: const sp& inputWindowHandle) = 0; michael@0: michael@0: /* Notifies the system that an input channel is unrecoverably broken. */ michael@0: virtual void notifyInputChannelBroken(const sp& inputWindowHandle) = 0; michael@0: michael@0: /* Gets the input dispatcher configuration. */ michael@0: virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; michael@0: michael@0: /* Returns true if automatic key repeating is enabled. */ michael@0: virtual bool isKeyRepeatEnabled() = 0; michael@0: michael@0: /* Filters an input event. michael@0: * Return true to dispatch the event unmodified, false to consume the event. michael@0: * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED michael@0: * to injectInputEvent. michael@0: */ michael@0: virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0; michael@0: michael@0: /* Intercepts a key event immediately before queueing it. michael@0: * The policy can use this method as an opportunity to perform power management functions michael@0: * and early event preprocessing such as updating policy flags. michael@0: * michael@0: * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event michael@0: * should be dispatched to applications. michael@0: */ michael@0: virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; michael@0: michael@0: /* Intercepts a touch, trackball or other motion event before queueing it. michael@0: * The policy can use this method as an opportunity to perform power management functions michael@0: * and early event preprocessing such as updating policy flags. michael@0: * michael@0: * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event michael@0: * should be dispatched to applications. michael@0: */ michael@0: virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0; michael@0: michael@0: /* Allows the policy a chance to intercept a key before dispatching. */ michael@0: virtual nsecs_t interceptKeyBeforeDispatching(const sp& inputWindowHandle, michael@0: const KeyEvent* keyEvent, uint32_t policyFlags) = 0; michael@0: michael@0: /* Allows the policy a chance to perform default processing for an unhandled key. michael@0: * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ michael@0: virtual bool dispatchUnhandledKey(const sp& inputWindowHandle, michael@0: const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; michael@0: michael@0: /* Notifies the policy about switch events. michael@0: */ michael@0: virtual void notifySwitch(nsecs_t when, michael@0: uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0; michael@0: michael@0: /* Poke user activity for an event dispatched to a window. */ michael@0: virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; michael@0: michael@0: /* Checks whether a given application pid/uid has permission to inject input events michael@0: * into other applications. michael@0: * michael@0: * This method is special in that its implementation promises to be non-reentrant and michael@0: * is safe to call while holding other locks. (Most other methods make no such guarantees!) michael@0: */ michael@0: virtual bool checkInjectEventsPermissionNonReentrant( michael@0: int32_t injectorPid, int32_t injectorUid) = 0; michael@0: }; michael@0: michael@0: michael@0: /* Notifies the system about input events generated by the input reader. michael@0: * The dispatcher is expected to be mostly asynchronous. */ michael@0: class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { michael@0: protected: michael@0: InputDispatcherInterface() { } michael@0: virtual ~InputDispatcherInterface() { } michael@0: michael@0: public: michael@0: /* Dumps the state of the input dispatcher. michael@0: * michael@0: * This method may be called on any thread (usually by the input manager). */ michael@0: virtual void dump(String8& dump) = 0; michael@0: michael@0: /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ michael@0: virtual void monitor() = 0; michael@0: michael@0: /* Runs a single iteration of the dispatch loop. michael@0: * Nominally processes one queued event, a timeout, or a response from an input consumer. michael@0: * michael@0: * This method should only be called on the input dispatcher thread. michael@0: */ michael@0: virtual void dispatchOnce() = 0; michael@0: michael@0: /* Injects an input event and optionally waits for sync. michael@0: * The synchronization mode determines whether the method blocks while waiting for michael@0: * input injection to proceed. michael@0: * Returns one of the INPUT_EVENT_INJECTION_XXX constants. michael@0: * michael@0: * This method may be called on any thread (usually by the input manager). michael@0: */ michael@0: virtual int32_t injectInputEvent(const InputEvent* event, michael@0: int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, michael@0: uint32_t policyFlags) = 0; michael@0: michael@0: /* Sets the list of input windows. michael@0: * michael@0: * This method may be called on any thread (usually by the input manager). michael@0: */ michael@0: virtual void setInputWindows(const Vector >& inputWindowHandles) = 0; michael@0: michael@0: /* Sets the focused application. michael@0: * michael@0: * This method may be called on any thread (usually by the input manager). michael@0: */ michael@0: virtual void setFocusedApplication( michael@0: const sp& inputApplicationHandle) = 0; michael@0: michael@0: /* Sets the input dispatching mode. michael@0: * michael@0: * This method may be called on any thread (usually by the input manager). michael@0: */ michael@0: virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; michael@0: michael@0: /* Sets whether input event filtering is enabled. michael@0: * When enabled, incoming input events are sent to the policy's filterInputEvent michael@0: * method instead of being dispatched. The filter is expected to use michael@0: * injectInputEvent to inject the events it would like to have dispatched. michael@0: * It should include POLICY_FLAG_FILTERED in the policy flags during injection. michael@0: */ michael@0: virtual void setInputFilterEnabled(bool enabled) = 0; michael@0: michael@0: /* Transfers touch focus from the window associated with one channel to the michael@0: * window associated with the other channel. michael@0: * michael@0: * Returns true on success. False if the window did not actually have touch focus. michael@0: */ michael@0: virtual bool transferTouchFocus(const sp& fromChannel, michael@0: const sp& toChannel) = 0; michael@0: michael@0: /* Registers or unregister input channels that may be used as targets for input events. michael@0: * If monitor is true, the channel will receive a copy of all input events. michael@0: * michael@0: * These methods may be called on any thread (usually by the input manager). michael@0: */ michael@0: virtual status_t registerInputChannel(const sp& inputChannel, michael@0: const sp& inputWindowHandle, bool monitor) = 0; michael@0: virtual status_t unregisterInputChannel(const sp& inputChannel) = 0; michael@0: }; michael@0: michael@0: /* Dispatches events to input targets. Some functions of the input dispatcher, such as michael@0: * identifying input targets, are controlled by a separate policy object. michael@0: * michael@0: * IMPORTANT INVARIANT: michael@0: * Because the policy can potentially block or cause re-entrance into the input dispatcher, michael@0: * the input dispatcher never calls into the policy while holding its internal locks. michael@0: * The implementation is also carefully designed to recover from scenarios such as an michael@0: * input channel becoming unregistered while identifying input targets or processing timeouts. michael@0: * michael@0: * Methods marked 'Locked' must be called with the lock acquired. michael@0: * michael@0: * Methods marked 'LockedInterruptible' must be called with the lock acquired but michael@0: * may during the course of their execution release the lock, call into the policy, and michael@0: * then reacquire the lock. The caller is responsible for recovering gracefully. michael@0: * michael@0: * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. michael@0: */ michael@0: class InputDispatcher : public InputDispatcherInterface { michael@0: protected: michael@0: virtual ~InputDispatcher(); michael@0: michael@0: public: michael@0: explicit InputDispatcher(const sp& policy); michael@0: michael@0: virtual void dump(String8& dump); michael@0: virtual void monitor(); michael@0: michael@0: virtual void dispatchOnce(); michael@0: michael@0: virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args); michael@0: virtual void notifyKey(const NotifyKeyArgs* args); michael@0: virtual void notifyMotion(const NotifyMotionArgs* args); michael@0: virtual void notifySwitch(const NotifySwitchArgs* args); michael@0: virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args); michael@0: michael@0: virtual int32_t injectInputEvent(const InputEvent* event, michael@0: int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, michael@0: uint32_t policyFlags); michael@0: michael@0: virtual void setInputWindows(const Vector >& inputWindowHandles); michael@0: virtual void setFocusedApplication(const sp& inputApplicationHandle); michael@0: virtual void setInputDispatchMode(bool enabled, bool frozen); michael@0: virtual void setInputFilterEnabled(bool enabled); michael@0: michael@0: virtual bool transferTouchFocus(const sp& fromChannel, michael@0: const sp& toChannel); michael@0: michael@0: virtual status_t registerInputChannel(const sp& inputChannel, michael@0: const sp& inputWindowHandle, bool monitor); michael@0: virtual status_t unregisterInputChannel(const sp& inputChannel); michael@0: michael@0: private: michael@0: template michael@0: struct Link { michael@0: T* next; michael@0: T* prev; michael@0: michael@0: protected: michael@0: inline Link() : next(NULL), prev(NULL) { } michael@0: }; michael@0: michael@0: struct InjectionState { michael@0: mutable int32_t refCount; michael@0: michael@0: int32_t injectorPid; michael@0: int32_t injectorUid; michael@0: int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING michael@0: bool injectionIsAsync; // set to true if injection is not waiting for the result michael@0: int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress michael@0: michael@0: InjectionState(int32_t injectorPid, int32_t injectorUid); michael@0: void release(); michael@0: michael@0: private: michael@0: ~InjectionState(); michael@0: }; michael@0: michael@0: struct EventEntry : Link { michael@0: enum { michael@0: TYPE_CONFIGURATION_CHANGED, michael@0: TYPE_DEVICE_RESET, michael@0: TYPE_KEY, michael@0: TYPE_MOTION michael@0: }; michael@0: michael@0: mutable int32_t refCount; michael@0: int32_t type; michael@0: nsecs_t eventTime; michael@0: uint32_t policyFlags; michael@0: InjectionState* injectionState; michael@0: michael@0: bool dispatchInProgress; // initially false, set to true while dispatching michael@0: michael@0: inline bool isInjected() const { return injectionState != NULL; } michael@0: michael@0: void release(); michael@0: michael@0: virtual void appendDescription(String8& msg) const = 0; michael@0: michael@0: protected: michael@0: EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags); michael@0: virtual ~EventEntry(); michael@0: void releaseInjectionState(); michael@0: }; michael@0: michael@0: struct ConfigurationChangedEntry : EventEntry { michael@0: ConfigurationChangedEntry(nsecs_t eventTime); michael@0: virtual void appendDescription(String8& msg) const; michael@0: michael@0: protected: michael@0: virtual ~ConfigurationChangedEntry(); michael@0: }; michael@0: michael@0: struct DeviceResetEntry : EventEntry { michael@0: int32_t deviceId; michael@0: michael@0: DeviceResetEntry(nsecs_t eventTime, int32_t deviceId); michael@0: virtual void appendDescription(String8& msg) const; michael@0: michael@0: protected: michael@0: virtual ~DeviceResetEntry(); michael@0: }; michael@0: michael@0: struct KeyEntry : EventEntry { michael@0: int32_t deviceId; michael@0: uint32_t source; michael@0: int32_t action; michael@0: int32_t flags; michael@0: int32_t keyCode; michael@0: int32_t scanCode; michael@0: int32_t metaState; michael@0: int32_t repeatCount; michael@0: nsecs_t downTime; michael@0: michael@0: bool syntheticRepeat; // set to true for synthetic key repeats michael@0: michael@0: enum InterceptKeyResult { michael@0: INTERCEPT_KEY_RESULT_UNKNOWN, michael@0: INTERCEPT_KEY_RESULT_SKIP, michael@0: INTERCEPT_KEY_RESULT_CONTINUE, michael@0: INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, michael@0: }; michael@0: InterceptKeyResult interceptKeyResult; // set based on the interception result michael@0: nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER michael@0: michael@0: KeyEntry(nsecs_t eventTime, michael@0: int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, michael@0: int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, michael@0: int32_t repeatCount, nsecs_t downTime); michael@0: virtual void appendDescription(String8& msg) const; michael@0: void recycle(); michael@0: michael@0: protected: michael@0: virtual ~KeyEntry(); michael@0: }; michael@0: michael@0: struct MotionEntry : EventEntry { michael@0: nsecs_t eventTime; michael@0: int32_t deviceId; michael@0: uint32_t source; michael@0: int32_t action; michael@0: int32_t flags; michael@0: int32_t metaState; michael@0: int32_t buttonState; michael@0: int32_t edgeFlags; michael@0: float xPrecision; michael@0: float yPrecision; michael@0: nsecs_t downTime; michael@0: int32_t displayId; michael@0: uint32_t pointerCount; michael@0: PointerProperties pointerProperties[MAX_POINTERS]; michael@0: PointerCoords pointerCoords[MAX_POINTERS]; michael@0: michael@0: MotionEntry(nsecs_t eventTime, michael@0: int32_t deviceId, uint32_t source, uint32_t policyFlags, michael@0: int32_t action, int32_t flags, michael@0: int32_t metaState, int32_t buttonState, int32_t edgeFlags, michael@0: float xPrecision, float yPrecision, michael@0: nsecs_t downTime, int32_t displayId, uint32_t pointerCount, michael@0: const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); michael@0: virtual void appendDescription(String8& msg) const; michael@0: michael@0: protected: michael@0: virtual ~MotionEntry(); michael@0: }; michael@0: michael@0: // Tracks the progress of dispatching a particular event to a particular connection. michael@0: struct DispatchEntry : Link { michael@0: const uint32_t seq; // unique sequence number, never 0 michael@0: michael@0: EventEntry* eventEntry; // the event to dispatch michael@0: int32_t targetFlags; michael@0: float xOffset; michael@0: float yOffset; michael@0: float scaleFactor; michael@0: nsecs_t deliveryTime; // time when the event was actually delivered michael@0: michael@0: // Set to the resolved action and flags when the event is enqueued. michael@0: int32_t resolvedAction; michael@0: int32_t resolvedFlags; michael@0: michael@0: DispatchEntry(EventEntry* eventEntry, michael@0: int32_t targetFlags, float xOffset, float yOffset, float scaleFactor); michael@0: ~DispatchEntry(); michael@0: michael@0: inline bool hasForegroundTarget() const { michael@0: return targetFlags & InputTarget::FLAG_FOREGROUND; michael@0: } michael@0: michael@0: inline bool isSplit() const { michael@0: return targetFlags & InputTarget::FLAG_SPLIT; michael@0: } michael@0: michael@0: private: michael@0: static volatile int32_t sNextSeqAtomic; michael@0: michael@0: static uint32_t nextSeq(); michael@0: }; michael@0: michael@0: // A command entry captures state and behavior for an action to be performed in the michael@0: // dispatch loop after the initial processing has taken place. It is essentially michael@0: // a kind of continuation used to postpone sensitive policy interactions to a point michael@0: // in the dispatch loop where it is safe to release the lock (generally after finishing michael@0: // the critical parts of the dispatch cycle). michael@0: // michael@0: // The special thing about commands is that they can voluntarily release and reacquire michael@0: // the dispatcher lock at will. Initially when the command starts running, the michael@0: // dispatcher lock is held. However, if the command needs to call into the policy to michael@0: // do some work, it can release the lock, do the work, then reacquire the lock again michael@0: // before returning. michael@0: // michael@0: // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch michael@0: // never calls into the policy while holding its lock. michael@0: // michael@0: // Commands are implicitly 'LockedInterruptible'. michael@0: struct CommandEntry; michael@0: typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); michael@0: michael@0: class Connection; michael@0: struct CommandEntry : Link { michael@0: CommandEntry(Command command); michael@0: ~CommandEntry(); michael@0: michael@0: Command command; michael@0: michael@0: // parameters for the command (usage varies by command) michael@0: sp connection; michael@0: nsecs_t eventTime; michael@0: KeyEntry* keyEntry; michael@0: sp inputApplicationHandle; michael@0: sp inputWindowHandle; michael@0: int32_t userActivityEventType; michael@0: uint32_t seq; michael@0: bool handled; michael@0: }; michael@0: michael@0: // Generic queue implementation. michael@0: template michael@0: struct Queue { michael@0: T* head; michael@0: T* tail; michael@0: michael@0: inline Queue() : head(NULL), tail(NULL) { michael@0: } michael@0: michael@0: inline bool isEmpty() const { michael@0: return !head; michael@0: } michael@0: michael@0: inline void enqueueAtTail(T* entry) { michael@0: entry->prev = tail; michael@0: if (tail) { michael@0: tail->next = entry; michael@0: } else { michael@0: head = entry; michael@0: } michael@0: entry->next = NULL; michael@0: tail = entry; michael@0: } michael@0: michael@0: inline void enqueueAtHead(T* entry) { michael@0: entry->next = head; michael@0: if (head) { michael@0: head->prev = entry; michael@0: } else { michael@0: tail = entry; michael@0: } michael@0: entry->prev = NULL; michael@0: head = entry; michael@0: } michael@0: michael@0: inline void dequeue(T* entry) { michael@0: if (entry->prev) { michael@0: entry->prev->next = entry->next; michael@0: } else { michael@0: head = entry->next; michael@0: } michael@0: if (entry->next) { michael@0: entry->next->prev = entry->prev; michael@0: } else { michael@0: tail = entry->prev; michael@0: } michael@0: } michael@0: michael@0: inline T* dequeueAtHead() { michael@0: T* entry = head; michael@0: head = entry->next; michael@0: if (head) { michael@0: head->prev = NULL; michael@0: } else { michael@0: tail = NULL; michael@0: } michael@0: return entry; michael@0: } michael@0: michael@0: uint32_t count() const; michael@0: }; michael@0: michael@0: /* Specifies which events are to be canceled and why. */ michael@0: struct CancelationOptions { michael@0: enum Mode { michael@0: CANCEL_ALL_EVENTS = 0, michael@0: CANCEL_POINTER_EVENTS = 1, michael@0: CANCEL_NON_POINTER_EVENTS = 2, michael@0: CANCEL_FALLBACK_EVENTS = 3, michael@0: }; michael@0: michael@0: // The criterion to use to determine which events should be canceled. michael@0: Mode mode; michael@0: michael@0: // Descriptive reason for the cancelation. michael@0: const char* reason; michael@0: michael@0: // The specific keycode of the key event to cancel, or -1 to cancel any key event. michael@0: int32_t keyCode; michael@0: michael@0: // The specific device id of events to cancel, or -1 to cancel events from any device. michael@0: int32_t deviceId; michael@0: michael@0: CancelationOptions(Mode mode, const char* reason) : michael@0: mode(mode), reason(reason), keyCode(-1), deviceId(-1) { } michael@0: }; michael@0: michael@0: /* Tracks dispatched key and motion event state so that cancelation events can be michael@0: * synthesized when events are dropped. */ michael@0: class InputState { michael@0: public: michael@0: InputState(); michael@0: ~InputState(); michael@0: michael@0: // Returns true if there is no state to be canceled. michael@0: bool isNeutral() const; michael@0: michael@0: // Returns true if the specified source is known to have received a hover enter michael@0: // motion event. michael@0: bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; michael@0: michael@0: // Records tracking information for a key event that has just been published. michael@0: // Returns true if the event should be delivered, false if it is inconsistent michael@0: // and should be skipped. michael@0: bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); michael@0: michael@0: // Records tracking information for a motion event that has just been published. michael@0: // Returns true if the event should be delivered, false if it is inconsistent michael@0: // and should be skipped. michael@0: bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); michael@0: michael@0: // Synthesizes cancelation events for the current state and resets the tracked state. michael@0: void synthesizeCancelationEvents(nsecs_t currentTime, michael@0: Vector& outEvents, const CancelationOptions& options); michael@0: michael@0: // Clears the current state. michael@0: void clear(); michael@0: michael@0: // Copies pointer-related parts of the input state to another instance. michael@0: void copyPointerStateTo(InputState& other) const; michael@0: michael@0: // Gets the fallback key associated with a keycode. michael@0: // Returns -1 if none. michael@0: // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy. michael@0: int32_t getFallbackKey(int32_t originalKeyCode); michael@0: michael@0: // Sets the fallback key for a particular keycode. michael@0: void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode); michael@0: michael@0: // Removes the fallback key for a particular keycode. michael@0: void removeFallbackKey(int32_t originalKeyCode); michael@0: michael@0: inline const KeyedVector& getFallbackKeys() const { michael@0: return mFallbackKeys; michael@0: } michael@0: michael@0: private: michael@0: struct KeyMemento { michael@0: int32_t deviceId; michael@0: uint32_t source; michael@0: int32_t keyCode; michael@0: int32_t scanCode; michael@0: int32_t metaState; michael@0: int32_t flags; michael@0: nsecs_t downTime; michael@0: uint32_t policyFlags; michael@0: }; michael@0: michael@0: struct MotionMemento { michael@0: int32_t deviceId; michael@0: uint32_t source; michael@0: int32_t flags; michael@0: float xPrecision; michael@0: float yPrecision; michael@0: nsecs_t downTime; michael@0: int32_t displayId; michael@0: uint32_t pointerCount; michael@0: PointerProperties pointerProperties[MAX_POINTERS]; michael@0: PointerCoords pointerCoords[MAX_POINTERS]; michael@0: bool hovering; michael@0: uint32_t policyFlags; michael@0: michael@0: void setPointers(const MotionEntry* entry); michael@0: }; michael@0: michael@0: Vector mKeyMementos; michael@0: Vector mMotionMementos; michael@0: KeyedVector mFallbackKeys; michael@0: michael@0: ssize_t findKeyMemento(const KeyEntry* entry) const; michael@0: ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; michael@0: michael@0: void addKeyMemento(const KeyEntry* entry, int32_t flags); michael@0: void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); michael@0: michael@0: static bool shouldCancelKey(const KeyMemento& memento, michael@0: const CancelationOptions& options); michael@0: static bool shouldCancelMotion(const MotionMemento& memento, michael@0: const CancelationOptions& options); michael@0: }; michael@0: michael@0: /* Manages the dispatch state associated with a single input channel. */ michael@0: class Connection : public RefBase { michael@0: protected: michael@0: virtual ~Connection(); michael@0: michael@0: public: michael@0: enum Status { michael@0: // Everything is peachy. michael@0: STATUS_NORMAL, michael@0: // An unrecoverable communication error has occurred. michael@0: STATUS_BROKEN, michael@0: // The input channel has been unregistered. michael@0: STATUS_ZOMBIE michael@0: }; michael@0: michael@0: Status status; michael@0: sp inputChannel; // never null michael@0: sp inputWindowHandle; // may be null michael@0: bool monitor; michael@0: InputPublisher inputPublisher; michael@0: InputState inputState; michael@0: michael@0: // True if the socket is full and no further events can be published until michael@0: // the application consumes some of the input. michael@0: bool inputPublisherBlocked; michael@0: michael@0: // Queue of events that need to be published to the connection. michael@0: Queue outboundQueue; michael@0: michael@0: // Queue of events that have been published to the connection but that have not michael@0: // yet received a "finished" response from the application. michael@0: Queue waitQueue; michael@0: michael@0: explicit Connection(const sp& inputChannel, michael@0: const sp& inputWindowHandle, bool monitor); michael@0: michael@0: inline const char* getInputChannelName() const { return inputChannel->getName().string(); } michael@0: michael@0: const char* getWindowName() const; michael@0: const char* getStatusLabel() const; michael@0: michael@0: DispatchEntry* findWaitQueueEntry(uint32_t seq); michael@0: }; michael@0: michael@0: enum DropReason { michael@0: DROP_REASON_NOT_DROPPED = 0, michael@0: DROP_REASON_POLICY = 1, michael@0: DROP_REASON_APP_SWITCH = 2, michael@0: DROP_REASON_DISABLED = 3, michael@0: DROP_REASON_BLOCKED = 4, michael@0: DROP_REASON_STALE = 5, michael@0: }; michael@0: michael@0: sp mPolicy; michael@0: InputDispatcherConfiguration mConfig; michael@0: michael@0: Mutex mLock; michael@0: michael@0: Condition mDispatcherIsAliveCondition; michael@0: michael@0: sp mLooper; michael@0: michael@0: EventEntry* mPendingEvent; michael@0: Queue mInboundQueue; michael@0: Queue mCommandQueue; michael@0: michael@0: void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime); michael@0: michael@0: // Enqueues an inbound event. Returns true if mLooper->wake() should be called. michael@0: bool enqueueInboundEventLocked(EventEntry* entry); michael@0: michael@0: // Cleans up input state when dropping an inbound event. michael@0: void dropInboundEventLocked(EventEntry* entry, DropReason dropReason); michael@0: michael@0: // App switch latency optimization. michael@0: bool mAppSwitchSawKeyDown; michael@0: nsecs_t mAppSwitchDueTime; michael@0: michael@0: static bool isAppSwitchKeyCode(int32_t keyCode); michael@0: bool isAppSwitchKeyEventLocked(KeyEntry* keyEntry); michael@0: bool isAppSwitchPendingLocked(); michael@0: void resetPendingAppSwitchLocked(bool handled); michael@0: michael@0: // Stale event latency optimization. michael@0: static bool isStaleEventLocked(nsecs_t currentTime, EventEntry* entry); michael@0: michael@0: // Blocked event latency optimization. Drops old events when the user intends michael@0: // to transfer focus to a new application. michael@0: EventEntry* mNextUnblockedEvent; michael@0: michael@0: sp findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y); michael@0: michael@0: // All registered connections mapped by channel file descriptor. michael@0: KeyedVector > mConnectionsByFd; michael@0: michael@0: ssize_t getConnectionIndexLocked(const sp& inputChannel); michael@0: michael@0: // Input channels that will receive a copy of all input events. michael@0: Vector > mMonitoringChannels; michael@0: michael@0: // Event injection and synchronization. michael@0: Condition mInjectionResultAvailableCondition; michael@0: bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); michael@0: void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult); michael@0: michael@0: Condition mInjectionSyncFinishedCondition; michael@0: void incrementPendingForegroundDispatchesLocked(EventEntry* entry); michael@0: void decrementPendingForegroundDispatchesLocked(EventEntry* entry); michael@0: michael@0: // Key repeat tracking. michael@0: struct KeyRepeatState { michael@0: KeyEntry* lastKeyEntry; // or null if no repeat michael@0: nsecs_t nextRepeatTime; michael@0: } mKeyRepeatState; michael@0: michael@0: void resetKeyRepeatLocked(); michael@0: KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime); michael@0: michael@0: // Deferred command processing. michael@0: bool haveCommandsLocked() const; michael@0: bool runCommandsLockedInterruptible(); michael@0: CommandEntry* postCommandLocked(Command command); michael@0: michael@0: // Input filter processing. michael@0: bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args); michael@0: bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args); michael@0: michael@0: // Inbound event processing. michael@0: void drainInboundQueueLocked(); michael@0: void releasePendingEventLocked(); michael@0: void releaseInboundEventLocked(EventEntry* entry); michael@0: michael@0: // Dispatch state. michael@0: bool mDispatchEnabled; michael@0: bool mDispatchFrozen; michael@0: bool mInputFilterEnabled; michael@0: michael@0: Vector > mWindowHandles; michael@0: michael@0: sp getWindowHandleLocked(const sp& inputChannel) const; michael@0: bool hasWindowHandleLocked(const sp& windowHandle) const; michael@0: michael@0: // Focus tracking for keys, trackball, etc. michael@0: sp mFocusedWindowHandle; michael@0: michael@0: // Focus tracking for touch. michael@0: struct TouchedWindow { michael@0: sp windowHandle; michael@0: int32_t targetFlags; michael@0: BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set michael@0: }; michael@0: struct TouchState { michael@0: bool down; michael@0: bool split; michael@0: int32_t deviceId; // id of the device that is currently down, others are rejected michael@0: uint32_t source; // source of the device that is current down, others are rejected michael@0: int32_t displayId; // id to the display that currently has a touch, others are rejected michael@0: Vector windows; michael@0: michael@0: TouchState(); michael@0: ~TouchState(); michael@0: void reset(); michael@0: void copyFrom(const TouchState& other); michael@0: void addOrUpdateWindow(const sp& windowHandle, michael@0: int32_t targetFlags, BitSet32 pointerIds); michael@0: void removeWindow(const sp& windowHandle); michael@0: void filterNonAsIsTouchWindows(); michael@0: sp getFirstForegroundWindowHandle() const; michael@0: bool isSlippery() const; michael@0: }; michael@0: michael@0: TouchState mTouchState; michael@0: TouchState mTempTouchState; michael@0: michael@0: // Focused application. michael@0: sp mFocusedApplicationHandle; michael@0: michael@0: // Dispatcher state at time of last ANR. michael@0: String8 mLastANRState; michael@0: michael@0: // Dispatch inbound events. michael@0: bool dispatchConfigurationChangedLocked( michael@0: nsecs_t currentTime, ConfigurationChangedEntry* entry); michael@0: bool dispatchDeviceResetLocked( michael@0: nsecs_t currentTime, DeviceResetEntry* entry); michael@0: bool dispatchKeyLocked( michael@0: nsecs_t currentTime, KeyEntry* entry, michael@0: DropReason* dropReason, nsecs_t* nextWakeupTime); michael@0: bool dispatchMotionLocked( michael@0: nsecs_t currentTime, MotionEntry* entry, michael@0: DropReason* dropReason, nsecs_t* nextWakeupTime); michael@0: void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, michael@0: const Vector& inputTargets); michael@0: michael@0: void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry); michael@0: void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry); michael@0: michael@0: // Keeping track of ANR timeouts. michael@0: enum InputTargetWaitCause { michael@0: INPUT_TARGET_WAIT_CAUSE_NONE, michael@0: INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, michael@0: INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, michael@0: }; michael@0: michael@0: InputTargetWaitCause mInputTargetWaitCause; michael@0: nsecs_t mInputTargetWaitStartTime; michael@0: nsecs_t mInputTargetWaitTimeoutTime; michael@0: bool mInputTargetWaitTimeoutExpired; michael@0: sp mInputTargetWaitApplicationHandle; michael@0: michael@0: // Contains the last window which received a hover event. michael@0: sp mLastHoverWindowHandle; michael@0: michael@0: // Finding targets for input events. michael@0: int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, michael@0: const sp& applicationHandle, michael@0: const sp& windowHandle, michael@0: nsecs_t* nextWakeupTime, const char* reason); michael@0: void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, michael@0: const sp& inputChannel); michael@0: nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime); michael@0: void resetANRTimeoutsLocked(); michael@0: michael@0: int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, michael@0: Vector& inputTargets, nsecs_t* nextWakeupTime); michael@0: int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, michael@0: Vector& inputTargets, nsecs_t* nextWakeupTime, michael@0: bool* outConflictingPointerActions); michael@0: michael@0: void addWindowTargetLocked(const sp& windowHandle, michael@0: int32_t targetFlags, BitSet32 pointerIds, Vector& inputTargets); michael@0: void addMonitoringTargetsLocked(Vector& inputTargets); michael@0: michael@0: void pokeUserActivityLocked(const EventEntry* eventEntry); michael@0: bool checkInjectionPermission(const sp& windowHandle, michael@0: const InjectionState* injectionState); michael@0: bool isWindowObscuredAtPointLocked(const sp& windowHandle, michael@0: int32_t x, int32_t y) const; michael@0: bool isWindowReadyForMoreInputLocked(nsecs_t currentTime, michael@0: const sp& windowHandle, const EventEntry* eventEntry); michael@0: String8 getApplicationWindowLabelLocked(const sp& applicationHandle, michael@0: const sp& windowHandle); michael@0: michael@0: // Manage the dispatch cycle for a single connection. michael@0: // These methods are deliberately not Interruptible because doing all of the work michael@0: // with the mutex held makes it easier to ensure that connection invariants are maintained. michael@0: // If needed, the methods post commands to run later once the critical bits are done. michael@0: void prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, michael@0: EventEntry* eventEntry, const InputTarget* inputTarget); michael@0: void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, michael@0: EventEntry* eventEntry, const InputTarget* inputTarget); michael@0: void enqueueDispatchEntryLocked(const sp& connection, michael@0: EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode); michael@0: void startDispatchCycleLocked(nsecs_t currentTime, const sp& connection); michael@0: void finishDispatchCycleLocked(nsecs_t currentTime, const sp& connection, michael@0: uint32_t seq, bool handled); michael@0: void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp& connection, michael@0: bool notify); michael@0: void drainDispatchQueueLocked(Queue* queue); michael@0: void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry); michael@0: static int handleReceiveCallback(int fd, int events, void* data); michael@0: michael@0: void synthesizeCancelationEventsForAllConnectionsLocked( michael@0: const CancelationOptions& options); michael@0: void synthesizeCancelationEventsForInputChannelLocked(const sp& channel, michael@0: const CancelationOptions& options); michael@0: void synthesizeCancelationEventsForConnectionLocked(const sp& connection, michael@0: const CancelationOptions& options); michael@0: michael@0: // Splitting motion events across windows. michael@0: MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); michael@0: michael@0: // Reset and drop everything the dispatcher is doing. michael@0: void resetAndDropEverythingLocked(const char* reason); michael@0: michael@0: // Dump state. michael@0: void dumpDispatchStateLocked(String8& dump); michael@0: void logDispatchStateLocked(); michael@0: michael@0: // Registration. michael@0: void removeMonitorChannelLocked(const sp& inputChannel); michael@0: status_t unregisterInputChannelLocked(const sp& inputChannel, bool notify); michael@0: michael@0: // Add or remove a connection to the mActiveConnections vector. michael@0: void activateConnectionLocked(Connection* connection); michael@0: void deactivateConnectionLocked(Connection* connection); michael@0: michael@0: // Interesting events that we might like to log or tell the framework about. michael@0: void onDispatchCycleFinishedLocked( michael@0: nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled); michael@0: void onDispatchCycleBrokenLocked( michael@0: nsecs_t currentTime, const sp& connection); michael@0: void onANRLocked( michael@0: nsecs_t currentTime, const sp& applicationHandle, michael@0: const sp& windowHandle, michael@0: nsecs_t eventTime, nsecs_t waitStartTime, const char* reason); michael@0: michael@0: // Outbound policy interactions. michael@0: void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry); michael@0: void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); michael@0: void doNotifyANRLockedInterruptible(CommandEntry* commandEntry); michael@0: void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry); michael@0: void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry); michael@0: bool afterKeyEventLockedInterruptible(const sp& connection, michael@0: DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled); michael@0: bool afterMotionEventLockedInterruptible(const sp& connection, michael@0: DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled); michael@0: void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry); michael@0: void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry); michael@0: michael@0: // Statistics gathering. michael@0: void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, michael@0: int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); michael@0: void traceInboundQueueLengthLocked(); michael@0: void traceOutboundQueueLengthLocked(const sp& connection); michael@0: void traceWaitQueueLengthLocked(const sp& connection); michael@0: }; michael@0: michael@0: /* Enqueues and dispatches input events, endlessly. */ michael@0: class InputDispatcherThread : public Thread { michael@0: public: michael@0: explicit InputDispatcherThread(const sp& dispatcher); michael@0: ~InputDispatcherThread(); michael@0: michael@0: private: michael@0: virtual bool threadLoop(); michael@0: michael@0: sp mDispatcher; michael@0: }; michael@0: michael@0: } // namespace android michael@0: michael@0: #endif // _UI_INPUT_DISPATCHER_H