From 9c3cda04d969912bc46184f2b326d1db95e0aba5 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 15 Jun 2010 01:31:58 -0700 Subject: 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 --- core/jni/android_view_InputQueue.cpp | 17 +- core/jni/android_view_InputTarget.cpp | 2 +- include/ui/Input.h | 33 +- include/ui/InputDispatchPolicy.h | 198 ----- include/ui/InputDispatcher.h | 208 ++++- include/ui/InputManager.h | 44 +- include/ui/InputReader.h | 203 ++++- libs/ui/InputDispatcher.cpp | 393 +++++++--- libs/ui/InputManager.cpp | 65 +- libs/ui/InputReader.cpp | 203 +++-- services/jni/com_android_server_InputManager.cpp | 916 +++++++++++++---------- 11 files changed, 1373 insertions(+), 909 deletions(-) delete mode 100644 include/ui/InputDispatchPolicy.h diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp index 9cbde25..63e00d7 100644 --- a/core/jni/android_view_InputQueue.cpp +++ b/core/jni/android_view_InputQueue.cpp @@ -21,6 +21,9 @@ // Log debug messages about the dispatch cycle. #define DEBUG_DISPATCH_CYCLE 1 +// Log debug messages about registrations. +#define DEBUG_REGISTRATION 1 + #include "JNIHelp.h" @@ -51,7 +54,7 @@ static struct { class NativeInputQueue { public: NativeInputQueue(); - virtual ~NativeInputQueue(); + ~NativeInputQueue(); status_t registerInputChannel(JNIEnv* env, jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj); @@ -75,7 +78,7 @@ private: Connection(const sp& inputChannel, const sp& pollLoop); - inline const char* getInputChannelName() { return inputChannel->getName().string(); } + inline const char* getInputChannelName() const { return inputChannel->getName().string(); } Status status; @@ -125,6 +128,10 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne return BAD_VALUE; } +#if DEBUG_REGISTRATION + LOGD("channel '%s' - Registered", inputChannel->getName().string()); +#endif + sp pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj); int receiveFd; @@ -154,7 +161,7 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne android_view_InputChannel_setDisposeCallback(env, inputChannelObj, handleInputChannelDisposed, this); - pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, NULL); + pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); return OK; } @@ -166,6 +173,10 @@ status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChan return BAD_VALUE; } +#if DEBUG_REGISTRATION + LOGD("channel '%s' - Unregistered", inputChannel->getName().string()); +#endif + int32_t receiveFd; sp connection; { // acquire lock diff --git a/core/jni/android_view_InputTarget.cpp b/core/jni/android_view_InputTarget.cpp index e2a1f23..a0a7054 100644 --- a/core/jni/android_view_InputTarget.cpp +++ b/core/jni/android_view_InputTarget.cpp @@ -19,7 +19,7 @@ #include "JNIHelp.h" #include -#include +#include #include #include "android_view_InputTarget.h" #include "android_view_InputChannel.h" 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 -#include -#include -#include -#include -#include - -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; - - // 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& outTargets) = 0; - virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - Vector& 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& outVirtualKeyDefinitions) = 0; - virtual void getExcludedDeviceNames(Vector& 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 -#include #include #include #include @@ -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; + + // 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) = 0; + + /* Notifies the system that an input channel is not responding. */ + virtual void notifyInputChannelANR(const sp& inputChannel) = 0; + + /* Notifies the system that an input channel recovered from ANR. */ + virtual void notifyInputChannelRecoveredFromANR(const sp& 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& outTargets) = 0; + + /* Gets the input targets for a motion event. */ + virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + Vector& 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) = 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& policy); + explicit InputDispatcher(const sp& policy); virtual void dispatchOnce(); - virtual void notifyConfigurationChanged(nsecs_t eventTime, - int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig); - virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen); + virtual void 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 { 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(); + + Command command; + + // parameters for the command (usage varies by command) + sp inputChannel; + }; + + // Generic queue implementation. template 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 mConfigurationChangeEntryPool; @@ -260,6 +375,7 @@ private: Pool mMotionEntryPool; Pool mMotionSamplePool; Pool mDispatchEntryPool; + Pool mCommandEntryPool; }; /* Manages the dispatch state associated with a single input channel. */ @@ -291,7 +407,9 @@ private: explicit Connection(const sp& 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 mPolicy; + sp mPolicy; Mutex mLock; - Queue mInboundQueue; Allocator mAllocator; - sp mPollLoop; + Queue mInboundQueue; + Queue mCommandQueue; + // All registered connections mapped by receive pipe file descriptor. KeyedVector > mConnectionsByReceiveFd; // Active connections are connections that have a non-empty outbound queue. Vector mActiveConnections; - // Pool of key and motion event objects used only to ask the input dispatch policy + // 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 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 #include -#include #include #include #include @@ -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) = 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& eventHub, - const sp& policy); + InputManager( + const sp& eventHub, + const sp& readerPolicy, + const sp& dispatcherPolicy); + + // (used for testing purposes) + InputManager( + const sp& reader, + const sp& dispatcher); virtual status_t start(); virtual status_t stop(); @@ -108,6 +118,7 @@ public: virtual status_t registerInputChannel(const sp& inputChannel); virtual status_t unregisterInputChannel(const sp& 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 mEventHub; - sp mPolicy; + sp mReader; + sp mReaderThread; - sp mDispatcher; + sp mDispatcher; sp mDispatcherThread; - sp mReader; - sp 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 #include -#include #include #include #include @@ -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& outVirtualKeyDefinitions) = 0; + + /* Gets the excluded device names for the platform. */ + virtual void getExcludedDeviceNames(Vector& 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& eventHub, - const sp& policy, + const sp& policy, const sp& 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 mEventHub; - sp mPolicy; + sp mPolicy; sp mDispatcher; KeyedVector 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& policy) : +InputDispatcher::InputDispatcher(const sp& policy) : mPolicy(policy) { mPollLoop = new PollLoop(); @@ -55,6 +58,8 @@ InputDispatcher::InputDispatcher(const sp& 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(entry); - processConfigurationChangedLocked(currentTime, typedEntry); - mAllocator.releaseConfigurationChangedEntry(typedEntry); + processConfigurationChangedLockedInterruptible(currentTime, typedEntry); break; } case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast(entry); - processKeyLocked(currentTime, typedEntry); - mAllocator.releaseKeyEntry(typedEntry); + processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout); break; } case EventEntry::TYPE_MOTION: { MotionEntry* typedEntry = static_cast(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( - 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( + 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) { +#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& inputChan } mConnectionsByReceiveFd.add(receiveFd, connection); + + runCommandsLockedInterruptible(); } // release lock mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); @@ -1076,6 +1149,10 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan } status_t InputDispatcher::unregisterInputChannel(const sp& 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& 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) : @@ -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& eventHub, - const sp& 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& eventHub, + const sp& readerPolicy, + const sp& dispatcherPolicy) { + mDispatcher = new InputDispatcher(dispatcherPolicy); + mReader = new InputReader(eventHub, readerPolicy, mDispatcher); + initialize(); +} - configureExcludedDevices(); +InputManager::InputManager( + const sp& reader, + const sp& dispatcher) : + mReader(reader), + mDispatcher(dispatcher) { + initialize(); } InputManager::~InputManager() { stop(); } -void InputManager::configureExcludedDevices() { - Vector 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& 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 #include +/** 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& eventHub, - const sp& policy, + const sp& policy, const sp& 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 virtualKeyDefinitions; + Vector 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 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); } diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 53262ae..a988a96 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -16,9 +16,20 @@ #define LOG_TAG "InputManager-JNI" +//#define LOG_NDEBUG 0 + +// Log debug messages about InputReaderPolicy +#define DEBUG_INPUT_READER_POLICY 1 + +// Log debug messages about InputDispatcherPolicy +#define DEBUG_INPUT_DISPATCHER_POLICY 1 + + #include "JNIHelp.h" #include "jni.h" #include +#include +#include #include #include #include @@ -30,106 +41,530 @@ namespace android { -class InputDispatchPolicy : public InputDispatchPolicyInterface { +// ---------------------------------------------------------------------------- + +static struct { + jclass clazz; + + jmethodID isScreenOn; + jmethodID isScreenBright; + jmethodID notifyConfigurationChanged; + jmethodID notifyLidSwitchChanged; + jmethodID virtualKeyFeedback; + jmethodID hackInterceptKey; + jmethodID goToSleep; + jmethodID pokeUserActivityForKey; + jmethodID notifyAppSwitchComing; + jmethodID filterTouchEvents; + jmethodID filterJumpyTouchEvents; + jmethodID getVirtualKeyDefinitions; + jmethodID getExcludedDeviceNames; + jmethodID getKeyEventTargets; + jmethodID getMotionEventTargets; +} gCallbacksClassInfo; + +static struct { + jclass clazz; + + jfieldID scanCode; + jfieldID centerX; + jfieldID centerY; + jfieldID width; + jfieldID height; +} gVirtualKeyDefinitionClassInfo; + +// ---------------------------------------------------------------------------- + +class NativeInputManager : public virtual RefBase, + public virtual InputReaderPolicyInterface, + public virtual InputDispatcherPolicyInterface { +protected: + virtual ~NativeInputManager(); + public: - InputDispatchPolicy(JNIEnv* env, jobject callbacks); - virtual ~InputDispatchPolicy(); + NativeInputManager(jobject callbacksObj); + + inline sp getInputManager() const { return mInputManager; } void setDisplaySize(int32_t displayId, int32_t width, int32_t height); void setDisplayOrientation(int32_t displayId, int32_t orientation); + /* --- InputReaderPolicyInterface implementation --- */ + virtual bool getDisplayInfo(int32_t displayId, int32_t* width, int32_t* height, int32_t* orientation); - - virtual void notifyConfigurationChanged(nsecs_t when, - int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig); - - virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen); - 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); - virtual int32_t interceptKey(nsecs_t when, int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags); virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown, bool rolled); virtual int32_t interceptTouch(nsecs_t when); - + virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue); virtual bool filterTouchEvents(); virtual bool filterJumpyTouchEvents(); virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector& outVirtualKeyDefinitions); + Vector& outVirtualKeyDefinitions); virtual void getExcludedDeviceNames(Vector& outExcludedDeviceNames); - virtual bool allowKeyRepeat(); - virtual nsecs_t getKeyRepeatTimeout(); + /* --- InputDispatcherPolicyInterface implementation --- */ + virtual void notifyConfigurationChanged(nsecs_t when); + virtual void notifyInputChannelBroken(const sp& inputChannel); + virtual void notifyInputChannelANR(const sp& inputChannel); + virtual void notifyInputChannelRecoveredFromANR(const sp& inputChannel); + virtual nsecs_t getKeyRepeatTimeout(); virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, Vector& outTargets); virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, Vector& outTargets); private: - bool isScreenOn(); - bool isScreenBright(); + sp mInputManager; -private: - jobject mCallbacks; + jobject mCallbacksObj; + // Cached filtering policies. int32_t mFilterTouchEvents; int32_t mFilterJumpyTouchEvents; + // Cached display state. (lock mDisplayLock) Mutex mDisplayLock; int32_t mDisplayWidth, mDisplayHeight; int32_t mDisplayOrientation; - inline JNIEnv* threadEnv() const { + // Callbacks. + bool isScreenOn(); + bool isScreenBright(); + + static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); } + + static bool isAppSwitchKey(int32_t keyCode); + static bool checkExceptionFromCallback(JNIEnv* env, const char* methodName); }; +// ---------------------------------------------------------------------------- -// globals +NativeInputManager::NativeInputManager(jobject callbacksObj) : + mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), + mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) { + JNIEnv* env = jniEnv(); -static sp gEventHub; -static sp gInputDispatchPolicy; -static sp gInputManager; + mCallbacksObj = env->NewGlobalRef(callbacksObj); -// JNI + sp eventHub = new EventHub(); + mInputManager = new InputManager(eventHub, this, this); +} -static struct { - jclass clazz; +NativeInputManager::~NativeInputManager() { + JNIEnv* env = jniEnv(); - jmethodID isScreenOn; - jmethodID isScreenBright; - jmethodID notifyConfigurationChanged; - jmethodID notifyLidSwitchChanged; - jmethodID virtualKeyFeedback; - jmethodID hackInterceptKey; - jmethodID goToSleep; - jmethodID pokeUserActivityForKey; - jmethodID notifyAppSwitchComing; - jmethodID filterTouchEvents; - jmethodID filterJumpyTouchEvents; - jmethodID getVirtualKeyDefinitions; - jmethodID getExcludedDeviceNames; - jmethodID getKeyEventTargets; - jmethodID getMotionEventTargets; -} gCallbacksClassInfo; + env->DeleteGlobalRef(mCallbacksObj); +} -static struct { - jclass clazz; +bool NativeInputManager::isAppSwitchKey(int32_t keyCode) { + return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL; +} - jfieldID scanCode; - jfieldID centerX; - jfieldID centerY; - jfieldID width; - jfieldID height; -} gVirtualKeyDefinitionClassInfo; +bool NativeInputManager::checkExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + return true; + } + return false; +} + +void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) { + if (displayId == 0) { + AutoMutex _l(mDisplayLock); + + mDisplayWidth = width; + mDisplayHeight = height; + } +} + +void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orientation) { + if (displayId == 0) { + AutoMutex _l(mDisplayLock); + + mDisplayOrientation = orientation; + } +} + +bool NativeInputManager::getDisplayInfo(int32_t displayId, + int32_t* width, int32_t* height, int32_t* orientation) { + bool result = false; + if (displayId == 0) { + AutoMutex _l(mDisplayLock); + + if (mDisplayWidth > 0) { + *width = mDisplayWidth; + *height = mDisplayHeight; + *orientation = mDisplayOrientation; + result = true; + } + } + return result; +} + +bool NativeInputManager::isScreenOn() { + JNIEnv* env = jniEnv(); + + jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn); + if (checkExceptionFromCallback(env, "isScreenOn")) { + return true; + } + return result; +} + +bool NativeInputManager::isScreenBright() { + JNIEnv* env = jniEnv(); + + jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright); + if (checkExceptionFromCallback(env, "isScreenBright")) { + return true; + } + return result; +} + +void NativeInputManager::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) { +#if DEBUG_INPUT_READER_POLICY + LOGD("virtualKeyFeedback - when=%lld, deviceId=%d, action=%d, flags=%d, keyCode=%d, " + "scanCode=%d, metaState=%d, downTime=%lld", + when, deviceId, action, flags, keyCode, scanCode, metaState, downTime); +#endif + + JNIEnv* env = jniEnv(); + + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback, + when, deviceId, action, flags, keyCode, scanCode, metaState, downTime); + checkExceptionFromCallback(env, "virtualKeyFeedback"); +} + +int32_t NativeInputManager::interceptKey(nsecs_t when, + int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) { +#if DEBUG_INPUT_READER_POLICY + LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, " + "policyFlags=%d", + when, deviceId, down, keyCode, scanCode, policyFlags); +#endif + + const int32_t WM_ACTION_PASS_TO_USER = 1; + const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2; + const int32_t WM_ACTION_GO_TO_SLEEP = 4; + + JNIEnv* env = jniEnv(); + + bool isScreenOn = this->isScreenOn(); + bool isScreenBright = this->isScreenBright(); + + jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey, + deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn); + if (checkExceptionFromCallback(env, "hackInterceptKey")) { + wmActions = 0; + } + + int32_t actions = InputReaderPolicyInterface::ACTION_NONE; + if (! isScreenOn) { + // Key presses and releases wake the device. + actions |= InputReaderPolicyInterface::ACTION_WOKE_HERE; + } + + if (! isScreenBright) { + // Key presses and releases brighten the screen if dimmed. + actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE; + } + + if (wmActions & WM_ACTION_GO_TO_SLEEP) { + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when); + checkExceptionFromCallback(env, "goToSleep"); + } + + if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when); + checkExceptionFromCallback(env, "pokeUserActivityForKey"); + } + + if (wmActions & WM_ACTION_PASS_TO_USER) { + actions |= InputReaderPolicyInterface::ACTION_DISPATCH; + } + + if (! (wmActions & WM_ACTION_PASS_TO_USER)) { + if (down && isAppSwitchKey(keyCode)) { + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing); + checkExceptionFromCallback(env, "notifyAppSwitchComing"); + + actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING; + } + } + return actions; +} + +int32_t NativeInputManager::interceptTouch(nsecs_t when) { +#if DEBUG_INPUT_READER_POLICY + LOGD("interceptTouch - when=%lld", when); +#endif + + if (! isScreenOn()) { + // Touch events do not wake the device. + return InputReaderPolicyInterface::ACTION_NONE; + } + + return InputReaderPolicyInterface::ACTION_DISPATCH; +} + +int32_t NativeInputManager::interceptTrackball(nsecs_t when, + bool buttonChanged, bool buttonDown, bool rolled) { +#if DEBUG_INPUT_READER_POLICY + LOGD("interceptTrackball - when=%lld, buttonChanged=%d, buttonDown=%d, rolled=%d", + when, buttonChanged, buttonDown, rolled); +#endif + + if (! isScreenOn()) { + // Trackball motions and button presses do not wake the device. + return InputReaderPolicyInterface::ACTION_NONE; + } + + return InputReaderPolicyInterface::ACTION_DISPATCH; +} + +int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode, + int32_t switchValue) { +#if DEBUG_INPUT_READER_POLICY + LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d", + when, switchCode, switchValue); +#endif + + JNIEnv* env = jniEnv(); + + switch (switchCode) { + case SW_LID: + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged, + when, switchValue == 0); + checkExceptionFromCallback(env, "notifyLidSwitchChanged"); + break; + } + + return InputReaderPolicyInterface::ACTION_NONE; +} + +bool NativeInputManager::filterTouchEvents() { + if (mFilterTouchEvents < 0) { + JNIEnv* env = jniEnv(); + + jboolean result = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.filterTouchEvents); + if (checkExceptionFromCallback(env, "filterTouchEvents")) { + result = false; + } + + mFilterTouchEvents = result ? 1 : 0; + } + return mFilterTouchEvents; +} + +bool NativeInputManager::filterJumpyTouchEvents() { + if (mFilterJumpyTouchEvents < 0) { + JNIEnv* env = jniEnv(); + + jboolean result = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.filterJumpyTouchEvents); + if (checkExceptionFromCallback(env, "filterJumpyTouchEvents")) { + result = false; + } + + mFilterJumpyTouchEvents = result ? 1 : 0; + } + return mFilterJumpyTouchEvents; +} + +void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, + Vector& outVirtualKeyDefinitions) { + JNIEnv* env = jniEnv(); + + jstring deviceNameStr = env->NewStringUTF(deviceName.string()); + if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions")) { + jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, + gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr)); + if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) { + jsize length = env->GetArrayLength(result); + for (jsize i = 0; i < length; i++) { + jobject item = env->GetObjectArrayElement(result, i); + + outVirtualKeyDefinitions.add(); + outVirtualKeyDefinitions.editTop().scanCode = + int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode)); + outVirtualKeyDefinitions.editTop().centerX = + int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX)); + outVirtualKeyDefinitions.editTop().centerY = + int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY)); + outVirtualKeyDefinitions.editTop().width = + int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width)); + outVirtualKeyDefinitions.editTop().height = + int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height)); + + env->DeleteLocalRef(item); + } + env->DeleteLocalRef(result); + } + env->DeleteLocalRef(deviceNameStr); + } +} + +void NativeInputManager::getExcludedDeviceNames(Vector& outExcludedDeviceNames) { + JNIEnv* env = jniEnv(); + + jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, + gCallbacksClassInfo.getExcludedDeviceNames)); + if (! checkExceptionFromCallback(env, "getExcludedDeviceNames") && result) { + jsize length = env->GetArrayLength(result); + for (jsize i = 0; i < length; i++) { + jstring item = jstring(env->GetObjectArrayElement(result, i)); + + const char* deviceNameChars = env->GetStringUTFChars(item, NULL); + outExcludedDeviceNames.add(String8(deviceNameChars)); + env->ReleaseStringUTFChars(item, deviceNameChars); + + env->DeleteLocalRef(item); + } + env->DeleteLocalRef(result); + } +} + +void NativeInputManager::notifyConfigurationChanged(nsecs_t when) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("notifyConfigurationChanged - when=%lld", when); +#endif + + JNIEnv* env = jniEnv(); + + InputConfiguration config; + mInputManager->getInputConfiguration(& config); + + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged, + when, config.touchScreen, config.keyboard, config.navigation); + checkExceptionFromCallback(env, "notifyConfigurationChanged"); +} + +void NativeInputManager::notifyInputChannelBroken(const sp& inputChannel) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string()); +#endif + + // TODO +} + +void NativeInputManager::notifyInputChannelANR(const sp& inputChannel) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("notifyInputChannelANR - inputChannel='%s'", + inputChannel->getName().string()); +#endif + + // TODO +} + +void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp& inputChannel) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'", + inputChannel->getName().string()); +#endif + + // TODO +} + +nsecs_t NativeInputManager::getKeyRepeatTimeout() { + if (! isScreenOn()) { + // Disable key repeat when the screen is off. + return -1; + } else { + // TODO use ViewConfiguration.getLongPressTimeout() + return milliseconds_to_nanoseconds(500); + } +} + +void NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + Vector& outTargets) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("getKeyEventTargets - policyFlags=%d", policyFlags); +#endif + + JNIEnv* env = jniEnv(); + + jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); + if (! keyEventObj) { + LOGE("Could not obtain DVM KeyEvent object to get key event targets."); + } else { + jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, + gCallbacksClassInfo.getKeyEventTargets, + keyEventObj, jint(keyEvent->getNature()), jint(policyFlags))); + if (! checkExceptionFromCallback(env, "getKeyEventTargets") && result) { + jsize length = env->GetArrayLength(result); + for (jsize i = 0; i < length; i++) { + jobject item = env->GetObjectArrayElement(result, i); + if (! item) { + break; // found null element indicating end of used portion of the array + } + + outTargets.add(); + android_view_InputTarget_toNative(env, item, & outTargets.editTop()); + + env->DeleteLocalRef(item); + } + env->DeleteLocalRef(result); + } + env->DeleteLocalRef(keyEventObj); + } +} + +void NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + Vector& outTargets) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("getMotionEventTargets - policyFlags=%d", policyFlags); +#endif + + JNIEnv* env = jniEnv(); + + jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent); + if (! motionEventObj) { + LOGE("Could not obtain DVM MotionEvent object to get key event targets."); + } else { + jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, + gCallbacksClassInfo.getMotionEventTargets, + motionEventObj, jint(motionEvent->getNature()), jint(policyFlags))); + if (! checkExceptionFromCallback(env, "getMotionEventTargets") && result) { + jsize length = env->GetArrayLength(result); + for (jsize i = 0; i < length; i++) { + jobject item = env->GetObjectArrayElement(result, i); + if (! item) { + break; // found null element indicating end of used portion of the array + } + + outTargets.add(); + android_view_InputTarget_toNative(env, item, & outTargets.editTop()); + + env->DeleteLocalRef(item); + } + env->DeleteLocalRef(result); + } + android_view_MotionEvent_recycle(env, motionEventObj); + env->DeleteLocalRef(motionEventObj); + } +} + + +// ---------------------------------------------------------------------------- + +static sp gNativeInputManager; static bool checkInputManagerUnitialized(JNIEnv* env) { - if (gInputManager == NULL) { + if (gNativeInputManager == NULL) { LOGE("Input manager not initialized."); jniThrowRuntimeException(env, "Input manager not initialized."); return true; @@ -139,16 +574,11 @@ static bool checkInputManagerUnitialized(JNIEnv* env) { static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks) { - if (gEventHub == NULL) { - gEventHub = new EventHub(); - } - - if (gInputDispatchPolicy == NULL) { - gInputDispatchPolicy = new InputDispatchPolicy(env, callbacks); - } - - if (gInputManager == NULL) { - gInputManager = new InputManager(gEventHub, gInputDispatchPolicy); + if (gNativeInputManager == NULL) { + gNativeInputManager = new NativeInputManager(callbacks); + } else { + LOGE("Input manager already initialized."); + jniThrowRuntimeException(env, "Input manager already initialized."); } } @@ -157,7 +587,7 @@ static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) { return; } - status_t result = gInputManager->start(); + status_t result = gNativeInputManager->getInputManager()->start(); if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } @@ -173,7 +603,7 @@ static void android_server_InputManager_nativeSetDisplaySize(JNIEnv* env, jclass // to be passed in like this, not sure which is better but leaving it like this // keeps the window manager in direct control of when display transitions propagate down // to the input dispatcher - gInputDispatchPolicy->setDisplaySize(displayId, width, height); + gNativeInputManager->setDisplaySize(displayId, width, height); } static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz, @@ -182,7 +612,7 @@ static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, return; } - gInputDispatchPolicy->setDisplayOrientation(displayId, orientation); + gNativeInputManager->setDisplayOrientation(displayId, orientation); } static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz, @@ -191,7 +621,8 @@ static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jcla return KEY_STATE_UNKNOWN; } - return gInputManager->getScanCodeState(deviceId, deviceClasses, scanCode); + return gNativeInputManager->getInputManager()->getScanCodeState( + deviceId, deviceClasses, scanCode); } static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz, @@ -200,7 +631,8 @@ static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclas return KEY_STATE_UNKNOWN; } - return gInputManager->getKeyCodeState(deviceId, deviceClasses, keyCode); + return gNativeInputManager->getInputManager()->getKeyCodeState( + deviceId, deviceClasses, keyCode); } static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz, @@ -209,7 +641,7 @@ static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass return KEY_STATE_UNKNOWN; } - return gInputManager->getSwitchState(deviceId, deviceClasses, sw); + return gNativeInputManager->getInputManager()->getSwitchState(deviceId, deviceClasses, sw); } static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz, @@ -223,7 +655,7 @@ static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass cl jsize numCodes = env->GetArrayLength(keyCodes); jboolean result; if (numCodes == env->GetArrayLength(outFlags)) { - result = gInputManager->hasKeys(numCodes, codes, flags); + result = gNativeInputManager->getInputManager()->hasKeys(numCodes, codes, flags); } else { result = JNI_FALSE; } @@ -243,7 +675,9 @@ static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env, LOGW("Input channel object '%s' was disposed without first being unregistered with " "the input manager!", inputChannel->getName().string()); - gInputManager->unregisterInputChannel(inputChannel); + if (gNativeInputManager != NULL) { + gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel); + } } static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz, @@ -259,7 +693,7 @@ static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, return; } - status_t status = gInputManager->registerInputChannel(inputChannel); + status_t status = gNativeInputManager->getInputManager()->registerInputChannel(inputChannel); if (status) { jniThrowRuntimeException(env, "Failed to register input channel. " "Check logs for details."); @@ -285,13 +719,15 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); - status_t status = gInputManager->unregisterInputChannel(inputChannel); + status_t status = gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel); if (status) { jniThrowRuntimeException(env, "Failed to unregister input channel. " "Check logs for details."); } } +// ---------------------------------------------------------------------------- + static JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V", @@ -334,7 +770,7 @@ int register_android_server_InputManager(JNIEnv* env) { gInputManagerMethods, NELEM(gInputManagerMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - // Policy + // Callbacks FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks"); @@ -407,340 +843,4 @@ int register_android_server_InputManager(JNIEnv* env) { return 0; } -// static functions - -static bool isAppSwitchKey(int32_t keyCode) { - return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL; -} - -static bool checkException(JNIEnv* env, const char* methodName) { - if (env->ExceptionCheck()) { - LOGE("An exception was thrown by an InputDispatchPolicy callback '%s'.", methodName); - LOGE_EX(env); - env->ExceptionClear(); - return true; - } - return false; -} - - -// InputDispatchPolicy implementation - -InputDispatchPolicy::InputDispatchPolicy(JNIEnv* env, jobject callbacks) : - mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), - mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) { - mCallbacks = env->NewGlobalRef(callbacks); -} - -InputDispatchPolicy::~InputDispatchPolicy() { - JNIEnv* env = threadEnv(); - - env->DeleteGlobalRef(mCallbacks); -} - -void InputDispatchPolicy::setDisplaySize(int32_t displayId, int32_t width, int32_t height) { - if (displayId == 0) { - AutoMutex _l(mDisplayLock); - - mDisplayWidth = width; - mDisplayHeight = height; - } -} - -void InputDispatchPolicy::setDisplayOrientation(int32_t displayId, int32_t orientation) { - if (displayId == 0) { - AutoMutex _l(mDisplayLock); - - mDisplayOrientation = orientation; - } -} - -bool InputDispatchPolicy::getDisplayInfo(int32_t displayId, - int32_t* width, int32_t* height, int32_t* orientation) { - bool result = false; - if (displayId == 0) { - AutoMutex _l(mDisplayLock); - - if (mDisplayWidth > 0) { - *width = mDisplayWidth; - *height = mDisplayHeight; - *orientation = mDisplayOrientation; - result = true; - } - } - return result; -} - -bool InputDispatchPolicy::isScreenOn() { - JNIEnv* env = threadEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenOn); - if (checkException(env, "isScreenOn")) { - return true; - } - return result; -} - -bool InputDispatchPolicy::isScreenBright() { - JNIEnv* env = threadEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenBright); - if (checkException(env, "isScreenBright")) { - return true; - } - return result; -} - -void InputDispatchPolicy::notifyConfigurationChanged(nsecs_t when, - int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) { - JNIEnv* env = threadEnv(); - - env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyConfigurationChanged, - when, touchScreenConfig, keyboardConfig, navigationConfig); - checkException(env, "notifyConfigurationChanged"); -} - -void InputDispatchPolicy::notifyLidSwitchChanged(nsecs_t when, bool lidOpen) { - JNIEnv* env = threadEnv(); - env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyLidSwitchChanged, - when, lidOpen); - checkException(env, "notifyLidSwitchChanged"); -} - -void InputDispatchPolicy::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) { - JNIEnv* env = threadEnv(); - - env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.virtualKeyFeedback, - when, deviceId, action, flags, keyCode, scanCode, metaState, downTime); - checkException(env, "virtualKeyFeedback"); -} - -int32_t InputDispatchPolicy::interceptKey(nsecs_t when, - int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) { - const int32_t WM_ACTION_PASS_TO_USER = 1; - const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2; - const int32_t WM_ACTION_GO_TO_SLEEP = 4; - - JNIEnv* env = threadEnv(); - - bool isScreenOn = this->isScreenOn(); - bool isScreenBright = this->isScreenBright(); - - jint wmActions = env->CallIntMethod(mCallbacks, gCallbacksClassInfo.hackInterceptKey, - deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn); - if (checkException(env, "hackInterceptKey")) { - wmActions = 0; - } - - int32_t actions = ACTION_NONE; - if (! isScreenOn) { - // Key presses and releases wake the device. - actions |= ACTION_WOKE_HERE; - } - - if (! isScreenBright) { - // Key presses and releases brighten the screen if dimmed. - actions |= ACTION_BRIGHT_HERE; - } - - if (wmActions & WM_ACTION_GO_TO_SLEEP) { - env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.goToSleep, when); - checkException(env, "goToSleep"); - } - - if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { - env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.pokeUserActivityForKey, when); - checkException(env, "pokeUserActivityForKey"); - } - - if (wmActions & WM_ACTION_PASS_TO_USER) { - actions |= ACTION_DISPATCH; - } - - if (! (wmActions & WM_ACTION_PASS_TO_USER)) { - if (down && isAppSwitchKey(keyCode)) { - env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyAppSwitchComing); - checkException(env, "notifyAppSwitchComing"); - - actions |= ACTION_APP_SWITCH_COMING; - } - } - return actions; -} - -int32_t InputDispatchPolicy::interceptTouch(nsecs_t when) { - if (! isScreenOn()) { - // Touch events do not wake the device. - return ACTION_NONE; - } - - return ACTION_DISPATCH; -} - -int32_t InputDispatchPolicy::interceptTrackball(nsecs_t when, - bool buttonChanged, bool buttonDown, bool rolled) { - if (! isScreenOn()) { - // Trackball motions and button presses do not wake the device. - return ACTION_NONE; - } - - return ACTION_DISPATCH; -} - -bool InputDispatchPolicy::filterTouchEvents() { - if (mFilterTouchEvents < 0) { - JNIEnv* env = threadEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacks, - gCallbacksClassInfo.filterTouchEvents); - if (checkException(env, "filterTouchEvents")) { - result = false; - } - - mFilterTouchEvents = result ? 1 : 0; - } - return mFilterTouchEvents; -} - -bool InputDispatchPolicy::filterJumpyTouchEvents() { - if (mFilterJumpyTouchEvents < 0) { - JNIEnv* env = threadEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacks, - gCallbacksClassInfo.filterJumpyTouchEvents); - if (checkException(env, "filterJumpyTouchEvents")) { - result = false; - } - - mFilterJumpyTouchEvents = result ? 1 : 0; - } - return mFilterJumpyTouchEvents; -} - -void InputDispatchPolicy::getVirtualKeyDefinitions(const String8& deviceName, - Vector& outVirtualKeyDefinitions) { - JNIEnv* env = threadEnv(); - - jstring deviceNameStr = env->NewStringUTF(deviceName.string()); - if (! checkException(env, "getVirtualKeyDefinitions")) { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks, - gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr)); - if (! checkException(env, "getVirtualKeyDefinitions") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - - outVirtualKeyDefinitions.add(); - outVirtualKeyDefinitions.editTop().scanCode = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode)); - outVirtualKeyDefinitions.editTop().centerX = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX)); - outVirtualKeyDefinitions.editTop().centerY = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY)); - outVirtualKeyDefinitions.editTop().width = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width)); - outVirtualKeyDefinitions.editTop().height = - int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height)); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); - } - env->DeleteLocalRef(deviceNameStr); - } -} - -void InputDispatchPolicy::getExcludedDeviceNames(Vector& outExcludedDeviceNames) { - JNIEnv* env = threadEnv(); - - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks, - gCallbacksClassInfo.getExcludedDeviceNames)); - if (! checkException(env, "getExcludedDeviceNames") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jstring item = jstring(env->GetObjectArrayElement(result, i)); - - const char* deviceNameChars = env->GetStringUTFChars(item, NULL); - outExcludedDeviceNames.add(String8(deviceNameChars)); - env->ReleaseStringUTFChars(item, deviceNameChars); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); - } -} - -bool InputDispatchPolicy::allowKeyRepeat() { - // Disable key repeat when the screen is off. - return isScreenOn(); -} - -nsecs_t InputDispatchPolicy::getKeyRepeatTimeout() { - // TODO use ViewConfiguration.getLongPressTimeout() - return milliseconds_to_nanoseconds(500); -} - -void InputDispatchPolicy::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - Vector& outTargets) { - JNIEnv* env = threadEnv(); - - jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); - if (! keyEventObj) { - LOGE("Could not obtain DVM KeyEvent object to get key event targets."); - } else { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks, - gCallbacksClassInfo.getKeyEventTargets, - keyEventObj, jint(keyEvent->getNature()), jint(policyFlags))); - if (! checkException(env, "getKeyEventTargets") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - if (! item) { - break; // found null element indicating end of used portion of the array - } - - outTargets.add(); - android_view_InputTarget_toNative(env, item, & outTargets.editTop()); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); - } - env->DeleteLocalRef(keyEventObj); - } -} - -void InputDispatchPolicy::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - Vector& outTargets) { - JNIEnv* env = threadEnv(); - - jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent); - if (! motionEventObj) { - LOGE("Could not obtain DVM MotionEvent object to get key event targets."); - } else { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks, - gCallbacksClassInfo.getMotionEventTargets, - motionEventObj, jint(motionEvent->getNature()), jint(policyFlags))); - if (! checkException(env, "getMotionEventTargets") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - if (! item) { - break; // found null element indicating end of used portion of the array - } - - outTargets.add(); - android_view_InputTarget_toNative(env, item, & outTargets.editTop()); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); - } - android_view_MotionEvent_recycle(env, motionEventObj); - env->DeleteLocalRef(motionEventObj); - } -} - } /* namespace android */ -- cgit v1.1