diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-06-15 01:31:58 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2010-06-15 16:43:18 -0700 |
commit | 54bc281639e5ff6955d7a86104f6f21624941aca (patch) | |
tree | b8c1a3094530f39942f26a436cc97e3989c5deaa | |
parent | 4607338eaf2988dc4dc4b0b738098f17878a90ab (diff) | |
download | frameworks_native-54bc281639e5ff6955d7a86104f6f21624941aca.zip frameworks_native-54bc281639e5ff6955d7a86104f6f21624941aca.tar.gz frameworks_native-54bc281639e5ff6955d7a86104f6f21624941aca.tar.bz2 |
More work in progress on native events.
Refactored the code to eliminate potential deadlocks due to re-entrant
calls from the policy into the dispatcher. Also added some plumbing
that will be used to notify the framework about ANRs.
Change-Id: Iba7a10de0cb3c56cd7520d6ce716db52fdcc94ff
-rw-r--r-- | include/ui/Input.h | 33 | ||||
-rw-r--r-- | include/ui/InputDispatchPolicy.h | 198 | ||||
-rw-r--r-- | include/ui/InputDispatcher.h | 208 | ||||
-rw-r--r-- | include/ui/InputManager.h | 44 | ||||
-rw-r--r-- | include/ui/InputReader.h | 203 | ||||
-rw-r--r-- | libs/ui/InputDispatcher.cpp | 393 | ||||
-rw-r--r-- | libs/ui/InputManager.cpp | 65 | ||||
-rw-r--r-- | libs/ui/InputReader.cpp | 203 |
8 files changed, 850 insertions, 497 deletions
diff --git a/include/ui/Input.h b/include/ui/Input.h index 6a5c8a8..d45bfcf 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -73,7 +73,7 @@ enum { POLICY_FLAG_MENU = 0x00000040, POLICY_FLAG_LAUNCHER = 0x00000080, - /* These flags are set by the input dispatch policy as it intercepts each event. */ + /* These flags are set by the input reader policy as it intercepts each event. */ // Indicates that the screen was off when the event was received and the event // should wake the device. @@ -85,6 +85,37 @@ enum { }; /* + * Describes the basic configuration of input devices that are present. + */ +struct InputConfiguration { + enum { + TOUCHSCREEN_UNDEFINED = 0, + TOUCHSCREEN_NOTOUCH = 1, + TOUCHSCREEN_STYLUS = 2, + TOUCHSCREEN_FINGER = 3 + }; + + enum { + KEYBOARD_UNDEFINED = 0, + KEYBOARD_NOKEYS = 1, + KEYBOARD_QWERTY = 2, + KEYBOARD_12KEY = 3 + }; + + enum { + NAVIGATION_UNDEFINED = 0, + NAVIGATION_NONAV = 1, + NAVIGATION_DPAD = 2, + NAVIGATION_TRACKBALL = 3, + NAVIGATION_WHEEL = 4 + }; + + int32_t touchScreen; + int32_t keyboard; + int32_t navigation; +}; + +/* * Pointer coordinate data. */ struct PointerCoords { diff --git a/include/ui/InputDispatchPolicy.h b/include/ui/InputDispatchPolicy.h deleted file mode 100644 index 3546813..0000000 --- a/include/ui/InputDispatchPolicy.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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_DISPATCH_POLICY_H -#define _UI_INPUT_DISPATCH_POLICY_H - -/** - * Native input dispatch policy. - */ - -#include <ui/Input.h> -#include <utils/Errors.h> -#include <utils/Vector.h> -#include <utils/Timers.h> -#include <utils/RefBase.h> -#include <utils/String8.h> - -namespace android { - -class InputChannel; - -/* - * 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 dispatch policy interface. - * - * The input dispatch policy is used by the input dispatcher to interact with the - * Window Manager and other system components. This separation of concerns keeps - * the input dispatcher relatively free of special case logic such as is required - * to determine the target of iput events, when to wake the device, how to interact - * with key guard, and when to transition to the home screen. - * - * The actual implementation is partially supported by callbacks into the DVM - * via JNI. This class is also mocked in the input dispatcher unit tests since - * it is an ideal test seam. - */ -class InputDispatchPolicyInterface : public virtual RefBase { -protected: - InputDispatchPolicyInterface() { } - virtual ~InputDispatchPolicyInterface() { } - -public: - enum { - ROTATION_0 = 0, - ROTATION_90 = 1, - ROTATION_180 = 2, - ROTATION_270 = 3 - }; - - enum { - // The input dispatcher should do nothing and discard the input unless other - // flags are set. - ACTION_NONE = 0, - - // The input dispatcher should dispatch the input to the application. - ACTION_DISPATCH = 0x00000001, - - // The input dispatcher should perform special filtering in preparation for - // a pending app switch. - ACTION_APP_SWITCH_COMING = 0x00000002, - - // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it - // passes through the dispatch pipeline. - ACTION_WOKE_HERE = 0x00000004, - - // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it - // passes through the dispatch pipeline. - ACTION_BRIGHT_HERE = 0x00000008 - }; - - enum { - TOUCHSCREEN_UNDEFINED = 0, - TOUCHSCREEN_NOTOUCH = 1, - TOUCHSCREEN_STYLUS = 2, - TOUCHSCREEN_FINGER = 3 - }; - - enum { - KEYBOARD_UNDEFINED = 0, - KEYBOARD_NOKEYS = 1, - KEYBOARD_QWERTY = 2, - KEYBOARD_12KEY = 3 - }; - - enum { - NAVIGATION_UNDEFINED = 0, - NAVIGATION_NONAV = 1, - NAVIGATION_DPAD = 2, - NAVIGATION_TRACKBALL = 3, - NAVIGATION_WHEEL = 4 - }; - - struct VirtualKeyDefinition { - int32_t scanCode; - - // configured position data, specified in display coords - int32_t centerX; - int32_t centerY; - int32_t width; - int32_t height; - }; - - /* Gets information about the display with the specified id. - * Returns true if the display info is available, false otherwise. - */ - virtual bool getDisplayInfo(int32_t displayId, - int32_t* width, int32_t* height, int32_t* orientation) = 0; - - virtual void notifyConfigurationChanged(nsecs_t when, - int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0; - - virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen) = 0; - - virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId, - int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; - - virtual int32_t interceptKey(nsecs_t when, int32_t deviceId, - bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0; - - virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown, - bool rolled) = 0; - - virtual int32_t interceptTouch(nsecs_t when) = 0; - - virtual bool allowKeyRepeat() = 0; - virtual nsecs_t getKeyRepeatTimeout() = 0; - - virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets) = 0; - virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets) = 0; - - /* Determine whether to turn on some hacks we have to improve the touch interaction with a - * certain device whose screen currently is not all that good. - */ - virtual bool filterTouchEvents() = 0; - - /* Determine whether to turn on some hacks to improve touch interaction with another device - * where touch coordinate data can get corrupted. - */ - virtual bool filterJumpyTouchEvents() = 0; - - virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0; - virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0; -}; - -} // namespace android - -#endif // _UI_INPUT_DISPATCH_POLICY_H 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. */ diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h index eb27513..3872c26 100644 --- a/include/ui/InputManager.h +++ b/include/ui/InputManager.h @@ -23,7 +23,6 @@ #include <ui/EventHub.h> #include <ui/Input.h> -#include <ui/InputDispatchPolicy.h> #include <utils/Errors.h> #include <utils/Vector.h> #include <utils/Timers.h> @@ -32,9 +31,14 @@ namespace android { -class InputReader; -class InputDispatcher; +class InputChannel; + +class InputReaderInterface; +class InputReaderPolicyInterface; class InputReaderThread; + +class InputDispatcherInterface; +class InputDispatcherPolicyInterface; class InputDispatcherThread; /* @@ -74,8 +78,11 @@ public: /* Unregisters an input channel. */ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; + /* Gets input device configuration. */ + virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0; + /* - * Query current input state. + * Queries current input state. * deviceId may be -1 to search for the device automatically, filtered by class. * deviceClasses may be -1 to ignore device class while searching. */ @@ -86,7 +93,7 @@ public: virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const = 0; - /* Determine whether physical keys exist for the given framework-domain key codes. */ + /* Determines whether physical keys exist for the given framework-domain key codes. */ virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0; }; @@ -95,12 +102,15 @@ protected: virtual ~InputManager(); public: - /* - * Creates an input manager that reads events from the given - * event hub and applies the given input dispatch policy. - */ - InputManager(const sp<EventHubInterface>& eventHub, - const sp<InputDispatchPolicyInterface>& policy); + InputManager( + const sp<EventHubInterface>& eventHub, + const sp<InputReaderPolicyInterface>& readerPolicy, + const sp<InputDispatcherPolicyInterface>& dispatcherPolicy); + + // (used for testing purposes) + InputManager( + const sp<InputReaderInterface>& reader, + const sp<InputDispatcherInterface>& dispatcher); virtual status_t start(); virtual status_t stop(); @@ -108,6 +118,7 @@ public: virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel); virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); + virtual void getInputConfiguration(InputConfiguration* outConfiguration) const; virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses, int32_t scanCode) const; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses, @@ -117,16 +128,13 @@ public: virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; private: - sp<EventHubInterface> mEventHub; - sp<InputDispatchPolicyInterface> mPolicy; + sp<InputReaderInterface> mReader; + sp<InputReaderThread> mReaderThread; - sp<InputDispatcher> mDispatcher; + sp<InputDispatcherInterface> mDispatcher; sp<InputDispatcherThread> mDispatcherThread; - sp<InputReader> mReader; - sp<InputReaderThread> mReaderThread; - - void configureExcludedDevices(); + void initialize(); }; } // namespace android diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index 7e7a64c..d76b8fe 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -19,7 +19,6 @@ #include <ui/EventHub.h> #include <ui/Input.h> -#include <ui/InputDispatchPolicy.h> #include <ui/InputDispatcher.h> #include <utils/KeyedVector.h> #include <utils/threads.h> @@ -35,21 +34,6 @@ * (This is limited by our use of BitSet32 to track pointer assignments.) */ #define MAX_POINTER_ID 32 -/** Amount that trackball needs to move in order to generate a key event. */ -#define TRACKBALL_MOVEMENT_THRESHOLD 6 - -/* Slop distance for jumpy pointer detection. - * The vertical range of the screen divided by this is our epsilon value. */ -#define JUMPY_EPSILON_DIVISOR 212 - -/* Number of jumpy points to drop for touchscreens that need it. */ -#define JUMPY_TRANSITION_DROPS 3 -#define JUMPY_DROP_LIMIT 3 - -/* Maximum squared distance for averaging. - * If moving farther than this, turn of averaging to avoid lag in response. */ -#define AVERAGING_DISTANCE_LIMIT (75 * 75) - /* Maximum number of historical samples to average. */ #define AVERAGING_HISTORY_SIZE 5 @@ -335,8 +319,129 @@ struct InputDevice { }; -/* Processes raw input events and sends cooked event data to an input dispatcher - * in accordance with the input dispatch policy. */ +/* + * Input reader 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 InputReaderPolicyInterface : public virtual RefBase { +protected: + InputReaderPolicyInterface() { } + virtual ~InputReaderPolicyInterface() { } + +public: + /* Display orientations. */ + enum { + ROTATION_0 = 0, + ROTATION_90 = 1, + ROTATION_180 = 2, + ROTATION_270 = 3 + }; + + /* Actions returned by interceptXXX methods. */ + enum { + // The input dispatcher should do nothing and discard the input unless other + // flags are set. + ACTION_NONE = 0, + + // The input dispatcher should dispatch the input to the application. + ACTION_DISPATCH = 0x00000001, + + // The input dispatcher should perform special filtering in preparation for + // a pending app switch. + ACTION_APP_SWITCH_COMING = 0x00000002, + + // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it + // passes through the dispatch pipeline. + ACTION_WOKE_HERE = 0x00000004, + + // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it + // passes through the dispatch pipeline. + ACTION_BRIGHT_HERE = 0x00000008 + }; + + /* Describes a virtual key. */ + struct VirtualKeyDefinition { + int32_t scanCode; + + // configured position data, specified in display coords + int32_t centerX; + int32_t centerY; + int32_t width; + int32_t height; + }; + + /* Gets information about the display with the specified id. + * Returns true if the display info is available, false otherwise. + */ + virtual bool getDisplayInfo(int32_t displayId, + int32_t* width, int32_t* height, int32_t* orientation) = 0; + + /* Provides feedback for a virtual key. + */ + virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId, + int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; + + /* Intercepts a key event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing. + * + * Returns a policy action constant such as ACTION_DISPATCH. + */ + virtual int32_t interceptKey(nsecs_t when, int32_t deviceId, + bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0; + + /* Intercepts a trackball event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing. + * + * Returns a policy action constant such as ACTION_DISPATCH. + */ + virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown, + bool rolled) = 0; + + /* Intercepts a touch event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing. + * + * Returns a policy action constant such as ACTION_DISPATCH. + */ + virtual int32_t interceptTouch(nsecs_t when) = 0; + + /* Intercepts a switch event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing. + * + * Switches are not dispatched to applications so this method should + * usually return ACTION_NONE. + */ + virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) = 0; + + /* Determines whether to turn on some hacks we have to improve the touch interaction with a + * certain device whose screen currently is not all that good. + */ + virtual bool filterTouchEvents() = 0; + + /* Determines whether to turn on some hacks to improve touch interaction with another device + * where touch coordinate data can get corrupted. + */ + virtual bool filterJumpyTouchEvents() = 0; + + /* Gets the configured virtual key definitions for an input device. */ + virtual void getVirtualKeyDefinitions(const String8& deviceName, + Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0; + + /* Gets the excluded device names for the platform. */ + virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0; +}; + + +/* Processes raw input events and sends cooked event data to an input dispatcher. */ class InputReaderInterface : public virtual RefBase { protected: InputReaderInterface() { } @@ -355,16 +460,42 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const = 0; + + /* Gets the current input device configuration. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const = 0; + + /* + * Query current input state. + * deviceId may be -1 to search for the device automatically, filtered by class. + * deviceClasses may be -1 to ignore device class while searching. + */ + virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t scanCode) const = 0; + virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t keyCode) const = 0; + virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses, + int32_t sw) const = 0; + + /* Determine whether physical keys exist for the given framework-domain key codes. */ + virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0; }; + /* The input reader reads raw event data from the event hub and processes it into input events - * that it sends to the input dispatcher. Some functions of the input reader are controlled - * by the input dispatch policy, such as early event filtering in low power states. + * that it sends to the input dispatcher. Some functions of the input reader, such as early + * event filtering in low power states, are controlled by a separate policy object. + * + * IMPORTANT INVARIANT: + * Because the policy can potentially block or cause re-entrance into the input reader, + * the input reader never calls into the policy while holding its internal locks. */ class InputReader : public InputReaderInterface { public: InputReader(const sp<EventHubInterface>& eventHub, - const sp<InputDispatchPolicyInterface>& policy, + const sp<InputReaderPolicyInterface>& policy, const sp<InputDispatcherInterface>& dispatcher); virtual ~InputReader(); @@ -372,6 +503,17 @@ public: virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const; + virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const; + + virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t scanCode) const; + virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t keyCode) const; + virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses, + int32_t sw) const; + + virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; + private: // Lock that must be acquired while manipulating state that may be concurrently accessed // from other threads by input state query methods. It should be held for as short a @@ -383,15 +525,18 @@ private: // (but not other internal device state) mutable Mutex mExportedStateLock; - // current virtual key information - int32_t mGlobalVirtualKeyCode; - int32_t mGlobalVirtualScanCode; + // current virtual key information (lock mExportedStateLock) + int32_t mExportedVirtualKeyCode; + int32_t mExportedVirtualScanCode; + + // current input configuration (lock mExportedStateLock) + InputConfiguration mExportedInputConfiguration; // combined key meta state int32_t mGlobalMetaState; sp<EventHubInterface> mEventHub; - sp<InputDispatchPolicyInterface> mPolicy; + sp<InputReaderPolicyInterface> mPolicy; sp<InputDispatcherInterface> mDispatcher; KeyedVector<int32_t, InputDevice*> mDevices; @@ -414,7 +559,7 @@ private: // input policy processing and dispatch void onKey(nsecs_t when, InputDevice* device, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags); - void onSwitch(nsecs_t when, InputDevice* device, bool down, int32_t code); + void onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, int32_t switchValue); void onSingleTouchScreenStateChanged(nsecs_t when, InputDevice* device); void onMultiTouchScreenStateChanged(nsecs_t when, InputDevice* device); void onTouchScreenChanged(nsecs_t when, InputDevice* device, bool havePointerIds); @@ -445,13 +590,17 @@ private: void configureVirtualKeys(InputDevice* device); void configureAbsoluteAxisInfo(InputDevice* device, int axis, const char* name, InputDevice::AbsoluteAxisInfo* out); + void configureExcludedDevices(); // global meta state management for all devices void resetGlobalMetaState(); int32_t globalMetaState(); // virtual key management - void updateGlobalVirtualKeyState(); + void updateExportedVirtualKeyState(); + + // input configuration management + void updateExportedInputConfiguration(); }; diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 0ccde0d..f058271 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -19,6 +19,9 @@ // Log debug messages about the dispatch cycle. #define DEBUG_DISPATCH_CYCLE 1 +// Log debug messages about registrations. +#define DEBUG_REGISTRATION 1 + // Log debug messages about performance statistics. #define DEBUG_PERFORMANCE_STATISTICS 1 @@ -42,7 +45,7 @@ static inline bool isMovementKey(int32_t keyCode) { // --- InputDispatcher --- -InputDispatcher::InputDispatcher(const sp<InputDispatchPolicyInterface>& policy) : +InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : mPolicy(policy) { mPollLoop = new PollLoop(); @@ -55,6 +58,8 @@ InputDispatcher::InputDispatcher(const sp<InputDispatchPolicyInterface>& policy) mInboundQueue.tail.eventTime = LONG_LONG_MAX; mKeyRepeatState.lastKeyEntry = NULL; + + mCurrentInputTargetsValid = false; } InputDispatcher::~InputDispatcher() { @@ -72,8 +77,9 @@ InputDispatcher::~InputDispatcher() { } void InputDispatcher::dispatchOnce() { - bool allowKeyRepeat = mPolicy->allowKeyRepeat(); + nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); + bool skipPoll = false; nsecs_t currentTime; nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock @@ -84,7 +90,7 @@ void InputDispatcher::dispatchOnce() { // is not a key. This is to ensure that we abort a key repeat if the device is just coming // out of sleep. // XXX we should handle resetting input state coming out of sleep more generally elsewhere - if (! allowKeyRepeat) { + if (keyRepeatTimeout < 0) { resetKeyRepeatLocked(); } @@ -121,8 +127,8 @@ void InputDispatcher::dispatchOnce() { if (mInboundQueue.isEmpty()) { if (mKeyRepeatState.lastKeyEntry) { if (currentTime >= mKeyRepeatState.nextRepeatTime) { - processKeyRepeatLocked(currentTime); - return; // dispatched once + processKeyRepeatLockedInterruptible(currentTime, keyRepeatTimeout); + skipPoll = true; } else { if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) { nextWakeupTime = mKeyRepeatState.nextRepeatTime; @@ -130,31 +136,30 @@ void InputDispatcher::dispatchOnce() { } } } else { - // Inbound queue has at least one entry. Dequeue it and begin dispatching. - // Note that we do not hold the lock for this process because dispatching may - // involve making many callbacks. - EventEntry* entry = mInboundQueue.dequeueAtHead(); + // Inbound queue has at least one entry. + // Start processing it but leave it on the queue until later so that the + // input reader can keep appending samples onto a motion event between the + // time we started processing it and the time we finally enqueue dispatch + // entries for it. + EventEntry* entry = mInboundQueue.head.next; switch (entry->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: { ConfigurationChangedEntry* typedEntry = static_cast<ConfigurationChangedEntry*>(entry); - processConfigurationChangedLocked(currentTime, typedEntry); - mAllocator.releaseConfigurationChangedEntry(typedEntry); + processConfigurationChangedLockedInterruptible(currentTime, typedEntry); break; } case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast<KeyEntry*>(entry); - processKeyLocked(currentTime, typedEntry); - mAllocator.releaseKeyEntry(typedEntry); + processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout); break; } case EventEntry::TYPE_MOTION: { MotionEntry* typedEntry = static_cast<MotionEntry*>(entry); - processMotionLocked(currentTime, typedEntry); - mAllocator.releaseMotionEntry(typedEntry); + processMotionLockedInterruptible(currentTime, typedEntry); break; } @@ -162,30 +167,67 @@ void InputDispatcher::dispatchOnce() { assert(false); break; } - return; // dispatched once + + // Dequeue and release the event entry that we just processed. + mInboundQueue.dequeue(entry); + mAllocator.releaseEventEntry(entry); + skipPoll = true; } } + + // Run any deferred commands. + skipPoll |= runCommandsLockedInterruptible(); } // release lock + // If we dispatched anything, don't poll just now. Wait for the next iteration. + // Contents may have shifted during flight. + if (skipPoll) { + return; + } + // Wait for callback or timeout or wake. nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime); int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; mPollLoop->pollOnce(timeoutMillis); } -void InputDispatcher::processConfigurationChangedLocked(nsecs_t currentTime, - ConfigurationChangedEntry* entry) { +bool InputDispatcher::runCommandsLockedInterruptible() { + if (mCommandQueue.isEmpty()) { + return false; + } + + do { + CommandEntry* commandEntry = mCommandQueue.dequeueAtHead(); + + Command command = commandEntry->command; + (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible' + + mAllocator.releaseCommandEntry(commandEntry); + } while (! mCommandQueue.isEmpty()); + return true; +} + +InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { + CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command); + mCommandQueue.enqueueAtTail(commandEntry); + return commandEntry; +} + +void InputDispatcher::processConfigurationChangedLockedInterruptible( + nsecs_t currentTime, ConfigurationChangedEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processConfigurationChanged - eventTime=%lld, touchScreenConfig=%d, " - "keyboardConfig=%d, navigationConfig=%d", entry->eventTime, - entry->touchScreenConfig, entry->keyboardConfig, entry->navigationConfig); + LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime); #endif - mPolicy->notifyConfigurationChanged(entry->eventTime, entry->touchScreenConfig, - entry->keyboardConfig, entry->navigationConfig); + mLock.unlock(); + + mPolicy->notifyConfigurationChanged(entry->eventTime); + + mLock.lock(); } -void InputDispatcher::processKeyLocked(nsecs_t currentTime, KeyEntry* entry) { +void InputDispatcher::processKeyLockedInterruptible( + nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) { #if DEBUG_OUTBOUND_EVENT_DETAILS LOGD("processKey - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, " "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", @@ -209,7 +251,7 @@ void InputDispatcher::processKeyLocked(nsecs_t currentTime, KeyEntry* entry) { } else { // Not a repeat. Save key down state in case we do see a repeat later. resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = entry->eventTime + mPolicy->getKeyRepeatTimeout(); + mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; } mKeyRepeatState.lastKeyEntry = entry; entry->refCount += 1; @@ -217,10 +259,11 @@ void InputDispatcher::processKeyLocked(nsecs_t currentTime, KeyEntry* entry) { resetKeyRepeatLocked(); } - identifyInputTargetsAndDispatchKeyLocked(currentTime, entry); + identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); } -void InputDispatcher::processKeyRepeatLocked(nsecs_t currentTime) { +void InputDispatcher::processKeyRepeatLockedInterruptible( + nsecs_t currentTime, nsecs_t keyRepeatTimeout) { // TODO Old WindowManagerServer code sniffs the input queue for following key up // events and drops the repeat if one is found. We should do something similar. // One good place to do it is in notifyKey as soon as the key up enters the @@ -252,7 +295,7 @@ void InputDispatcher::processKeyRepeatLocked(nsecs_t currentTime) { entry->downTime = currentTime; entry->policyFlags = 0; - mKeyRepeatState.nextRepeatTime = currentTime + mPolicy->getKeyRepeatTimeout(); + mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout; #if DEBUG_OUTBOUND_EVENT_DETAILS LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, " @@ -263,10 +306,11 @@ void InputDispatcher::processKeyRepeatLocked(nsecs_t currentTime) { entry->repeatCount, entry->downTime); #endif - identifyInputTargetsAndDispatchKeyLocked(currentTime, entry); + identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); } -void InputDispatcher::processMotionLocked(nsecs_t currentTime, MotionEntry* entry) { +void InputDispatcher::processMotionLockedInterruptible( + nsecs_t currentTime, MotionEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS LOGD("processMotion - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, " "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld", @@ -296,15 +340,19 @@ void InputDispatcher::processMotionLocked(nsecs_t currentTime, MotionEntry* entr } #endif - identifyInputTargetsAndDispatchMotionLocked(currentTime, entry); + identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry); } -void InputDispatcher::identifyInputTargetsAndDispatchKeyLocked( +void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( nsecs_t currentTime, KeyEntry* entry) { #if DEBUG_DISPATCH_CYCLE LOGD("identifyInputTargetsAndDispatchKey"); #endif + entry->dispatchInProgress = true; + mCurrentInputTargetsValid = false; + mLock.unlock(); + mReusableKeyEvent.initialize(entry->deviceId, entry->nature, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, entry->downTime, entry->eventTime); @@ -313,15 +361,22 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLocked( mPolicy->getKeyEventTargets(& mReusableKeyEvent, entry->policyFlags, mCurrentInputTargets); + mLock.lock(); + mCurrentInputTargetsValid = true; + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); } -void InputDispatcher::identifyInputTargetsAndDispatchMotionLocked( +void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( nsecs_t currentTime, MotionEntry* entry) { #if DEBUG_DISPATCH_CYCLE LOGD("identifyInputTargetsAndDispatchMotion"); #endif + entry->dispatchInProgress = true; + mCurrentInputTargetsValid = false; + mLock.unlock(); + mReusableMotionEvent.initialize(entry->deviceId, entry->nature, entry->action, entry->edgeFlags, entry->metaState, entry->firstSample.pointerCoords[0].x, entry->firstSample.pointerCoords[0].y, @@ -333,17 +388,22 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLocked( mPolicy->getMotionEventTargets(& mReusableMotionEvent, entry->policyFlags, mCurrentInputTargets); + mLock.lock(); + mCurrentInputTargetsValid = true; + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); } void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { #if DEBUG_DISPATCH_CYCLE - LOGD("dispatchEventToCurrentInputTargets, " + LOGD("dispatchEventToCurrentInputTargets - " "resumeWithAppendedMotionSample=%s", resumeWithAppendedMotionSample ? "true" : "false"); #endif + assert(eventEntry->dispatchInProgress); // should already have been set to true + for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); @@ -365,7 +425,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection EventEntry* eventEntry, const InputTarget* inputTarget, bool resumeWithAppendedMotionSample) { #if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ prepareDispatchCycle, flags=%d, timeout=%lldns, " + LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, " "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s", connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout, inputTarget->xOffset, inputTarget->yOffset, @@ -377,7 +437,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection // not responding. if (connection->status != Connection::STATUS_NORMAL) { LOGV("channel '%s' ~ Dropping event because the channel status is %s", - connection->status == Connection::STATUS_BROKEN ? "BROKEN" : "NOT RESPONDING"); + connection->getStatusLabel()); return; } @@ -631,14 +691,15 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, Connection* void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection) { #if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ finishDispatchCycle: %01.1fms since event, " + LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, " "%01.1fms since dispatch", connection->getInputChannelName(), connection->getEventLatencyMillis(currentTime), connection->getDispatchLatencyMillis(currentTime)); #endif - if (connection->status == Connection::STATUS_BROKEN) { + if (connection->status == Connection::STATUS_BROKEN + || connection->status == Connection::STATUS_ZOMBIE) { return; } @@ -722,34 +783,37 @@ bool InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, Connection bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection, bool broken) { #if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ abortDispatchCycle, broken=%s", + LOGD("channel '%s' ~ abortDispatchCycle - broken=%s", connection->getInputChannelName(), broken ? "true" : "false"); #endif - if (connection->status == Connection::STATUS_BROKEN) { - return false; - } - // Clear the pending timeout. connection->nextTimeoutTime = LONG_LONG_MAX; // Clear the outbound queue. - while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); - mAllocator.releaseDispatchEntry(dispatchEntry); - } + bool deactivated = ! connection->outboundQueue.isEmpty(); + if (deactivated) { + do { + DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); + mAllocator.releaseDispatchEntry(dispatchEntry); + } while (! connection->outboundQueue.isEmpty()); - // Outbound queue is empty, deactivate the connection. - deactivateConnectionLocked(connection); + deactivateConnectionLocked(connection); + } // Handle the case where the connection appears to be unrecoverably broken. + // Ignore already broken or zombie connections. if (broken) { - connection->status = Connection::STATUS_BROKEN; + if (connection->status == Connection::STATUS_NORMAL + || connection->status == Connection::STATUS_NOT_RESPONDING) { + connection->status = Connection::STATUS_BROKEN; - // Notify other system components. - onDispatchCycleBrokenLocked(currentTime, connection); + // Notify other system components. + onDispatchCycleBrokenLocked(currentTime, connection); + } } - return true; /*deactivated*/ + + return deactivated; } bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { @@ -772,6 +836,7 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " "events=0x%x", connection->getInputChannelName(), events); d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/); + d->runCommandsLockedInterruptible(); return false; // remove the callback } @@ -786,20 +851,19 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat LOGE("channel '%s' ~ Failed to receive finished signal. status=%d", connection->getInputChannelName(), status); d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/); + d->runCommandsLockedInterruptible(); return false; // remove the callback } d->finishDispatchCycleLocked(currentTime, connection.get()); + d->runCommandsLockedInterruptible(); return true; } // release lock } -void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime, int32_t touchScreenConfig, - int32_t keyboardConfig, int32_t navigationConfig) { +void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { #if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyConfigurationChanged - eventTime=%lld, touchScreenConfig=%d, " - "keyboardConfig=%d, navigationConfig=%d", eventTime, - touchScreenConfig, keyboardConfig, navigationConfig); + LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime); #endif bool wasEmpty; @@ -808,9 +872,6 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime, int32_t touc ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(); newEntry->eventTime = eventTime; - newEntry->touchScreenConfig = touchScreenConfig; - newEntry->keyboardConfig = keyboardConfig; - newEntry->navigationConfig = navigationConfig; wasEmpty = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(newEntry); @@ -821,16 +882,6 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime, int32_t touc } } -void InputDispatcher::notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyLidSwitchChanged - eventTime=%lld, open=%s", eventTime, - lidOpen ? "true" : "false"); -#endif - - // Send lid switch notification immediately and synchronously. - mPolicy->notifyLidSwitchChanged(eventTime, lidOpen); -} - void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) { #if DEBUG_INBOUND_EVENT_DETAILS LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime); @@ -949,13 +1000,25 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t } // The last motion event is a move and is compatible for appending. - // Do the batching magic and exit. + // Do the batching magic. mAllocator.appendMotionSample(motionEntry, eventTime, pointerCount, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for most recent " "motion event for this device in the inbound queue."); #endif - return; // done + + // Sanity check for special case because dispatch is interruptible. + // The dispatch logic is partially interruptible and releases its lock while + // identifying targets. However, as soon as the targets have been identified, + // the dispatcher proceeds to write a dispatch entry into all relevant outbound + // queues and then promptly removes the motion entry from the queue. + // + // Consequently, we should never observe the case where the inbound queue contains + // an in-progress motion entry unless the current input targets are invalid + // (currently being computed). Check for this! + assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid)); + + return; // done! } // STREAMING CASE @@ -977,34 +1040,38 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t // Note: This code crucially depends on the invariant that an outbound queue always // contains at most one synchronous event and it is always last (but it might // not be first!). - for (size_t i = 0; i < mActiveConnections.size(); i++) { - Connection* connection = mActiveConnections.itemAt(i); - if (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev; - if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) { - if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { - goto NoBatchingOrStreaming; - } - - MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>( - dispatchEntry->eventEntry); - if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE - || syncedMotionEntry->deviceId != deviceId - || syncedMotionEntry->pointerCount != pointerCount) { - goto NoBatchingOrStreaming; + if (mCurrentInputTargetsValid) { + for (size_t i = 0; i < mActiveConnections.size(); i++) { + Connection* connection = mActiveConnections.itemAt(i); + if (! connection->outboundQueue.isEmpty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev; + if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) { + if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { + goto NoBatchingOrStreaming; + } + + MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>( + dispatchEntry->eventEntry); + if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE + || syncedMotionEntry->deviceId != deviceId + || syncedMotionEntry->pointerCount != pointerCount) { + goto NoBatchingOrStreaming; + } + + // Found synced move entry. Append sample and resume dispatch. + mAllocator.appendMotionSample(syncedMotionEntry, eventTime, + pointerCount, pointerCoords); + #if DEBUG_BATCHING + LOGD("Appended motion sample onto batch for most recent synchronously " + "dispatched motion event for this device in the outbound queues."); + #endif + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry, + true /*resumeWithAppendedMotionSample*/); + + runCommandsLockedInterruptible(); + return; // done! } - - // Found synced move entry. Append sample and resume dispatch. - mAllocator.appendMotionSample(syncedMotionEntry, eventTime, - pointerCount, pointerCoords); -#if DEBUG_BATCHING - LOGD("Appended motion sample onto batch for most recent synchronously " - "dispatched motion event for this device in the outbound queues."); -#endif - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry, - true /*resumeWithAppendedMotionSample*/); - return; // done! } } } @@ -1049,6 +1116,10 @@ void InputDispatcher::resetKeyRepeatLocked() { } status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) { +#if DEBUG_REGISTRATION + LOGD("channel '%s' - Registered", inputChannel->getName().string()); +#endif + int receiveFd; { // acquire lock AutoMutex _l(mLock); @@ -1069,6 +1140,8 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan } mConnectionsByReceiveFd.add(receiveFd, connection); + + runCommandsLockedInterruptible(); } // release lock mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); @@ -1076,6 +1149,10 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan } status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) { +#if DEBUG_REGISTRATION + LOGD("channel '%s' - Unregistered", inputChannel->getName().string()); +#endif + int32_t receiveFd; { // acquire lock AutoMutex _l(mLock); @@ -1095,6 +1172,8 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/); + + runCommandsLockedInterruptible(); } // release lock mPollLoop->removeCallback(receiveFd); @@ -1123,11 +1202,12 @@ void InputDispatcher::deactivateConnectionLocked(Connection* connection) { } } -void InputDispatcher::onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection) { +void InputDispatcher::onDispatchCycleStartedLocked( + nsecs_t currentTime, Connection* connection) { } -void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, - Connection* connection, bool recoveredFromANR) { +void InputDispatcher::onDispatchCycleFinishedLocked( + nsecs_t currentTime, Connection* connection, bool recoveredFromANR) { if (recoveredFromANR) { LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, " "%01.1fms since dispatch, %01.1fms since ANR", @@ -1136,26 +1216,65 @@ void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, connection->getDispatchLatencyMillis(currentTime), connection->getANRLatencyMillis(currentTime)); - // TODO tell framework + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible); + commandEntry->inputChannel = connection->inputChannel; } } -void InputDispatcher::onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection) { +void InputDispatcher::onDispatchCycleANRLocked( + nsecs_t currentTime, Connection* connection) { LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch", connection->getInputChannelName(), connection->getEventLatencyMillis(currentTime), connection->getDispatchLatencyMillis(currentTime)); - // TODO tell framework + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyInputChannelANRLockedInterruptible); + commandEntry->inputChannel = connection->inputChannel; } -void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection) { +void InputDispatcher::onDispatchCycleBrokenLocked( + nsecs_t currentTime, Connection* connection) { LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", connection->getInputChannelName()); - // TODO tell framework + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); + commandEntry->inputChannel = connection->inputChannel; } +void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( + CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyInputChannelBroken(commandEntry->inputChannel); + commandEntry->inputChannel.clear(); + + mLock.lock(); +} + +void InputDispatcher::doNotifyInputChannelANRLockedInterruptible( + CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyInputChannelANR(commandEntry->inputChannel); + commandEntry->inputChannel.clear(); + + mLock.lock(); +} + +void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( + CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyInputChannelRecoveredFromANR(commandEntry->inputChannel); + commandEntry->inputChannel.clear(); + + mLock.lock(); +} + + // --- InputDispatcher::Allocator --- InputDispatcher::Allocator::Allocator() { @@ -1166,6 +1285,7 @@ InputDispatcher::Allocator::obtainConfigurationChangedEntry() { ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc(); entry->refCount = 1; entry->type = EventEntry::TYPE_CONFIGURATION_CHANGED; + entry->dispatchInProgress = false; return entry; } @@ -1173,6 +1293,7 @@ InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry() { KeyEntry* entry = mKeyEntryPool.alloc(); entry->refCount = 1; entry->type = EventEntry::TYPE_KEY; + entry->dispatchInProgress = false; return entry; } @@ -1181,6 +1302,7 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry() { entry->refCount = 1; entry->type = EventEntry::TYPE_MOTION; entry->firstSample.next = NULL; + entry->dispatchInProgress = false; return entry; } @@ -1192,6 +1314,12 @@ InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( return entry; } +InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) { + CommandEntry* entry = mCommandEntryPool.alloc(); + entry->command = command; + return entry; +} + void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) { switch (entry->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: @@ -1231,7 +1359,11 @@ void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) { void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) { entry->refCount -= 1; if (entry->refCount == 0) { - freeMotionSampleList(entry->firstSample.next); + for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) { + MotionSample* next = sample->next; + mMotionSamplePool.free(sample); + sample = next; + } mMotionEntryPool.free(entry); } else { assert(entry->refCount > 0); @@ -1243,6 +1375,10 @@ void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) { mDispatchEntryPool.free(entry); } +void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) { + mCommandEntryPool.free(entry); +} + void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords) { MotionSample* sample = mMotionSamplePool.alloc(); @@ -1256,18 +1392,6 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, motionEntry->lastSample = sample; } -void InputDispatcher::Allocator::freeMotionSample(MotionSample* sample) { - mMotionSamplePool.free(sample); -} - -void InputDispatcher::Allocator::freeMotionSampleList(MotionSample* head) { - while (head) { - MotionSample* next = head->next; - mMotionSamplePool.free(head); - head = next; - } -} - // --- InputDispatcher::Connection --- InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : @@ -1284,6 +1408,25 @@ status_t InputDispatcher::Connection::initialize() { return inputPublisher.initialize(); } +const char* InputDispatcher::Connection::getStatusLabel() const { + switch (status) { + case STATUS_NORMAL: + return "NORMAL"; + + case STATUS_BROKEN: + return "BROKEN"; + + case STATUS_NOT_RESPONDING: + return "NOT_RESPONDING"; + + case STATUS_ZOMBIE: + return "ZOMBIE"; + + default: + return "UNKNOWN"; + } +} + InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent( const EventEntry* eventEntry) const { for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev; @@ -1295,6 +1438,14 @@ InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchE return NULL; } +// --- InputDispatcher::CommandEntry --- + +InputDispatcher::CommandEntry::CommandEntry() { +} + +InputDispatcher::CommandEntry::~CommandEntry() { +} + // --- InputDispatcherThread --- diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index ab354a5..7538dd0 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -14,29 +14,30 @@ namespace android { -InputManager::InputManager(const sp<EventHubInterface>& eventHub, - const sp<InputDispatchPolicyInterface>& policy) : - mEventHub(eventHub), mPolicy(policy) { - mDispatcher = new InputDispatcher(policy); - mReader = new InputReader(eventHub, policy, mDispatcher); - - mDispatcherThread = new InputDispatcherThread(mDispatcher); - mReaderThread = new InputReaderThread(mReader); +InputManager::InputManager( + const sp<EventHubInterface>& eventHub, + const sp<InputReaderPolicyInterface>& readerPolicy, + const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { + mDispatcher = new InputDispatcher(dispatcherPolicy); + mReader = new InputReader(eventHub, readerPolicy, mDispatcher); + initialize(); +} - configureExcludedDevices(); +InputManager::InputManager( + const sp<InputReaderInterface>& reader, + const sp<InputDispatcherInterface>& dispatcher) : + mReader(reader), + mDispatcher(dispatcher) { + initialize(); } InputManager::~InputManager() { stop(); } -void InputManager::configureExcludedDevices() { - Vector<String8> excludedDeviceNames; - mPolicy->getExcludedDeviceNames(excludedDeviceNames); - - for (size_t i = 0; i < excludedDeviceNames.size(); i++) { - mEventHub->addExcludedDevice(excludedDeviceNames[i]); - } +void InputManager::initialize() { + mReaderThread = new InputReaderThread(mReader); + mDispatcherThread = new InputDispatcherThread(mDispatcher); } status_t InputManager::start() { @@ -79,36 +80,26 @@ status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChann return mDispatcher->unregisterInputChannel(inputChannel); } -int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses, int32_t scanCode) - const { - int32_t vkKeyCode, vkScanCode; - if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) { - if (vkScanCode == scanCode) { - return KEY_STATE_VIRTUAL; - } - } - - return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode); +void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const { + mReader->getCurrentInputConfiguration(outConfiguration); } -int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, int32_t keyCode) - const { - int32_t vkKeyCode, vkScanCode; - if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) { - if (vkKeyCode == keyCode) { - return KEY_STATE_VIRTUAL; - } - } +int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t scanCode) const { + return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode); +} - return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode); +int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t keyCode) const { + return mReader->getCurrentKeyCodeState(deviceId, deviceClasses, keyCode); } int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const { - return mEventHub->getSwitchState(deviceId, deviceClasses, sw); + return mReader->getCurrentSwitchState(deviceId, deviceClasses, sw); } bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - return mEventHub->hasKeys(numCodes, keyCodes, outFlags); + return mReader->hasKeys(numCodes, keyCodes, outFlags); } } // namespace android diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 796abd0..62b5f28 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -27,6 +27,22 @@ #include <errno.h> #include <limits.h> +/** Amount that trackball needs to move in order to generate a key event. */ +#define TRACKBALL_MOVEMENT_THRESHOLD 6 + +/* Slop distance for jumpy pointer detection. + * The vertical range of the screen divided by this is our epsilon value. */ +#define JUMPY_EPSILON_DIVISOR 212 + +/* Number of jumpy points to drop for touchscreens that need it. */ +#define JUMPY_TRANSITION_DROPS 3 +#define JUMPY_DROP_LIMIT 3 + +/* Maximum squared distance for averaging. + * If moving farther than this, turn of averaging to avoid lag in response. */ +#define AVERAGING_DISTANCE_LIMIT (75 * 75) + + namespace android { // --- Static Functions --- @@ -89,7 +105,7 @@ static const int keyCodeRotationMapSize = sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - if (orientation != InputDispatchPolicyInterface::ROTATION_0) { + if (orientation != InputReaderPolicyInterface::ROTATION_0) { for (int i = 0; i < keyCodeRotationMapSize; i++) { if (keyCode == keyCodeRotationMap[i][0]) { return keyCodeRotationMap[i][orientation]; @@ -677,12 +693,13 @@ void InputDevice::MultiTouchScreenState::reset() { // --- InputReader --- InputReader::InputReader(const sp<EventHubInterface>& eventHub, - const sp<InputDispatchPolicyInterface>& policy, + const sp<InputReaderPolicyInterface>& policy, const sp<InputDispatcherInterface>& dispatcher) : mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher) { + configureExcludedDevices(); resetGlobalMetaState(); resetDisplayProperties(); - updateGlobalVirtualKeyState(); + updateExportedVirtualKeyState(); } InputReader::~InputReader() { @@ -925,7 +942,7 @@ void InputReader::handleSwitch(const RawEvent* rawEvent) { InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); if (! device) return; - onSwitch(rawEvent->when, device, rawEvent->value != 0, rawEvent->scanCode); + onSwitch(rawEvent->when, device, rawEvent->scanCode, rawEvent->value); } void InputReader::onKey(nsecs_t when, InputDevice* device, @@ -974,7 +991,7 @@ void InputReader::onKey(nsecs_t when, InputDevice* device, } int32_t keyEventFlags = KEY_EVENT_FLAG_FROM_SYSTEM; - if (policyActions & InputDispatchPolicyInterface::ACTION_WOKE_HERE) { + if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) { keyEventFlags = keyEventFlags | KEY_EVENT_FLAG_WOKE_HERE; } @@ -984,12 +1001,12 @@ void InputReader::onKey(nsecs_t when, InputDevice* device, device->keyboard.current.downTime); } -void InputReader::onSwitch(nsecs_t when, InputDevice* device, bool down, - int32_t code) { - switch (code) { - case SW_LID: - mDispatcher->notifyLidSwitchChanged(when, ! down); - } +void InputReader::onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, + int32_t switchValue) { + int32_t policyActions = mPolicy->interceptSwitch(when, switchCode, switchValue); + + uint32_t policyFlags = 0; + applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags); } void InputReader::onMultiTouchScreenStateChanged(nsecs_t when, @@ -1256,7 +1273,7 @@ void InputReader::dispatchVirtualKey(nsecs_t when, nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime; int32_t metaState = globalMetaState(); - updateGlobalVirtualKeyState(); + updateExportedVirtualKeyState(); mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); @@ -1333,8 +1350,8 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli int32_t motionEventAction) { int32_t orientedWidth, orientedHeight; switch (mDisplayOrientation) { - case InputDispatchPolicyInterface::ROTATION_90: - case InputDispatchPolicyInterface::ROTATION_270: + case InputReaderPolicyInterface::ROTATION_90: + case InputReaderPolicyInterface::ROTATION_270: orientedWidth = mDisplayHeight; orientedHeight = mDisplayWidth; break; @@ -1369,18 +1386,18 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli * device->touchScreen.precalculated.sizeScale; switch (mDisplayOrientation) { - case InputDispatchPolicyInterface::ROTATION_90: { + case InputReaderPolicyInterface::ROTATION_90: { float xTemp = x; x = y; y = mDisplayHeight - xTemp; break; } - case InputDispatchPolicyInterface::ROTATION_180: { + case InputReaderPolicyInterface::ROTATION_180: { x = mDisplayWidth - x; y = mDisplayHeight - y; break; } - case InputDispatchPolicyInterface::ROTATION_270: { + case InputReaderPolicyInterface::ROTATION_270: { float xTemp = x; x = mDisplayWidth - y; y = xTemp; @@ -1483,18 +1500,18 @@ void InputReader::onTrackballStateChanged(nsecs_t when, float temp; switch (mDisplayOrientation) { - case InputDispatchPolicyInterface::ROTATION_90: + case InputReaderPolicyInterface::ROTATION_90: temp = pointerCoords.x; pointerCoords.x = pointerCoords.y; pointerCoords.y = - temp; break; - case InputDispatchPolicyInterface::ROTATION_180: + case InputReaderPolicyInterface::ROTATION_180: pointerCoords.x = - pointerCoords.x; pointerCoords.y = - pointerCoords.y; break; - case InputDispatchPolicyInterface::ROTATION_270: + case InputReaderPolicyInterface::ROTATION_270: temp = pointerCoords.x; pointerCoords.x = - pointerCoords.y; pointerCoords.y = temp; @@ -1514,51 +1531,30 @@ void InputReader::onConfigurationChanged(nsecs_t when) { resetGlobalMetaState(); // Reset virtual keys, just in case. - updateGlobalVirtualKeyState(); + updateExportedVirtualKeyState(); - // Enqueue configuration changed. - // XXX This stuff probably needs to be tracked elsewhere in an input device registry - // of some kind that can be asynchronously updated and queried. (Same as above?) - int32_t touchScreenConfig = InputDispatchPolicyInterface::TOUCHSCREEN_NOTOUCH; - int32_t keyboardConfig = InputDispatchPolicyInterface::KEYBOARD_NOKEYS; - int32_t navigationConfig = InputDispatchPolicyInterface::NAVIGATION_NONAV; - - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - int32_t deviceClasses = device->classes; - - if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) { - touchScreenConfig = InputDispatchPolicyInterface::TOUCHSCREEN_FINGER; - } - if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) { - keyboardConfig = InputDispatchPolicyInterface::KEYBOARD_QWERTY; - } - if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) { - navigationConfig = InputDispatchPolicyInterface::NAVIGATION_TRACKBALL; - } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) { - navigationConfig = InputDispatchPolicyInterface::NAVIGATION_DPAD; - } - } + // Update input configuration. + updateExportedInputConfiguration(); - mDispatcher->notifyConfigurationChanged(when, touchScreenConfig, - keyboardConfig, navigationConfig); + // Enqueue configuration changed. + mDispatcher->notifyConfigurationChanged(when); } bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when, int32_t policyActions, uint32_t* policyFlags) { - if (policyActions & InputDispatchPolicyInterface::ACTION_APP_SWITCH_COMING) { + if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { mDispatcher->notifyAppSwitchComing(when); } - if (policyActions & InputDispatchPolicyInterface::ACTION_WOKE_HERE) { + if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) { *policyFlags |= POLICY_FLAG_WOKE_HERE; } - if (policyActions & InputDispatchPolicyInterface::ACTION_BRIGHT_HERE) { + if (policyActions & InputReaderPolicyInterface::ACTION_BRIGHT_HERE) { *policyFlags |= POLICY_FLAG_BRIGHT_HERE; } - return policyActions & InputDispatchPolicyInterface::ACTION_DISPATCH; + return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; } void InputReader::resetDisplayProperties() { @@ -1706,7 +1702,7 @@ void InputReader::configureDeviceForCurrentDisplaySize(InputDevice* device) { void InputReader::configureVirtualKeys(InputDevice* device) { device->touchScreen.virtualKeys.clear(); - Vector<InputDispatchPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; + Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; mPolicy->getVirtualKeyDefinitions(device->name, virtualKeyDefinitions); if (virtualKeyDefinitions.size() == 0) { return; @@ -1720,7 +1716,7 @@ void InputReader::configureVirtualKeys(InputDevice* device) { int32_t touchScreenHeight = device->touchScreen.parameters.yAxis.range; for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { - const InputDispatchPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = + const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = virtualKeyDefinitions[i]; device->touchScreen.virtualKeys.add(); @@ -1779,6 +1775,15 @@ void InputReader::configureAbsoluteAxisInfo(InputDevice* device, LOGI(" %s: unknown axis values, setting to zero", name); } +void InputReader::configureExcludedDevices() { + Vector<String8> excludedDeviceNames; + mPolicy->getExcludedDeviceNames(excludedDeviceNames); + + for (size_t i = 0; i < excludedDeviceNames.size(); i++) { + mEventHub->addExcludedDevice(excludedDeviceNames[i]); + } +} + void InputReader::resetGlobalMetaState() { mGlobalMetaState = -1; } @@ -1796,7 +1801,7 @@ int32_t InputReader::globalMetaState() { return mGlobalMetaState; } -void InputReader::updateGlobalVirtualKeyState() { +void InputReader::updateExportedVirtualKeyState() { int32_t keyCode = -1, scanCode = -1; for (size_t i = 0; i < mDevices.size(); i++) { @@ -1809,20 +1814,96 @@ void InputReader::updateGlobalVirtualKeyState() { } } - { + { // acquire exported state lock AutoMutex _l(mExportedStateLock); - mGlobalVirtualKeyCode = keyCode; - mGlobalVirtualScanCode = scanCode; - } + mExportedVirtualKeyCode = keyCode; + mExportedVirtualScanCode = scanCode; + } // release exported state lock } bool InputReader::getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const { - AutoMutex _l(mExportedStateLock); + { // acquire exported state lock + AutoMutex _l(mExportedStateLock); + + *outKeyCode = mExportedVirtualKeyCode; + *outScanCode = mExportedVirtualScanCode; + return mExportedVirtualKeyCode != -1; + } // release exported state lock +} + +void InputReader::updateExportedInputConfiguration() { + int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; + int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; + int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + int32_t deviceClasses = device->classes; + + if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) { + touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; + } + if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) { + keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; + } + if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) { + navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; + } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) { + navigationConfig = InputConfiguration::NAVIGATION_DPAD; + } + } + + { // acquire exported state lock + AutoMutex _l(mExportedStateLock); + + mExportedInputConfiguration.touchScreen = touchScreenConfig; + mExportedInputConfiguration.keyboard = keyboardConfig; + mExportedInputConfiguration.navigation = navigationConfig; + } // release exported state lock +} + +void InputReader::getCurrentInputConfiguration(InputConfiguration* outConfiguration) const { + { // acquire exported state lock + AutoMutex _l(mExportedStateLock); + + *outConfiguration = mExportedInputConfiguration; + } // release exported state lock +} + +int32_t InputReader::getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t scanCode) const { + { // acquire exported state lock + AutoMutex _l(mExportedStateLock); + + if (mExportedVirtualScanCode == scanCode) { + return KEY_STATE_VIRTUAL; + } + } // release exported state lock + + return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode); +} + +int32_t InputReader::getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses, + int32_t keyCode) const { + { // acquire exported state lock + AutoMutex _l(mExportedStateLock); + + if (mExportedVirtualKeyCode == keyCode) { + return KEY_STATE_VIRTUAL; + } + } // release exported state lock + + return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode); +} + +int32_t InputReader::getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses, + int32_t sw) const { + return mEventHub->getSwitchState(deviceId, deviceClasses, sw); +} - *outKeyCode = mGlobalVirtualKeyCode; - *outScanCode = mGlobalVirtualScanCode; - return mGlobalVirtualKeyCode != -1; +bool InputReader::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { + return mEventHub->hasKeys(numCodes, keyCodes, outFlags); } |