am 9795a25d: am 6d0fec2d: Refactor input reader to support new device types more easily.

Merge commit '9795a25da060f9a7df87da8ab43fb1086d4322a5'

* commit '9795a25da060f9a7df87da8ab43fb1086d4322a5':
  Refactor input reader to support new device types more easily.
diff --git a/api/current.xml b/api/current.xml
index 850073e..f678fa2 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -178278,6 +178278,17 @@
  visibility="public"
 >
 </method>
+<method name="getKeyboardType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getMotionRange"
  return="android.view.InputDevice.MotionRange"
  abstract="false"
@@ -178326,6 +178337,39 @@
 <parameter name="keyCode" type="int">
 </parameter>
 </method>
+<field name="KEYBOARD_TYPE_ALPHABETIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYBOARD_TYPE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYBOARD_TYPE_NON_ALPHABETIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="MOTION_RANGE_ORIENTATION"
  type="int"
  transient="false"
@@ -178425,6 +178469,17 @@
  visibility="public"
 >
 </field>
+<field name="SOURCE_ANY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-256"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SOURCE_CLASS_BUTTON"
  type="int"
  transient="false"
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 21c2976..d5b2121 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -30,11 +30,13 @@
  * As a further wrinkle, different kinds of input sources uses different coordinate systems
  * to describe motion events.  Refer to the comments on the input source constants for
  * the appropriate interpretation.
+ * </p>
  */
 public final class InputDevice {
     private int mId;
     private String mName;
     private int mSources;
+    private int mKeyboardType;
     
     /**
      * A mask for input source classes.
@@ -173,6 +175,12 @@
      * @see #SOURCE_CLASS_JOYSTICK
      */
     public static final int SOURCE_JOYSTICK_RIGHT = 0x02000000 | SOURCE_CLASS_JOYSTICK;
+    
+    /**
+     * A special input source constant that is used when filtering input devices
+     * to match devices that provide any type of input source.
+     */
+    public static final int SOURCE_ANY = 0xffffff00;
 
     /**
      * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#x}.
@@ -237,6 +245,22 @@
      * @see #getMotionRange
      */
     public static final int MOTION_RANGE_ORIENTATION = 8;
+    
+    /**
+     * There is no keyboard.
+     */
+    public static final int KEYBOARD_TYPE_NONE = 0;
+    
+    /**
+     * The keyboard is not fully alphabetic.  It may be a numeric keypad or an assortment
+     * of buttons that are not mapped as alphabetic keys suitable for text input.
+     */
+    public static final int KEYBOARD_TYPE_NON_ALPHABETIC = 1;
+    
+    /**
+     * The keyboard supports a complement of alphabetic keys.
+     */
+    public static final int KEYBOARD_TYPE_ALPHABETIC = 2;
 
     /**
      * Gets information about the input device with the specified id.
@@ -265,6 +289,14 @@
     }
     
     /**
+     * Gets the keyboard type.
+     * @return The keyboard type.
+     */
+    public int getKeyboardType() {
+        return mKeyboardType;
+    }
+    
+    /**
      * Gets the key character map associated with this input device.
      * @return The key character map.
      */
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 67bce99..c7be751 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -181,61 +181,61 @@
      * Offset for the sample's X coordinate.
      * @hide
      */
-    static public final int SAMPLE_X = 0;
+    static private final int SAMPLE_X = 0;
     
     /**
      * Offset for the sample's Y coordinate.
      * @hide
      */
-    static public final int SAMPLE_Y = 1;
+    static private final int SAMPLE_Y = 1;
     
     /**
      * Offset for the sample's pressure.
      * @hide
      */
-    static public final int SAMPLE_PRESSURE = 2;
+    static private final int SAMPLE_PRESSURE = 2;
     
     /**
      * Offset for the sample's size
      * @hide
      */
-    static public final int SAMPLE_SIZE = 3;
+    static private final int SAMPLE_SIZE = 3;
     
     /**
      * Offset for the sample's touch major axis length.
      * @hide
      */
-    static public final int SAMPLE_TOUCH_MAJOR = 4;
+    static private final int SAMPLE_TOUCH_MAJOR = 4;
 
     /**
      * Offset for the sample's touch minor axis length.
      * @hide
      */
-    static public final int SAMPLE_TOUCH_MINOR = 5;
+    static private final int SAMPLE_TOUCH_MINOR = 5;
     
     /**
      * Offset for the sample's tool major axis length.
      * @hide
      */
-    static public final int SAMPLE_TOOL_MAJOR = 6;
+    static private final int SAMPLE_TOOL_MAJOR = 6;
 
     /**
      * Offset for the sample's tool minor axis length.
      * @hide
      */
-    static public final int SAMPLE_TOOL_MINOR = 7;
+    static private final int SAMPLE_TOOL_MINOR = 7;
     
     /**
      * Offset for the sample's orientation.
      * @hide
      */
-    static public final int SAMPLE_ORIENTATION = 8;
+    static private final int SAMPLE_ORIENTATION = 8;
 
     /**
      * Number of data items for each sample.
      * @hide
      */
-    static public final int NUM_SAMPLE_DATA = 9;
+    static private final int NUM_SAMPLE_DATA = 9;
     
     /**
      * Number of possible pointers.
@@ -918,7 +918,7 @@
      * upwards, is perfectly circular or is of unknown orientation.  A positive angle
      * indicates that the major axis of contact is oriented to the right.  A negative angle
      * indicates that the major axis of contact is oriented to the left.
-     * The full range is from -PI/4 radians (finger pointing fully left) to PI/4 radians
+     * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
      * (finger pointing fully right).
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
@@ -1614,28 +1614,28 @@
          * upwards, is perfectly circular or is of unknown orientation.  A positive angle
          * indicates that the major axis of contact is oriented to the right.  A negative angle
          * indicates that the major axis of contact is oriented to the left.
-         * The full range is from -PI/4 radians (finger pointing fully left) to PI/4 radians
+         * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
          * (finger pointing fully right).
          */
         public float orientation;
         
         /*
-        private static final float PI_8 = (float) (Math.PI / 8);
+        private static final float PI_4 = (float) (Math.PI / 4);
         
         public float getTouchWidth() {
-            return Math.abs(orientation) > PI_8 ? touchMajor : touchMinor;
+            return Math.abs(orientation) > PI_4 ? touchMajor : touchMinor;
         }
         
         public float getTouchHeight() {
-            return Math.abs(orientation) > PI_8 ? touchMinor : touchMajor;
+            return Math.abs(orientation) > PI_4 ? touchMinor : touchMajor;
         }
         
         public float getToolWidth() {
-            return Math.abs(orientation) > PI_8 ? toolMajor : toolMinor;
+            return Math.abs(orientation) > PI_4 ? toolMajor : toolMinor;
         }
         
         public float getToolHeight() {
-            return Math.abs(orientation) > PI_8 ? toolMinor : toolMajor;
+            return Math.abs(orientation) > PI_4 ? toolMinor : toolMajor;
         }
         */
     }
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 5be17d3..dab35b3 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -60,6 +60,31 @@
 class KeyLayoutMap;
 
 /*
+ * A raw event as retrieved from the EventHub.
+ */
+struct RawEvent {
+    nsecs_t when;
+    int32_t deviceId;
+    int32_t type;
+    int32_t scanCode;
+    int32_t keyCode;
+    int32_t value;
+    uint32_t flags;
+};
+
+/* Describes an absolute axis. */
+struct RawAbsoluteAxisInfo {
+    bool valid; // true if the information is valid, false otherwise
+
+    int32_t minValue;  // minimum value
+    int32_t maxValue;  // maximum value
+    int32_t flat;      // center flat position, eg. flat == 8 means center is between -8 and 8
+    int32_t fuzz;      // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
+
+    inline int32_t getRange() { return maxValue - minValue; }
+};
+
+/*
  * Input device classes.
  */
 enum {
@@ -82,7 +107,10 @@
     INPUT_DEVICE_CLASS_DPAD          = 0x00000020,
 
     /* The input device is a gamepad (implies keyboard). */
-    INPUT_DEVICE_CLASS_GAMEPAD       = 0x00000040
+    INPUT_DEVICE_CLASS_GAMEPAD       = 0x00000040,
+
+    /* The input device has switches. */
+    INPUT_DEVICE_CLASS_SWITCH        = 0x00000080,
 };
 
 /*
@@ -114,8 +142,8 @@
 
     virtual String8 getDeviceName(int32_t deviceId) const = 0;
 
-    virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
-            int* outMaxValue, int* outFlat, int* outFuzz) const = 0;
+    virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+            RawAbsoluteAxisInfo* outAxisInfo) const = 0;
 
     virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const = 0;
@@ -131,26 +159,19 @@
      * If the device needs to remain awake longer than that, then the caller is responsible
      * for taking care of it (say, by poking the power manager user activity timer).
      */
-    virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
-            int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
-            int32_t* outValue, nsecs_t* outWhen) = 0;
+    virtual bool getEvent(RawEvent* outEvent) = 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 getScanCodeState(int32_t deviceId, int32_t deviceClasses,
-            int32_t scanCode) const = 0;
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
-            int32_t keyCode) const = 0;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
-            int32_t sw) const = 0;
+    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
+    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
+    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
 
     /*
      * Examine key input devices for specific framework keycode support
      */
-    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes,
+    virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
             uint8_t* outFlags) const = 0;
 };
 
@@ -165,33 +186,28 @@
     
     virtual String8 getDeviceName(int32_t deviceId) const;
     
-    virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
-            int* outMaxValue, int* outFlat, int* outFuzz) const;
-        
+    virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+            RawAbsoluteAxisInfo* outAxisInfo) const;
+
     virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const;
 
     virtual void addExcludedDevice(const char* deviceName);
 
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
-            int32_t scanCode) const;
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
-            int32_t keyCode) const;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
-            int32_t sw) const;
+    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
+    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
+    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
 
-    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+    virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
+            const int32_t* keyCodes, uint8_t* outFlags) const;
 
-    virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
-            int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
-            int32_t* outValue, nsecs_t* outWhen);
+    virtual bool getEvent(RawEvent* outEvent);
 
 protected:
     virtual ~EventHub();
     
 private:
     bool openPlatformInput(void);
-    int32_t convertDeviceKey_TI_P2(int code);
 
     int open_device(const char *device);
     int close_device(const char *device);
@@ -220,6 +236,8 @@
     int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
     int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
     int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
+    bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+            const int32_t* keyCodes, uint8_t* outFlags) const;
 
     // Protect all internal state.
     mutable Mutex   mLock;
diff --git a/include/ui/Input.h b/include/ui/Input.h
index d9b1091..2385973 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -23,7 +23,10 @@
 
 #include <android/input.h>
 #include <utils/Vector.h>
+#include <utils/KeyedVector.h>
 #include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
 
 /*
  * Additional private constants not defined in ndk/ui/input.h.
@@ -47,21 +50,16 @@
     virtual ~AInputEvent() { }
 };
 
-namespace android {
-
 /*
- * A raw event as retrieved from the EventHub.
+ * Declare a concrete type for the NDK's input device forward declaration.
  */
-struct RawEvent {
-    nsecs_t when;
-    int32_t deviceId;
-    int32_t type;
-    int32_t scanCode;
-    int32_t keyCode;
-    int32_t value;
-    uint32_t flags;
+struct AInputDevice {
+    virtual ~AInputDevice() { }
 };
 
+
+namespace android {
+
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
@@ -424,6 +422,69 @@
     MotionEvent mMotionEvent;
 };
 
+/*
+ * Describes the characteristics and capabilities of an input device.
+ */
+class InputDeviceInfo {
+public:
+    InputDeviceInfo();
+    InputDeviceInfo(const InputDeviceInfo& other);
+    ~InputDeviceInfo();
+
+    struct MotionRange {
+        float min;
+        float max;
+        float flat;
+        float fuzz;
+    };
+
+    void initialize(int32_t id, const String8& name);
+
+    inline int32_t getId() const { return mId; }
+    inline const String8 getName() const { return mName; }
+    inline uint32_t getSources() const { return mSources; }
+
+    const MotionRange* getMotionRange(int32_t rangeType) const;
+
+    void addSource(uint32_t source);
+    void addMotionRange(int32_t rangeType, float min, float max, float flat, float fuzz);
+    void addMotionRange(int32_t rangeType, const MotionRange& range);
+
+    inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
+    inline int32_t getKeyboardType() const { return mKeyboardType; }
+
+private:
+    int32_t mId;
+    String8 mName;
+    uint32_t mSources;
+    int32_t mKeyboardType;
+
+    KeyedVector<int32_t, MotionRange> mMotionRanges;
+};
+
+/*
+ * Provides remote access to information about an input device.
+ *
+ * Note: This is essentially a wrapper for Binder calls into the Window Manager Service.
+ */
+class InputDeviceProxy : public RefBase, public AInputDevice {
+protected:
+    InputDeviceProxy();
+    virtual ~InputDeviceProxy();
+
+public:
+    static void getDeviceIds(Vector<int32_t>& outIds);
+
+    static sp<InputDeviceProxy> getDevice(int32_t id);
+
+    inline const InputDeviceInfo* getInfo() { return & mInfo; }
+
+    // TODO add hasKeys, keymap, etc...
+
+private:
+    InputDeviceInfo mInfo;
+};
+
 
 } // namespace android
 
diff --git a/include/ui/InputDevice.h b/include/ui/InputDevice.h
deleted file mode 100644
index 3b9c70e..0000000
--- a/include/ui/InputDevice.h
+++ /dev/null
@@ -1,353 +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_DEVICE_H
-#define _UI_INPUT_DEVICE_H
-
-#include <ui/EventHub.h>
-#include <ui/Input.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/BitSet.h>
-
-#include <stddef.h>
-#include <unistd.h>
-
-/* Maximum pointer id value supported.
- * (This is limited by our use of BitSet32 to track pointer assignments.) */
-#define MAX_POINTER_ID 31
-
-/* Maximum number of historical samples to average. */
-#define AVERAGING_HISTORY_SIZE 5
-
-
-namespace android {
-
-extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
-extern int32_t rotateKeyCode(int32_t keyCode, int32_t orientation);
-
-
-/*
- * An input device structure tracks the state of a single input device.
- *
- * This structure is only used by ReaderThread and is not intended to be shared with
- * DispatcherThread (because that would require locking).  This works out fine because
- * DispatcherThread is only interested in cooked event data anyways and does not need
- * any of the low-level data from InputDevice.
- */
-struct InputDevice {
-    struct AbsoluteAxisInfo {
-        bool valid;        // set to true if axis parameters are known, false otherwise
-
-        int32_t minValue;  // minimum value
-        int32_t maxValue;  // maximum value
-        int32_t range;     // range of values, equal to maxValue - minValue
-        int32_t flat;      // center flat position, eg. flat == 8 means center is between -8 and 8
-        int32_t fuzz;      // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
-    };
-
-    struct VirtualKey {
-        int32_t keyCode;
-        int32_t scanCode;
-        uint32_t flags;
-
-        // computed hit box, specified in touch screen coords based on known display size
-        int32_t hitLeft;
-        int32_t hitTop;
-        int32_t hitRight;
-        int32_t hitBottom;
-
-        inline bool isHit(int32_t x, int32_t y) const {
-            return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
-        }
-    };
-
-    struct KeyboardState {
-        struct Current {
-            int32_t metaState;
-            nsecs_t downTime; // time of most recent key down
-        } current;
-
-        void reset();
-    };
-
-    struct TrackballState {
-        struct Accumulator {
-            enum {
-                FIELD_BTN_MOUSE = 1,
-                FIELD_REL_X = 2,
-                FIELD_REL_Y = 4
-            };
-
-            uint32_t fields;
-
-            bool btnMouse;
-            int32_t relX;
-            int32_t relY;
-
-            inline void clear() {
-                fields = 0;
-            }
-
-            inline bool isDirty() {
-                return fields != 0;
-            }
-        } accumulator;
-
-        struct Current {
-            bool down;
-            nsecs_t downTime;
-        } current;
-
-        struct Precalculated {
-            float xScale;
-            float yScale;
-            float xPrecision;
-            float yPrecision;
-        } precalculated;
-
-        void reset();
-    };
-
-    struct SingleTouchScreenState {
-        struct Accumulator {
-            enum {
-                FIELD_BTN_TOUCH = 1,
-                FIELD_ABS_X = 2,
-                FIELD_ABS_Y = 4,
-                FIELD_ABS_PRESSURE = 8,
-                FIELD_ABS_TOOL_WIDTH = 16
-            };
-
-            uint32_t fields;
-
-            bool btnTouch;
-            int32_t absX;
-            int32_t absY;
-            int32_t absPressure;
-            int32_t absToolWidth;
-
-            inline void clear() {
-                fields = 0;
-            }
-
-            inline bool isDirty() {
-                return fields != 0;
-            }
-        } accumulator;
-
-        struct Current {
-            bool down;
-            int32_t x;
-            int32_t y;
-            int32_t pressure;
-            int32_t size;
-        } current;
-
-        void reset();
-    };
-
-    struct MultiTouchScreenState {
-        struct Accumulator {
-            enum {
-                FIELD_ABS_MT_POSITION_X = 1,
-                FIELD_ABS_MT_POSITION_Y = 2,
-                FIELD_ABS_MT_TOUCH_MAJOR = 4,
-                FIELD_ABS_MT_TOUCH_MINOR = 8,
-                FIELD_ABS_MT_WIDTH_MAJOR = 16,
-                FIELD_ABS_MT_WIDTH_MINOR = 32,
-                FIELD_ABS_MT_ORIENTATION = 64,
-                FIELD_ABS_MT_TRACKING_ID = 128
-            };
-
-            uint32_t pointerCount;
-            struct Pointer {
-                uint32_t fields;
-
-                int32_t absMTPositionX;
-                int32_t absMTPositionY;
-                int32_t absMTTouchMajor;
-                int32_t absMTTouchMinor;
-                int32_t absMTWidthMajor;
-                int32_t absMTWidthMinor;
-                int32_t absMTOrientation;
-                int32_t absMTTrackingId;
-
-                inline void clear() {
-                    fields = 0;
-                }
-            } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
-
-            inline void clear() {
-                pointerCount = 0;
-                pointers[0].clear();
-            }
-
-            inline bool isDirty() {
-                return pointerCount != 0;
-            }
-        } accumulator;
-
-        void reset();
-    };
-
-    struct PointerData {
-        uint32_t id;
-        int32_t x;
-        int32_t y;
-        int32_t pressure;
-        int32_t size;
-        int32_t touchMajor;
-        int32_t touchMinor;
-        int32_t toolMajor;
-        int32_t toolMinor;
-        int32_t orientation;
-    };
-
-    struct TouchData {
-        uint32_t pointerCount;
-        PointerData pointers[MAX_POINTERS];
-        BitSet32 idBits;
-        uint32_t idToIndex[MAX_POINTER_ID + 1];
-
-        void copyFrom(const TouchData& other);
-
-        inline void clear() {
-            pointerCount = 0;
-            idBits.clear();
-        }
-    };
-
-    // common state used for both single-touch and multi-touch screens after the initial
-    // touch decoding has been performed
-    struct TouchScreenState {
-        Vector<VirtualKey> virtualKeys;
-
-        struct Parameters {
-            bool useBadTouchFilter;
-            bool useJumpyTouchFilter;
-            bool useAveragingTouchFilter;
-
-            AbsoluteAxisInfo xAxis;
-            AbsoluteAxisInfo yAxis;
-            AbsoluteAxisInfo pressureAxis;
-            AbsoluteAxisInfo sizeAxis;
-            AbsoluteAxisInfo orientationAxis;
-        } parameters;
-
-        // The touch data of the current sample being processed.
-        TouchData currentTouch;
-
-        // The touch data of the previous sample that was processed.  This is updated
-        // incrementally while the current sample is being processed.
-        TouchData lastTouch;
-
-        // The time the primary pointer last went down.
-        nsecs_t downTime;
-
-        struct CurrentVirtualKeyState {
-            enum Status {
-                STATUS_UP,
-                STATUS_DOWN,
-                STATUS_CANCELED
-            };
-
-            Status status;
-            nsecs_t downTime;
-            int32_t keyCode;
-            int32_t scanCode;
-        } currentVirtualKey;
-
-        struct AveragingTouchFilterState {
-            // Individual history tracks are stored by pointer id
-            uint32_t historyStart[MAX_POINTERS];
-            uint32_t historyEnd[MAX_POINTERS];
-            struct {
-                struct {
-                    int32_t x;
-                    int32_t y;
-                    int32_t pressure;
-                } pointers[MAX_POINTERS];
-            } historyData[AVERAGING_HISTORY_SIZE];
-        } averagingTouchFilter;
-
-        struct JumpTouchFilterState {
-            int32_t jumpyPointsDropped;
-        } jumpyTouchFilter;
-
-        struct Precalculated {
-            int32_t xOrigin;
-            float xScale;
-
-            int32_t yOrigin;
-            float yScale;
-
-            int32_t pressureOrigin;
-            float pressureScale;
-
-            int32_t sizeOrigin;
-            float sizeScale;
-
-            float orientationScale;
-        } precalculated;
-
-        void reset();
-
-        bool applyBadTouchFilter();
-        bool applyJumpyTouchFilter();
-        void applyAveragingTouchFilter();
-        void calculatePointerIds();
-
-        bool isPointInsideDisplay(int32_t x, int32_t y) const;
-        const InputDevice::VirtualKey* findVirtualKeyHit() const;
-    };
-
-    InputDevice(int32_t id, uint32_t classes, String8 name);
-
-    int32_t id;
-    uint32_t classes;
-    String8 name;
-    bool ignored;
-
-    KeyboardState keyboard;
-    TrackballState trackball;
-    TouchScreenState touchScreen;
-    union {
-        SingleTouchScreenState singleTouchScreen;
-        MultiTouchScreenState multiTouchScreen;
-    };
-
-    void reset();
-
-    inline bool isKeyboard() const { return classes & INPUT_DEVICE_CLASS_KEYBOARD; }
-    inline bool isAlphaKey() const { return classes & INPUT_DEVICE_CLASS_ALPHAKEY; }
-    inline bool isTrackball() const { return classes & INPUT_DEVICE_CLASS_TRACKBALL; }
-    inline bool isDPad() const { return classes & INPUT_DEVICE_CLASS_DPAD; }
-    inline bool isSingleTouchScreen() const { return (classes
-            & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT))
-            == INPUT_DEVICE_CLASS_TOUCHSCREEN; }
-    inline bool isMultiTouchScreen() const { return classes
-            & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; }
-    inline bool isTouchScreen() const { return classes
-            & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT); }
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_DEVICE_H
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index e755238..7ebec10 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -96,22 +96,28 @@
     virtual void preemptInputDispatch() = 0;
 
     /* Gets input device configuration. */
-    virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
 
-    /*
-     * 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.
+    /* Gets information about the specified input device.
+     * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
+     * was no such device.
      */
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
-            int32_t scanCode) const = 0;
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
-            int32_t keyCode) const = 0;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
-            int32_t sw) const = 0;
+    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
+
+    /* Gets the list of all registered device ids. */
+    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
+
+    /* Queries current input state. */
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t scanCode) = 0;
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t keyCode) = 0;
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+            int32_t sw) = 0;
 
     /* 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;
+    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
 };
 
 class InputManager : public InputManagerInterface {
@@ -140,14 +146,17 @@
 
     virtual void preemptInputDispatch();
 
-    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,
-            int32_t keyCode) const;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
-            int32_t sw) const;
-    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration);
+    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
+    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t scanCode);
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t keyCode);
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+            int32_t sw);
+    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
 
 private:
     sp<InputReaderInterface> mReader;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 14bea65..d7ec8ea 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/InputDevice.h>
 #include <ui/InputDispatcher.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
@@ -33,6 +32,10 @@
 
 namespace android {
 
+class InputDevice;
+class InputMapper;
+
+
 /*
  * Input reader policy interface.
  *
@@ -68,14 +71,6 @@
         // 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. */
@@ -101,38 +96,30 @@
 
     /* Intercepts a key event.
      * The policy can use this method as an opportunity to perform power management functions
-     * and early event preprocessing.
+     * and early event preprocessing such as updating policy flags.
      *
      * 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;
+            bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) = 0;
 
     /* Intercepts a switch event.
      * The policy can use this method as an opportunity to perform power management functions
-     * and early event preprocessing.
+     * and early event preprocessing such as updating policy flags.
      *
      * 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;
+    virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+            uint32_t& policyFlags) = 0;
+
+    /* Intercepts a generic touch, trackball or other event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing such as updating policy flags.
+     *
+     * Returns a policy action constant such as ACTION_DISPATCH.
+     */
+    virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags) = 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.
@@ -167,32 +154,52 @@
      */
     virtual void loopOnce() = 0;
 
-    /* Gets the current virtual key.  Returns false if not down.
-     *
-     * 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;
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 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.
+    /* Gets information about the specified input device.
+     * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
+     * was no such device.
+     *
+     * This method may be called on any thread (usually by the input manager).
      */
-    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;
+    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
+
+    /* Gets the list of all registered device ids. */
+    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
+
+    /* Query current input state. */
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t scanCode) = 0;
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t keyCode) = 0;
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+            int32_t sw) = 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;
+    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+};
+
+
+/* Internal interface used by individual input devices to access global input device state
+ * and parameters maintained by the input reader.
+ */
+class InputReaderContext {
+protected:
+    InputReaderContext() { }
+    virtual ~InputReaderContext() { }
+
+public:
+    virtual void updateGlobalMetaState() = 0;
+    virtual int32_t getGlobalMetaState() = 0;
+
+    virtual InputReaderPolicyInterface* getPolicy() = 0;
+    virtual InputDispatcherInterface* getDispatcher() = 0;
+    virtual EventHubInterface* getEventHub() = 0;
 };
 
 
@@ -201,10 +208,11 @@
  * 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.
+ *     Because the policy and dispatcher can potentially block or cause re-entrance into
+ *     the input reader, the input reader never calls into other components while holding
+ *     an exclusive internal lock.
  */
-class InputReader : public InputReaderInterface {
+class InputReader : public InputReaderInterface, private InputReaderContext {
 public:
     InputReader(const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& policy,
@@ -213,107 +221,69 @@
 
     virtual void loopOnce();
 
-    virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const;
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration);
 
-    virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const;
+    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
+    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
 
-    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 int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t scanCode);
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t keyCode);
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+            int32_t sw);
 
-    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
 
 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
-    // time as possible.
-    //
-    // Exported state:
-    //   - global virtual key code and scan code
-    //   - device list and immutable properties of devices such as id, name, and class
-    //     (but not other internal device state)
-    mutable Mutex mExportedStateLock;
-
-    // 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<InputReaderPolicyInterface> mPolicy;
     sp<InputDispatcherInterface> mDispatcher;
 
+    virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); }
+    virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); }
+    virtual EventHubInterface* getEventHub() { return mEventHub.get(); }
+
+    // This reader/writer lock guards the list of input devices.
+    // The writer lock must be held whenever the list of input devices is modified
+    //   and then promptly released.
+    // The reader lock must be held whenever the list of input devices is traversed or an
+    //   input device in the list is accessed.
+    // This lock only protects the registry and prevents inadvertent deletion of device objects
+    // that are in use.  Individual devices are responsible for guarding their own internal state
+    // as needed for concurrent operation.
+    RWLock mDeviceRegistryLock;
     KeyedVector<int32_t, InputDevice*> mDevices;
 
-    // display properties needed to translate touch screen coordinates into display coordinates
-    int32_t mDisplayOrientation;
-    int32_t mDisplayWidth;
-    int32_t mDisplayHeight;
-
-    // low-level input event decoding
+    // low-level input event decoding and device management
     void process(const RawEvent* rawEvent);
-    void handleDeviceAdded(const RawEvent* rawEvent);
-    void handleDeviceRemoved(const RawEvent* rawEvent);
-    void handleSync(const RawEvent* rawEvent);
-    void handleKey(const RawEvent* rawEvent);
-    void handleRelativeMotion(const RawEvent* rawEvent);
-    void handleAbsoluteMotion(const RawEvent* rawEvent);
-    void handleSwitch(const RawEvent* rawEvent);
 
-    // 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, 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);
-    void onTrackballStateChanged(nsecs_t when, InputDevice* device);
-    void onConfigurationChanged(nsecs_t when);
-
-    bool applyStandardInputDispatchPolicyActions(nsecs_t when,
-            int32_t policyActions, uint32_t* policyFlags);
-
-    bool consumeVirtualKeyTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags);
-    void dispatchVirtualKey(nsecs_t when, InputDevice* device, uint32_t policyFlags,
-            int32_t keyEventAction, int32_t keyEventFlags);
-    void dispatchTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags);
-    void dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags,
-            InputDevice::TouchData* touch, BitSet32 idBits, uint32_t changedId,
-            int32_t motionEventAction);
-
-    // display
-    void resetDisplayProperties();
-    bool refreshDisplayProperties();
-
-    // device management
-    InputDevice* getDevice(int32_t deviceId);
-    InputDevice* getNonIgnoredDevice(int32_t deviceId);
     void addDevice(nsecs_t when, int32_t deviceId);
-    void removeDevice(nsecs_t when, InputDevice* device);
-    void configureDevice(InputDevice* device);
-    void configureDeviceForCurrentDisplaySize(InputDevice* device);
-    void configureVirtualKeys(InputDevice* device);
-    void configureAbsoluteAxisInfo(InputDevice* device, int axis, const char* name,
-            InputDevice::AbsoluteAxisInfo* out);
+    void removeDevice(nsecs_t when, int32_t deviceId);
+    InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
     void configureExcludedDevices();
 
-    // global meta state management for all devices
-    void resetGlobalMetaState();
-    int32_t globalMetaState();
+    void consumeEvent(const RawEvent* rawEvent);
 
-    // virtual key management
-    void updateExportedVirtualKeyState();
+    void handleConfigurationChanged(nsecs_t when);
 
-    // input configuration management
-    void updateExportedInputConfiguration();
+    // state management for all devices
+    Mutex mStateLock;
+
+    int32_t mGlobalMetaState;
+    virtual void updateGlobalMetaState();
+    virtual int32_t getGlobalMetaState();
+
+    InputConfiguration mInputConfiguration;
+    void updateInputConfiguration();
+
+    // state queries
+    typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+    int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+            GetStateFunc getStateFunc);
+    bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+            const int32_t* keyCodes, uint8_t* outFlags);
 };
 
 
@@ -329,6 +299,527 @@
     virtual bool threadLoop();
 };
 
+
+/* Represents the state of a single input device. */
+class InputDevice {
+public:
+    InputDevice(InputReaderContext* context, int32_t id, const String8& name);
+    ~InputDevice();
+
+    inline InputReaderContext* getContext() { return mContext; }
+    inline int32_t getId() { return mId; }
+    inline const String8& getName() { return mName; }
+    inline uint32_t getSources() { return mSources; }
+
+    inline bool isIgnored() { return mMappers.isEmpty(); }
+
+    void addMapper(InputMapper* mapper);
+    void configure();
+    void reset();
+    void process(const RawEvent* rawEvent);
+
+    void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
+    int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+    int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+    bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+            const int32_t* keyCodes, uint8_t* outFlags);
+
+    int32_t getMetaState();
+
+private:
+    InputReaderContext* mContext;
+    int32_t mId;
+
+    Vector<InputMapper*> mMappers;
+
+    String8 mName;
+    uint32_t mSources;
+
+    typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+    int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
+};
+
+
+/* An input mapper transforms raw input events into cooked event data.
+ * A single input device can have multiple associated input mappers in order to interpret
+ * different classes of events.
+ */
+class InputMapper {
+public:
+    InputMapper(InputDevice* device);
+    virtual ~InputMapper();
+
+    inline InputDevice* getDevice() { return mDevice; }
+    inline int32_t getDeviceId() { return mDevice->getId(); }
+    inline const String8 getDeviceName() { return mDevice->getName(); }
+    inline InputReaderContext* getContext() { return mContext; }
+    inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
+    inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); }
+    inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+
+    virtual uint32_t getSources() = 0;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void configure();
+    virtual void reset();
+    virtual void process(const RawEvent* rawEvent) = 0;
+
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+            const int32_t* keyCodes, uint8_t* outFlags);
+
+    virtual int32_t getMetaState();
+
+protected:
+    InputDevice* mDevice;
+    InputReaderContext* mContext;
+
+    bool applyStandardPolicyActions(nsecs_t when, int32_t policyActions);
+};
+
+
+class SwitchInputMapper : public InputMapper {
+public:
+    SwitchInputMapper(InputDevice* device);
+    virtual ~SwitchInputMapper();
+
+    virtual uint32_t getSources();
+    virtual void process(const RawEvent* rawEvent);
+
+    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+
+private:
+    void processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
+};
+
+
+class KeyboardInputMapper : public InputMapper {
+public:
+    KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources,
+            int32_t keyboardType);
+    virtual ~KeyboardInputMapper();
+
+    virtual uint32_t getSources();
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void reset();
+    virtual void process(const RawEvent* rawEvent);
+
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+            const int32_t* keyCodes, uint8_t* outFlags);
+
+    virtual int32_t getMetaState();
+
+private:
+    struct KeyDown {
+        int32_t keyCode;
+        int32_t scanCode;
+    };
+
+    int32_t mAssociatedDisplayId;
+    uint32_t mSources;
+    int32_t mKeyboardType;
+
+    Vector<KeyDown> mKeyDowns; // keys that are down
+    int32_t mMetaState;
+    nsecs_t mDownTime; // time of most recent key down
+
+    void initialize();
+
+    bool isKeyboardOrGamepadKey(int32_t scanCode);
+    void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
+            uint32_t policyFlags);
+
+    ssize_t findKeyDown(int32_t scanCode);
+};
+
+
+class TrackballInputMapper : public InputMapper {
+public:
+    TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    virtual ~TrackballInputMapper();
+
+    virtual uint32_t getSources();
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void reset();
+    virtual void process(const RawEvent* rawEvent);
+
+private:
+    // Amount that trackball needs to move in order to generate a key event.
+    static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
+
+    int32_t mAssociatedDisplayId;
+
+    struct Accumulator {
+        enum {
+            FIELD_BTN_MOUSE = 1,
+            FIELD_REL_X = 2,
+            FIELD_REL_Y = 4
+        };
+
+        uint32_t fields;
+
+        bool btnMouse;
+        int32_t relX;
+        int32_t relY;
+
+        inline void clear() {
+            fields = 0;
+        }
+
+        inline bool isDirty() {
+            return fields != 0;
+        }
+    } mAccumulator;
+
+    bool mDown;
+    nsecs_t mDownTime;
+
+    float mXScale;
+    float mYScale;
+    float mXPrecision;
+    float mYPrecision;
+
+    void initialize();
+
+    void sync(nsecs_t when);
+};
+
+
+class TouchInputMapper : public InputMapper {
+public:
+    TouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    virtual ~TouchInputMapper();
+
+    virtual uint32_t getSources();
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void configure();
+    virtual void reset();
+
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+            const int32_t* keyCodes, uint8_t* outFlags);
+
+protected:
+    /* Maximum pointer id value supported.
+     * (This is limited by our use of BitSet32 to track pointer assignments.) */
+    static const uint32_t MAX_POINTER_ID = 31;
+
+    struct VirtualKey {
+        int32_t keyCode;
+        int32_t scanCode;
+        uint32_t flags;
+
+        // computed hit box, specified in touch screen coords based on known display size
+        int32_t hitLeft;
+        int32_t hitTop;
+        int32_t hitRight;
+        int32_t hitBottom;
+
+        inline bool isHit(int32_t x, int32_t y) const {
+            return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
+        }
+    };
+
+    struct PointerData {
+        uint32_t id;
+        int32_t x;
+        int32_t y;
+        int32_t pressure;
+        int32_t size;
+        int32_t touchMajor;
+        int32_t touchMinor;
+        int32_t toolMajor;
+        int32_t toolMinor;
+        int32_t orientation;
+    };
+
+    struct TouchData {
+        uint32_t pointerCount;
+        PointerData pointers[MAX_POINTERS];
+        BitSet32 idBits;
+        uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+        void copyFrom(const TouchData& other) {
+            pointerCount = other.pointerCount;
+            idBits = other.idBits;
+
+            for (uint32_t i = 0; i < pointerCount; i++) {
+                pointers[i] = other.pointers[i];
+                idToIndex[i] = other.idToIndex[i];
+            }
+        }
+
+        inline void clear() {
+            pointerCount = 0;
+            idBits.clear();
+        }
+    };
+
+    int32_t mAssociatedDisplayId;
+    Vector<VirtualKey> mVirtualKeys;
+
+    // Immutable configuration parameters.
+    struct Parameters {
+        bool useBadTouchFilter;
+        bool useJumpyTouchFilter;
+        bool useAveragingTouchFilter;
+    } mParameters;
+
+    // Raw axis information.
+    struct Axes {
+        RawAbsoluteAxisInfo x;
+        RawAbsoluteAxisInfo y;
+        RawAbsoluteAxisInfo pressure;
+        RawAbsoluteAxisInfo size;
+        RawAbsoluteAxisInfo touchMajor;
+        RawAbsoluteAxisInfo touchMinor;
+        RawAbsoluteAxisInfo toolMajor;
+        RawAbsoluteAxisInfo toolMinor;
+        RawAbsoluteAxisInfo orientation;
+    } mAxes;
+
+    // The surface orientation and width and height set by configureSurface().
+    int32_t mSurfaceOrientation;
+    int32_t mSurfaceWidth, mSurfaceHeight;
+
+    // Translation and scaling factors, orientation-independent.
+    int32_t mXOrigin;
+    float mXScale;
+    float mXPrecision;
+
+    int32_t mYOrigin;
+    float mYScale;
+    float mYPrecision;
+
+    int32_t mPressureOrigin;
+    float mPressureScale;
+
+    int32_t mSizeOrigin;
+    float mSizeScale;
+
+    float mOrientationScale;
+
+    // Oriented motion ranges for input device info.
+    struct OrientedRanges {
+        InputDeviceInfo::MotionRange x;
+        InputDeviceInfo::MotionRange y;
+        InputDeviceInfo::MotionRange pressure;
+        InputDeviceInfo::MotionRange size;
+        InputDeviceInfo::MotionRange touchMajor;
+        InputDeviceInfo::MotionRange touchMinor;
+        InputDeviceInfo::MotionRange toolMajor;
+        InputDeviceInfo::MotionRange toolMinor;
+        InputDeviceInfo::MotionRange orientation;
+    } mOrientedRanges;
+
+    // Oriented dimensions and precision.
+    float mOrientedSurfaceWidth, mOrientedSurfaceHeight;
+    float mOrientedXPrecision, mOrientedYPrecision;
+
+    // The touch data of the current sample being processed.
+    TouchData mCurrentTouch;
+
+    // The touch data of the previous sample that was processed.  This is updated
+    // incrementally while the current sample is being processed.
+    TouchData mLastTouch;
+
+    // The time the primary pointer last went down.
+    nsecs_t mDownTime;
+
+    struct CurrentVirtualKeyState {
+        bool down;
+        nsecs_t downTime;
+        int32_t keyCode;
+        int32_t scanCode;
+    } mCurrentVirtualKey;
+
+    // Lock for virtual key state.
+    Mutex mVirtualKeyLock; // methods use "Lvk" suffix
+
+    virtual void configureAxes();
+    virtual bool configureSurface();
+    virtual void configureVirtualKeys();
+
+    enum TouchResult {
+        // Dispatch the touch normally.
+        DISPATCH_TOUCH,
+        // Do not dispatch the touch, but keep tracking the current stroke.
+        SKIP_TOUCH,
+        // Do not dispatch the touch, and drop all information associated with the current stoke
+        // so the next movement will appear as a new down.
+        DROP_STROKE
+    };
+
+    void syncTouch(nsecs_t when, bool havePointerIds);
+
+private:
+    /* Maximum number of historical samples to average. */
+    static const uint32_t AVERAGING_HISTORY_SIZE = 5;
+
+    /* Slop distance for jumpy pointer detection.
+     * The vertical range of the screen divided by this is our epsilon value. */
+    static const uint32_t JUMPY_EPSILON_DIVISOR = 212;
+
+    /* Number of jumpy points to drop for touchscreens that need it. */
+    static const uint32_t JUMPY_TRANSITION_DROPS = 3;
+    static const uint32_t JUMPY_DROP_LIMIT = 3;
+
+    /* Maximum squared distance for averaging.
+     * If moving farther than this, turn of averaging to avoid lag in response. */
+    static const uint64_t AVERAGING_DISTANCE_LIMIT = 75 * 75;
+
+    struct AveragingTouchFilterState {
+        // Individual history tracks are stored by pointer id
+        uint32_t historyStart[MAX_POINTERS];
+        uint32_t historyEnd[MAX_POINTERS];
+        struct {
+            struct {
+                int32_t x;
+                int32_t y;
+                int32_t pressure;
+            } pointers[MAX_POINTERS];
+        } historyData[AVERAGING_HISTORY_SIZE];
+    } mAveragingTouchFilter;
+
+    struct JumpTouchFilterState {
+        uint32_t jumpyPointsDropped;
+    } mJumpyTouchFilter;
+
+    struct PointerDistanceHeapElement {
+        uint32_t currentPointerIndex : 8;
+        uint32_t lastPointerIndex : 8;
+        uint64_t distance : 48; // squared distance
+    };
+
+    void initialize();
+
+    TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags);
+    void dispatchTouches(nsecs_t when, uint32_t policyFlags);
+    void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch,
+            BitSet32 idBits, uint32_t changedId, int32_t motionEventAction);
+
+    bool isPointInsideSurface(int32_t x, int32_t y);
+    const VirtualKey* findVirtualKeyHitLvk(int32_t x, int32_t y);
+
+    bool applyBadTouchFilter();
+    bool applyJumpyTouchFilter();
+    void applyAveragingTouchFilter();
+    void calculatePointerIds();
+};
+
+
+class SingleTouchInputMapper : public TouchInputMapper {
+public:
+    SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    virtual ~SingleTouchInputMapper();
+
+    virtual void reset();
+    virtual void process(const RawEvent* rawEvent);
+
+protected:
+    virtual void configureAxes();
+
+private:
+    struct Accumulator {
+        enum {
+            FIELD_BTN_TOUCH = 1,
+            FIELD_ABS_X = 2,
+            FIELD_ABS_Y = 4,
+            FIELD_ABS_PRESSURE = 8,
+            FIELD_ABS_TOOL_WIDTH = 16
+        };
+
+        uint32_t fields;
+
+        bool btnTouch;
+        int32_t absX;
+        int32_t absY;
+        int32_t absPressure;
+        int32_t absToolWidth;
+
+        inline void clear() {
+            fields = 0;
+        }
+
+        inline bool isDirty() {
+            return fields != 0;
+        }
+    } mAccumulator;
+
+    bool mDown;
+    int32_t mX;
+    int32_t mY;
+    int32_t mPressure;
+    int32_t mSize;
+
+    void initialize();
+
+    void sync(nsecs_t when);
+};
+
+
+class MultiTouchInputMapper : public TouchInputMapper {
+public:
+    MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+    virtual ~MultiTouchInputMapper();
+
+    virtual void reset();
+    virtual void process(const RawEvent* rawEvent);
+
+protected:
+    virtual void configureAxes();
+
+private:
+    struct Accumulator {
+        enum {
+            FIELD_ABS_MT_POSITION_X = 1,
+            FIELD_ABS_MT_POSITION_Y = 2,
+            FIELD_ABS_MT_TOUCH_MAJOR = 4,
+            FIELD_ABS_MT_TOUCH_MINOR = 8,
+            FIELD_ABS_MT_WIDTH_MAJOR = 16,
+            FIELD_ABS_MT_WIDTH_MINOR = 32,
+            FIELD_ABS_MT_ORIENTATION = 64,
+            FIELD_ABS_MT_TRACKING_ID = 128
+        };
+
+        uint32_t pointerCount;
+        struct Pointer {
+            uint32_t fields;
+
+            int32_t absMTPositionX;
+            int32_t absMTPositionY;
+            int32_t absMTTouchMajor;
+            int32_t absMTTouchMinor;
+            int32_t absMTWidthMajor;
+            int32_t absMTWidthMinor;
+            int32_t absMTOrientation;
+            int32_t absMTTrackingId;
+
+            inline void clear() {
+                fields = 0;
+            }
+        } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
+
+        inline void clear() {
+            pointerCount = 0;
+            pointers[0].clear();
+        }
+
+        inline bool isDirty() {
+            return pointerCount != 0;
+        }
+    } mAccumulator;
+
+    void initialize();
+
+    void sync(nsecs_t when);
+};
+
 } // namespace android
 
 #endif // _UI_INPUT_READER_H
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 4243bbf..9f49348 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -12,7 +12,6 @@
 	KeyLayoutMap.cpp \
 	KeyCharacterMap.cpp \
 	Input.cpp \
-	InputDevice.cpp \
 	InputDispatcher.cpp \
 	InputManager.cpp \
 	InputReader.cpp \
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 33dd3732..124f7b3 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -137,9 +137,14 @@
     return device->classes;
 }
 
-int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
-        int* outMaxValue, int* outFlat, int* outFuzz) const
-{
+status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
+        RawAbsoluteAxisInfo* outAxisInfo) const {
+    outAxisInfo->valid = false;
+    outAxisInfo->minValue = 0;
+    outAxisInfo->maxValue = 0;
+    outAxisInfo->flat = 0;
+    outAxisInfo->fuzz = 0;
+
     AutoMutex _l(mLock);
     device_t* device = getDevice(deviceId);
     if (device == NULL) return -1;
@@ -147,38 +152,28 @@
     struct input_absinfo info;
 
     if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
-        LOGE("Error reading absolute controller %d for device %s fd %d\n",
+        LOGW("Error reading absolute controller %d for device %s fd %d\n",
              axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
-        return -1;
+        return -errno;
     }
-    *outMinValue = info.minimum;
-    *outMaxValue = info.maximum;
-    *outFlat = info.flat;
-    *outFuzz = info.fuzz;
-    return 0;
+
+    if (info.minimum != info.maximum) {
+        outAxisInfo->valid = true;
+        outAxisInfo->minValue = info.minimum;
+        outAxisInfo->maxValue = info.maximum;
+        outAxisInfo->flat = info.flat;
+        outAxisInfo->fuzz = info.fuzz;
+    }
+    return OK;
 }
 
-int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
-        int32_t scanCode) const {
+int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
     if (scanCode >= 0 && scanCode <= KEY_MAX) {
         AutoMutex _l(mLock);
 
-        if (deviceId == -1) {
-            for (int i = 0; i < mNumDevicesById; i++) {
-                device_t* device = mDevicesById[i].device;
-                if (device != NULL && (device->classes & deviceClasses) != 0) {
-                    int32_t result = getScanCodeStateLocked(device, scanCode);
-                    if (result >= AKEY_STATE_DOWN) {
-                        return result;
-                    }
-                }
-            }
-            return AKEY_STATE_UP;
-        } else {
-            device_t* device = getDevice(deviceId);
-            if (device != NULL) {
-                return getScanCodeStateLocked(device, scanCode);
-            }
+        device_t* device = getDevice(deviceId);
+        if (device != NULL) {
+            return getScanCodeStateLocked(device, scanCode);
         }
     }
     return AKEY_STATE_UNKNOWN;
@@ -194,25 +189,12 @@
     return AKEY_STATE_UNKNOWN;
 }
 
-int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
-        int32_t keyCode) const {
+int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+    AutoMutex _l(mLock);
 
-    if (deviceId == -1) {
-        for (int i = 0; i < mNumDevicesById; i++) {
-            device_t* device = mDevicesById[i].device;
-            if (device != NULL && (device->classes & deviceClasses) != 0) {
-                int32_t result = getKeyCodeStateLocked(device, keyCode);
-                if (result >= AKEY_STATE_DOWN) {
-                    return result;
-                }
-            }
-        }
-        return AKEY_STATE_UP;
-    } else {
-        device_t* device = getDevice(deviceId);
-        if (device != NULL) {
-            return getKeyCodeStateLocked(device, keyCode);
-        }
+    device_t* device = getDevice(deviceId);
+    if (device != NULL) {
+        return getKeyCodeStateLocked(device, keyCode);
     }
     return AKEY_STATE_UNKNOWN;
 }
@@ -243,24 +225,15 @@
     return AKEY_STATE_UNKNOWN;
 }
 
-int32_t EventHub::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
+int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
 #ifdef EV_SW
     if (sw >= 0 && sw <= SW_MAX) {
         AutoMutex _l(mLock);
 
-        if (deviceId == -1) {
-            deviceId = mSwitches[sw];
-            if (deviceId == 0) {
-                return AKEY_STATE_UNKNOWN;
-            }
-        }
-
         device_t* device = getDevice(deviceId);
-        if (device == NULL) {
-            return AKEY_STATE_UNKNOWN;
+        if (device != NULL) {
+            return getSwitchStateLocked(device, sw);
         }
-
-        return getSwitchStateLocked(device, sw);
     }
 #endif
     return AKEY_STATE_UNKNOWN;
@@ -276,6 +249,42 @@
     return AKEY_STATE_UNKNOWN;
 }
 
+bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
+        const int32_t* keyCodes, uint8_t* outFlags) const {
+    AutoMutex _l(mLock);
+
+    device_t* device = getDevice(deviceId);
+    if (device != NULL) {
+        return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
+    }
+    return false;
+}
+
+bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+        const int32_t* keyCodes, uint8_t* outFlags) const {
+    if (device->layoutMap == NULL || device->keyBitmask == NULL) {
+        return false;
+    }
+
+    Vector<int32_t> scanCodes;
+    for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
+        scanCodes.clear();
+
+        status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
+        if (! err) {
+            // check the possible scan codes identified by the layout map against the
+            // map of codes actually emitted by the driver
+            for (size_t sc = 0; sc < scanCodes.size(); sc++) {
+                if (test_bit(scanCodes[sc], device->keyBitmask)) {
+                    outFlags[codeIndex] = 1;
+                    break;
+                }
+            }
+        }
+    }
+    return true;
+}
+
 status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
         int32_t* outKeycode, uint32_t* outFlags) const
 {
@@ -324,17 +333,15 @@
     return NULL;
 }
 
-bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
-        int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
-        int32_t* outValue, nsecs_t* outWhen)
+bool EventHub::getEvent(RawEvent* outEvent)
 {
-    *outDeviceId = 0;
-    *outType = 0;
-    *outScancode = 0;
-    *outKeycode = 0;
-    *outFlags = 0;
-    *outValue = 0;
-    *outWhen = 0;
+    outEvent->deviceId = 0;
+    outEvent->type = 0;
+    outEvent->scanCode = 0;
+    outEvent->keyCode = 0;
+    outEvent->flags = 0;
+    outEvent->value = 0;
+    outEvent->when = 0;
 
     status_t err;
 
@@ -359,20 +366,27 @@
             LOGV("Reporting device closed: id=0x%x, name=%s\n",
                  device->id, device->path.string());
             mClosingDevices = device->next;
-            *outDeviceId = device->id;
-            if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
-            *outType = DEVICE_REMOVED;
+            if (device->id == mFirstKeyboardId) {
+                outEvent->deviceId = 0;
+            } else {
+                outEvent->deviceId = device->id;
+            }
+            outEvent->type = DEVICE_REMOVED;
             delete device;
             return true;
         }
+
         if (mOpeningDevices != NULL) {
             device_t* device = mOpeningDevices;
             LOGV("Reporting device opened: id=0x%x, name=%s\n",
                  device->id, device->path.string());
             mOpeningDevices = device->next;
-            *outDeviceId = device->id;
-            if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
-            *outType = DEVICE_ADDED;
+            if (device->id == mFirstKeyboardId) {
+                outEvent->deviceId = 0;
+            } else {
+                outEvent->deviceId = device->id;
+            }
+            outEvent->type = DEVICE_ADDED;
             return true;
         }
 
@@ -399,27 +413,36 @@
                 if(mFDs[i].revents & POLLIN) {
                     res = read(mFDs[i].fd, &iev, sizeof(iev));
                     if (res == sizeof(iev)) {
+                        device_t* device = mDevices[i];
                         LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
-                             mDevices[i]->path.string(),
+                             device->path.string(),
                              (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                              iev.type, iev.code, iev.value);
-                        *outDeviceId = mDevices[i]->id;
-                        if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
-                        *outType = iev.type;
-                        *outScancode = iev.code;
+                        if (device->id == mFirstKeyboardId) {
+                            outEvent->deviceId = 0;
+                        } else {
+                            outEvent->deviceId = device->id;
+                        }
+                        outEvent->type = iev.type;
+                        outEvent->scanCode = iev.code;
                         if (iev.type == EV_KEY) {
-                            err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
-                            LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
-                                iev.code, *outKeycode, *outFlags, err);
+                            err = device->layoutMap->map(iev.code,
+                                    & outEvent->keyCode, & outEvent->flags);
+                            LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+                                iev.code, outEvent->keyCode, outEvent->flags, err);
                             if (err != 0) {
-                                *outKeycode = AKEYCODE_UNKNOWN;
-                                *outFlags = 0;
+                                outEvent->keyCode = AKEYCODE_UNKNOWN;
+                                outEvent->flags = 0;
                             }
                         } else {
-                            *outKeycode = iev.code;
+                            outEvent->keyCode = iev.code;
                         }
-                        *outValue = iev.value;
-                        *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
+                        outEvent->value = iev.value;
+
+                        // Use an event timestamp in the same timebase as
+                        // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
+                        // as expected by the rest of the system.
+                        outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
                         return true;
                     } else {
                         if (res<0) {
@@ -479,37 +502,6 @@
     return true;
 }
 
-/*
- * Inspect the known devices to determine whether physical keys exist for the given
- * framework-domain key codes.
- */
-bool EventHub::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
-    for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
-        outFlags[codeIndex] = 0;
-
-        // check each available hardware device for support for this keycode
-        Vector<int32_t> scanCodes;
-        for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) {
-            if (mDevices[n]) {
-                status_t err = mDevices[n]->layoutMap->findScancodes(
-                        keyCodes[codeIndex], &scanCodes);
-                if (!err) {
-                    // check the possible scan codes identified by the layout map against the
-                    // map of codes actually emitted by the driver
-                    for (size_t sc = 0; sc < scanCodes.size(); sc++) {
-                        if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) {
-                            outFlags[codeIndex] = 1;
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    return true;
-}
-
 // ----------------------------------------------------------------------------
 
 static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
@@ -715,16 +707,21 @@
     // figure out the switches this device reports
     uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
     memset(sw_bitmask, 0, sizeof(sw_bitmask));
+    bool hasSwitches = false;
     if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
         for (int i=0; i<EV_SW; i++) {
             //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
             if (test_bit(i, sw_bitmask)) {
+                hasSwitches = true;
                 if (mSwitches[i] == 0) {
                     mSwitches[i] = device->id;
                 }
             }
         }
     }
+    if (hasSwitches) {
+        device->classes |= INPUT_DEVICE_CLASS_SWITCH;
+    }
 #endif
 
     if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 5253c72..5fbaf09 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -168,4 +168,63 @@
     mYOffset += yOffset;
 }
 
+// class InputDeviceInfo
+
+InputDeviceInfo::InputDeviceInfo() {
+    initialize(-1, String8("uninitialized device info"));
+}
+
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
+        mId(other.mId), mName(other.mName), mSources(other.mSources),
+        mKeyboardType(other.mKeyboardType),
+        mMotionRanges(other.mMotionRanges) {
+}
+
+InputDeviceInfo::~InputDeviceInfo() {
+}
+
+void InputDeviceInfo::initialize(int32_t id, const String8& name) {
+    mId = id;
+    mName = name;
+    mSources = 0;
+    mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+    mMotionRanges.clear();
+}
+
+const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const {
+    ssize_t index = mMotionRanges.indexOfKey(rangeType);
+    return index >= 0 ? & mMotionRanges.valueAt(index) : NULL;
+}
+
+void InputDeviceInfo::addSource(uint32_t source) {
+    mSources |= source;
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max,
+        float flat, float fuzz) {
+    MotionRange range = { min, max, flat, fuzz };
+    addMotionRange(rangeType, range);
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) {
+    mMotionRanges.add(rangeType, range);
+}
+
+// class InputDeviceProxy
+
+InputDeviceProxy::InputDeviceProxy() {
+}
+
+InputDeviceProxy::~InputDeviceProxy() {
+}
+
+void InputDeviceProxy::getDeviceIds(Vector<int32_t>& outIds) {
+    // TODO use Binder
+}
+
+sp<InputDeviceProxy> InputDeviceProxy::getDevice(int32_t id) {
+    // TODO use Binder
+    return NULL;
+}
+
 } // namespace android
diff --git a/libs/ui/InputDevice.cpp b/libs/ui/InputDevice.cpp
deleted file mode 100644
index b2a4d6c..0000000
--- a/libs/ui/InputDevice.cpp
+++ /dev/null
@@ -1,729 +0,0 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// The input reader.
-//
-#define LOG_TAG "InputDevice"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
-
-// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
-
-// Log debug messages about pointers.
-#define DEBUG_POINTERS 0
-
-// Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
-
-#include <cutils/log.h>
-#include <ui/InputDevice.h>
-
-#include <stddef.h>
-#include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-
-/* 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 ---
-
-template<typename T>
-inline static T abs(const T& value) {
-    return value < 0 ? - value : value;
-}
-
-template<typename T>
-inline static T min(const T& a, const T& b) {
-    return a < b ? a : b;
-}
-
-template<typename T>
-inline static void swap(T& a, T& b) {
-    T temp = a;
-    a = b;
-    b = temp;
-}
-
-
-// --- InputDevice ---
-
-InputDevice::InputDevice(int32_t id, uint32_t classes, String8 name) :
-    id(id), classes(classes), name(name), ignored(false) {
-}
-
-void InputDevice::reset() {
-    if (isKeyboard()) {
-        keyboard.reset();
-    }
-
-    if (isTrackball()) {
-        trackball.reset();
-    }
-
-    if (isMultiTouchScreen()) {
-        multiTouchScreen.reset();
-    } else if (isSingleTouchScreen()) {
-        singleTouchScreen.reset();
-    }
-
-    if (isTouchScreen()) {
-        touchScreen.reset();
-    }
-}
-
-
-// --- InputDevice::TouchData ---
-
-void InputDevice::TouchData::copyFrom(const TouchData& other) {
-    pointerCount = other.pointerCount;
-    idBits = other.idBits;
-
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointers[i] = other.pointers[i];
-        idToIndex[i] = other.idToIndex[i];
-    }
-}
-
-
-// --- InputDevice::KeyboardState ---
-
-void InputDevice::KeyboardState::reset() {
-    current.metaState = AMETA_NONE;
-    current.downTime = 0;
-}
-
-
-// --- InputDevice::TrackballState ---
-
-void InputDevice::TrackballState::reset() {
-    accumulator.clear();
-    current.down = false;
-    current.downTime = 0;
-}
-
-
-// --- InputDevice::TouchScreenState ---
-
-void InputDevice::TouchScreenState::reset() {
-    lastTouch.clear();
-    downTime = 0;
-    currentVirtualKey.status = CurrentVirtualKeyState::STATUS_UP;
-
-    for (uint32_t i = 0; i < MAX_POINTERS; i++) {
-        averagingTouchFilter.historyStart[i] = 0;
-        averagingTouchFilter.historyEnd[i] = 0;
-    }
-
-    jumpyTouchFilter.jumpyPointsDropped = 0;
-}
-
-struct PointerDistanceHeapElement {
-    uint32_t currentPointerIndex : 8;
-    uint32_t lastPointerIndex : 8;
-    uint64_t distance : 48; // squared distance
-};
-
-void InputDevice::TouchScreenState::calculatePointerIds() {
-    uint32_t currentPointerCount = currentTouch.pointerCount;
-    uint32_t lastPointerCount = lastTouch.pointerCount;
-
-    if (currentPointerCount == 0) {
-        // No pointers to assign.
-        currentTouch.idBits.clear();
-    } else if (lastPointerCount == 0) {
-        // All pointers are new.
-        currentTouch.idBits.clear();
-        for (uint32_t i = 0; i < currentPointerCount; i++) {
-            currentTouch.pointers[i].id = i;
-            currentTouch.idToIndex[i] = i;
-            currentTouch.idBits.markBit(i);
-        }
-    } else if (currentPointerCount == 1 && lastPointerCount == 1) {
-        // Only one pointer and no change in count so it must have the same id as before.
-        uint32_t id = lastTouch.pointers[0].id;
-        currentTouch.pointers[0].id = id;
-        currentTouch.idToIndex[id] = 0;
-        currentTouch.idBits.value = BitSet32::valueForBit(id);
-    } else {
-        // General case.
-        // We build a heap of squared euclidean distances between current and last pointers
-        // associated with the current and last pointer indices.  Then, we find the best
-        // match (by distance) for each current pointer.
-        PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
-
-        uint32_t heapSize = 0;
-        for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
-                currentPointerIndex++) {
-            for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
-                    lastPointerIndex++) {
-                int64_t deltaX = currentTouch.pointers[currentPointerIndex].x
-                        - lastTouch.pointers[lastPointerIndex].x;
-                int64_t deltaY = currentTouch.pointers[currentPointerIndex].y
-                        - lastTouch.pointers[lastPointerIndex].y;
-
-                uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
-                // Insert new element into the heap (sift up).
-                heap[heapSize].currentPointerIndex = currentPointerIndex;
-                heap[heapSize].lastPointerIndex = lastPointerIndex;
-                heap[heapSize].distance = distance;
-                heapSize += 1;
-            }
-        }
-
-        // Heapify
-        for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
-            startIndex -= 1;
-            for (uint32_t parentIndex = startIndex; ;) {
-                uint32_t childIndex = parentIndex * 2 + 1;
-                if (childIndex >= heapSize) {
-                    break;
-                }
-
-                if (childIndex + 1 < heapSize
-                        && heap[childIndex + 1].distance < heap[childIndex].distance) {
-                    childIndex += 1;
-                }
-
-                if (heap[parentIndex].distance <= heap[childIndex].distance) {
-                    break;
-                }
-
-                swap(heap[parentIndex], heap[childIndex]);
-                parentIndex = childIndex;
-            }
-        }
-
-#if DEBUG_POINTER_ASSIGNMENT
-        LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
-        for (size_t i = 0; i < heapSize; i++) {
-            LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
-                    i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
-                    heap[i].distance);
-        }
-#endif
-
-        // Pull matches out by increasing order of distance.
-        // To avoid reassigning pointers that have already been matched, the loop keeps track
-        // of which last and current pointers have been matched using the matchedXXXBits variables.
-        // It also tracks the used pointer id bits.
-        BitSet32 matchedLastBits(0);
-        BitSet32 matchedCurrentBits(0);
-        BitSet32 usedIdBits(0);
-        bool first = true;
-        for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
-            for (;;) {
-                if (first) {
-                    // The first time through the loop, we just consume the root element of
-                    // the heap (the one with smallest distance).
-                    first = false;
-                } else {
-                    // Previous iterations consumed the root element of the heap.
-                    // Pop root element off of the heap (sift down).
-                    heapSize -= 1;
-                    assert(heapSize > 0);
-
-                    // Sift down.
-                    heap[0] = heap[heapSize];
-                    for (uint32_t parentIndex = 0; ;) {
-                        uint32_t childIndex = parentIndex * 2 + 1;
-                        if (childIndex >= heapSize) {
-                            break;
-                        }
-
-                        if (childIndex + 1 < heapSize
-                                && heap[childIndex + 1].distance < heap[childIndex].distance) {
-                            childIndex += 1;
-                        }
-
-                        if (heap[parentIndex].distance <= heap[childIndex].distance) {
-                            break;
-                        }
-
-                        swap(heap[parentIndex], heap[childIndex]);
-                        parentIndex = childIndex;
-                    }
-
-#if DEBUG_POINTER_ASSIGNMENT
-                    LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
-                    for (size_t i = 0; i < heapSize; i++) {
-                        LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
-                                i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
-                                heap[i].distance);
-                    }
-#endif
-                }
-
-                uint32_t currentPointerIndex = heap[0].currentPointerIndex;
-                if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
-
-                uint32_t lastPointerIndex = heap[0].lastPointerIndex;
-                if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
-
-                matchedCurrentBits.markBit(currentPointerIndex);
-                matchedLastBits.markBit(lastPointerIndex);
-
-                uint32_t id = lastTouch.pointers[lastPointerIndex].id;
-                currentTouch.pointers[currentPointerIndex].id = id;
-                currentTouch.idToIndex[id] = currentPointerIndex;
-                usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
-                LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
-                        lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
-                break;
-            }
-        }
-
-        // Assign fresh ids to new pointers.
-        if (currentPointerCount > lastPointerCount) {
-            for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
-                uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
-                uint32_t id = usedIdBits.firstUnmarkedBit();
-
-                currentTouch.pointers[currentPointerIndex].id = id;
-                currentTouch.idToIndex[id] = currentPointerIndex;
-                usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
-                LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
-                        currentPointerIndex, id);
-#endif
-
-                if (--i == 0) break; // done
-                matchedCurrentBits.markBit(currentPointerIndex);
-            }
-        }
-
-        // Fix id bits.
-        currentTouch.idBits = usedIdBits;
-    }
-}
-
-/* Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it. */
-bool InputDevice::TouchScreenState::applyBadTouchFilter() {
-    // This hack requires valid axis parameters.
-    if (! parameters.yAxis.valid) {
-        return false;
-    }
-
-    uint32_t pointerCount = currentTouch.pointerCount;
-
-    // Nothing to do if there are no points.
-    if (pointerCount == 0) {
-        return false;
-    }
-
-    // Don't do anything if a finger is going down or up.  We run
-    // here before assigning pointer IDs, so there isn't a good
-    // way to do per-finger matching.
-    if (pointerCount != lastTouch.pointerCount) {
-        return false;
-    }
-
-    // We consider a single movement across more than a 7/16 of
-    // the long size of the screen to be bad.  This was a magic value
-    // determined by looking at the maximum distance it is feasible
-    // to actually move in one sample.
-    int32_t maxDeltaY = parameters.yAxis.range * 7 / 16;
-
-    // XXX The original code in InputDevice.java included commented out
-    //     code for testing the X axis.  Note that when we drop a point
-    //     we don't actually restore the old X either.  Strange.
-    //     The old code also tries to track when bad points were previously
-    //     detected but it turns out that due to the placement of a "break"
-    //     at the end of the loop, we never set mDroppedBadPoint to true
-    //     so it is effectively dead code.
-    // Need to figure out if the old code is busted or just overcomplicated
-    // but working as intended.
-
-    // Look through all new points and see if any are farther than
-    // acceptable from all previous points.
-    for (uint32_t i = pointerCount; i-- > 0; ) {
-        int32_t y = currentTouch.pointers[i].y;
-        int32_t closestY = INT_MAX;
-        int32_t closestDeltaY = 0;
-
-#if DEBUG_HACKS
-        LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
-#endif
-
-        for (uint32_t j = pointerCount; j-- > 0; ) {
-            int32_t lastY = lastTouch.pointers[j].y;
-            int32_t deltaY = abs(y - lastY);
-
-#if DEBUG_HACKS
-            LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
-                    j, lastY, deltaY);
-#endif
-
-            if (deltaY < maxDeltaY) {
-                goto SkipSufficientlyClosePoint;
-            }
-            if (deltaY < closestDeltaY) {
-                closestDeltaY = deltaY;
-                closestY = lastY;
-            }
-        }
-
-        // Must not have found a close enough match.
-#if DEBUG_HACKS
-        LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
-                i, y, closestY, closestDeltaY, maxDeltaY);
-#endif
-
-        currentTouch.pointers[i].y = closestY;
-        return true; // XXX original code only corrects one point
-
-    SkipSufficientlyClosePoint: ;
-    }
-
-    // No change.
-    return false;
-}
-
-/* Special hack for devices that have bad screen data: drop points where
- * the coordinate value for one axis has jumped to the other pointer's location.
- */
-bool InputDevice::TouchScreenState::applyJumpyTouchFilter() {
-    // This hack requires valid axis parameters.
-    if (! parameters.yAxis.valid) {
-        return false;
-    }
-
-    uint32_t pointerCount = currentTouch.pointerCount;
-    if (lastTouch.pointerCount != pointerCount) {
-#if DEBUG_HACKS
-        LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
-                lastTouch.pointerCount, pointerCount);
-        for (uint32_t i = 0; i < pointerCount; i++) {
-            LOGD("  Pointer %d (%d, %d)", i,
-                    currentTouch.pointers[i].x, currentTouch.pointers[i].y);
-        }
-#endif
-
-        if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
-            if (lastTouch.pointerCount == 1 && pointerCount == 2) {
-                // Just drop the first few events going from 1 to 2 pointers.
-                // They're bad often enough that they're not worth considering.
-                currentTouch.pointerCount = 1;
-                jumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
-                LOGD("JumpyTouchFilter: Pointer 2 dropped");
-#endif
-                return true;
-            } else if (lastTouch.pointerCount == 2 && pointerCount == 1) {
-                // The event when we go from 2 -> 1 tends to be messed up too
-                currentTouch.pointerCount = 2;
-                currentTouch.pointers[0] = lastTouch.pointers[0];
-                currentTouch.pointers[1] = lastTouch.pointers[1];
-                jumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
-                for (int32_t i = 0; i < 2; i++) {
-                    LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
-                            currentTouch.pointers[i].x, currentTouch.pointers[i].y);
-                }
-#endif
-                return true;
-            }
-        }
-        // Reset jumpy points dropped on other transitions or if limit exceeded.
-        jumpyTouchFilter.jumpyPointsDropped = 0;
-
-#if DEBUG_HACKS
-        LOGD("JumpyTouchFilter: Transition - drop limit reset");
-#endif
-        return false;
-    }
-
-    // We have the same number of pointers as last time.
-    // A 'jumpy' point is one where the coordinate value for one axis
-    // has jumped to the other pointer's location. No need to do anything
-    // else if we only have one pointer.
-    if (pointerCount < 2) {
-        return false;
-    }
-
-    if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
-        int jumpyEpsilon = parameters.yAxis.range / JUMPY_EPSILON_DIVISOR;
-
-        // We only replace the single worst jumpy point as characterized by pointer distance
-        // in a single axis.
-        int32_t badPointerIndex = -1;
-        int32_t badPointerReplacementIndex = -1;
-        int32_t badPointerDistance = INT_MIN; // distance to be corrected
-
-        for (uint32_t i = pointerCount; i-- > 0; ) {
-            int32_t x = currentTouch.pointers[i].x;
-            int32_t y = currentTouch.pointers[i].y;
-
-#if DEBUG_HACKS
-            LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
-#endif
-
-            // Check if a touch point is too close to another's coordinates
-            bool dropX = false, dropY = false;
-            for (uint32_t j = 0; j < pointerCount; j++) {
-                if (i == j) {
-                    continue;
-                }
-
-                if (abs(x - currentTouch.pointers[j].x) <= jumpyEpsilon) {
-                    dropX = true;
-                    break;
-                }
-
-                if (abs(y - currentTouch.pointers[j].y) <= jumpyEpsilon) {
-                    dropY = true;
-                    break;
-                }
-            }
-            if (! dropX && ! dropY) {
-                continue; // not jumpy
-            }
-
-            // Find a replacement candidate by comparing with older points on the
-            // complementary (non-jumpy) axis.
-            int32_t distance = INT_MIN; // distance to be corrected
-            int32_t replacementIndex = -1;
-
-            if (dropX) {
-                // X looks too close.  Find an older replacement point with a close Y.
-                int32_t smallestDeltaY = INT_MAX;
-                for (uint32_t j = 0; j < pointerCount; j++) {
-                    int32_t deltaY = abs(y - lastTouch.pointers[j].y);
-                    if (deltaY < smallestDeltaY) {
-                        smallestDeltaY = deltaY;
-                        replacementIndex = j;
-                    }
-                }
-                distance = abs(x - lastTouch.pointers[replacementIndex].x);
-            } else {
-                // Y looks too close.  Find an older replacement point with a close X.
-                int32_t smallestDeltaX = INT_MAX;
-                for (uint32_t j = 0; j < pointerCount; j++) {
-                    int32_t deltaX = abs(x - lastTouch.pointers[j].x);
-                    if (deltaX < smallestDeltaX) {
-                        smallestDeltaX = deltaX;
-                        replacementIndex = j;
-                    }
-                }
-                distance = abs(y - lastTouch.pointers[replacementIndex].y);
-            }
-
-            // If replacing this pointer would correct a worse error than the previous ones
-            // considered, then use this replacement instead.
-            if (distance > badPointerDistance) {
-                badPointerIndex = i;
-                badPointerReplacementIndex = replacementIndex;
-                badPointerDistance = distance;
-            }
-        }
-
-        // Correct the jumpy pointer if one was found.
-        if (badPointerIndex >= 0) {
-#if DEBUG_HACKS
-            LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
-                    badPointerIndex,
-                    lastTouch.pointers[badPointerReplacementIndex].x,
-                    lastTouch.pointers[badPointerReplacementIndex].y);
-#endif
-
-            currentTouch.pointers[badPointerIndex].x =
-                    lastTouch.pointers[badPointerReplacementIndex].x;
-            currentTouch.pointers[badPointerIndex].y =
-                    lastTouch.pointers[badPointerReplacementIndex].y;
-            jumpyTouchFilter.jumpyPointsDropped += 1;
-            return true;
-        }
-    }
-
-    jumpyTouchFilter.jumpyPointsDropped = 0;
-    return false;
-}
-
-/* Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications. */
-void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
-    for (uint32_t currentIndex = 0; currentIndex < currentTouch.pointerCount; currentIndex++) {
-        uint32_t id = currentTouch.pointers[currentIndex].id;
-        int32_t x = currentTouch.pointers[currentIndex].x;
-        int32_t y = currentTouch.pointers[currentIndex].y;
-        int32_t pressure = currentTouch.pointers[currentIndex].pressure;
-
-        if (lastTouch.idBits.hasBit(id)) {
-            // Pointer was down before and is still down now.
-            // Compute average over history trace.
-            uint32_t start = averagingTouchFilter.historyStart[id];
-            uint32_t end = averagingTouchFilter.historyEnd[id];
-
-            int64_t deltaX = x - averagingTouchFilter.historyData[end].pointers[id].x;
-            int64_t deltaY = y - averagingTouchFilter.historyData[end].pointers[id].y;
-            uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
-#if DEBUG_HACKS
-            LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
-                    id, distance);
-#endif
-
-            if (distance < AVERAGING_DISTANCE_LIMIT) {
-                // Increment end index in preparation for recording new historical data.
-                end += 1;
-                if (end > AVERAGING_HISTORY_SIZE) {
-                    end = 0;
-                }
-
-                // If the end index has looped back to the start index then we have filled
-                // the historical trace up to the desired size so we drop the historical
-                // data at the start of the trace.
-                if (end == start) {
-                    start += 1;
-                    if (start > AVERAGING_HISTORY_SIZE) {
-                        start = 0;
-                    }
-                }
-
-                // Add the raw data to the historical trace.
-                averagingTouchFilter.historyStart[id] = start;
-                averagingTouchFilter.historyEnd[id] = end;
-                averagingTouchFilter.historyData[end].pointers[id].x = x;
-                averagingTouchFilter.historyData[end].pointers[id].y = y;
-                averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
-
-                // Average over all historical positions in the trace by total pressure.
-                int32_t averagedX = 0;
-                int32_t averagedY = 0;
-                int32_t totalPressure = 0;
-                for (;;) {
-                    int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
-                    int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
-                    int32_t historicalPressure = averagingTouchFilter.historyData[start]
-                            .pointers[id].pressure;
-
-                    averagedX += historicalX * historicalPressure;
-                    averagedY += historicalY * historicalPressure;
-                    totalPressure += historicalPressure;
-
-                    if (start == end) {
-                        break;
-                    }
-
-                    start += 1;
-                    if (start > AVERAGING_HISTORY_SIZE) {
-                        start = 0;
-                    }
-                }
-
-                averagedX /= totalPressure;
-                averagedY /= totalPressure;
-
-#if DEBUG_HACKS
-                LOGD("AveragingTouchFilter: Pointer id %d - "
-                        "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
-                        averagedX, averagedY);
-#endif
-
-                currentTouch.pointers[currentIndex].x = averagedX;
-                currentTouch.pointers[currentIndex].y = averagedY;
-            } else {
-#if DEBUG_HACKS
-                LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
-#endif
-            }
-        } else {
-#if DEBUG_HACKS
-            LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
-#endif
-        }
-
-        // Reset pointer history.
-        averagingTouchFilter.historyStart[id] = 0;
-        averagingTouchFilter.historyEnd[id] = 0;
-        averagingTouchFilter.historyData[0].pointers[id].x = x;
-        averagingTouchFilter.historyData[0].pointers[id].y = y;
-        averagingTouchFilter.historyData[0].pointers[id].pressure = pressure;
-    }
-}
-
-bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const {
-    if (! parameters.xAxis.valid || ! parameters.yAxis.valid) {
-        // Assume all points on a touch screen without valid axis parameters are
-        // inside the display.
-        return true;
-    }
-
-    return x >= parameters.xAxis.minValue
-        && x <= parameters.xAxis.maxValue
-        && y >= parameters.yAxis.minValue
-        && y <= parameters.yAxis.maxValue;
-}
-
-const InputDevice::VirtualKey* InputDevice::TouchScreenState::findVirtualKeyHit() const {
-    int32_t x = currentTouch.pointers[0].x;
-    int32_t y = currentTouch.pointers[0].y;
-    for (size_t i = 0; i < virtualKeys.size(); i++) {
-        const InputDevice::VirtualKey& virtualKey = virtualKeys[i];
-
-#if DEBUG_VIRTUAL_KEYS
-        LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
-                "left=%d, top=%d, right=%d, bottom=%d",
-                x, y,
-                virtualKey.keyCode, virtualKey.scanCode,
-                virtualKey.hitLeft, virtualKey.hitTop,
-                virtualKey.hitRight, virtualKey.hitBottom);
-#endif
-
-        if (virtualKey.isHit(x, y)) {
-            return & virtualKey;
-        }
-    }
-
-    return NULL;
-}
-
-
-// --- InputDevice::SingleTouchScreenState ---
-
-void InputDevice::SingleTouchScreenState::reset() {
-    accumulator.clear();
-    current.down = false;
-    current.x = 0;
-    current.y = 0;
-    current.pressure = 0;
-    current.size = 0;
-}
-
-
-// --- InputDevice::MultiTouchScreenState ---
-
-void InputDevice::MultiTouchScreenState::reset() {
-    accumulator.clear();
-}
-
-} // namespace android
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index e1d15a4..bf23479 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -89,26 +89,35 @@
     mDispatcher->preemptInputDispatch();
 }
 
-void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
-    mReader->getCurrentInputConfiguration(outConfiguration);
+void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) {
+    mReader->getInputConfiguration(outConfiguration);
 }
 
-int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
-        int32_t scanCode) const {
-    return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode);
+status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+    return mReader->getInputDeviceInfo(deviceId, outDeviceInfo);
 }
 
-int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
-        int32_t keyCode) const {
-    return mReader->getCurrentKeyCodeState(deviceId, deviceClasses, keyCode);
+void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+    mReader->getInputDeviceIds(outDeviceIds);
 }
 
-int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
-    return mReader->getCurrentSwitchState(deviceId, deviceClasses, sw);
+int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+        int32_t scanCode) {
+    return mReader->getScanCodeState(deviceId, sourceMask, scanCode);
 }
 
-bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
-    return mReader->hasKeys(numCodes, keyCodes, outFlags);
+int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+        int32_t keyCode) {
+    return mReader->getKeyCodeState(deviceId, sourceMask, keyCode);
+}
+
+int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
+    return mReader->getSwitchState(deviceId, sourceMask, sw);
+}
+
+bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask,
+        size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+    return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags);
 }
 
 } // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 30e391f..c5183e4 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -31,10 +31,6 @@
 #include <limits.h>
 #include <math.h>
 
-/** Amount that trackball needs to move in order to generate a key event. */
-#define TRACKBALL_MOVEMENT_THRESHOLD 6
-
-
 namespace android {
 
 // --- Static Functions ---
@@ -115,17 +111,21 @@
     return keyCode;
 }
 
+static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
+    return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
+}
+
 
 // --- InputReader ---
 
 InputReader::InputReader(const sp<EventHubInterface>& eventHub,
         const sp<InputReaderPolicyInterface>& policy,
         const sp<InputDispatcherInterface>& dispatcher) :
-        mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher) {
+        mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
+        mGlobalMetaState(0) {
     configureExcludedDevices();
-    resetGlobalMetaState();
-    resetDisplayProperties();
-    updateExportedVirtualKeyState();
+    updateGlobalMetaState();
+    updateInputConfiguration();
 }
 
 InputReader::~InputReader() {
@@ -136,12 +136,7 @@
 
 void InputReader::loopOnce() {
     RawEvent rawEvent;
-    mEventHub->getEvent(& rawEvent.deviceId, & rawEvent.type, & rawEvent.scanCode,
-            & rawEvent.keyCode, & rawEvent.flags, & rawEvent.value, & rawEvent.when);
-
-    // Replace the event timestamp so it is in same timebase as java.lang.System.nanoTime()
-    // and android.os.SystemClock.uptimeMillis() as expected by the rest of the system.
-    rawEvent.when = systemTime(SYSTEM_TIME_MONOTONIC);
+    mEventHub->getEvent(& rawEvent);
 
 #if DEBUG_RAW_EVENTS
     LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
@@ -155,621 +150,1371 @@
 void InputReader::process(const RawEvent* rawEvent) {
     switch (rawEvent->type) {
     case EventHubInterface::DEVICE_ADDED:
-        handleDeviceAdded(rawEvent);
+        addDevice(rawEvent->when, rawEvent->deviceId);
         break;
 
     case EventHubInterface::DEVICE_REMOVED:
-        handleDeviceRemoved(rawEvent);
+        removeDevice(rawEvent->when, rawEvent->deviceId);
         break;
 
-    case EV_SYN:
-        handleSync(rawEvent);
-        break;
-
-    case EV_KEY:
-        handleKey(rawEvent);
-        break;
-
-    case EV_REL:
-        handleRelativeMotion(rawEvent);
-        break;
-
-    case EV_ABS:
-        handleAbsoluteMotion(rawEvent);
-        break;
-
-    case EV_SW:
-        handleSwitch(rawEvent);
+    default:
+        consumeEvent(rawEvent);
         break;
     }
 }
 
-void InputReader::handleDeviceAdded(const RawEvent* rawEvent) {
-    InputDevice* device = getDevice(rawEvent->deviceId);
-    if (device) {
-        LOGW("Ignoring spurious device added event for deviceId %d.", rawEvent->deviceId);
+void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
+    String8 name = mEventHub->getDeviceName(deviceId);
+    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
+
+    InputDevice* device = createDevice(deviceId, name, classes);
+    device->configure();
+
+    bool added = false;
+    { // acquire device registry writer lock
+        RWLock::AutoWLock _wl(mDeviceRegistryLock);
+
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex < 0) {
+            mDevices.add(deviceId, device);
+            added = true;
+        }
+    } // release device registry writer lock
+
+    if (! added) {
+        LOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+        delete device;
         return;
     }
 
-    addDevice(rawEvent->when, rawEvent->deviceId);
+    if (device->isIgnored()) {
+        LOGI("Device added: id=0x%x, name=%s (ignored non-input device)",
+                deviceId, name.string());
+    } else {
+        LOGI("Device added: id=0x%x, name=%s, sources=%08x",
+                deviceId, name.string(), device->getSources());
+    }
+
+    handleConfigurationChanged(when);
 }
 
-void InputReader::handleDeviceRemoved(const RawEvent* rawEvent) {
-    InputDevice* device = getDevice(rawEvent->deviceId);
-    if (! device) {
-        LOGW("Ignoring spurious device removed event for deviceId %d.", rawEvent->deviceId);
+void InputReader::removeDevice(nsecs_t when, int32_t deviceId) {
+    bool removed = false;
+    InputDevice* device = NULL;
+    { // acquire device registry writer lock
+        RWLock::AutoWLock _wl(mDeviceRegistryLock);
+
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex >= 0) {
+            device = mDevices.valueAt(deviceIndex);
+            mDevices.removeItemsAt(deviceIndex, 1);
+            removed = true;
+        }
+    } // release device registry writer lock
+
+    if (! removed) {
+        LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
         return;
     }
 
-    removeDevice(rawEvent->when, device);
+    device->reset();
+
+    if (device->isIgnored()) {
+        LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
+                device->getId(), device->getName().string());
+    } else {
+        LOGI("Device removed: id=0x%x, name=%s, sources=%08x",
+                device->getId(), device->getName().string(), device->getSources());
+    }
+
+    delete device;
+
+    handleConfigurationChanged(when);
 }
 
-void InputReader::handleSync(const RawEvent* rawEvent) {
-    InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
-    if (! device) return;
+InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+    InputDevice* device = new InputDevice(this, deviceId, name);
 
-    if (rawEvent->scanCode == SYN_MT_REPORT) {
-        // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
-        // We drop pointers with pressure <= 0 since that indicates they are not down.
-        if (device->isMultiTouchScreen()) {
-            uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount;
+    const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices
 
-            if (device->multiTouchScreen.accumulator.pointers[pointerIndex].fields) {
-                if (pointerIndex == MAX_POINTERS) {
-                    LOGW("MultiTouch device driver returned more than maximum of %d pointers.",
-                            MAX_POINTERS);
-                } else {
-                    pointerIndex += 1;
-                    device->multiTouchScreen.accumulator.pointerCount = pointerIndex;
+    // Switch-like devices.
+    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+        device->addMapper(new SwitchInputMapper(device));
+    }
+
+    // Keyboard-like devices.
+    uint32_t keyboardSources = 0;
+    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+        keyboardSources |= AINPUT_SOURCE_KEYBOARD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+    }
+    if (classes & INPUT_DEVICE_CLASS_DPAD) {
+        keyboardSources |= AINPUT_SOURCE_DPAD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+        keyboardSources |= AINPUT_SOURCE_GAMEPAD;
+    }
+
+    if (keyboardSources != 0) {
+        device->addMapper(new KeyboardInputMapper(device,
+                associatedDisplayId, keyboardSources, keyboardType));
+    }
+
+    // Trackball-like devices.
+    if (classes & INPUT_DEVICE_CLASS_TRACKBALL) {
+        device->addMapper(new TrackballInputMapper(device, associatedDisplayId));
+    }
+
+    // Touchscreen-like devices.
+    if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) {
+        device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId));
+    } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
+        device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId));
+    }
+
+    return device;
+}
+
+void InputReader::consumeEvent(const RawEvent* rawEvent) {
+    int32_t deviceId = rawEvent->deviceId;
+
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex < 0) {
+            LOGW("Discarding event for unknown deviceId %d.", deviceId);
+            return;
+        }
+
+        InputDevice* device = mDevices.valueAt(deviceIndex);
+        if (device->isIgnored()) {
+            //LOGD("Discarding event for ignored deviceId %d.", deviceId);
+            return;
+        }
+
+        device->process(rawEvent);
+    } // release device registry reader lock
+}
+
+void InputReader::handleConfigurationChanged(nsecs_t when) {
+    // Reset global meta state because it depends on the list of all configured devices.
+    updateGlobalMetaState();
+
+    // Update input configuration.
+    updateInputConfiguration();
+
+    // Enqueue configuration changed.
+    mDispatcher->notifyConfigurationChanged(when);
+}
+
+void InputReader::configureExcludedDevices() {
+    Vector<String8> excludedDeviceNames;
+    mPolicy->getExcludedDeviceNames(excludedDeviceNames);
+
+    for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
+        mEventHub->addExcludedDevice(excludedDeviceNames[i]);
+    }
+}
+
+void InputReader::updateGlobalMetaState() {
+    { // acquire state lock
+        AutoMutex _l(mStateLock);
+
+        mGlobalMetaState = 0;
+
+        { // acquire device registry reader lock
+            RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+            for (size_t i = 0; i < mDevices.size(); i++) {
+                InputDevice* device = mDevices.valueAt(i);
+                mGlobalMetaState |= device->getMetaState();
+            }
+        } // release device registry reader lock
+    } // release state lock
+}
+
+int32_t InputReader::getGlobalMetaState() {
+    { // acquire state lock
+        AutoMutex _l(mStateLock);
+
+        return mGlobalMetaState;
+    } // release state lock
+}
+
+void InputReader::updateInputConfiguration() {
+    { // acquire state lock
+        AutoMutex _l(mStateLock);
+
+        int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
+        int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
+        int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
+        { // acquire device registry reader lock
+            RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+            InputDeviceInfo deviceInfo;
+            for (size_t i = 0; i < mDevices.size(); i++) {
+                InputDevice* device = mDevices.valueAt(i);
+                device->getDeviceInfo(& deviceInfo);
+                uint32_t sources = deviceInfo.getSources();
+
+                if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) {
+                    touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+                }
+                if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) {
+                    navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
+                } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) {
+                    navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+                }
+                if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+                    keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
                 }
             }
+        } // release device registry reader lock
 
-            device->multiTouchScreen.accumulator.pointers[pointerIndex].clear();
-        }
-    } else if (rawEvent->scanCode == SYN_REPORT) {
-        // General Sync: The driver has returned all data for the current event update.
-        if (device->isMultiTouchScreen()) {
-            if (device->multiTouchScreen.accumulator.isDirty()) {
-                onMultiTouchScreenStateChanged(rawEvent->when, device);
-                device->multiTouchScreen.accumulator.clear();
-            }
-        } else if (device->isSingleTouchScreen()) {
-            if (device->singleTouchScreen.accumulator.isDirty()) {
-                onSingleTouchScreenStateChanged(rawEvent->when, device);
-                device->singleTouchScreen.accumulator.clear();
-            }
+        mInputConfiguration.touchScreen = touchScreenConfig;
+        mInputConfiguration.keyboard = keyboardConfig;
+        mInputConfiguration.navigation = navigationConfig;
+    } // release state lock
+}
+
+void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
+    { // acquire state lock
+        AutoMutex _l(mStateLock);
+
+        *outConfiguration = mInputConfiguration;
+    } // release state lock
+}
+
+status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex < 0) {
+            return NAME_NOT_FOUND;
         }
 
-        if (device->trackball.accumulator.isDirty()) {
-            onTrackballStateChanged(rawEvent->when, device);
-            device->trackball.accumulator.clear();
+        InputDevice* device = mDevices.valueAt(deviceIndex);
+        if (device->isIgnored()) {
+            return NAME_NOT_FOUND;
         }
+
+        device->getDeviceInfo(outDeviceInfo);
+        return OK;
+    } // release device registy reader lock
+}
+
+void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+    outDeviceIds.clear();
+
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+        size_t numDevices = mDevices.size();
+        for (size_t i = 0; i < numDevices; i++) {
+            InputDevice* device = mDevices.valueAt(i);
+            if (! device->isIgnored()) {
+                outDeviceIds.add(device->getId());
+            }
+        }
+    } // release device registy reader lock
+}
+
+int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+        int32_t keyCode) {
+    return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState);
+}
+
+int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+        int32_t scanCode) {
+    return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState);
+}
+
+int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
+    return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState);
+}
+
+int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+        GetStateFunc getStateFunc) {
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+        int32_t result = AKEY_STATE_UNKNOWN;
+        if (deviceId >= 0) {
+            ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+            if (deviceIndex >= 0) {
+                InputDevice* device = mDevices.valueAt(deviceIndex);
+                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                    result = (device->*getStateFunc)(sourceMask, code);
+                }
+            }
+        } else {
+            size_t numDevices = mDevices.size();
+            for (size_t i = 0; i < numDevices; i++) {
+                InputDevice* device = mDevices.valueAt(i);
+                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                    result = (device->*getStateFunc)(sourceMask, code);
+                    if (result >= AKEY_STATE_DOWN) {
+                        return result;
+                    }
+                }
+            }
+        }
+        return result;
+    } // release device registy reader lock
+}
+
+bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
+        size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+    memset(outFlags, 0, numCodes);
+    return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+}
+
+bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+        const int32_t* keyCodes, uint8_t* outFlags) {
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+        bool result = false;
+        if (deviceId >= 0) {
+            ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+            if (deviceIndex >= 0) {
+                InputDevice* device = mDevices.valueAt(deviceIndex);
+                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                    result = device->markSupportedKeyCodes(sourceMask,
+                            numCodes, keyCodes, outFlags);
+                }
+            }
+        } else {
+            size_t numDevices = mDevices.size();
+            for (size_t i = 0; i < numDevices; i++) {
+                InputDevice* device = mDevices.valueAt(i);
+                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                    result |= device->markSupportedKeyCodes(sourceMask,
+                            numCodes, keyCodes, outFlags);
+                }
+            }
+        }
+        return result;
+    } // release device registy reader lock
+}
+
+
+// --- InputReaderThread ---
+
+InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
+        Thread(/*canCallJava*/ true), mReader(reader) {
+}
+
+InputReaderThread::~InputReaderThread() {
+}
+
+bool InputReaderThread::threadLoop() {
+    mReader->loopOnce();
+    return true;
+}
+
+
+// --- InputDevice ---
+
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
+        mContext(context), mId(id), mName(name), mSources(0) {
+}
+
+InputDevice::~InputDevice() {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        delete mMappers[i];
+    }
+    mMappers.clear();
+}
+
+void InputDevice::addMapper(InputMapper* mapper) {
+    mMappers.add(mapper);
+}
+
+void InputDevice::configure() {
+    mSources = 0;
+
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->configure();
+        mSources |= mapper->getSources();
     }
 }
 
-void InputReader::handleKey(const RawEvent* rawEvent) {
-    InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
-    if (! device) return;
+void InputDevice::reset() {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->reset();
+    }
+}
 
-    bool down = rawEvent->value != 0;
-    int32_t scanCode = rawEvent->scanCode;
+void InputDevice::process(const RawEvent* rawEvent) {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->process(rawEvent);
+    }
+}
 
-    if (device->isSingleTouchScreen()) {
-        switch (rawEvent->scanCode) {
-        case BTN_TOUCH:
-            device->singleTouchScreen.accumulator.fields |=
-                    InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH;
-            device->singleTouchScreen.accumulator.btnTouch = down;
+void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
+    outDeviceInfo->initialize(mId, mName);
+
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->populateDeviceInfo(outDeviceInfo);
+    }
+}
+
+int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
+}
+
+int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
+}
+
+int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+    return getState(sourceMask, switchCode, & InputMapper::getSwitchState);
+}
+
+int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
+    int32_t result = AKEY_STATE_UNKNOWN;
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+            result = (mapper->*getStateFunc)(sourceMask, code);
+            if (result >= AKEY_STATE_DOWN) {
+                return result;
+            }
+        }
+    }
+    return result;
+}
+
+bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+        const int32_t* keyCodes, uint8_t* outFlags) {
+    bool result = false;
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+            result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+        }
+    }
+    return result;
+}
+
+int32_t InputDevice::getMetaState() {
+    int32_t result = 0;
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        result |= mapper->getMetaState();
+    }
+    return result;
+}
+
+
+// --- InputMapper ---
+
+InputMapper::InputMapper(InputDevice* device) :
+        mDevice(device), mContext(device->getContext()) {
+}
+
+InputMapper::~InputMapper() {
+}
+
+void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    info->addSource(getSources());
+}
+
+void InputMapper::configure() {
+}
+
+void InputMapper::reset() {
+}
+
+int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+    return AKEY_STATE_UNKNOWN;
+}
+
+bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+        const int32_t* keyCodes, uint8_t* outFlags) {
+    return false;
+}
+
+int32_t InputMapper::getMetaState() {
+    return 0;
+}
+
+bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
+        getDispatcher()->notifyAppSwitchComing(when);
+    }
+
+    return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
+}
+
+
+// --- SwitchInputMapper ---
+
+SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
+        InputMapper(device) {
+}
+
+SwitchInputMapper::~SwitchInputMapper() {
+}
+
+uint32_t SwitchInputMapper::getSources() {
+    return 0;
+}
+
+void SwitchInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+    case EV_SW:
+        processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value);
+        break;
+    }
+}
+
+void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) {
+    uint32_t policyFlags = 0;
+    int32_t policyActions = getPolicy()->interceptSwitch(
+            when, switchCode, switchValue, policyFlags);
+
+    applyStandardPolicyActions(when, policyActions);
+}
+
+int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+    return getEventHub()->getSwitchState(getDeviceId(), switchCode);
+}
+
+
+// --- KeyboardInputMapper ---
+
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId,
+        uint32_t sources, int32_t keyboardType) :
+        InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources),
+        mKeyboardType(keyboardType) {
+    initialize();
+}
+
+KeyboardInputMapper::~KeyboardInputMapper() {
+}
+
+void KeyboardInputMapper::initialize() {
+    mMetaState = AMETA_NONE;
+    mDownTime = 0;
+}
+
+uint32_t KeyboardInputMapper::getSources() {
+    return mSources;
+}
+
+void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    info->setKeyboardType(mKeyboardType);
+}
+
+void KeyboardInputMapper::reset() {
+    // Synthesize key up event on reset if keys are currently down.
+    while (! mKeyDowns.isEmpty()) {
+        const KeyDown& keyDown = mKeyDowns.top();
+        nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+        processKey(when, false, keyDown.keyCode, keyDown.scanCode, 0);
+    }
+
+    InputMapper::reset();
+
+    // Reinitialize.
+    initialize();
+    getContext()->updateGlobalMetaState();
+}
+
+void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+    case EV_KEY: {
+        int32_t scanCode = rawEvent->scanCode;
+        if (isKeyboardOrGamepadKey(scanCode)) {
+            processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode,
+                    rawEvent->flags);
+        }
+        break;
+    }
+    }
+}
+
+bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
+    return scanCode < BTN_MOUSE
+        || scanCode >= KEY_OK
+        || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI);
+}
+
+void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
+        uint32_t policyFlags) {
+    if (down) {
+        // Rotate key codes according to orientation.
+        if (mAssociatedDisplayId >= 0) {
+            int32_t orientation;
+            if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+                return;
+            }
+
+            keyCode = rotateKeyCode(keyCode, orientation);
+        }
+
+        // Add key down.
+        ssize_t keyDownIndex = findKeyDown(scanCode);
+        if (keyDownIndex >= 0) {
+            // key repeat, be sure to use same keycode as before in case of rotation
+            keyCode = mKeyDowns.top().keyCode;
+        } else {
+            // key down
+            mKeyDowns.push();
+            KeyDown& keyDown = mKeyDowns.editTop();
+            keyDown.keyCode = keyCode;
+            keyDown.scanCode = scanCode;
+        }
+    } else {
+        // Remove key down.
+        ssize_t keyDownIndex = findKeyDown(scanCode);
+        if (keyDownIndex >= 0) {
+            // key up, be sure to use same keycode as before in case of rotation
+            keyCode = mKeyDowns.top().keyCode;
+            mKeyDowns.removeAt(size_t(keyDownIndex));
+        } else {
+            // key was not actually down
+            LOGI("Dropping key up from device %s because the key was not down.  "
+                    "keyCode=%d, scanCode=%d",
+                    getDeviceName().string(), keyCode, scanCode);
             return;
         }
     }
 
-    if (device->isTrackball()) {
-        switch (rawEvent->scanCode) {
-        case BTN_MOUSE:
-            device->trackball.accumulator.fields |=
-                    InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
-            device->trackball.accumulator.btnMouse = down;
-
-            // Process the trackball change now since we may not receive a sync immediately.
-            onTrackballStateChanged(rawEvent->when, device);
-            device->trackball.accumulator.clear();
-            return;
-        }
-    }
-
-    if (device->isKeyboard()) {
-        int32_t keyCode = rawEvent->keyCode;
-        onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags);
-    }
-}
-
-void InputReader::handleRelativeMotion(const RawEvent* rawEvent) {
-    InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
-    if (! device) return;
-
-    if (device->isTrackball()) {
-        switch (rawEvent->scanCode) {
-        case REL_X:
-            device->trackball.accumulator.fields |=
-                    InputDevice::TrackballState::Accumulator::FIELD_REL_X;
-            device->trackball.accumulator.relX = rawEvent->value;
-            break;
-        case REL_Y:
-            device->trackball.accumulator.fields |=
-                    InputDevice::TrackballState::Accumulator::FIELD_REL_Y;
-            device->trackball.accumulator.relY = rawEvent->value;
-            break;
-        }
-    }
-}
-
-void InputReader::handleAbsoluteMotion(const RawEvent* rawEvent) {
-    InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
-    if (! device) return;
-
-    if (device->isMultiTouchScreen()) {
-        uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount;
-        InputDevice::MultiTouchScreenState::Accumulator::Pointer* pointer =
-                & device->multiTouchScreen.accumulator.pointers[pointerIndex];
-
-        switch (rawEvent->scanCode) {
-        case ABS_MT_POSITION_X:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X;
-            pointer->absMTPositionX = rawEvent->value;
-            break;
-        case ABS_MT_POSITION_Y:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y;
-            pointer->absMTPositionY = rawEvent->value;
-            break;
-        case ABS_MT_TOUCH_MAJOR:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
-            pointer->absMTTouchMajor = rawEvent->value;
-            break;
-        case ABS_MT_TOUCH_MINOR:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
-            pointer->absMTTouchMinor = rawEvent->value;
-            break;
-        case ABS_MT_WIDTH_MAJOR:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
-            pointer->absMTWidthMajor = rawEvent->value;
-            break;
-        case ABS_MT_WIDTH_MINOR:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
-            pointer->absMTWidthMinor = rawEvent->value;
-            break;
-        case ABS_MT_ORIENTATION:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION;
-            pointer->absMTOrientation = rawEvent->value;
-            break;
-        case ABS_MT_TRACKING_ID:
-            pointer->fields |=
-                    InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TRACKING_ID;
-            pointer->absMTTrackingId = rawEvent->value;
-            break;
-        }
-    } else if (device->isSingleTouchScreen()) {
-        switch (rawEvent->scanCode) {
-        case ABS_X:
-            device->singleTouchScreen.accumulator.fields |=
-                    InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X;
-            device->singleTouchScreen.accumulator.absX = rawEvent->value;
-            break;
-        case ABS_Y:
-            device->singleTouchScreen.accumulator.fields |=
-                    InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y;
-            device->singleTouchScreen.accumulator.absY = rawEvent->value;
-            break;
-        case ABS_PRESSURE:
-            device->singleTouchScreen.accumulator.fields |=
-                    InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE;
-            device->singleTouchScreen.accumulator.absPressure = rawEvent->value;
-            break;
-        case ABS_TOOL_WIDTH:
-            device->singleTouchScreen.accumulator.fields |=
-                    InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH;
-            device->singleTouchScreen.accumulator.absToolWidth = rawEvent->value;
-            break;
-        }
-    }
-}
-
-void InputReader::handleSwitch(const RawEvent* rawEvent) {
-    InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
-    if (! device) return;
-
-    onSwitch(rawEvent->when, device, rawEvent->scanCode, rawEvent->value);
-}
-
-void InputReader::onKey(nsecs_t when, InputDevice* device,
-        bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
-    /* Refresh display properties so we can rotate key codes according to display orientation */
-
-    if (! refreshDisplayProperties()) {
-        return;
-    }
-
-    /* Update device state */
-
-    int32_t oldMetaState = device->keyboard.current.metaState;
+    int32_t oldMetaState = mMetaState;
     int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
     if (oldMetaState != newMetaState) {
-        device->keyboard.current.metaState = newMetaState;
-        resetGlobalMetaState();
+        mMetaState = newMetaState;
+        getContext()->updateGlobalMetaState();
     }
 
-    // FIXME if we send a down event about a rotated key press we should ensure that we send
-    //       a corresponding up event about the rotated key press even if the orientation
-    //       has changed in the meantime
-    keyCode = rotateKeyCode(keyCode, mDisplayOrientation);
+    /* Apply policy. */
 
-    if (down) {
-        device->keyboard.current.downTime = when;
-    }
+    int32_t policyActions = getPolicy()->interceptKey(when,
+            getDeviceId(), down, keyCode, scanCode, policyFlags);
 
-    /* Apply policy */
-
-    int32_t policyActions = mPolicy->interceptKey(when, device->id,
-            down, keyCode, scanCode, policyFlags);
-
-    if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
+    if (! applyStandardPolicyActions(when, policyActions)) {
         return; // event dropped
     }
 
-    /* Enqueue key event for dispatch */
+    /* Enqueue key event for dispatch. */
 
     int32_t keyEventAction;
     if (down) {
-        device->keyboard.current.downTime = when;
+        mDownTime = when;
         keyEventAction = AKEY_EVENT_ACTION_DOWN;
     } else {
         keyEventAction = AKEY_EVENT_ACTION_UP;
     }
 
     int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
-    if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
+    if (policyFlags & POLICY_FLAG_WOKE_HERE) {
         keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
     }
 
-    mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags,
+    getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
             keyEventAction, keyEventFlags, keyCode, scanCode,
-            device->keyboard.current.metaState,
-            device->keyboard.current.downTime);
+            mMetaState, mDownTime);
 }
 
-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);
+ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+    size_t n = mKeyDowns.size();
+    for (size_t i = 0; i < n; i++) {
+        if (mKeyDowns[i].scanCode == scanCode) {
+            return i;
+        }
+    }
+    return -1;
 }
 
-void InputReader::onMultiTouchScreenStateChanged(nsecs_t when,
-        InputDevice* device) {
-    static const uint32_t REQUIRED_FIELDS =
-            InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X
-            | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y
-            | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
-            | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
+}
 
-    /* Refresh display properties so we can map touch screen coords into display coords */
+int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+}
 
-    if (! refreshDisplayProperties()) {
-        return;
+bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+        const int32_t* keyCodes, uint8_t* outFlags) {
+    return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
+}
+
+int32_t KeyboardInputMapper::getMetaState() {
+    return mMetaState;
+}
+
+
+// --- TrackballInputMapper ---
+
+TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+        InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+    mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+    mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+    mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+    mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+
+    initialize();
+}
+
+TrackballInputMapper::~TrackballInputMapper() {
+}
+
+uint32_t TrackballInputMapper::getSources() {
+    return AINPUT_SOURCE_TRACKBALL;
+}
+
+void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale);
+    info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
+}
+
+void TrackballInputMapper::initialize() {
+    mAccumulator.clear();
+
+    mDown = false;
+    mDownTime = 0;
+}
+
+void TrackballInputMapper::reset() {
+    // Synthesize trackball button up event on reset if trackball button is currently down.
+    if (mDown) {
+        nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+        mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
+        mAccumulator.btnMouse = false;
+        sync(when);
     }
 
-    /* Update device state */
+    InputMapper::reset();
 
-    InputDevice::MultiTouchScreenState* in = & device->multiTouchScreen;
-    InputDevice::TouchData* out = & device->touchScreen.currentTouch;
+    // Reinitialize.
+    initialize();
+}
 
-    uint32_t inCount = in->accumulator.pointerCount;
-    uint32_t outCount = 0;
-    bool havePointerIds = true;
+void TrackballInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+    case EV_KEY:
+        switch (rawEvent->scanCode) {
+        case BTN_MOUSE:
+            mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
+            mAccumulator.btnMouse = rawEvent->value != 0;
 
-    out->clear();
-
-    for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
-        uint32_t fields = in->accumulator.pointers[inIndex].fields;
-
-        if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
-#if DEBUG_POINTERS
-            LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
-                    inIndex, fields);
-            continue;
-#endif
+            sync(rawEvent->when);
+            mAccumulator.clear();
+            break;
         }
+        break;
 
-        if (in->accumulator.pointers[inIndex].absMTTouchMajor <= 0) {
-            // Pointer is not down.  Drop it.
-            continue;
+    case EV_REL:
+        switch (rawEvent->scanCode) {
+        case REL_X:
+            mAccumulator.fields |= Accumulator::FIELD_REL_X;
+            mAccumulator.relX = rawEvent->value;
+            break;
+        case REL_Y:
+            mAccumulator.fields |= Accumulator::FIELD_REL_Y;
+            mAccumulator.relY = rawEvent->value;
+            break;
         }
+        break;
 
-        out->pointers[outCount].x = in->accumulator.pointers[inIndex].absMTPositionX;
-        out->pointers[outCount].y = in->accumulator.pointers[inIndex].absMTPositionY;
-
-        out->pointers[outCount].touchMajor = in->accumulator.pointers[inIndex].absMTTouchMajor;
-        out->pointers[outCount].touchMinor = (fields
-                & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
-                ? in->accumulator.pointers[inIndex].absMTTouchMinor
-                        : in->accumulator.pointers[inIndex].absMTTouchMajor;
-
-        out->pointers[outCount].toolMajor = in->accumulator.pointers[inIndex].absMTWidthMajor;
-        out->pointers[outCount].toolMinor = (fields
-                & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
-                ? in->accumulator.pointers[inIndex].absMTWidthMinor
-                        : in->accumulator.pointers[inIndex].absMTWidthMajor;
-
-        out->pointers[outCount].orientation = (fields
-                & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
-                ? in->accumulator.pointers[inIndex].absMTOrientation : 0;
-
-        // Derive an approximation of pressure and size.
-        // FIXME assignment of pressure may be incorrect, probably better to let
-        // pressure = touch / width.  Later on we pass width to MotionEvent as a size, which
-        // isn't quite right either.  Should be using touch for that.
-        out->pointers[outCount].pressure = in->accumulator.pointers[inIndex].absMTTouchMajor;
-        out->pointers[outCount].size = in->accumulator.pointers[inIndex].absMTWidthMajor;
-
-        if (havePointerIds) {
-            if (fields & InputDevice::MultiTouchScreenState::Accumulator::
-                    FIELD_ABS_MT_TRACKING_ID) {
-                uint32_t id = uint32_t(in->accumulator.pointers[inIndex].absMTTrackingId);
-
-                if (id > MAX_POINTER_ID) {
-#if DEBUG_POINTERS
-                    LOGD("Pointers: Ignoring driver provided pointer id %d because "
-                            "it is larger than max supported id %d for optimizations",
-                            id, MAX_POINTER_ID);
-#endif
-                    havePointerIds = false;
-                }
-                else {
-                    out->pointers[outCount].id = id;
-                    out->idToIndex[id] = outCount;
-                    out->idBits.markBit(id);
-                }
-            } else {
-                havePointerIds = false;
+    case EV_SYN:
+        switch (rawEvent->scanCode) {
+        case SYN_REPORT:
+            if (mAccumulator.isDirty()) {
+                sync(rawEvent->when);
+                mAccumulator.clear();
             }
+            break;
         }
-
-        outCount += 1;
+        break;
     }
-
-    out->pointerCount = outCount;
-
-    onTouchScreenChanged(when, device, havePointerIds);
 }
 
-void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
-        InputDevice* device) {
-    /* Refresh display properties so we can map touch screen coords into display coords */
+void TrackballInputMapper::sync(nsecs_t when) {
+    /* Get display properties so for rotation based on display orientation. */
 
-    if (! refreshDisplayProperties()) {
-        return;
+    int32_t orientation;
+    if (mAssociatedDisplayId >= 0) {
+        if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+            return;
+        }
+    } else {
+        orientation = InputReaderPolicyInterface::ROTATION_0;
     }
 
-    /* Update device state */
+    /* Update saved trackball state */
 
-    InputDevice::SingleTouchScreenState* in = & device->singleTouchScreen;
-    InputDevice::TouchData* out = & device->touchScreen.currentTouch;
+    uint32_t fields = mAccumulator.fields;
+    bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
 
-    uint32_t fields = in->accumulator.fields;
-
-    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH) {
-        in->current.down = in->accumulator.btnTouch;
+    if (downChanged) {
+        if (mAccumulator.btnMouse) {
+            mDown = true;
+            mDownTime = when;
+        } else {
+            mDown = false;
+        }
     }
 
-    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) {
-        in->current.x = in->accumulator.absX;
-    }
-
-    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) {
-        in->current.y = in->accumulator.absY;
-    }
-
-    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) {
-        in->current.pressure = in->accumulator.absPressure;
-    }
-
-    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) {
-        in->current.size = in->accumulator.absToolWidth;
-    }
-
-    out->clear();
-
-    if (in->current.down) {
-        out->pointerCount = 1;
-        out->pointers[0].id = 0;
-        out->pointers[0].x = in->current.x;
-        out->pointers[0].y = in->current.y;
-        out->pointers[0].pressure = in->current.pressure;
-        out->pointers[0].size = in->current.size;
-        out->pointers[0].touchMajor = in->current.pressure;
-        out->pointers[0].touchMinor = in->current.pressure;
-        out->pointers[0].toolMajor = in->current.size;
-        out->pointers[0].toolMinor = in->current.size;
-        out->pointers[0].orientation = 0;
-        out->idToIndex[0] = 0;
-        out->idBits.markBit(0);
-    }
-
-    onTouchScreenChanged(when, device, true);
-}
-
-void InputReader::onTouchScreenChanged(nsecs_t when,
-        InputDevice* device, bool havePointerIds) {
     /* Apply policy */
 
-    int32_t policyActions = mPolicy->interceptTouch(when);
+    uint32_t policyFlags = 0;
+    int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
+
+    if (! applyStandardPolicyActions(when, policyActions)) {
+        return; // event dropped
+    }
+
+    /* Enqueue motion event for dispatch. */
+
+    int32_t motionEventAction;
+    if (downChanged) {
+        motionEventAction = mDown ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+    } else {
+        motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+    }
+
+    int32_t pointerId = 0;
+    PointerCoords pointerCoords;
+    pointerCoords.x = fields & Accumulator::FIELD_REL_X
+            ? mAccumulator.relX * mXScale : 0;
+    pointerCoords.y = fields & Accumulator::FIELD_REL_Y
+            ? mAccumulator.relY * mYScale : 0;
+    pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise.
+    pointerCoords.size = 0;
+    pointerCoords.touchMajor = 0;
+    pointerCoords.touchMinor = 0;
+    pointerCoords.toolMajor = 0;
+    pointerCoords.toolMinor = 0;
+    pointerCoords.orientation = 0;
+
+    float temp;
+    switch (orientation) {
+    case InputReaderPolicyInterface::ROTATION_90:
+        temp = pointerCoords.x;
+        pointerCoords.x = pointerCoords.y;
+        pointerCoords.y = - temp;
+        break;
+
+    case InputReaderPolicyInterface::ROTATION_180:
+        pointerCoords.x = - pointerCoords.x;
+        pointerCoords.y = - pointerCoords.y;
+        break;
+
+    case InputReaderPolicyInterface::ROTATION_270:
+        temp = pointerCoords.x;
+        pointerCoords.x = - pointerCoords.y;
+        pointerCoords.y = temp;
+        break;
+    }
+
+    int32_t metaState = mContext->getGlobalMetaState();
+    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags,
+            motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+            1, & pointerId, & pointerCoords, mXPrecision, mYPrecision, mDownTime);
+}
+
+
+// --- TouchInputMapper ---
+
+TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+        InputMapper(device), mAssociatedDisplayId(associatedDisplayId),
+        mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1) {
+    initialize();
+}
+
+TouchInputMapper::~TouchInputMapper() {
+}
+
+uint32_t TouchInputMapper::getSources() {
+    return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD;
+}
+
+void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    // FIXME: Should ensure the surface information is up to date so that orientation changes
+    // are noticed immediately.  Unfortunately we will need to add some extra locks here
+    // to prevent race conditions.
+    // configureSurface();
+
+    info->addMotionRange(AINPUT_MOTION_RANGE_X, mOrientedRanges.x);
+    info->addMotionRange(AINPUT_MOTION_RANGE_Y, mOrientedRanges.y);
+    info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mOrientedRanges.pressure);
+    info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mOrientedRanges.size);
+    info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mOrientedRanges.touchMajor);
+    info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mOrientedRanges.touchMinor);
+    info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mOrientedRanges.toolMajor);
+    info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mOrientedRanges.toolMinor);
+    info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mOrientedRanges.orientation);
+}
+
+void TouchInputMapper::initialize() {
+    mLastTouch.clear();
+    mDownTime = 0;
+    mCurrentVirtualKey.down = false;
+
+    for (uint32_t i = 0; i < MAX_POINTERS; i++) {
+        mAveragingTouchFilter.historyStart[i] = 0;
+        mAveragingTouchFilter.historyEnd[i] = 0;
+    }
+
+    mJumpyTouchFilter.jumpyPointsDropped = 0;
+}
+
+void TouchInputMapper::configure() {
+    InputMapper::configure();
+
+    // Configure basic parameters.
+    mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
+    mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
+    mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
+
+    // Configure absolute axis information.
+    configureAxes();
+
+    // Configure pressure factors.
+    if (mAxes.pressure.valid) {
+        mPressureOrigin = mAxes.pressure.minValue;
+        mPressureScale = 1.0f / mAxes.pressure.getRange();
+    } else {
+        mPressureOrigin = 0;
+        mPressureScale = 1.0f;
+    }
+
+    mOrientedRanges.pressure.min = 0.0f;
+    mOrientedRanges.pressure.max = 1.0f;
+    mOrientedRanges.pressure.flat = 0.0f;
+    mOrientedRanges.pressure.fuzz = mPressureScale;
+
+    // Configure size factors.
+    if (mAxes.size.valid) {
+        mSizeOrigin = mAxes.size.minValue;
+        mSizeScale = 1.0f / mAxes.size.getRange();
+    } else {
+        mSizeOrigin = 0;
+        mSizeScale = 1.0f;
+    }
+
+    mOrientedRanges.size.min = 0.0f;
+    mOrientedRanges.size.max = 1.0f;
+    mOrientedRanges.size.flat = 0.0f;
+    mOrientedRanges.size.fuzz = mSizeScale;
+
+    // Configure orientation factors.
+    if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) {
+        mOrientationScale = float(M_PI_2) / mAxes.orientation.maxValue;
+    } else {
+        mOrientationScale = 0.0f;
+    }
+
+    mOrientedRanges.orientation.min = - M_PI_2;
+    mOrientedRanges.orientation.max = M_PI_2;
+    mOrientedRanges.orientation.flat = 0;
+    mOrientedRanges.orientation.fuzz = mOrientationScale;
+
+    // Configure surface dimensions and orientation.
+    configureSurface();
+}
+
+void TouchInputMapper::configureAxes() {
+    mAxes.x.valid = false;
+    mAxes.y.valid = false;
+    mAxes.pressure.valid = false;
+    mAxes.size.valid = false;
+    mAxes.touchMajor.valid = false;
+    mAxes.touchMinor.valid = false;
+    mAxes.toolMajor.valid = false;
+    mAxes.toolMinor.valid = false;
+    mAxes.orientation.valid = false;
+}
+
+bool TouchInputMapper::configureSurface() {
+    // Update orientation and dimensions if needed.
+    int32_t orientation;
+    int32_t width, height;
+    if (mAssociatedDisplayId >= 0) {
+        if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) {
+            return false;
+        }
+    } else {
+        orientation = InputReaderPolicyInterface::ROTATION_0;
+        width = mAxes.x.getRange();
+        height = mAxes.y.getRange();
+    }
+
+    bool orientationChanged = mSurfaceOrientation != orientation;
+    if (orientationChanged) {
+        mSurfaceOrientation = orientation;
+    }
+
+    bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height;
+    if (sizeChanged) {
+        mSurfaceWidth = width;
+        mSurfaceHeight = height;
+
+        // Compute size-dependent translation and scaling factors and place virtual keys.
+        if (mAxes.x.valid && mAxes.y.valid) {
+            mXOrigin = mAxes.x.minValue;
+            mYOrigin = mAxes.y.minValue;
+
+            LOGI("Device configured: id=0x%x, name=%s (display size was changed)",
+                    getDeviceId(), getDeviceName().string());
+
+            mXScale = float(width) / mAxes.x.getRange();
+            mYScale = float(height) / mAxes.y.getRange();
+            mXPrecision = 1.0f / mXScale;
+            mYPrecision = 1.0f / mYScale;
+
+            configureVirtualKeys();
+        } else {
+            mXOrigin = 0;
+            mYOrigin = 0;
+            mXScale = 1.0f;
+            mYScale = 1.0f;
+            mXPrecision = 1.0f;
+            mYPrecision = 1.0f;
+        }
+
+        // Configure touch and tool area ranges.
+        float diagonal = sqrt(float(width * width + height * height));
+        float diagonalFuzz = sqrt(mXScale * mXScale + mYScale * mYScale);
+
+        mOrientedRanges.touchMajor.min = 0.0f;
+        mOrientedRanges.touchMajor.max = diagonal;
+        mOrientedRanges.touchMajor.flat = 0.0f;
+        mOrientedRanges.touchMajor.fuzz = diagonalFuzz;
+        mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
+
+        mOrientedRanges.toolMinor = mOrientedRanges.toolMajor = mOrientedRanges.touchMajor;
+    }
+
+    if (orientationChanged || sizeChanged) {
+        // Compute oriented surface dimensions, precision, and scales.
+        float orientedXScale, orientedYScale;
+        switch (mSurfaceOrientation) {
+        case InputReaderPolicyInterface::ROTATION_90:
+        case InputReaderPolicyInterface::ROTATION_270:
+            mOrientedSurfaceWidth = mSurfaceHeight;
+            mOrientedSurfaceHeight = mSurfaceWidth;
+            mOrientedXPrecision = mYPrecision;
+            mOrientedYPrecision = mXPrecision;
+            orientedXScale = mYScale;
+            orientedYScale = mXScale;
+            break;
+        default:
+            mOrientedSurfaceWidth = mSurfaceWidth;
+            mOrientedSurfaceHeight = mSurfaceHeight;
+            mOrientedXPrecision = mXPrecision;
+            mOrientedYPrecision = mYPrecision;
+            orientedXScale = mXScale;
+            orientedYScale = mYScale;
+            break;
+        }
+
+        // Configure position ranges.
+        mOrientedRanges.x.min = 0;
+        mOrientedRanges.x.max = mOrientedSurfaceWidth;
+        mOrientedRanges.x.flat = 0;
+        mOrientedRanges.x.fuzz = orientedXScale;
+
+        mOrientedRanges.y.min = 0;
+        mOrientedRanges.y.max = mOrientedSurfaceHeight;
+        mOrientedRanges.y.flat = 0;
+        mOrientedRanges.y.fuzz = orientedYScale;
+    }
+
+    return true;
+}
+
+void TouchInputMapper::configureVirtualKeys() {
+    assert(mAxes.x.valid && mAxes.y.valid);
+
+    Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
+    getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
+
+    { // acquire virtual key lock
+        AutoMutex _l(mVirtualKeyLock);
+
+        mVirtualKeys.clear();
+
+        if (virtualKeyDefinitions.size() == 0) {
+            return;
+        }
+
+        mVirtualKeys.setCapacity(virtualKeyDefinitions.size());
+
+        int32_t touchScreenLeft = mAxes.x.minValue;
+        int32_t touchScreenTop = mAxes.y.minValue;
+        int32_t touchScreenWidth = mAxes.x.getRange();
+        int32_t touchScreenHeight = mAxes.y.getRange();
+
+        for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
+            const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
+                    virtualKeyDefinitions[i];
+
+            mVirtualKeys.add();
+            VirtualKey& virtualKey = mVirtualKeys.editTop();
+
+            virtualKey.scanCode = virtualKeyDefinition.scanCode;
+            int32_t keyCode;
+            uint32_t flags;
+            if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode,
+                    & keyCode, & flags)) {
+                LOGW("  VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
+                mVirtualKeys.pop(); // drop the key
+                continue;
+            }
+
+            virtualKey.keyCode = keyCode;
+            virtualKey.flags = flags;
+
+            // convert the key definition's display coordinates into touch coordinates for a hit box
+            int32_t halfWidth = virtualKeyDefinition.width / 2;
+            int32_t halfHeight = virtualKeyDefinition.height / 2;
+
+            virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
+                    * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
+            virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
+                    * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
+            virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
+                    * touchScreenHeight / mSurfaceHeight + touchScreenTop;
+            virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
+                    * touchScreenHeight / mSurfaceHeight + touchScreenTop;
+
+            LOGI("  VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
+                    virtualKey.scanCode, virtualKey.keyCode,
+                    virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+        }
+    } // release virtual key lock
+}
+
+void TouchInputMapper::reset() {
+    // Synthesize touch up event if touch is currently down.
+    // This will also take care of finishing virtual key processing if needed.
+    if (mLastTouch.pointerCount != 0) {
+        nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+        mCurrentTouch.clear();
+        syncTouch(when, true);
+    }
+
+    InputMapper::reset();
+
+    // Reinitialize.
+    initialize();
+}
+
+void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
+    /* Refresh associated display information and update our size configuration if needed. */
+
+    if (! configureSurface()) {
+        return;
+    }
+
+    /* Apply policy */
 
     uint32_t policyFlags = 0;
-    if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
-        device->touchScreen.lastTouch.clear();
+    int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
+
+    if (! applyStandardPolicyActions(when, policyActions)) {
+        mLastTouch.clear();
         return; // event dropped
     }
 
     /* Preprocess pointer data */
 
-    if (device->touchScreen.parameters.useBadTouchFilter) {
-        if (device->touchScreen.applyBadTouchFilter()) {
+    if (mParameters.useBadTouchFilter) {
+        if (applyBadTouchFilter()) {
             havePointerIds = false;
         }
     }
 
-    if (device->touchScreen.parameters.useJumpyTouchFilter) {
-        if (device->touchScreen.applyJumpyTouchFilter()) {
+    if (mParameters.useJumpyTouchFilter) {
+        if (applyJumpyTouchFilter()) {
             havePointerIds = false;
         }
     }
 
     if (! havePointerIds) {
-        device->touchScreen.calculatePointerIds();
+        calculatePointerIds();
     }
 
-    InputDevice::TouchData temp;
-    InputDevice::TouchData* savedTouch;
-    if (device->touchScreen.parameters.useAveragingTouchFilter) {
-        temp.copyFrom(device->touchScreen.currentTouch);
+    TouchData temp;
+    TouchData* savedTouch;
+    if (mParameters.useAveragingTouchFilter) {
+        temp.copyFrom(mCurrentTouch);
         savedTouch = & temp;
 
-        device->touchScreen.applyAveragingTouchFilter();
+        applyAveragingTouchFilter();
     } else {
-        savedTouch = & device->touchScreen.currentTouch;
+        savedTouch = & mCurrentTouch;
     }
 
-    /* Process virtual keys or touches */
+    /* Process touches and virtual keys */
 
-    if (! consumeVirtualKeyTouches(when, device, policyFlags)) {
-        dispatchTouches(when, device, policyFlags);
+    TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
+    if (touchResult == DISPATCH_TOUCH) {
+        dispatchTouches(when, policyFlags);
     }
 
-    // Copy current touch to last touch in preparation for the next cycle.
-    device->touchScreen.lastTouch.copyFrom(*savedTouch);
-}
+    /* Copy current touch to last touch in preparation for the next cycle. */
 
-bool InputReader::consumeVirtualKeyTouches(nsecs_t when,
-        InputDevice* device, uint32_t policyFlags) {
-    switch (device->touchScreen.currentVirtualKey.status) {
-    case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED:
-        if (device->touchScreen.currentTouch.pointerCount == 0) {
-            // Pointer went up after virtual key canceled.
-            device->touchScreen.currentVirtualKey.status =
-                    InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP;
-        }
-        return true; // consumed
-
-    case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN:
-        if (device->touchScreen.currentTouch.pointerCount == 0) {
-            // Pointer went up while virtual key was down.
-            device->touchScreen.currentVirtualKey.status =
-                    InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP;
-#if DEBUG_VIRTUAL_KEYS
-            LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
-                    device->touchScreen.currentVirtualKey.keyCode,
-                    device->touchScreen.currentVirtualKey.scanCode);
-#endif
-            dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP,
-                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
-            return true; // consumed
-        }
-
-        if (device->touchScreen.currentTouch.pointerCount == 1) {
-            const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit();
-            if (virtualKey
-                    && virtualKey->keyCode == device->touchScreen.currentVirtualKey.keyCode) {
-                // Pointer is still within the space of the virtual key.
-                return true; // consumed
-            }
-        }
-
-        // Pointer left virtual key area or another pointer also went down.
-        // Send key cancellation.
-        device->touchScreen.currentVirtualKey.status =
-                InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED;
-#if DEBUG_VIRTUAL_KEYS
-        LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
-                device->touchScreen.currentVirtualKey.keyCode,
-                device->touchScreen.currentVirtualKey.scanCode);
-#endif
-        dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP,
-                AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
-                        | AKEY_EVENT_FLAG_CANCELED);
-        return true; // consumed
-
-    default:
-        if (device->touchScreen.currentTouch.pointerCount == 1
-                && device->touchScreen.lastTouch.pointerCount == 0) {
-            // Pointer just went down.  Check for virtual key hit.
-            const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit();
-            if (virtualKey) {
-                device->touchScreen.currentVirtualKey.status =
-                        InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN;
-                device->touchScreen.currentVirtualKey.downTime = when;
-                device->touchScreen.currentVirtualKey.keyCode = virtualKey->keyCode;
-                device->touchScreen.currentVirtualKey.scanCode = virtualKey->scanCode;
-#if DEBUG_VIRTUAL_KEYS
-                LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
-                        device->touchScreen.currentVirtualKey.keyCode,
-                        device->touchScreen.currentVirtualKey.scanCode);
-#endif
-                dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_DOWN,
-                        AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
-                return true; // consumed
-            }
-        }
-        return false; // not consumed
+    if (touchResult == DROP_STROKE) {
+        mLastTouch.clear();
+    } else {
+        mLastTouch.copyFrom(*savedTouch);
     }
 }
 
-void InputReader::dispatchVirtualKey(nsecs_t when,
-        InputDevice* device, uint32_t policyFlags,
-        int32_t keyEventAction, int32_t keyEventFlags) {
-    updateExportedVirtualKeyState();
+TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
+        nsecs_t when, uint32_t policyFlags) {
+    int32_t keyEventAction, keyEventFlags;
+    int32_t keyCode, scanCode, downTime;
+    TouchResult touchResult;
 
-    int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode;
-    int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode;
-    nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
-    int32_t metaState = globalMetaState();
+    { // acquire virtual key lock
+        AutoMutex _l(mVirtualKeyLock);
+
+        if (mCurrentVirtualKey.down) {
+            if (mCurrentTouch.pointerCount == 0) {
+                // Pointer went up while virtual key was down.
+                mCurrentVirtualKey.down = false;
+#if DEBUG_VIRTUAL_KEYS
+                LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+                        mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+                keyEventAction = AKEY_EVENT_ACTION_UP;
+                keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+                touchResult = SKIP_TOUCH;
+                goto DispatchVirtualKey;
+            }
+
+            if (mCurrentTouch.pointerCount == 1) {
+                int32_t x = mCurrentTouch.pointers[0].x;
+                int32_t y = mCurrentTouch.pointers[0].y;
+                const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y);
+                if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
+                    // Pointer is still within the space of the virtual key.
+                    return SKIP_TOUCH;
+                }
+            }
+
+            // Pointer left virtual key area or another pointer also went down.
+            // Send key cancellation and drop the stroke so subsequent motions will be
+            // considered fresh downs.  This is useful when the user swipes away from the
+            // virtual key area into the main display surface.
+            mCurrentVirtualKey.down = false;
+#if DEBUG_VIRTUAL_KEYS
+            LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+                    mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+            keyEventAction = AKEY_EVENT_ACTION_UP;
+            keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+                    | AKEY_EVENT_FLAG_CANCELED;
+            touchResult = DROP_STROKE;
+            goto DispatchVirtualKey;
+        } else {
+            if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) {
+                // Pointer just went down.  Handle off-screen touches, if needed.
+                int32_t x = mCurrentTouch.pointers[0].x;
+                int32_t y = mCurrentTouch.pointers[0].y;
+                if (! isPointInsideSurface(x, y)) {
+                    // If exactly one pointer went down, check for virtual key hit.
+                    // Otherwise we will drop the entire stroke.
+                    if (mCurrentTouch.pointerCount == 1) {
+                        const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y);
+                        if (virtualKey) {
+                            mCurrentVirtualKey.down = true;
+                            mCurrentVirtualKey.downTime = when;
+                            mCurrentVirtualKey.keyCode = virtualKey->keyCode;
+                            mCurrentVirtualKey.scanCode = virtualKey->scanCode;
+#if DEBUG_VIRTUAL_KEYS
+                            LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+                                    mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+                            keyEventAction = AKEY_EVENT_ACTION_DOWN;
+                            keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM
+                                    | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+                            touchResult = SKIP_TOUCH;
+                            goto DispatchVirtualKey;
+                        }
+                    }
+                    return DROP_STROKE;
+                }
+            }
+            return DISPATCH_TOUCH;
+        }
+
+    DispatchVirtualKey:
+        // Collect remaining state needed to dispatch virtual key.
+        keyCode = mCurrentVirtualKey.keyCode;
+        scanCode = mCurrentVirtualKey.scanCode;
+        downTime = mCurrentVirtualKey.downTime;
+    } // release virtual key lock
+
+    // Dispatch virtual key.
+    int32_t metaState = mContext->getGlobalMetaState();
 
     if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
-        mPolicy->virtualKeyDownFeedback();
+        getPolicy()->virtualKeyDownFeedback();
     }
 
-    int32_t policyActions = mPolicy->interceptKey(when, device->id,
+    int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
             keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
 
-    if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
-        mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags,
+    if (applyStandardPolicyActions(when, policyActions)) {
+        getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
                 keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
     }
+    return touchResult;
 }
 
-void InputReader::dispatchTouches(nsecs_t when,
-        InputDevice* device, uint32_t policyFlags) {
-    uint32_t currentPointerCount = device->touchScreen.currentTouch.pointerCount;
-    uint32_t lastPointerCount = device->touchScreen.lastTouch.pointerCount;
+void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+    uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+    uint32_t lastPointerCount = mLastTouch.pointerCount;
     if (currentPointerCount == 0 && lastPointerCount == 0) {
         return; // nothing to do!
     }
 
-    BitSet32 currentIdBits = device->touchScreen.currentTouch.idBits;
-    BitSet32 lastIdBits = device->touchScreen.lastTouch.idBits;
+    BitSet32 currentIdBits = mCurrentTouch.idBits;
+    BitSet32 lastIdBits = mLastTouch.idBits;
 
     if (currentIdBits == lastIdBits) {
         // No pointer id changes so this is a move event.
         // The dispatcher takes care of batching moves so we don't have to deal with that here.
         int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE;
-        dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch,
+        dispatchTouch(when, policyFlags, & mCurrentTouch,
                 currentIdBits, -1, motionEventAction);
     } else {
         // There may be pointers going up and pointers going down at the same time when pointer
@@ -791,7 +1536,7 @@
                 motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP;
             }
 
-            dispatchTouch(when, device, policyFlags, & device->touchScreen.lastTouch,
+            dispatchTouch(when, policyFlags, & mLastTouch,
                     oldActiveIdBits, upId, motionEventAction);
         }
 
@@ -804,40 +1549,24 @@
             int32_t motionEventAction;
             if (oldActiveIdBits.isEmpty()) {
                 motionEventAction = AMOTION_EVENT_ACTION_DOWN;
-                device->touchScreen.downTime = when;
+                mDownTime = when;
             } else {
                 motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN;
             }
 
-            dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch,
+            dispatchTouch(when, policyFlags, & mCurrentTouch,
                     activeIdBits, downId, motionEventAction);
         }
     }
 }
 
-void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags,
-        InputDevice::TouchData* touch, BitSet32 idBits, uint32_t changedId,
+void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
+        TouchData* touch, BitSet32 idBits, uint32_t changedId,
         int32_t motionEventAction) {
-    int32_t orientedWidth, orientedHeight;
-    switch (mDisplayOrientation) {
-    case InputReaderPolicyInterface::ROTATION_90:
-    case InputReaderPolicyInterface::ROTATION_270:
-        orientedWidth = mDisplayHeight;
-        orientedHeight = mDisplayWidth;
-        break;
-    default:
-        orientedWidth = mDisplayWidth;
-        orientedHeight = mDisplayHeight;
-        break;
-    }
-
     uint32_t pointerCount = 0;
     int32_t pointerIds[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
 
-    const InputDevice::TouchScreenState::Precalculated& precalculated =
-            device->touchScreen.precalculated;
-
     // Walk through the the active pointers and map touch screen coordinates (TouchData) into
     // display coordinates (PointerCoords) and adjust for display orientation.
     while (! idBits.isEmpty()) {
@@ -845,55 +1574,57 @@
         idBits.clearBit(id);
         uint32_t index = touch->idToIndex[id];
 
-        float x = float(touch->pointers[index].x
-                - precalculated.xOrigin) * precalculated.xScale;
-        float y = float(touch->pointers[index].y
-                - precalculated.yOrigin) * precalculated.yScale;
-        float pressure = float(touch->pointers[index].pressure
-                - precalculated.pressureOrigin) * precalculated.pressureScale;
-        float size = float(touch->pointers[index].size
-                - precalculated.sizeOrigin) * precalculated.sizeScale;
+        float x = float(touch->pointers[index].x - mXOrigin) * mXScale;
+        float y = float(touch->pointers[index].y - mYOrigin) * mYScale;
+        float pressure = float(touch->pointers[index].pressure - mPressureOrigin) * mPressureScale;
+        float size = float(touch->pointers[index].size - mSizeOrigin) * mSizeScale;
 
-        float orientation = float(touch->pointers[index].orientation)
-                * precalculated.orientationScale;
+        float orientation = float(touch->pointers[index].orientation) * mOrientationScale;
 
-        bool vertical = abs(orientation) <= M_PI / 8;
+        float touchMajor, touchMinor, toolMajor, toolMinor;
+        if (abs(orientation) <= M_PI_4) {
+            // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X.
+            touchMajor = float(touch->pointers[index].touchMajor) * mYScale;
+            touchMinor = float(touch->pointers[index].touchMinor) * mXScale;
+            toolMajor = float(touch->pointers[index].toolMajor) * mYScale;
+            toolMinor = float(touch->pointers[index].toolMinor) * mXScale;
+        } else {
+            // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y.
+            touchMajor = float(touch->pointers[index].touchMajor) * mXScale;
+            touchMinor = float(touch->pointers[index].touchMinor) * mYScale;
+            toolMajor = float(touch->pointers[index].toolMajor) * mXScale;
+            toolMinor = float(touch->pointers[index].toolMinor) * mYScale;
+        }
 
-        switch (mDisplayOrientation) {
+        switch (mSurfaceOrientation) {
         case InputReaderPolicyInterface::ROTATION_90: {
             float xTemp = x;
             x = y;
-            y = mDisplayWidth - xTemp;
-            vertical = ! vertical;
+            y = mOrientedSurfaceWidth - xTemp;
+            orientation -= M_PI_2;
+            if (orientation < - M_PI_2) {
+                orientation += M_PI;
+            }
             break;
         }
         case InputReaderPolicyInterface::ROTATION_180: {
-            x = mDisplayWidth - x;
-            y = mDisplayHeight - y;
+            x = mOrientedSurfaceWidth - x;
+            y = mOrientedSurfaceHeight - y;
+            orientation = - orientation;
             break;
         }
         case InputReaderPolicyInterface::ROTATION_270: {
             float xTemp = x;
-            x = mDisplayHeight - y;
+            x = mOrientedSurfaceHeight - y;
             y = xTemp;
-            vertical = ! vertical;
+            orientation += M_PI_2;
+            if (orientation > M_PI_2) {
+                orientation -= M_PI;
+            }
             break;
         }
         }
 
-        float touchMajor, touchMinor, toolMajor, toolMinor;
-        if (vertical) {
-            touchMajor = float(touch->pointers[index].touchMajor) * precalculated.yScale;
-            touchMinor = float(touch->pointers[index].touchMinor) * precalculated.xScale;
-            toolMajor = float(touch->pointers[index].toolMajor) * precalculated.yScale;
-            toolMinor = float(touch->pointers[index].toolMinor) * precalculated.xScale;
-        } else {
-            touchMajor = float(touch->pointers[index].touchMajor) * precalculated.xScale;
-            touchMinor = float(touch->pointers[index].touchMinor) * precalculated.yScale;
-            toolMajor = float(touch->pointers[index].toolMajor) * precalculated.xScale;
-            toolMinor = float(touch->pointers[index].toolMinor) * precalculated.yScale;
-        }
-
         pointerIds[pointerCount] = int32_t(id);
 
         pointerCoords[pointerCount].x = x;
@@ -919,561 +1650,984 @@
     if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
         if (pointerCoords[0].x <= 0) {
             motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
-        } else if (pointerCoords[0].x >= orientedWidth) {
+        } else if (pointerCoords[0].x >= mOrientedSurfaceWidth) {
             motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
         }
         if (pointerCoords[0].y <= 0) {
             motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
-        } else if (pointerCoords[0].y >= orientedHeight) {
+        } else if (pointerCoords[0].y >= mOrientedSurfaceHeight) {
             motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
         }
     }
 
-    nsecs_t downTime = device->touchScreen.downTime;
-    mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
-            motionEventAction, globalMetaState(), motionEventEdgeFlags,
+    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
+            motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
             pointerCount, pointerIds, pointerCoords,
-            0, 0, downTime);
+            mOrientedXPrecision, mOrientedYPrecision, mDownTime);
 }
 
-void InputReader::onTrackballStateChanged(nsecs_t when,
-        InputDevice* device) {
-    static const uint32_t DELTA_FIELDS =
-            InputDevice::TrackballState::Accumulator::FIELD_REL_X
-            | InputDevice::TrackballState::Accumulator::FIELD_REL_Y;
-
-    /* Refresh display properties so we can trackball moves according to display orientation */
-
-    if (! refreshDisplayProperties()) {
-        return;
+bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+    if (mAxes.x.valid && mAxes.y.valid) {
+        return x >= mAxes.x.minValue && x <= mAxes.x.maxValue
+                && y >= mAxes.y.minValue && y <= mAxes.y.maxValue;
     }
+    return true;
+}
+
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLvk(int32_t x, int32_t y) {
+    for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+        const VirtualKey& virtualKey = mVirtualKeys[i];
+
+#if DEBUG_VIRTUAL_KEYS
+        LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+                "left=%d, top=%d, right=%d, bottom=%d",
+                x, y,
+                virtualKey.keyCode, virtualKey.scanCode,
+                virtualKey.hitLeft, virtualKey.hitTop,
+                virtualKey.hitRight, virtualKey.hitBottom);
+#endif
+
+        if (virtualKey.isHit(x, y)) {
+            return & virtualKey;
+        }
+    }
+
+    return NULL;
+}
+
+void TouchInputMapper::calculatePointerIds() {
+    uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+    uint32_t lastPointerCount = mLastTouch.pointerCount;
+
+    if (currentPointerCount == 0) {
+        // No pointers to assign.
+        mCurrentTouch.idBits.clear();
+    } else if (lastPointerCount == 0) {
+        // All pointers are new.
+        mCurrentTouch.idBits.clear();
+        for (uint32_t i = 0; i < currentPointerCount; i++) {
+            mCurrentTouch.pointers[i].id = i;
+            mCurrentTouch.idToIndex[i] = i;
+            mCurrentTouch.idBits.markBit(i);
+        }
+    } else if (currentPointerCount == 1 && lastPointerCount == 1) {
+        // Only one pointer and no change in count so it must have the same id as before.
+        uint32_t id = mLastTouch.pointers[0].id;
+        mCurrentTouch.pointers[0].id = id;
+        mCurrentTouch.idToIndex[id] = 0;
+        mCurrentTouch.idBits.value = BitSet32::valueForBit(id);
+    } else {
+        // General case.
+        // We build a heap of squared euclidean distances between current and last pointers
+        // associated with the current and last pointer indices.  Then, we find the best
+        // match (by distance) for each current pointer.
+        PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+        uint32_t heapSize = 0;
+        for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+                currentPointerIndex++) {
+            for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+                    lastPointerIndex++) {
+                int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x
+                        - mLastTouch.pointers[lastPointerIndex].x;
+                int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y
+                        - mLastTouch.pointers[lastPointerIndex].y;
+
+                uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+                // Insert new element into the heap (sift up).
+                heap[heapSize].currentPointerIndex = currentPointerIndex;
+                heap[heapSize].lastPointerIndex = lastPointerIndex;
+                heap[heapSize].distance = distance;
+                heapSize += 1;
+            }
+        }
+
+        // Heapify
+        for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+            startIndex -= 1;
+            for (uint32_t parentIndex = startIndex; ;) {
+                uint32_t childIndex = parentIndex * 2 + 1;
+                if (childIndex >= heapSize) {
+                    break;
+                }
+
+                if (childIndex + 1 < heapSize
+                        && heap[childIndex + 1].distance < heap[childIndex].distance) {
+                    childIndex += 1;
+                }
+
+                if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                    break;
+                }
+
+                swap(heap[parentIndex], heap[childIndex]);
+                parentIndex = childIndex;
+            }
+        }
+
+#if DEBUG_POINTER_ASSIGNMENT
+        LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
+        for (size_t i = 0; i < heapSize; i++) {
+            LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                    i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                    heap[i].distance);
+        }
+#endif
+
+        // Pull matches out by increasing order of distance.
+        // To avoid reassigning pointers that have already been matched, the loop keeps track
+        // of which last and current pointers have been matched using the matchedXXXBits variables.
+        // It also tracks the used pointer id bits.
+        BitSet32 matchedLastBits(0);
+        BitSet32 matchedCurrentBits(0);
+        BitSet32 usedIdBits(0);
+        bool first = true;
+        for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
+            for (;;) {
+                if (first) {
+                    // The first time through the loop, we just consume the root element of
+                    // the heap (the one with smallest distance).
+                    first = false;
+                } else {
+                    // Previous iterations consumed the root element of the heap.
+                    // Pop root element off of the heap (sift down).
+                    heapSize -= 1;
+                    assert(heapSize > 0);
+
+                    // Sift down.
+                    heap[0] = heap[heapSize];
+                    for (uint32_t parentIndex = 0; ;) {
+                        uint32_t childIndex = parentIndex * 2 + 1;
+                        if (childIndex >= heapSize) {
+                            break;
+                        }
+
+                        if (childIndex + 1 < heapSize
+                                && heap[childIndex + 1].distance < heap[childIndex].distance) {
+                            childIndex += 1;
+                        }
+
+                        if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                            break;
+                        }
+
+                        swap(heap[parentIndex], heap[childIndex]);
+                        parentIndex = childIndex;
+                    }
+
+#if DEBUG_POINTER_ASSIGNMENT
+                    LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
+                    for (size_t i = 0; i < heapSize; i++) {
+                        LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                                i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                                heap[i].distance);
+                    }
+#endif
+                }
+
+                uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+                if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
+
+                uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+                if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
+
+                matchedCurrentBits.markBit(currentPointerIndex);
+                matchedLastBits.markBit(lastPointerIndex);
+
+                uint32_t id = mLastTouch.pointers[lastPointerIndex].id;
+                mCurrentTouch.pointers[currentPointerIndex].id = id;
+                mCurrentTouch.idToIndex[id] = currentPointerIndex;
+                usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+                LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+                        lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+                break;
+            }
+        }
+
+        // Assign fresh ids to new pointers.
+        if (currentPointerCount > lastPointerCount) {
+            for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
+                uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
+                uint32_t id = usedIdBits.firstUnmarkedBit();
+
+                mCurrentTouch.pointers[currentPointerIndex].id = id;
+                mCurrentTouch.idToIndex[id] = currentPointerIndex;
+                usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+                LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
+                        currentPointerIndex, id);
+#endif
+
+                if (--i == 0) break; // done
+                matchedCurrentBits.markBit(currentPointerIndex);
+            }
+        }
+
+        // Fix id bits.
+        mCurrentTouch.idBits = usedIdBits;
+    }
+}
+
+/* Special hack for devices that have bad screen data: if one of the
+ * points has moved more than a screen height from the last position,
+ * then drop it. */
+bool TouchInputMapper::applyBadTouchFilter() {
+    // This hack requires valid axis parameters.
+    if (! mAxes.y.valid) {
+        return false;
+    }
+
+    uint32_t pointerCount = mCurrentTouch.pointerCount;
+
+    // Nothing to do if there are no points.
+    if (pointerCount == 0) {
+        return false;
+    }
+
+    // Don't do anything if a finger is going down or up.  We run
+    // here before assigning pointer IDs, so there isn't a good
+    // way to do per-finger matching.
+    if (pointerCount != mLastTouch.pointerCount) {
+        return false;
+    }
+
+    // We consider a single movement across more than a 7/16 of
+    // the long size of the screen to be bad.  This was a magic value
+    // determined by looking at the maximum distance it is feasible
+    // to actually move in one sample.
+    int32_t maxDeltaY = mAxes.y.getRange() * 7 / 16;
+
+    // XXX The original code in InputDevice.java included commented out
+    //     code for testing the X axis.  Note that when we drop a point
+    //     we don't actually restore the old X either.  Strange.
+    //     The old code also tries to track when bad points were previously
+    //     detected but it turns out that due to the placement of a "break"
+    //     at the end of the loop, we never set mDroppedBadPoint to true
+    //     so it is effectively dead code.
+    // Need to figure out if the old code is busted or just overcomplicated
+    // but working as intended.
+
+    // Look through all new points and see if any are farther than
+    // acceptable from all previous points.
+    for (uint32_t i = pointerCount; i-- > 0; ) {
+        int32_t y = mCurrentTouch.pointers[i].y;
+        int32_t closestY = INT_MAX;
+        int32_t closestDeltaY = 0;
+
+#if DEBUG_HACKS
+        LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
+#endif
+
+        for (uint32_t j = pointerCount; j-- > 0; ) {
+            int32_t lastY = mLastTouch.pointers[j].y;
+            int32_t deltaY = abs(y - lastY);
+
+#if DEBUG_HACKS
+            LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
+                    j, lastY, deltaY);
+#endif
+
+            if (deltaY < maxDeltaY) {
+                goto SkipSufficientlyClosePoint;
+            }
+            if (deltaY < closestDeltaY) {
+                closestDeltaY = deltaY;
+                closestY = lastY;
+            }
+        }
+
+        // Must not have found a close enough match.
+#if DEBUG_HACKS
+        LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
+                i, y, closestY, closestDeltaY, maxDeltaY);
+#endif
+
+        mCurrentTouch.pointers[i].y = closestY;
+        return true; // XXX original code only corrects one point
+
+    SkipSufficientlyClosePoint: ;
+    }
+
+    // No change.
+    return false;
+}
+
+/* Special hack for devices that have bad screen data: drop points where
+ * the coordinate value for one axis has jumped to the other pointer's location.
+ */
+bool TouchInputMapper::applyJumpyTouchFilter() {
+    // This hack requires valid axis parameters.
+    if (! mAxes.y.valid) {
+        return false;
+    }
+
+    uint32_t pointerCount = mCurrentTouch.pointerCount;
+    if (mLastTouch.pointerCount != pointerCount) {
+#if DEBUG_HACKS
+        LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
+                mLastTouch.pointerCount, pointerCount);
+        for (uint32_t i = 0; i < pointerCount; i++) {
+            LOGD("  Pointer %d (%d, %d)", i,
+                    mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+        }
+#endif
+
+        if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+            if (mLastTouch.pointerCount == 1 && pointerCount == 2) {
+                // Just drop the first few events going from 1 to 2 pointers.
+                // They're bad often enough that they're not worth considering.
+                mCurrentTouch.pointerCount = 1;
+                mJumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+                LOGD("JumpyTouchFilter: Pointer 2 dropped");
+#endif
+                return true;
+            } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) {
+                // The event when we go from 2 -> 1 tends to be messed up too
+                mCurrentTouch.pointerCount = 2;
+                mCurrentTouch.pointers[0] = mLastTouch.pointers[0];
+                mCurrentTouch.pointers[1] = mLastTouch.pointers[1];
+                mJumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+                for (int32_t i = 0; i < 2; i++) {
+                    LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
+                            mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+                }
+#endif
+                return true;
+            }
+        }
+        // Reset jumpy points dropped on other transitions or if limit exceeded.
+        mJumpyTouchFilter.jumpyPointsDropped = 0;
+
+#if DEBUG_HACKS
+        LOGD("JumpyTouchFilter: Transition - drop limit reset");
+#endif
+        return false;
+    }
+
+    // We have the same number of pointers as last time.
+    // A 'jumpy' point is one where the coordinate value for one axis
+    // has jumped to the other pointer's location. No need to do anything
+    // else if we only have one pointer.
+    if (pointerCount < 2) {
+        return false;
+    }
+
+    if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
+        int jumpyEpsilon = mAxes.y.getRange() / JUMPY_EPSILON_DIVISOR;
+
+        // We only replace the single worst jumpy point as characterized by pointer distance
+        // in a single axis.
+        int32_t badPointerIndex = -1;
+        int32_t badPointerReplacementIndex = -1;
+        int32_t badPointerDistance = INT_MIN; // distance to be corrected
+
+        for (uint32_t i = pointerCount; i-- > 0; ) {
+            int32_t x = mCurrentTouch.pointers[i].x;
+            int32_t y = mCurrentTouch.pointers[i].y;
+
+#if DEBUG_HACKS
+            LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
+#endif
+
+            // Check if a touch point is too close to another's coordinates
+            bool dropX = false, dropY = false;
+            for (uint32_t j = 0; j < pointerCount; j++) {
+                if (i == j) {
+                    continue;
+                }
+
+                if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) {
+                    dropX = true;
+                    break;
+                }
+
+                if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) {
+                    dropY = true;
+                    break;
+                }
+            }
+            if (! dropX && ! dropY) {
+                continue; // not jumpy
+            }
+
+            // Find a replacement candidate by comparing with older points on the
+            // complementary (non-jumpy) axis.
+            int32_t distance = INT_MIN; // distance to be corrected
+            int32_t replacementIndex = -1;
+
+            if (dropX) {
+                // X looks too close.  Find an older replacement point with a close Y.
+                int32_t smallestDeltaY = INT_MAX;
+                for (uint32_t j = 0; j < pointerCount; j++) {
+                    int32_t deltaY = abs(y - mLastTouch.pointers[j].y);
+                    if (deltaY < smallestDeltaY) {
+                        smallestDeltaY = deltaY;
+                        replacementIndex = j;
+                    }
+                }
+                distance = abs(x - mLastTouch.pointers[replacementIndex].x);
+            } else {
+                // Y looks too close.  Find an older replacement point with a close X.
+                int32_t smallestDeltaX = INT_MAX;
+                for (uint32_t j = 0; j < pointerCount; j++) {
+                    int32_t deltaX = abs(x - mLastTouch.pointers[j].x);
+                    if (deltaX < smallestDeltaX) {
+                        smallestDeltaX = deltaX;
+                        replacementIndex = j;
+                    }
+                }
+                distance = abs(y - mLastTouch.pointers[replacementIndex].y);
+            }
+
+            // If replacing this pointer would correct a worse error than the previous ones
+            // considered, then use this replacement instead.
+            if (distance > badPointerDistance) {
+                badPointerIndex = i;
+                badPointerReplacementIndex = replacementIndex;
+                badPointerDistance = distance;
+            }
+        }
+
+        // Correct the jumpy pointer if one was found.
+        if (badPointerIndex >= 0) {
+#if DEBUG_HACKS
+            LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
+                    badPointerIndex,
+                    mLastTouch.pointers[badPointerReplacementIndex].x,
+                    mLastTouch.pointers[badPointerReplacementIndex].y);
+#endif
+
+            mCurrentTouch.pointers[badPointerIndex].x =
+                    mLastTouch.pointers[badPointerReplacementIndex].x;
+            mCurrentTouch.pointers[badPointerIndex].y =
+                    mLastTouch.pointers[badPointerReplacementIndex].y;
+            mJumpyTouchFilter.jumpyPointsDropped += 1;
+            return true;
+        }
+    }
+
+    mJumpyTouchFilter.jumpyPointsDropped = 0;
+    return false;
+}
+
+/* Special hack for devices that have bad screen data: aggregate and
+ * compute averages of the coordinate data, to reduce the amount of
+ * jitter seen by applications. */
+void TouchInputMapper::applyAveragingTouchFilter() {
+    for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) {
+        uint32_t id = mCurrentTouch.pointers[currentIndex].id;
+        int32_t x = mCurrentTouch.pointers[currentIndex].x;
+        int32_t y = mCurrentTouch.pointers[currentIndex].y;
+        int32_t pressure = mCurrentTouch.pointers[currentIndex].pressure;
+
+        if (mLastTouch.idBits.hasBit(id)) {
+            // Pointer was down before and is still down now.
+            // Compute average over history trace.
+            uint32_t start = mAveragingTouchFilter.historyStart[id];
+            uint32_t end = mAveragingTouchFilter.historyEnd[id];
+
+            int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x;
+            int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y;
+            uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+#if DEBUG_HACKS
+            LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
+                    id, distance);
+#endif
+
+            if (distance < AVERAGING_DISTANCE_LIMIT) {
+                // Increment end index in preparation for recording new historical data.
+                end += 1;
+                if (end > AVERAGING_HISTORY_SIZE) {
+                    end = 0;
+                }
+
+                // If the end index has looped back to the start index then we have filled
+                // the historical trace up to the desired size so we drop the historical
+                // data at the start of the trace.
+                if (end == start) {
+                    start += 1;
+                    if (start > AVERAGING_HISTORY_SIZE) {
+                        start = 0;
+                    }
+                }
+
+                // Add the raw data to the historical trace.
+                mAveragingTouchFilter.historyStart[id] = start;
+                mAveragingTouchFilter.historyEnd[id] = end;
+                mAveragingTouchFilter.historyData[end].pointers[id].x = x;
+                mAveragingTouchFilter.historyData[end].pointers[id].y = y;
+                mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure;
+
+                // Average over all historical positions in the trace by total pressure.
+                int32_t averagedX = 0;
+                int32_t averagedY = 0;
+                int32_t totalPressure = 0;
+                for (;;) {
+                    int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x;
+                    int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y;
+                    int32_t historicalPressure = mAveragingTouchFilter.historyData[start]
+                            .pointers[id].pressure;
+
+                    averagedX += historicalX * historicalPressure;
+                    averagedY += historicalY * historicalPressure;
+                    totalPressure += historicalPressure;
+
+                    if (start == end) {
+                        break;
+                    }
+
+                    start += 1;
+                    if (start > AVERAGING_HISTORY_SIZE) {
+                        start = 0;
+                    }
+                }
+
+                averagedX /= totalPressure;
+                averagedY /= totalPressure;
+
+#if DEBUG_HACKS
+                LOGD("AveragingTouchFilter: Pointer id %d - "
+                        "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
+                        averagedX, averagedY);
+#endif
+
+                mCurrentTouch.pointers[currentIndex].x = averagedX;
+                mCurrentTouch.pointers[currentIndex].y = averagedY;
+            } else {
+#if DEBUG_HACKS
+                LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
+#endif
+            }
+        } else {
+#if DEBUG_HACKS
+            LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
+#endif
+        }
+
+        // Reset pointer history.
+        mAveragingTouchFilter.historyStart[id] = 0;
+        mAveragingTouchFilter.historyEnd[id] = 0;
+        mAveragingTouchFilter.historyData[0].pointers[id].x = x;
+        mAveragingTouchFilter.historyData[0].pointers[id].y = y;
+        mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure;
+    }
+}
+
+int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    { // acquire virtual key lock
+        AutoMutex _l(mVirtualKeyLock);
+
+        if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
+            return AKEY_STATE_VIRTUAL;
+        }
+
+        for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+            const VirtualKey& virtualKey = mVirtualKeys[i];
+            if (virtualKey.keyCode == keyCode) {
+                return AKEY_STATE_UP;
+            }
+        }
+    } // release virtual key lock
+
+    return AKEY_STATE_UNKNOWN;
+}
+
+int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    { // acquire virtual key lock
+        AutoMutex _l(mVirtualKeyLock);
+
+        if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
+            return AKEY_STATE_VIRTUAL;
+        }
+
+        for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+            const VirtualKey& virtualKey = mVirtualKeys[i];
+            if (virtualKey.scanCode == scanCode) {
+                return AKEY_STATE_UP;
+            }
+        }
+    } // release virtual key lock
+
+    return AKEY_STATE_UNKNOWN;
+}
+
+bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+        const int32_t* keyCodes, uint8_t* outFlags) {
+    { // acquire virtual key lock
+        AutoMutex _l(mVirtualKeyLock);
+
+        for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+            const VirtualKey& virtualKey = mVirtualKeys[i];
+
+            for (size_t i = 0; i < numCodes; i++) {
+                if (virtualKey.keyCode == keyCodes[i]) {
+                    outFlags[i] = 1;
+                }
+            }
+        }
+    } // release virtual key lock
+
+    return true;
+}
+
+
+// --- SingleTouchInputMapper ---
+
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+        TouchInputMapper(device, associatedDisplayId) {
+    initialize();
+}
+
+SingleTouchInputMapper::~SingleTouchInputMapper() {
+}
+
+void SingleTouchInputMapper::initialize() {
+    mAccumulator.clear();
+
+    mDown = false;
+    mX = 0;
+    mY = 0;
+    mPressure = 0;
+    mSize = 0;
+}
+
+void SingleTouchInputMapper::reset() {
+    TouchInputMapper::reset();
+
+    // Reinitialize.
+    initialize();
+ }
+
+void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+    case EV_KEY:
+        switch (rawEvent->scanCode) {
+        case BTN_TOUCH:
+            mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
+            mAccumulator.btnTouch = rawEvent->value != 0;
+
+            sync(rawEvent->when);
+            mAccumulator.clear();
+            break;
+        }
+        break;
+
+    case EV_ABS:
+        switch (rawEvent->scanCode) {
+        case ABS_X:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_X;
+            mAccumulator.absX = rawEvent->value;
+            break;
+        case ABS_Y:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_Y;
+            mAccumulator.absY = rawEvent->value;
+            break;
+        case ABS_PRESSURE:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE;
+            mAccumulator.absPressure = rawEvent->value;
+            break;
+        case ABS_TOOL_WIDTH:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH;
+            mAccumulator.absToolWidth = rawEvent->value;
+            break;
+        }
+        break;
+
+    case EV_SYN:
+        switch (rawEvent->scanCode) {
+        case SYN_REPORT:
+            if (mAccumulator.isDirty()) {
+                sync(rawEvent->when);
+                mAccumulator.clear();
+            }
+            break;
+        }
+        break;
+    }
+}
+
+void SingleTouchInputMapper::sync(nsecs_t when) {
+    /* Update device state */
+
+    uint32_t fields = mAccumulator.fields;
+
+    if (fields & Accumulator::FIELD_BTN_TOUCH) {
+        mDown = mAccumulator.btnTouch;
+    }
+
+    if (fields & Accumulator::FIELD_ABS_X) {
+        mX = mAccumulator.absX;
+    }
+
+    if (fields & Accumulator::FIELD_ABS_Y) {
+        mY = mAccumulator.absY;
+    }
+
+    if (fields & Accumulator::FIELD_ABS_PRESSURE) {
+        mPressure = mAccumulator.absPressure;
+    }
+
+    if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) {
+        mSize = mAccumulator.absToolWidth;
+    }
+
+    mCurrentTouch.clear();
+
+    if (mDown) {
+        mCurrentTouch.pointerCount = 1;
+        mCurrentTouch.pointers[0].id = 0;
+        mCurrentTouch.pointers[0].x = mX;
+        mCurrentTouch.pointers[0].y = mY;
+        mCurrentTouch.pointers[0].pressure = mPressure;
+        mCurrentTouch.pointers[0].size = mSize;
+        mCurrentTouch.pointers[0].touchMajor = mPressure;
+        mCurrentTouch.pointers[0].touchMinor = mPressure;
+        mCurrentTouch.pointers[0].toolMajor = mSize;
+        mCurrentTouch.pointers[0].toolMinor = mSize;
+        mCurrentTouch.pointers[0].orientation = 0;
+        mCurrentTouch.idToIndex[0] = 0;
+        mCurrentTouch.idBits.markBit(0);
+    }
+
+    syncTouch(when, true);
+}
+
+void SingleTouchInputMapper::configureAxes() {
+    TouchInputMapper::configureAxes();
+
+    // The axes are aliased to take into account the manner in which they are presented
+    // as part of the TouchData during the sync.
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mAxes.x);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mAxes.y);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size);
+
+    mAxes.touchMajor = mAxes.pressure;
+    mAxes.touchMinor = mAxes.pressure;
+    mAxes.toolMajor = mAxes.size;
+    mAxes.toolMinor = mAxes.size;
+}
+
+
+// --- MultiTouchInputMapper ---
+
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+        TouchInputMapper(device, associatedDisplayId) {
+    initialize();
+}
+
+MultiTouchInputMapper::~MultiTouchInputMapper() {
+}
+
+void MultiTouchInputMapper::initialize() {
+    mAccumulator.clear();
+}
+
+void MultiTouchInputMapper::reset() {
+    TouchInputMapper::reset();
+
+    // Reinitialize.
+    initialize();
+}
+
+void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+    case EV_ABS: {
+        uint32_t pointerIndex = mAccumulator.pointerCount;
+        Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex];
+
+        switch (rawEvent->scanCode) {
+        case ABS_MT_POSITION_X:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X;
+            pointer->absMTPositionX = rawEvent->value;
+            break;
+        case ABS_MT_POSITION_Y:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y;
+            pointer->absMTPositionY = rawEvent->value;
+            break;
+        case ABS_MT_TOUCH_MAJOR:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
+            pointer->absMTTouchMajor = rawEvent->value;
+            break;
+        case ABS_MT_TOUCH_MINOR:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
+            pointer->absMTTouchMinor = rawEvent->value;
+            break;
+        case ABS_MT_WIDTH_MAJOR:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+            pointer->absMTWidthMajor = rawEvent->value;
+            break;
+        case ABS_MT_WIDTH_MINOR:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
+            pointer->absMTWidthMinor = rawEvent->value;
+            break;
+        case ABS_MT_ORIENTATION:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION;
+            pointer->absMTOrientation = rawEvent->value;
+            break;
+        case ABS_MT_TRACKING_ID:
+            pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID;
+            pointer->absMTTrackingId = rawEvent->value;
+            break;
+        }
+        break;
+    }
+
+    case EV_SYN:
+        switch (rawEvent->scanCode) {
+        case SYN_MT_REPORT: {
+            // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
+            uint32_t pointerIndex = mAccumulator.pointerCount;
+
+            if (mAccumulator.pointers[pointerIndex].fields) {
+                if (pointerIndex == MAX_POINTERS) {
+                    LOGW("MultiTouch device driver returned more than maximum of %d pointers.",
+                            MAX_POINTERS);
+                } else {
+                    pointerIndex += 1;
+                    mAccumulator.pointerCount = pointerIndex;
+                }
+            }
+
+            mAccumulator.pointers[pointerIndex].clear();
+            break;
+        }
+
+        case SYN_REPORT:
+            if (mAccumulator.isDirty()) {
+                sync(rawEvent->when);
+                mAccumulator.clear();
+            }
+            break;
+        }
+        break;
+    }
+}
+
+void MultiTouchInputMapper::sync(nsecs_t when) {
+    static const uint32_t REQUIRED_FIELDS =
+            Accumulator::FIELD_ABS_MT_POSITION_X
+            | Accumulator::FIELD_ABS_MT_POSITION_Y
+            | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
+            | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
 
     /* Update device state */
 
-    uint32_t fields = device->trackball.accumulator.fields;
-    bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
-    bool deltaChanged = fields & DELTA_FIELDS;
+    uint32_t inCount = mAccumulator.pointerCount;
+    uint32_t outCount = 0;
+    bool havePointerIds = true;
 
-    bool down;
-    if (downChanged) {
-        if (device->trackball.accumulator.btnMouse) {
-            device->trackball.current.down = true;
-            device->trackball.current.downTime = when;
-            down = true;
-        } else {
-            device->trackball.current.down = false;
-            down = false;
-        }
-    } else {
-        down = device->trackball.current.down;
-    }
+    mCurrentTouch.clear();
 
-    /* Apply policy */
+    for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
+        uint32_t fields = mAccumulator.pointers[inIndex].fields;
 
-    int32_t policyActions = mPolicy->interceptTrackball(when, downChanged, down, deltaChanged);
-
-    uint32_t policyFlags = 0;
-    if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
-        return; // event dropped
-    }
-
-    /* Enqueue motion event for dispatch */
-
-    int32_t motionEventAction;
-    if (downChanged) {
-        motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
-    } else {
-        motionEventAction = AMOTION_EVENT_ACTION_MOVE;
-    }
-
-    int32_t pointerId = 0;
-    PointerCoords pointerCoords;
-    pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X
-            ? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0;
-    pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y
-            ? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0;
-    pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise.
-    pointerCoords.size = 0;
-    pointerCoords.touchMajor = 0;
-    pointerCoords.touchMinor = 0;
-    pointerCoords.toolMajor = 0;
-    pointerCoords.toolMinor = 0;
-    pointerCoords.orientation = 0;
-
-    float temp;
-    switch (mDisplayOrientation) {
-    case InputReaderPolicyInterface::ROTATION_90:
-        temp = pointerCoords.x;
-        pointerCoords.x = pointerCoords.y;
-        pointerCoords.y = - temp;
-        break;
-
-    case InputReaderPolicyInterface::ROTATION_180:
-        pointerCoords.x = - pointerCoords.x;
-        pointerCoords.y = - pointerCoords.y;
-        break;
-
-    case InputReaderPolicyInterface::ROTATION_270:
-        temp = pointerCoords.x;
-        pointerCoords.x = - pointerCoords.y;
-        pointerCoords.y = temp;
-        break;
-    }
-
-    mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TRACKBALL, policyFlags,
-            motionEventAction, globalMetaState(), AMOTION_EVENT_EDGE_FLAG_NONE,
-            1, & pointerId, & pointerCoords,
-            device->trackball.precalculated.xPrecision,
-            device->trackball.precalculated.yPrecision,
-            device->trackball.current.downTime);
-}
-
-void InputReader::onConfigurationChanged(nsecs_t when) {
-    // Reset global meta state because it depends on the list of all configured devices.
-    resetGlobalMetaState();
-
-    // Reset virtual keys, just in case.
-    updateExportedVirtualKeyState();
-
-    // Update input configuration.
-    updateExportedInputConfiguration();
-
-    // Enqueue configuration changed.
-    mDispatcher->notifyConfigurationChanged(when);
-}
-
-bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when,
-        int32_t policyActions, uint32_t* policyFlags) {
-    if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
-        mDispatcher->notifyAppSwitchComing(when);
-    }
-
-    if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
-        *policyFlags |= POLICY_FLAG_WOKE_HERE;
-    }
-
-    if (policyActions & InputReaderPolicyInterface::ACTION_BRIGHT_HERE) {
-        *policyFlags |= POLICY_FLAG_BRIGHT_HERE;
-    }
-
-    return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
-}
-
-void InputReader::resetDisplayProperties() {
-    mDisplayWidth = mDisplayHeight = -1;
-    mDisplayOrientation = -1;
-}
-
-bool InputReader::refreshDisplayProperties() {
-    int32_t newWidth, newHeight, newOrientation;
-    if (mPolicy->getDisplayInfo(0, & newWidth, & newHeight, & newOrientation)) {
-        if (newWidth != mDisplayWidth || newHeight != mDisplayHeight) {
-            LOGD("Display size changed from %dx%d to %dx%d, updating device configuration",
-                    mDisplayWidth, mDisplayHeight, newWidth, newHeight);
-
-            mDisplayWidth = newWidth;
-            mDisplayHeight = newHeight;
-
-            for (size_t i = 0; i < mDevices.size(); i++) {
-                configureDeviceForCurrentDisplaySize(mDevices.valueAt(i));
-            }
+        if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
+#if DEBUG_POINTERS
+            LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
+                    inIndex, fields);
+            continue;
+#endif
         }
 
-        if (newOrientation != mDisplayOrientation) {
-            LOGD("Display orientation changed to %d", mDisplayOrientation);
-
-            mDisplayOrientation = newOrientation;
-        }
-        return true;
-    } else {
-        resetDisplayProperties();
-        return false;
-    }
-}
-
-InputDevice* InputReader::getDevice(int32_t deviceId) {
-    ssize_t index = mDevices.indexOfKey(deviceId);
-    return index >= 0 ? mDevices.valueAt((size_t) index) : NULL;
-}
-
-InputDevice* InputReader::getNonIgnoredDevice(int32_t deviceId) {
-    InputDevice* device = getDevice(deviceId);
-    return device && ! device->ignored ? device : NULL;
-}
-
-void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
-    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
-    String8 name = mEventHub->getDeviceName(deviceId);
-    InputDevice* device = new InputDevice(deviceId, classes, name);
-
-    if (classes != 0) {
-        LOGI("Device added: id=0x%x, name=%s, classes=%02x", device->id,
-                device->name.string(), device->classes);
-
-        configureDevice(device);
-    } else {
-        LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", device->id,
-                device->name.string());
-
-        device->ignored = true;
-    }
-
-    device->reset();
-
-    mDevices.add(deviceId, device);
-
-    if (! device->ignored) {
-        onConfigurationChanged(when);
-    }
-}
-
-void InputReader::removeDevice(nsecs_t when, InputDevice* device) {
-    mDevices.removeItem(device->id);
-
-    if (! device->ignored) {
-        LOGI("Device removed: id=0x%x, name=%s, classes=%02x", device->id,
-                device->name.string(), device->classes);
-
-        onConfigurationChanged(when);
-    } else {
-        LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", device->id,
-                device->name.string());
-    }
-
-    delete device;
-}
-
-void InputReader::configureDevice(InputDevice* device) {
-    if (device->isMultiTouchScreen()) {
-        configureAbsoluteAxisInfo(device, ABS_MT_POSITION_X, "X",
-                & device->touchScreen.parameters.xAxis);
-        configureAbsoluteAxisInfo(device, ABS_MT_POSITION_Y, "Y",
-                & device->touchScreen.parameters.yAxis);
-        configureAbsoluteAxisInfo(device, ABS_MT_TOUCH_MAJOR, "Pressure",
-                & device->touchScreen.parameters.pressureAxis);
-        configureAbsoluteAxisInfo(device, ABS_MT_WIDTH_MAJOR, "Size",
-                & device->touchScreen.parameters.sizeAxis);
-        configureAbsoluteAxisInfo(device, ABS_MT_ORIENTATION, "Orientation",
-                & device->touchScreen.parameters.orientationAxis);
-    } else if (device->isSingleTouchScreen()) {
-        configureAbsoluteAxisInfo(device, ABS_X, "X",
-                & device->touchScreen.parameters.xAxis);
-        configureAbsoluteAxisInfo(device, ABS_Y, "Y",
-                & device->touchScreen.parameters.yAxis);
-        configureAbsoluteAxisInfo(device, ABS_PRESSURE, "Pressure",
-                & device->touchScreen.parameters.pressureAxis);
-        configureAbsoluteAxisInfo(device, ABS_TOOL_WIDTH, "Size",
-                & device->touchScreen.parameters.sizeAxis);
-        device->touchScreen.parameters.orientationAxis.valid = false;
-    }
-
-    if (device->isTouchScreen()) {
-        device->touchScreen.parameters.useBadTouchFilter =
-                mPolicy->filterTouchEvents();
-        device->touchScreen.parameters.useAveragingTouchFilter =
-                mPolicy->filterTouchEvents();
-        device->touchScreen.parameters.useJumpyTouchFilter =
-                mPolicy->filterJumpyTouchEvents();
-
-        if (device->touchScreen.parameters.pressureAxis.valid) {
-            device->touchScreen.precalculated.pressureOrigin =
-                    device->touchScreen.parameters.pressureAxis.minValue;
-            device->touchScreen.precalculated.pressureScale =
-                    1.0f / device->touchScreen.parameters.pressureAxis.range;
-        } else {
-            device->touchScreen.precalculated.pressureOrigin = 0;
-            device->touchScreen.precalculated.pressureScale = 1.0f;
-        }
-
-        if (device->touchScreen.parameters.sizeAxis.valid) {
-            device->touchScreen.precalculated.sizeOrigin =
-                    device->touchScreen.parameters.sizeAxis.minValue;
-            device->touchScreen.precalculated.sizeScale =
-                    1.0f / device->touchScreen.parameters.sizeAxis.range;
-        } else {
-            device->touchScreen.precalculated.sizeOrigin = 0;
-            device->touchScreen.precalculated.sizeScale = 1.0f;
-        }
-
-        if (device->touchScreen.parameters.orientationAxis.valid
-                && device->touchScreen.parameters.orientationAxis.maxValue > 0) {
-            device->touchScreen.precalculated.orientationScale =
-                    M_PI_4 / device->touchScreen.parameters.orientationAxis.maxValue;
-        } else {
-            device->touchScreen.precalculated.orientationScale = 0.0f;
-        }
-    }
-
-    if (device->isTrackball()) {
-        device->trackball.precalculated.xPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-        device->trackball.precalculated.yPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-        device->trackball.precalculated.xScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-        device->trackball.precalculated.yScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-    }
-
-    configureDeviceForCurrentDisplaySize(device);
-}
-
-void InputReader::configureDeviceForCurrentDisplaySize(InputDevice* device) {
-    if (device->isTouchScreen()) {
-        if (device->touchScreen.parameters.xAxis.valid
-                && device->touchScreen.parameters.yAxis.valid) {
-            device->touchScreen.precalculated.xOrigin =
-                    device->touchScreen.parameters.xAxis.minValue;
-            device->touchScreen.precalculated.yOrigin =
-                    device->touchScreen.parameters.yAxis.minValue;
-
-            if (mDisplayWidth < 0) {
-                LOGD("Skipping part of touch screen configuration since display size is unknown.");
-
-                device->touchScreen.precalculated.xScale = 1.0f;
-                device->touchScreen.precalculated.yScale = 1.0f;
-            } else {
-                LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id,
-                        device->name.string());
-
-                device->touchScreen.precalculated.xScale =
-                        float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range;
-                device->touchScreen.precalculated.yScale =
-                        float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range;
-
-                configureVirtualKeys(device);
-            }
-        } else {
-            device->touchScreen.precalculated.xOrigin = 0;
-            device->touchScreen.precalculated.xScale = 1.0f;
-            device->touchScreen.precalculated.yOrigin = 0;
-            device->touchScreen.precalculated.yScale = 1.0f;
-        }
-    }
-}
-
-void InputReader::configureVirtualKeys(InputDevice* device) {
-    assert(device->touchScreen.parameters.xAxis.valid
-            && device->touchScreen.parameters.yAxis.valid);
-
-    device->touchScreen.virtualKeys.clear();
-
-    Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
-    mPolicy->getVirtualKeyDefinitions(device->name, virtualKeyDefinitions);
-    if (virtualKeyDefinitions.size() == 0) {
-        return;
-    }
-
-    device->touchScreen.virtualKeys.setCapacity(virtualKeyDefinitions.size());
-
-    int32_t touchScreenLeft = device->touchScreen.parameters.xAxis.minValue;
-    int32_t touchScreenTop = device->touchScreen.parameters.yAxis.minValue;
-    int32_t touchScreenWidth = device->touchScreen.parameters.xAxis.range;
-    int32_t touchScreenHeight = device->touchScreen.parameters.yAxis.range;
-
-    for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
-        const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
-                virtualKeyDefinitions[i];
-
-        device->touchScreen.virtualKeys.add();
-        InputDevice::VirtualKey& virtualKey =
-                device->touchScreen.virtualKeys.editTop();
-
-        virtualKey.scanCode = virtualKeyDefinition.scanCode;
-        int32_t keyCode;
-        uint32_t flags;
-        if (mEventHub->scancodeToKeycode(device->id, virtualKey.scanCode,
-                & keyCode, & flags)) {
-            LOGW("  VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
-            device->touchScreen.virtualKeys.pop(); // drop the key
+        if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) {
+            // Pointer is not down.  Drop it.
             continue;
         }
 
-        virtualKey.keyCode = keyCode;
-        virtualKey.flags = flags;
+        mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX;
+        mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY;
 
-        // convert the key definition's display coordinates into touch coordinates for a hit box
-        int32_t halfWidth = virtualKeyDefinition.width / 2;
-        int32_t halfHeight = virtualKeyDefinition.height / 2;
+        mCurrentTouch.pointers[outCount].touchMajor =
+                mAccumulator.pointers[inIndex].absMTTouchMajor;
+        mCurrentTouch.pointers[outCount].touchMinor =
+                (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
+                ? mAccumulator.pointers[inIndex].absMTTouchMinor
+                        : mAccumulator.pointers[inIndex].absMTTouchMajor;
 
-        virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
-                * touchScreenWidth / mDisplayWidth + touchScreenLeft;
-        virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
-                * touchScreenWidth / mDisplayWidth + touchScreenLeft;
-        virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
-                * touchScreenHeight / mDisplayHeight + touchScreenTop;
-        virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
-                * touchScreenHeight / mDisplayHeight + touchScreenTop;
+        mCurrentTouch.pointers[outCount].toolMajor =
+                mAccumulator.pointers[inIndex].absMTWidthMajor;
+        mCurrentTouch.pointers[outCount].toolMinor =
+                (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
+                ? mAccumulator.pointers[inIndex].absMTWidthMinor
+                        : mAccumulator.pointers[inIndex].absMTWidthMajor;
 
-        LOGI("  VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
-                virtualKey.scanCode, virtualKey.keyCode,
-                virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
-    }
-}
+        mCurrentTouch.pointers[outCount].orientation =
+                (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
+                ? mAccumulator.pointers[inIndex].absMTOrientation : 0;
 
-void InputReader::configureAbsoluteAxisInfo(InputDevice* device,
-        int axis, const char* name, InputDevice::AbsoluteAxisInfo* out) {
-    if (! mEventHub->getAbsoluteInfo(device->id, axis,
-            & out->minValue, & out->maxValue, & out->flat, &out->fuzz)) {
-        out->range = out->maxValue - out->minValue;
-        if (out->range != 0) {
-            LOGI("  %s: min=%d max=%d flat=%d fuzz=%d",
-                    name, out->minValue, out->maxValue, out->flat, out->fuzz);
-            out->valid = true;
-            return;
-        }
-    }
+        // Derive an approximation of pressure and size.
+        // FIXME assignment of pressure may be incorrect, probably better to let
+        // pressure = touch / width.  Later on we pass width to MotionEvent as a size, which
+        // isn't quite right either.  Should be using touch for that.
+        mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor;
+        mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor;
 
-    out->valid = false;
-    out->minValue = 0;
-    out->maxValue = 0;
-    out->flat = 0;
-    out->fuzz = 0;
-    out->range = 0;
-    LOGI("  %s: unknown axis values, marking as invalid", name);
-}
+        if (havePointerIds) {
+            if (fields & Accumulator::
+                    FIELD_ABS_MT_TRACKING_ID) {
+                uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId);
 
-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;
-}
-
-int32_t InputReader::globalMetaState() {
-    if (mGlobalMetaState == -1) {
-        mGlobalMetaState = 0;
-        for (size_t i = 0; i < mDevices.size(); i++) {
-            InputDevice* device = mDevices.valueAt(i);
-            if (device->isKeyboard()) {
-                mGlobalMetaState |= device->keyboard.current.metaState;
+                if (id > MAX_POINTER_ID) {
+#if DEBUG_POINTERS
+                    LOGD("Pointers: Ignoring driver provided pointer id %d because "
+                            "it is larger than max supported id %d for optimizations",
+                            id, MAX_POINTER_ID);
+#endif
+                    havePointerIds = false;
+                }
+                else {
+                    mCurrentTouch.pointers[outCount].id = id;
+                    mCurrentTouch.idToIndex[id] = outCount;
+                    mCurrentTouch.idBits.markBit(id);
+                }
+            } else {
+                havePointerIds = false;
             }
         }
-    }
-    return mGlobalMetaState;
-}
 
-void InputReader::updateExportedVirtualKeyState() {
-    int32_t keyCode = -1, scanCode = -1;
-
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        if (device->isTouchScreen()) {
-            if (device->touchScreen.currentVirtualKey.status
-                    == InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN) {
-                keyCode = device->touchScreen.currentVirtualKey.keyCode;
-                scanCode = device->touchScreen.currentVirtualKey.scanCode;
-            }
-        }
+        outCount += 1;
     }
 
-    { // acquire exported state lock
-        AutoMutex _l(mExportedStateLock);
+    mCurrentTouch.pointerCount = outCount;
 
-        mExportedVirtualKeyCode = keyCode;
-        mExportedVirtualScanCode = scanCode;
-    } // release exported state lock
+    syncTouch(when, havePointerIds);
 }
 
-bool InputReader::getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const {
-    { // acquire exported state lock
-        AutoMutex _l(mExportedStateLock);
+void MultiTouchInputMapper::configureAxes() {
+    TouchInputMapper::configureAxes();
 
-        *outKeyCode = mExportedVirtualKeyCode;
-        *outScanCode = mExportedVirtualScanCode;
-        return mExportedVirtualKeyCode != -1;
-    } // release exported state lock
-}
+    // The axes are aliased to take into account the manner in which they are presented
+    // as part of the TouchData during the sync.
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mAxes.x);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mAxes.y);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mAxes.touchMajor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mAxes.touchMinor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation);
 
-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;
-        }
+    if (! mAxes.touchMinor.valid) {
+        mAxes.touchMinor = mAxes.touchMajor;
     }
 
-    { // acquire exported state lock
-        AutoMutex _l(mExportedStateLock);
+    if (! mAxes.toolMinor.valid) {
+        mAxes.toolMinor = mAxes.toolMajor;
+    }
 
-        mExportedInputConfiguration.touchScreen = touchScreenConfig;
-        mExportedInputConfiguration.keyboard = keyboardConfig;
-        mExportedInputConfiguration.navigation = navigationConfig;
-    } // release exported state lock
+    mAxes.pressure = mAxes.touchMajor;
+    mAxes.size = mAxes.toolMajor;
 }
 
-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 AKEY_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 AKEY_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);
-}
-
-
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
-        Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
-    mReader->loopOnce();
-    return true;
-}
 
 } // namespace android
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 59bf711..a82282d 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -21,14 +21,21 @@
 #include <ui/Input.h>
 #include <ui/InputTransport.h>
 #include <utils/PollLoop.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
 
 #include <android_runtime/android_app_NativeActivity.h>
 
 #include <poll.h>
+#include <errno.h>
 
 using android::InputEvent;
 using android::KeyEvent;
 using android::MotionEvent;
+using android::InputDeviceInfo;
+using android::InputDeviceProxy;
+using android::sp;
+using android::Vector;
 
 int32_t AInputEvent_getType(const AInputEvent* event) {
     return static_cast<const InputEvent*>(event)->getType();
@@ -263,3 +270,74 @@
 void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) {
     queue->finishEvent(event, handled != 0);
 }
+
+
+int32_t AInputDevice_getDeviceIds(int32_t* idBuf, size_t nMax, size_t* nActual) {
+    Vector<int32_t> ids;
+    InputDeviceProxy::getDeviceIds(ids);
+
+    if (nActual) {
+        *nActual = ids.size();
+    }
+
+    if (idBuf && ids.size() < nMax) {
+        memcpy(idBuf, ids.array(), ids.size() * sizeof(int32_t));
+        return 0;
+    }
+
+    return -ENOMEM;
+}
+
+AInputDevice* AInputDevice_acquire(int32_t deviceId) {
+    sp<InputDeviceProxy> proxy(InputDeviceProxy::getDevice(deviceId));
+    if (proxy == NULL) {
+        return NULL;
+    }
+    proxy->incStrong((void*)AInputDevice_acquire);
+    return static_cast<AInputDevice*>(proxy.get());
+}
+
+void AInputDevice_release(AInputDevice* device) {
+    if (device) {
+        InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+        proxy->decStrong((void*)AInputDevice_acquire);
+    }
+}
+
+const char* AInputDevice_getName(AInputDevice* device) {
+    InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+    return proxy->getInfo()->getName().string();
+}
+
+uint32_t AInputDevice_getSources(AInputDevice* device) {
+    InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+    return proxy->getInfo()->getSources();
+}
+
+int32_t AInputDevice_getKeyboardType(AInputDevice* device) {
+    InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+    return proxy->getInfo()->getKeyboardType();
+}
+
+int32_t AInputDevice_getMotionRange(AInputDevice* device, int32_t rangeType,
+        float* outMin, float* outMax, float* outFlat, float* outFuzz) {
+    InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+    const InputDeviceInfo::MotionRange* range = proxy->getInfo()->getMotionRange(rangeType);
+    if (range) {
+        if (outMin) {
+            *outMin = range->min;
+        }
+        if (outMax) {
+            *outMax = range->max;
+        }
+        if (outFlat) {
+            *outFlat = range->flat;
+        }
+        if (outFuzz) {
+            *outFuzz = range->fuzz;
+        }
+        return 0;
+    } else {
+        return -ENOTSUP;
+    }
+}
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 0b8c7e4..9883ac70 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -268,7 +268,6 @@
 /*
  * Input sources.
  *
- * The appropriate interpretation for an input event depends on its source.
  * Refer to the documentation on android.view.InputDevice for more details about input sources
  * and their correct interpretation.
  */
@@ -297,6 +296,37 @@
 };
 
 /*
+ * Keyboard types.
+ *
+ * Refer to the documentation on android.view.InputDevice for more details.
+ */
+enum {
+    AINPUT_KEYBOARD_TYPE_NONE = 0,
+    AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = 1,
+    AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2,
+};
+
+/*
+ * Constants used to retrieve information about the range of motion for a particular
+ * coordinate of a motion event.
+ *
+ * Refer to the documentation on android.view.InputDevice for more details about input sources
+ * and their correct interpretation.
+ */
+enum {
+    AINPUT_MOTION_RANGE_X = 0,
+    AINPUT_MOTION_RANGE_Y = 1,
+    AINPUT_MOTION_RANGE_PRESSURE = 2,
+    AINPUT_MOTION_RANGE_SIZE = 3,
+    AINPUT_MOTION_RANGE_TOUCH_MAJOR = 4,
+    AINPUT_MOTION_RANGE_TOUCH_MINOR = 5,
+    AINPUT_MOTION_RANGE_TOOL_MAJOR = 6,
+    AINPUT_MOTION_RANGE_TOOL_MINOR = 7,
+    AINPUT_MOTION_RANGE_ORIENTATION = 8,
+};
+
+
+/*
  * Input event accessors.
  *
  * Note that most functions can only be used on input events that are of a given type.
@@ -475,7 +505,7 @@
  * upwards, is perfectly circular or is of unknown orientation.  A positive angle
  * indicates that the major axis of contact is oriented to the right.  A negative angle
  * indicates that the major axis of contact is oriented to the left.
- * The full range is from -PI/4 radians (finger pointing fully left) to PI/4 radians
+ * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
  * (finger pointing fully right). */
 float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
 
@@ -575,7 +605,7 @@
  * upwards, is perfectly circular or is of unknown orientation.  A positive angle
  * indicates that the major axis of contact is oriented to the right.  A negative angle
  * indicates that the major axis of contact is oriented to the left.
- * The full range is from -PI/4 radians (finger pointing fully left) to PI/4 radians
+ * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
  * (finger pointing fully right). */
 float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
@@ -631,6 +661,64 @@
  */
 void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
 
+/*
+ * Input devices.
+ *
+ * These functions provide a mechanism for querying the set of available input devices
+ * and their characteristics and capabilities.
+ */
+struct AInputDevice;
+typedef struct AInputDevice AInputDevice;
+
+/*
+ * Populates the supplied array with the ids of all input devices in the system.
+ * Sets nActual to the actual number of devices.
+ * Returns zero if enumeration was successful.
+ * Returns non-zero if the actual number of devices is greater than nMax, in which case the
+ * client should call the method again with a larger id buffer.
+ */
+int32_t AInputDevice_getDeviceIds(int32_t* idBuf, size_t nMax, size_t* nActual);
+
+/*
+ * Acquires a device by id.
+ * Returns NULL if the device was not found.
+ *
+ * Note: The returned object must be freed using AInputDevice_release when no longer needed.
+ */
+AInputDevice* AInputDevice_acquire(int32_t deviceId);
+
+/*
+ * Releases a device previously acquired by AInputDevice_acquire.
+ * If device is NULL, this function does nothing.
+ */
+void AInputDevice_release(AInputDevice* device);
+
+/*
+ * Gets the name of an input device.
+ *
+ * Note: The caller should copy the name into a private buffer since the returned pointer
+ * will become invalid when the device object is released.
+ */
+const char* AInputDevice_getName(AInputDevice* device);
+
+/*
+ * Gets the combination of input sources provided by the input device.
+ */
+uint32_t AInputDevice_getSources(AInputDevice* device);
+
+/*
+ * Gets the keyboard type.
+ */
+int32_t AInputDevice_getKeyboardType(AInputDevice* device);
+
+/* Gets the minimum value, maximum value, flat position and error tolerance for a
+ * particular motion coodinate.
+ * Returns zero if the device supports the specified motion range. */
+int32_t AInputDevice_getMotionRange(AInputDevice* device, int32_t rangeType,
+        float* outMin, float* outMax, float* outFlat, float* outFuzz);
+
+//TODO hasKey, keymap stuff, etc...
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index b4f46ab..9a1d017 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -66,13 +66,14 @@
     private static native void nativeSetDisplaySize(int displayId, int width, int height);
     private static native void nativeSetDisplayOrientation(int displayId, int rotation);
     
-    private static native int nativeGetScanCodeState(int deviceId, int deviceClasses,
+    private static native int nativeGetScanCodeState(int deviceId, int sourceMask,
             int scanCode);
-    private static native int nativeGetKeyCodeState(int deviceId, int deviceClasses,
+    private static native int nativeGetKeyCodeState(int deviceId, int sourceMask,
             int keyCode);
-    private static native int nativeGetSwitchState(int deviceId, int deviceClasses,
+    private static native int nativeGetSwitchState(int deviceId, int sourceMask,
             int sw);
-    private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
+    private static native boolean nativeHasKeys(int deviceId, int sourceMask,
+            int[] keyCodes, boolean[] keyExists);
     private static native void nativeRegisterInputChannel(InputChannel inputChannel);
     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
     private static native int nativeInjectKeyEvent(KeyEvent event,
@@ -85,20 +86,28 @@
     private static native void nativePreemptInputDispatch();
     private static native String nativeDump();
     
-    // Device class as defined by EventHub.
-    private static final int CLASS_KEYBOARD = 0x00000001;
-    private static final int CLASS_ALPHAKEY = 0x00000002;
-    private static final int CLASS_TOUCHSCREEN = 0x00000004;
-    private static final int CLASS_TRACKBALL = 0x00000008;
-    private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
-    private static final int CLASS_DPAD = 0x00000020;
-    
     // Input event injection constants defined in InputDispatcher.h.
     static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
     static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
     static final int INPUT_EVENT_INJECTION_FAILED = 2;
     static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
     
+    // Key states (may be returned by queries about the current state of a
+    // particular key code, scan code or switch).
+    
+    /** The key state is unknown or the requested key itself is not supported. */
+    public static final int KEY_STATE_UNKNOWN = -1;
+
+    /** The key is up. /*/
+    public static final int KEY_STATE_UP = 0;
+
+    /** The key is down. */
+    public static final int KEY_STATE_DOWN = 1;
+
+    /** The key is down but is a virtual key press that is being emulated by the system. */
+    public static final int KEY_STATE_VIRTUAL = 2;
+
+
     public InputManager(Context context, WindowManagerService windowManagerService) {
         this.mContext = context;
         this.mWindowManagerService = windowManagerService;
@@ -150,55 +159,67 @@
         config.navigation = mNavigationConfig;
     }
     
-    public int getScancodeState(int code) {
-        return nativeGetScanCodeState(0, -1, code);
+    /**
+     * Gets the current state of a key or button by key code.
+     * @param deviceId The input device id, or -1 to consult all devices.
+     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+     * consider all input sources.  An input device is consulted if at least one of its
+     * non-class input source bits matches the specified source mask.
+     * @param keyCode The key code to check.
+     * @return The key state.
+     */
+    public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
+        return nativeGetKeyCodeState(deviceId, sourceMask, keyCode);
     }
     
-    public int getScancodeState(int deviceId, int code) {
-        return nativeGetScanCodeState(deviceId, -1, code);
+    /**
+     * Gets the current state of a key or button by scan code.
+     * @param deviceId The input device id, or -1 to consult all devices.
+     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+     * consider all input sources.  An input device is consulted if at least one of its
+     * non-class input source bits matches the specified source mask.
+     * @param scanCode The scan code to check.
+     * @return The key state.
+     */
+    public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
+        return nativeGetScanCodeState(deviceId, sourceMask, scanCode);
     }
     
-    public int getTrackballScancodeState(int code) {
-        return nativeGetScanCodeState(-1, CLASS_TRACKBALL, code);
-    }
-    
-    public int getDPadScancodeState(int code) {
-        return nativeGetScanCodeState(-1, CLASS_DPAD, code);
-    }
-    
-    public int getKeycodeState(int code) {
-        return nativeGetKeyCodeState(0, -1, code);
-    }
-    
-    public int getKeycodeState(int deviceId, int code) {
-        return nativeGetKeyCodeState(deviceId, -1, code);
-    }
-    
-    public int getTrackballKeycodeState(int code) {
-        return nativeGetKeyCodeState(-1, CLASS_TRACKBALL, code);
-    }
-    
-    public int getDPadKeycodeState(int code) {
-        return nativeGetKeyCodeState(-1, CLASS_DPAD, code);
+    /**
+     * Gets the current state of a switch by switch code.
+     * @param deviceId The input device id, or -1 to consult all devices.
+     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+     * consider all input sources.  An input device is consulted if at least one of its
+     * non-class input source bits matches the specified source mask.
+     * @param switchCode The switch code to check.
+     * @return The switch state.
+     */
+    public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
+        return nativeGetSwitchState(deviceId, sourceMask, switchCode);
     }
 
-    public int getSwitchState(int sw) {
-        return nativeGetSwitchState(-1, -1, sw);
-    }
-    
-    public int getSwitchState(int deviceId, int sw) {
-        return nativeGetSwitchState(deviceId, -1, sw);
-    }
-
-    public boolean hasKeys(int[] keyCodes, boolean[] keyExists) {
+    /**
+     * Determines whether the specified key codes are supported by a particular device.
+     * @param deviceId The input device id, or -1 to consult all devices.
+     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+     * consider all input sources.  An input device is consulted if at least one of its
+     * non-class input source bits matches the specified source mask.
+     * @param keyCodes The array of key codes to check.
+     * @param keyExists An array at least as large as keyCodes whose entries will be set
+     * to true or false based on the presence or absence of support for the corresponding
+     * key codes.
+     * @return True if the lookup was successful, false otherwise.
+     */
+    public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
         if (keyCodes == null) {
             throw new IllegalArgumentException("keyCodes must not be null.");
         }
-        if (keyExists == null) {
-            throw new IllegalArgumentException("keyExists must not be null.");
+        if (keyExists == null || keyExists.length < keyCodes.length) {
+            throw new IllegalArgumentException("keyExists must not be null and must be at "
+                    + "least as large as keyCodes.");
         }
         
-        return nativeHasKeys(keyCodes, keyExists);
+        return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
     }
     
     public void registerInputChannel(InputChannel inputChannel) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index b82ec01..1a8efa1 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -4306,7 +4306,7 @@
                 "getSwitchState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getSwitchState(sw);
+        return mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, sw);
     }
 
     public int getSwitchStateForDevice(int devid, int sw) {
@@ -4314,7 +4314,7 @@
                 "getSwitchStateForDevice()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getSwitchState(devid, sw);
+        return mInputManager.getSwitchState(devid, InputDevice.SOURCE_ANY, sw);
     }
 
     public int getScancodeState(int sw) {
@@ -4322,7 +4322,7 @@
                 "getScancodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getScancodeState(sw);
+        return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_ANY, sw);
     }
 
     public int getScancodeStateForDevice(int devid, int sw) {
@@ -4330,7 +4330,7 @@
                 "getScancodeStateForDevice()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getScancodeState(devid, sw);
+        return mInputManager.getScanCodeState(devid, InputDevice.SOURCE_ANY, sw);
     }
 
     public int getTrackballScancodeState(int sw) {
@@ -4338,7 +4338,7 @@
                 "getTrackballScancodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getTrackballScancodeState(sw);
+        return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
     }
 
     public int getDPadScancodeState(int sw) {
@@ -4346,7 +4346,7 @@
                 "getDPadScancodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getDPadScancodeState(sw);
+        return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_DPAD, sw);
     }
 
     public int getKeycodeState(int sw) {
@@ -4354,7 +4354,7 @@
                 "getKeycodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getKeycodeState(sw);
+        return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, sw);
     }
 
     public int getKeycodeStateForDevice(int devid, int sw) {
@@ -4362,7 +4362,7 @@
                 "getKeycodeStateForDevice()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getKeycodeState(devid, sw);
+        return mInputManager.getKeyCodeState(devid, InputDevice.SOURCE_ANY, sw);
     }
 
     public int getTrackballKeycodeState(int sw) {
@@ -4370,7 +4370,7 @@
                 "getTrackballKeycodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getTrackballKeycodeState(sw);
+        return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
     }
 
     public int getDPadKeycodeState(int sw) {
@@ -4378,11 +4378,11 @@
                 "getDPadKeycodeState()")) {
             throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
-        return mInputManager.getDPadKeycodeState(sw);
+        return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
     }
 
     public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
-        return mInputManager.hasKeys(keycodes, keyExists);
+        return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
     }
 
     public void enableScreenAfterBoot() {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0992b33..a332376 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -219,11 +219,10 @@
             int32_t* width, int32_t* height, int32_t* orientation);
     virtual void virtualKeyDownFeedback();
     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);
+            bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags);
+    virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+            uint32_t& policyFlags);
+    virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags);
     virtual bool filterTouchEvents();
     virtual bool filterJumpyTouchEvents();
     virtual void getVirtualKeyDefinitions(const String8& deviceName,
@@ -343,6 +342,7 @@
     InputApplication* mFocusedApplication;
     InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
 
+    void dumpDeviceInfo(String8& dump);
     void dumpDispatchStateLd(String8& dump);
     void logDispatchStateLd();
 
@@ -409,12 +409,16 @@
 
 String8 NativeInputManager::dump() {
     String8 dump;
-    dump.append("Native Input Dispatcher State:\n");
-
     { // acquire lock
         AutoMutex _l(mDisplayLock);
+        dump.append("Native Input Dispatcher State:\n");
         dumpDispatchStateLd(dump);
+        dump.append("\n");
     } // release lock
+
+    dump.append("Input Devices:\n");
+    dumpDeviceInfo(dump);
+
     return dump;
 }
 
@@ -566,9 +570,15 @@
         AutoMutex _l(mDisplayLock);
 
         if (mDisplayWidth > 0) {
-            *width = mDisplayWidth;
-            *height = mDisplayHeight;
-            *orientation = mDisplayOrientation;
+            if (width) {
+                *width = mDisplayWidth;
+            }
+            if (height) {
+                *height = mDisplayHeight;
+            }
+            if (orientation) {
+                *orientation = mDisplayOrientation;
+            }
             result = true;
         }
     }
@@ -595,7 +605,7 @@
 }
 
 int32_t NativeInputManager::interceptKey(nsecs_t when,
-        int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
+        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=0x%x",
@@ -626,12 +636,12 @@
     int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
     if (! isScreenOn) {
         // Key presses and releases wake the device.
-        actions |= InputReaderPolicyInterface::ACTION_WOKE_HERE;
+        policyFlags |= POLICY_FLAG_WOKE_HERE;
     }
 
     if (! isScreenBright) {
         // Key presses and releases brighten the screen if dimmed.
-        actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+        policyFlags |= POLICY_FLAG_BRIGHT_HERE;
     }
 
     if (wmActions & WM_ACTION_GO_TO_SLEEP) {
@@ -658,42 +668,20 @@
     return actions;
 }
 
-int32_t NativeInputManager::interceptTouch(nsecs_t when) {
+int32_t NativeInputManager::interceptGeneric(nsecs_t when, uint32_t& policyFlags) {
 #if DEBUG_INPUT_READER_POLICY
-    LOGD("interceptTouch - when=%lld", when);
+    LOGD("interceptGeneric - when=%lld, policyFlags=0x%x", when, policyFlags);
 #endif
 
     int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
     if (isScreenOn()) {
-        // Only dispatch touch events when the device is awake.
+        // Only dispatch events when the device is awake.
         // Do not wake the device.
         actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
 
         if (! isScreenBright()) {
             // Brighten the screen if dimmed.
-            actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
-        }
-    }
-
-    return actions;
-}
-
-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
-
-    int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
-    if (isScreenOn()) {
-        // Only dispatch trackball events when the device is awake.
-        // Do not wake the device.
-        actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
-
-        if (! isScreenBright()) {
-            // Brighten the screen if dimmed.
-            actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+            policyFlags |= POLICY_FLAG_BRIGHT_HERE;
         }
     }
 
@@ -701,10 +689,10 @@
 }
 
 int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode,
-        int32_t switchValue) {
+        int32_t switchValue, uint32_t& policyFlags) {
 #if DEBUG_INPUT_READER_POLICY
-    LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d",
-            when, switchCode, switchValue);
+    LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x",
+            when, switchCode, switchValue, policyFlags);
 #endif
 
     JNIEnv* env = jniEnv();
@@ -1718,6 +1706,56 @@
     android_server_PowerManagerService_userActivity(eventTime, eventType);
 }
 
+static void dumpMotionRange(String8& dump,
+        const char* name, const InputDeviceInfo::MotionRange* range) {
+    if (range) {
+        dump.appendFormat("      %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
+                name, range->min, range->max, range->flat, range->fuzz);
+    }
+}
+
+#define DUMP_MOTION_RANGE(range) \
+    dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
+
+void NativeInputManager::dumpDeviceInfo(String8& dump) {
+    Vector<int32_t> deviceIds;
+    mInputManager->getInputDeviceIds(deviceIds);
+
+    InputDeviceInfo deviceInfo;
+    for (size_t i = 0; i < deviceIds.size(); i++) {
+        int32_t deviceId = deviceIds[i];
+
+        status_t result = mInputManager->getInputDeviceInfo(deviceId, & deviceInfo);
+        if (result == NAME_NOT_FOUND) {
+            continue;
+        } else if (result != OK) {
+            dump.appendFormat("  ** Unexpected error %d getting information about input devices.\n",
+                    result);
+            continue;
+        }
+
+        dump.appendFormat("  Device %d: '%s'\n",
+                deviceInfo.getId(), deviceInfo.getName().string());
+        dump.appendFormat("    sources = 0x%08x\n",
+                deviceInfo.getSources());
+        dump.appendFormat("    keyboardType = %d\n",
+                deviceInfo.getKeyboardType());
+
+        dump.append("    motion ranges:\n");
+        DUMP_MOTION_RANGE(X);
+        DUMP_MOTION_RANGE(Y);
+        DUMP_MOTION_RANGE(PRESSURE);
+        DUMP_MOTION_RANGE(SIZE);
+        DUMP_MOTION_RANGE(TOUCH_MAJOR);
+        DUMP_MOTION_RANGE(TOUCH_MINOR);
+        DUMP_MOTION_RANGE(TOOL_MAJOR);
+        DUMP_MOTION_RANGE(TOOL_MINOR);
+        DUMP_MOTION_RANGE(ORIENTATION);
+    }
+}
+
+#undef DUMP_MOTION_RANGE
+
 void NativeInputManager::logDispatchStateLd() {
     String8 dump;
     dumpDispatchStateLd(dump);
@@ -1899,36 +1937,37 @@
 }
 
 static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
-        jint deviceId, jint deviceClasses, jint scanCode) {
+        jint deviceId, jint sourceMask, jint scanCode) {
     if (checkInputManagerUnitialized(env)) {
         return AKEY_STATE_UNKNOWN;
     }
 
     return gNativeInputManager->getInputManager()->getScanCodeState(
-            deviceId, deviceClasses, scanCode);
+            deviceId, uint32_t(sourceMask), scanCode);
 }
 
 static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
-        jint deviceId, jint deviceClasses, jint keyCode) {
+        jint deviceId, jint sourceMask, jint keyCode) {
     if (checkInputManagerUnitialized(env)) {
         return AKEY_STATE_UNKNOWN;
     }
 
     return gNativeInputManager->getInputManager()->getKeyCodeState(
-            deviceId, deviceClasses, keyCode);
+            deviceId, uint32_t(sourceMask), keyCode);
 }
 
 static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
-        jint deviceId, jint deviceClasses, jint sw) {
+        jint deviceId, jint sourceMask, jint sw) {
     if (checkInputManagerUnitialized(env)) {
         return AKEY_STATE_UNKNOWN;
     }
 
-    return gNativeInputManager->getInputManager()->getSwitchState(deviceId, deviceClasses, sw);
+    return gNativeInputManager->getInputManager()->getSwitchState(
+            deviceId, uint32_t(sourceMask), sw);
 }
 
 static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz,
-        jintArray keyCodes, jbooleanArray outFlags) {
+        jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) {
     if (checkInputManagerUnitialized(env)) {
         return JNI_FALSE;
     }
@@ -1937,8 +1976,9 @@
     uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
     jsize numCodes = env->GetArrayLength(keyCodes);
     jboolean result;
-    if (numCodes == env->GetArrayLength(outFlags)) {
-        result = gNativeInputManager->getInputManager()->hasKeys(numCodes, codes, flags);
+    if (numCodes == env->GetArrayLength(keyCodes)) {
+        result = gNativeInputManager->getInputManager()->hasKeys(
+                deviceId, uint32_t(sourceMask), numCodes, codes, flags);
     } else {
         result = JNI_FALSE;
     }
@@ -2102,7 +2142,7 @@
             (void*) android_server_InputManager_nativeGetKeyCodeState },
     { "nativeGetSwitchState", "(III)I",
             (void*) android_server_InputManager_nativeGetSwitchState },
-    { "nativeHasKeys", "([I[Z)Z",
+    { "nativeHasKeys", "(II[I[Z)Z",
             (void*) android_server_InputManager_nativeHasKeys },
     { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
             (void*) android_server_InputManager_nativeRegisterInputChannel },