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