More work in progress on native events.

Refactored the code to eliminate potential deadlocks due to re-entrant
calls from the policy into the dispatcher.  Also added some plumbing
that will be used to notify the framework about ANRs.

Change-Id: Iba7a10de0cb3c56cd7520d6ce716db52fdcc94ff
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 @@
 class NativeInputQueue {
 public:
     NativeInputQueue();
-    virtual ~NativeInputQueue();
+    ~NativeInputQueue();
 
     status_t registerInputChannel(JNIEnv* env, jobject inputChannelObj,
             jobject inputHandlerObj, jobject messageQueueObj);
@@ -75,7 +78,7 @@
 
         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 @@
         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 @@
     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 @@
         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 @@
     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 @@
 };
 
 /*
+ * 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 @@
     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 @@
     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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
 
         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 @@
         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 @@
     // 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 @@
 
     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 @@
     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 @@
     /* 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 @@
     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 @@
     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 @@
     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 @@
     virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
 
 private:
-    sp<EventHubInterface> mEventHub;
-    sp<InputDispatchPolicyInterface> mPolicy;
-
-    sp<InputDispatcher> mDispatcher;
-    sp<InputDispatcherThread> mDispatcherThread;
-
-    sp<InputReader> mReader;
+    sp<InputReaderInterface> mReader;
     sp<InputReaderThread> mReaderThread;
 
-    void configureExcludedDevices();
+    sp<InputDispatcherInterface> mDispatcher;
+    sp<InputDispatcherThread> mDispatcherThread;
+
+    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 @@
 };
 
 
-/* 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 @@
      * 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 @@
 
     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 @@
     //     (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 @@
     // 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 @@
     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 @@
 
 // --- InputDispatcher ---
 
-InputDispatcher::InputDispatcher(const sp<InputDispatchPolicyInterface>& policy) :
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
     mPolicy(policy) {
     mPollLoop = new PollLoop();
 
@@ -55,6 +58,8 @@
     mInboundQueue.tail.eventTime = LONG_LONG_MAX;
 
     mKeyRepeatState.lastKeyEntry = NULL;
+
+    mCurrentInputTargetsValid = false;
 }
 
 InputDispatcher::~InputDispatcher() {
@@ -72,8 +77,9 @@
 }
 
 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 @@
         // 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 @@
             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 @@
                     }
                 }
             } 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 @@
                     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) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processConfigurationChanged - eventTime=%lld, touchScreenConfig=%d, "
-            "keyboardConfig=%d, navigationConfig=%d", entry->eventTime,
-            entry->touchScreenConfig, entry->keyboardConfig, entry->navigationConfig);
-#endif
+bool InputDispatcher::runCommandsLockedInterruptible() {
+    if (mCommandQueue.isEmpty()) {
+        return false;
+    }
 
-    mPolicy->notifyConfigurationChanged(entry->eventTime, entry->touchScreenConfig,
-            entry->keyboardConfig, entry->navigationConfig);
+    do {
+        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
+
+        Command command = commandEntry->command;
+        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+
+        mAllocator.releaseCommandEntry(commandEntry);
+    } while (! mCommandQueue.isEmpty());
+    return true;
 }
 
-void InputDispatcher::processKeyLocked(nsecs_t currentTime, KeyEntry* entry) {
+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", entry->eventTime);
+#endif
+
+    mLock.unlock();
+
+    mPolicy->notifyConfigurationChanged(entry->eventTime);
+
+    mLock.lock();
+}
+
+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 @@
         } 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 @@
         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 @@
     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 @@
             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 @@
     }
 #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 @@
     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 @@
     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 @@
         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 @@
     // 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::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::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 @@
             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 @@
             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 @@
 
         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::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 @@
                 }
 
                 // 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 @@
             // 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;
-                        }
+            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;
-                        }
+                            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*/);
-                        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*/);
+
+                            runCommandsLockedInterruptible();
+                            return; // done!
+                        }
                     }
                 }
             }
@@ -1049,6 +1116,10 @@
 }
 
 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 @@
         }
 
         mConnectionsByReceiveFd.add(receiveFd, connection);
+
+        runCommandsLockedInterruptible();
     } // release lock
 
     mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
@@ -1076,6 +1149,10 @@
 }
 
 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 @@
 
         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::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 @@
                 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 @@
     ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
     entry->refCount = 1;
     entry->type = EventEntry::TYPE_CONFIGURATION_CHANGED;
+    entry->dispatchInProgress = false;
     return entry;
 }
 
@@ -1173,6 +1293,7 @@
     KeyEntry* entry = mKeyEntryPool.alloc();
     entry->refCount = 1;
     entry->type = EventEntry::TYPE_KEY;
+    entry->dispatchInProgress = false;
     return entry;
 }
 
@@ -1181,6 +1302,7 @@
     entry->refCount = 1;
     entry->type = EventEntry::TYPE_MOTION;
     entry->firstSample.next = NULL;
+    entry->dispatchInProgress = false;
     return entry;
 }
 
@@ -1192,6 +1314,12 @@
     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::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 @@
     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 @@
     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 @@
     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 @@
     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);
+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();
+}
 
-    mDispatcherThread = new InputDispatcherThread(mDispatcher);
-    mReaderThread = new InputReaderThread(mReader);
-
-    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 @@
     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 @@
         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 @@
 // --- 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 @@
     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 @@
     }
 
     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 @@
             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 @@
     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 @@
         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 @@
                 * 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 @@
 
     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 @@
     resetGlobalMetaState();
 
     // Reset virtual keys, just in case.
-    updateGlobalVirtualKeyState();
+    updateExportedVirtualKeyState();
+
+    // Update input configuration.
+    updateExportedInputConfiguration();
 
     // 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;
-        }
-    }
-
-    mDispatcher->notifyConfigurationChanged(when, touchScreenConfig,
-            keyboardConfig, navigationConfig);
+    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::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 @@
     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 @@
     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 @@
     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 @@
         }
     }
 
-    {
+    { // 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 = mGlobalVirtualKeyCode;
-    *outScanCode = mGlobalVirtualScanCode;
-    return mGlobalVirtualKeyCode != -1;
+        *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);
+}
+
+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,73 +41,7 @@
 
 namespace android {
 
-class InputDispatchPolicy : public InputDispatchPolicyInterface {
-public:
-    InputDispatchPolicy(JNIEnv* env, jobject callbacks);
-    virtual ~InputDispatchPolicy();
-
-    void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
-    void setDisplayOrientation(int32_t displayId, int32_t orientation);
-
-    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 bool filterTouchEvents();
-    virtual bool filterJumpyTouchEvents();
-    virtual void getVirtualKeyDefinitions(const String8& deviceName,
-            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions);
-    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
-
-    virtual bool allowKeyRepeat();
-    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();
-
-private:
-    jobject mCallbacks;
-
-    int32_t mFilterTouchEvents;
-    int32_t mFilterJumpyTouchEvents;
-
-    Mutex mDisplayLock;
-    int32_t mDisplayWidth, mDisplayHeight;
-    int32_t mDisplayOrientation;
-
-    inline JNIEnv* threadEnv() const {
-        return AndroidRuntime::getJNIEnv();
-    }
-};
-
-
-// globals
-
-static sp<EventHub> gEventHub;
-static sp<InputDispatchPolicy> gInputDispatchPolicy;
-static sp<InputManager> gInputManager;
-
-// JNI
+// ----------------------------------------------------------------------------
 
 static struct {
     jclass clazz;
@@ -128,8 +73,498 @@
     jfieldID height;
 } gVirtualKeyDefinitionClassInfo;
 
+// ----------------------------------------------------------------------------
+
+class NativeInputManager : public virtual RefBase,
+    public virtual InputReaderPolicyInterface,
+    public virtual InputDispatcherPolicyInterface {
+protected:
+    virtual ~NativeInputManager();
+
+public:
+    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 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<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions);
+    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
+
+    /* --- 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:
+    sp<InputManager> mInputManager;
+
+    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;
+
+    // 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);
+};
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::NativeInputManager(jobject callbacksObj) :
+    mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+    mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
+    JNIEnv* env = jniEnv();
+
+    mCallbacksObj = env->NewGlobalRef(callbacksObj);
+
+    sp<EventHub> eventHub = new EventHub();
+    mInputManager = new InputManager(eventHub, this, this);
+}
+
+NativeInputManager::~NativeInputManager() {
+    JNIEnv* env = jniEnv();
+
+    env->DeleteGlobalRef(mCallbacksObj);
+}
+
+bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
+    return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
+}
+
+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 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 @@
         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 @@
     // 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 @@
         return;
     }
 
-    gInputDispatchPolicy->setDisplayOrientation(displayId, orientation);
+    gNativeInputManager->setDisplayOrientation(displayId, orientation);
 }
 
 static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
@@ -191,7 +621,8 @@
         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 @@
         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 @@
         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 @@
     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 @@
     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 @@
         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 @@
 
     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 @@
             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 @@
     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 */