am efd3266b: Input improvements and bug fixes.

* commit 'efd3266b719eed5f1b217021c0a9e76e4b274b06':
  Input improvements and bug fixes.
diff --git a/api/current.xml b/api/current.xml
index cbccf93..cd3a7fb 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -211374,9 +211374,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="axis" type="int">
+</parameter>
 </method>
-<method name="getMotionAxes"
- return="int[]"
+<method name="getMotionRange"
+ return="android.view.InputDevice.MotionRange"
  abstract="false"
  native="false"
  synchronized="false"
@@ -211398,6 +211400,19 @@
 >
 <parameter name="axis" type="int">
 </parameter>
+<parameter name="source" type="int">
+</parameter>
+</method>
+<method name="getMotionRanges"
+ return="java.util.List&lt;android.view.InputDevice.MotionRange&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 </method>
 <method name="getName"
  return="java.lang.String"
@@ -211763,6 +211778,17 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="getAxis"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getFlat"
  return="float"
  abstract="false"
@@ -211818,6 +211844,17 @@
  visibility="public"
 >
 </method>
+<method name="getSource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 </class>
 <class name="InputEvent"
  extends="java.lang.Object"
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index def1161..98d4eb9 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -20,7 +20,9 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Describes the capabilities of a particular input device.
@@ -43,8 +45,7 @@
     private int mSources;
     private int mKeyboardType;
 
-    private final SparseArray<MotionRange> mMotionRanges = new SparseArray<MotionRange>();
-    private int[] mMotionAxes;
+    private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
 
     /**
      * A mask for input source classes.
@@ -354,6 +355,11 @@
 
     /**
      * Gets information about the range of values for a particular {@link MotionEvent} axis.
+     * If the device supports multiple sources, the same axis may have different meanings
+     * for each source.  Returns information about the first axis found for any source.
+     * To obtain information about the axis for a specific source, use
+     * {@link #getMotionRange(int, int)}.
+     *
      * @param axis The axis constant.
      * @return The range of values, or null if the requested axis is not
      * supported by the device.
@@ -363,30 +369,55 @@
      * @see #getSupportedAxes()
      */
     public MotionRange getMotionRange(int axis) {
-        return mMotionRanges.get(axis);
+        final int numRanges = mMotionRanges.size();
+        for (int i = 0; i < numRanges; i++) {
+            final MotionRange range = mMotionRanges.get(i);
+            if (range.mAxis == axis) {
+                return range;
+            }
+        }
+        return null;
     }
 
     /**
-     * Gets the axis ids of all motion axes supported by this device.
-     * @return The axis ids of all motion axes supported by this device.
+     * Gets information about the range of values for a particular {@link MotionEvent} axis
+     * used by a particular source on the device.
+     * If the device supports multiple sources, the same axis may have different meanings
+     * for each source.
      *
-     * @see #getMotionRange(int)
+     * @param axis The axis constant.
+     * @param source The source for which to return information.
+     * @return The range of values, or null if the requested axis is not
+     * supported by the device.
+     *
+     * @see MotionEvent#AXIS_X
+     * @see MotionEvent#AXIS_Y
+     * @see #getSupportedAxes()
      */
-    public int[] getMotionAxes() {
-        synchronized (this) {
-            if (mMotionAxes == null) {
-                final int count = mMotionRanges.size();
-                mMotionAxes = new int[count];
-                for (int i = 0; i < count; i++) {
-                    mMotionAxes[i] = mMotionRanges.keyAt(i);
-                }
+    public MotionRange getMotionRange(int axis, int source) {
+        final int numRanges = mMotionRanges.size();
+        for (int i = 0; i < numRanges; i++) {
+            final MotionRange range = mMotionRanges.get(i);
+            if (range.mAxis == axis && range.mSource == source) {
+                return range;
             }
-            return mMotionAxes;
         }
+        return null;
     }
 
-    private void addMotionRange(int axis, float min, float max, float flat, float fuzz) {
-        mMotionRanges.append(axis, new MotionRange(min, max, flat, fuzz));
+    /**
+     * Gets the ranges for all axes supported by the device.
+     * @return The motion ranges for the device.
+     *
+     * @see #getMotionRange(int, int)
+     */
+    public List<MotionRange> getMotionRanges() {
+        return mMotionRanges;
+    }
+
+    private void addMotionRange(int axis, int source,
+            float min, float max, float flat, float fuzz) {
+        mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz));
     }
 
     /**
@@ -395,12 +426,16 @@
      * @see InputDevice#getMotionRange(int)
      */
     public static final class MotionRange {
+        private int mAxis;
+        private int mSource;
         private float mMin;
         private float mMax;
         private float mFlat;
         private float mFuzz;
 
-        private MotionRange(float min, float max, float flat, float fuzz) {
+        private MotionRange(int axis, int source, float min, float max, float flat, float fuzz) {
+            mAxis = axis;
+            mSource = source;
             mMin = min;
             mMax = max;
             mFlat = flat;
@@ -408,6 +443,22 @@
         }
 
         /**
+         * Gets the axis id.
+         * @return The axis id.
+         */
+        public int getAxis() {
+            return mAxis;
+        }
+
+        /**
+         * Gets the source for which the axis is defined.
+         * @return The source.
+         */
+        public int getSource() {
+            return mSource;
+        }
+
+        /**
          * Gets the inclusive minimum value for the axis.
          * @return The inclusive minimum value.
          */
@@ -480,7 +531,8 @@
             if (axis < 0) {
                 break;
             }
-            addMotionRange(axis, in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
+            addMotionRange(axis, in.readInt(),
+                    in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
         }
     }
 
@@ -491,11 +543,11 @@
         out.writeInt(mSources);
         out.writeInt(mKeyboardType);
 
-        final int numAxes = mMotionRanges.size();
-        for (int i = 0; i < numAxes; i++) {
-            int axis = mMotionRanges.keyAt(i);
-            MotionRange range = mMotionRanges.valueAt(i);
-            out.writeInt(axis);
+        final int numRanges = mMotionRanges.size();
+        for (int i = 0; i < numRanges; i++) {
+            MotionRange range = mMotionRanges.get(i);
+            out.writeInt(range.mAxis);
+            out.writeInt(range.mSource);
             out.writeFloat(range.mMin);
             out.writeFloat(range.mMax);
             out.writeFloat(range.mFlat);
@@ -528,7 +580,7 @@
         }
         description.append("\n");
 
-        description.append("  Sources: ").append(Integer.toHexString(mSources)).append(" (");
+        description.append("  Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
         appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard");
         appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
         appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHSCREEN, "touchscreen");
@@ -541,10 +593,10 @@
 
         final int numAxes = mMotionRanges.size();
         for (int i = 0; i < numAxes; i++) {
-            int axis = mMotionRanges.keyAt(i);
-            MotionRange range = mMotionRanges.valueAt(i);
-            description.append("    ").append(MotionEvent.axisToString(axis));
-            description.append(": min=").append(range.mMin);
+            MotionRange range = mMotionRanges.get(i);
+            description.append("    ").append(MotionEvent.axisToString(range.mAxis));
+            description.append(": source=0x").append(Integer.toHexString(range.mSource));
+            description.append(" min=").append(range.mMin);
             description.append(" max=").append(range.mMax);
             description.append(" flat=").append(range.mFlat);
             description.append(" fuzz=").append(range.mFuzz);
diff --git a/include/ui/Input.h b/include/ui/Input.h
index e92d7f5..d9d77c4 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -144,6 +144,14 @@
 };
 
 /*
+ * Button state.
+ */
+enum {
+    // Primary button pressed (left mouse button).
+    BUTTON_STATE_PRIMARY = 1 << 0,
+};
+
+/*
  * Describes the basic configuration of input devices that are present.
  */
 struct InputConfiguration {
@@ -544,6 +552,8 @@
     ~InputDeviceInfo();
 
     struct MotionRange {
+        int32_t axis;
+        uint32_t source;
         float min;
         float max;
         float flat;
@@ -556,16 +566,17 @@
     inline const String8 getName() const { return mName; }
     inline uint32_t getSources() const { return mSources; }
 
-    const MotionRange* getMotionRange(int32_t axis) const;
+    const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
 
     void addSource(uint32_t source);
-    void addMotionRange(int32_t axis, float min, float max, float flat, float fuzz);
-    void addMotionRange(int32_t axis, const MotionRange& range);
+    void addMotionRange(int32_t axis, uint32_t source,
+            float min, float max, float flat, float fuzz);
+    void addMotionRange(const MotionRange& range);
 
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
     inline int32_t getKeyboardType() const { return mKeyboardType; }
 
-    inline const KeyedVector<int32_t, MotionRange> getMotionRanges() const {
+    inline const Vector<MotionRange>& getMotionRanges() const {
         return mMotionRanges;
     }
 
@@ -575,7 +586,7 @@
     uint32_t mSources;
     int32_t mKeyboardType;
 
-    KeyedVector<int32_t, MotionRange> mMotionRanges;
+    Vector<MotionRange> mMotionRanges;
 };
 
 /*
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 0ed0866..e2e698e 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -657,23 +657,30 @@
     mMotionRanges.clear();
 }
 
-const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t axis) const {
-    ssize_t index = mMotionRanges.indexOfKey(axis);
-    return index >= 0 ? & mMotionRanges.valueAt(index) : NULL;
+const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
+        int32_t axis, uint32_t source) const {
+    size_t numRanges = mMotionRanges.size();
+    for (size_t i = 0; i < numRanges; i++) {
+        const MotionRange& range = mMotionRanges.itemAt(i);
+        if (range.axis == axis && range.source == source) {
+            return &range;
+        }
+    }
+    return NULL;
 }
 
 void InputDeviceInfo::addSource(uint32_t source) {
     mSources |= source;
 }
 
-void InputDeviceInfo::addMotionRange(int32_t axis, float min, float max,
+void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
         float flat, float fuzz) {
-    MotionRange range = { min, max, flat, fuzz };
-    addMotionRange(axis, range);
+    MotionRange range = { axis, source, min, max, flat, fuzz };
+    mMotionRanges.add(range);
 }
 
-void InputDeviceInfo::addMotionRange(int32_t axis, const MotionRange& range) {
-    mMotionRanges.add(axis, range);
+void InputDeviceInfo::addMotionRange(const MotionRange& range) {
+    mMotionRanges.add(range);
 }
 
 } // namespace android
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 487ecff..19295e6d 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -2343,17 +2343,17 @@
                 }
 
                 MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-                if (motionEntry->deviceId != deviceId) {
-                    // Keep looking for this device.
+                if (motionEntry->deviceId != deviceId
+                        || motionEntry->source != source) {
+                    // Keep looking for this device and source.
                     continue;
                 }
 
                 if (motionEntry->action != action
-                        || motionEntry->source != source
                         || motionEntry->pointerCount != pointerCount
                         || motionEntry->isInjected()) {
-                    // Last motion event in the queue for this device is not compatible for
-                    // appending new samples.  Stop here.
+                    // Last motion event in the queue for this device and source is
+                    // not compatible for appending new samples.  Stop here.
                     goto NoBatchingOrStreaming;
                 }
 
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 3688bfc..3029028 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -33,6 +33,7 @@
 // Log debug messages about pointer assignment calculations.
 #define DEBUG_POINTER_ASSIGNMENT 0
 
+
 #include "InputReader.h"
 
 #include <cutils/log.h>
@@ -140,6 +141,48 @@
     return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
 }
 
+static uint32_t getButtonStateForScanCode(int32_t scanCode) {
+    // Currently all buttons are mapped to the primary button.
+    switch (scanCode) {
+    case BTN_LEFT:
+    case BTN_RIGHT:
+    case BTN_MIDDLE:
+    case BTN_SIDE:
+    case BTN_EXTRA:
+    case BTN_FORWARD:
+    case BTN_BACK:
+    case BTN_TASK:
+        return BUTTON_STATE_PRIMARY;
+    default:
+        return 0;
+    }
+}
+
+// Returns true if the pointer should be reported as being down given the specified
+// button states.
+static bool isPointerDown(uint32_t buttonState) {
+    return buttonState & BUTTON_STATE_PRIMARY;
+}
+
+static int32_t calculateEdgeFlagsUsingPointerBounds(
+        const sp<PointerControllerInterface>& pointerController, float x, float y) {
+    int32_t edgeFlags = 0;
+    float minX, minY, maxX, maxY;
+    if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+        if (x <= minX) {
+            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
+        } else if (x >= maxX) {
+            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
+        }
+        if (y <= minY) {
+            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
+        } else if (y >= maxY) {
+            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
+        }
+    }
+    return edgeFlags;
+}
+
 
 // --- InputReader ---
 
@@ -270,23 +313,23 @@
     }
 
     // Keyboard-like devices.
-    uint32_t keyboardSources = 0;
+    uint32_t keyboardSource = 0;
     int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
     if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
-        keyboardSources |= AINPUT_SOURCE_KEYBOARD;
+        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
     }
     if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
         keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
     }
     if (classes & INPUT_DEVICE_CLASS_DPAD) {
-        keyboardSources |= AINPUT_SOURCE_DPAD;
+        keyboardSource |= AINPUT_SOURCE_DPAD;
     }
     if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
-        keyboardSources |= AINPUT_SOURCE_GAMEPAD;
+        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
     }
 
-    if (keyboardSources != 0) {
-        device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType));
+    if (keyboardSource != 0) {
+        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
     }
 
     // Cursor-like devices.
@@ -617,22 +660,22 @@
     dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
 
-    const KeyedVector<int32_t, InputDeviceInfo::MotionRange> ranges = deviceInfo.getMotionRanges();
+    const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     if (!ranges.isEmpty()) {
         dump.append(INDENT2 "Motion Ranges:\n");
         for (size_t i = 0; i < ranges.size(); i++) {
-            int32_t axis = ranges.keyAt(i);
-            const char* label = getAxisLabel(axis);
+            const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
+            const char* label = getAxisLabel(range.axis);
             char name[32];
             if (label) {
                 strncpy(name, label, sizeof(name));
                 name[sizeof(name) - 1] = '\0';
             } else {
-                snprintf(name, sizeof(name), "%d", axis);
+                snprintf(name, sizeof(name), "%d", range.axis);
             }
-            const InputDeviceInfo::MotionRange& range = ranges.valueAt(i);
-            dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
-                    name, range.min, range.max, range.flat, range.fuzz);
+            dump.appendFormat(INDENT3 "%s: source=0x%08x, "
+                    "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
+                    name, range.source, range.min, range.max, range.flat, range.fuzz);
         }
     }
 
@@ -837,8 +880,8 @@
 // --- KeyboardInputMapper ---
 
 KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
-        uint32_t sources, int32_t keyboardType) :
-        InputMapper(device), mSources(sources),
+        uint32_t source, int32_t keyboardType) :
+        InputMapper(device), mSource(source),
         mKeyboardType(keyboardType) {
     initializeLocked();
 }
@@ -852,7 +895,7 @@
 }
 
 uint32_t KeyboardInputMapper::getSources() {
-    return mSources;
+    return mSource;
 }
 
 void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
@@ -1036,7 +1079,7 @@
         getContext()->fadePointer();
     }
 
-    getDispatcher()->notifyKey(when, getDeviceId(), mSources, policyFlags,
+    getDispatcher()->notifyKey(when, getDeviceId(), mSource, policyFlags,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
 }
@@ -1116,7 +1159,7 @@
 }
 
 uint32_t CursorInputMapper::getSources() {
-    return mSources;
+    return mSource;
 }
 
 void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
@@ -1125,20 +1168,20 @@
     if (mParameters.mode == Parameters::MODE_POINTER) {
         float minX, minY, maxX, maxY;
         if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_X, minX, maxX, 0.0f, 0.0f);
-            info->addMotionRange(AMOTION_EVENT_AXIS_Y, minY, maxY, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f);
         }
     } else {
-        info->addMotionRange(AMOTION_EVENT_AXIS_X, -1.0f, 1.0f, 0.0f, mXScale);
-        info->addMotionRange(AMOTION_EVENT_AXIS_Y, -1.0f, 1.0f, 0.0f, mYScale);
+        info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale);
+        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale);
     }
-    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, 0.0f, 1.0f, 0.0f, 0.0f);
+    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f);
 
     if (mHaveVWheel) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, -1.0f, 1.0f, 0.0f, 0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
     }
     if (mHaveHWheel) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, -1.0f, 1.0f, 0.0f, 0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
     }
 }
 
@@ -1155,7 +1198,8 @@
         dump.appendFormat(INDENT3 "HaveHWheel: %s\n", toString(mHaveHWheel));
         dump.appendFormat(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
         dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
-        dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
+        dump.appendFormat(INDENT3 "ButtonState: 0x%08x\n", mLocked.buttonState);
+        dump.appendFormat(INDENT3 "Down: %s\n", toString(isPointerDown(mLocked.buttonState)));
         dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
     } // release lock
 }
@@ -1169,7 +1213,7 @@
     // Configure device mode.
     switch (mParameters.mode) {
     case Parameters::MODE_POINTER:
-        mSources = AINPUT_SOURCE_MOUSE;
+        mSource = AINPUT_SOURCE_MOUSE;
         mXPrecision = 1.0f;
         mYPrecision = 1.0f;
         mXScale = 1.0f;
@@ -1177,7 +1221,7 @@
         mPointerController = getPolicy()->obtainPointerController(getDeviceId());
         break;
     case Parameters::MODE_NAVIGATION:
-        mSources = AINPUT_SOURCE_TRACKBALL;
+        mSource = AINPUT_SOURCE_TRACKBALL;
         mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
         mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
         mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
@@ -1234,16 +1278,18 @@
 void CursorInputMapper::initializeLocked() {
     mAccumulator.clear();
 
-    mLocked.down = false;
+    mLocked.buttonState = 0;
     mLocked.downTime = 0;
 }
 
 void CursorInputMapper::reset() {
     for (;;) {
+        uint32_t buttonState;
         { // acquire lock
             AutoMutex _l(mLock);
 
-            if (! mLocked.down) {
+            buttonState = mLocked.buttonState;
+            if (!buttonState) {
                 initializeLocked();
                 break; // done
             }
@@ -1251,8 +1297,10 @@
 
         // Synthesize button up event on reset.
         nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
-        mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
-        mAccumulator.btnMouse = false;
+        mAccumulator.clear();
+        mAccumulator.buttonDown = 0;
+        mAccumulator.buttonUp = buttonState;
+        mAccumulator.fields = Accumulator::FIELD_BUTTONS;
         sync(when);
     }
 
@@ -1261,24 +1309,25 @@
 
 void CursorInputMapper::process(const RawEvent* rawEvent) {
     switch (rawEvent->type) {
-    case EV_KEY:
-        switch (rawEvent->scanCode) {
-        case BTN_LEFT:
-        case BTN_RIGHT:
-        case BTN_MIDDLE:
-        case BTN_SIDE:
-        case BTN_EXTRA:
-        case BTN_FORWARD:
-        case BTN_BACK:
-        case BTN_TASK:
-            mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
-            mAccumulator.btnMouse = rawEvent->value != 0;
+    case EV_KEY: {
+        uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode);
+        if (buttonState) {
+            if (rawEvent->value) {
+                mAccumulator.buttonDown = buttonState;
+                mAccumulator.buttonUp = 0;
+            } else {
+                mAccumulator.buttonDown = 0;
+                mAccumulator.buttonUp = buttonState;
+            }
+            mAccumulator.fields |= Accumulator::FIELD_BUTTONS;
+
             // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
             // we need to ensure that we report the up/down promptly.
             sync(rawEvent->when);
             break;
         }
         break;
+    }
 
     case EV_REL:
         switch (rawEvent->scanCode) {
@@ -1325,23 +1374,26 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
+        bool down, downChanged;
+        bool wasDown = isPointerDown(mLocked.buttonState);
+        bool buttonsChanged = fields & Accumulator::FIELD_BUTTONS;
+        if (buttonsChanged) {
+            mLocked.buttonState = (mLocked.buttonState | mAccumulator.buttonDown)
+                    & ~mAccumulator.buttonUp;
 
-        if (downChanged) {
-            if (mAccumulator.btnMouse) {
-                if (!mLocked.down) {
-                    mLocked.down = true;
-                    mLocked.downTime = when;
-                } else {
-                    downChanged = false;
-                }
+            down = isPointerDown(mLocked.buttonState);
+
+            if (!wasDown && down) {
+                mLocked.downTime = when;
+                downChanged = true;
+            } else if (wasDown && !down) {
+                downChanged = true;
             } else {
-                if (mLocked.down) {
-                    mLocked.down = false;
-                } else {
-                    downChanged = false;
-                }
+                downChanged = false;
             }
+        } else {
+            down = wasDown;
+            downChanged = false;
         }
 
         downTime = mLocked.downTime;
@@ -1349,8 +1401,8 @@
         float deltaY = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f;
 
         if (downChanged) {
-            motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
-        } else if (mLocked.down || mPointerController == NULL) {
+            motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+        } else if (down || mPointerController == NULL) {
             motionEventAction = AMOTION_EVENT_ACTION_MOVE;
         } else {
             motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
@@ -1393,35 +1445,25 @@
 
         if (mPointerController != NULL) {
             mPointerController->move(deltaX, deltaY);
-            if (downChanged) {
-                mPointerController->setButtonState(mLocked.down ? POINTER_BUTTON_1 : 0);
+            if (buttonsChanged) {
+                mPointerController->setButtonState(mLocked.buttonState);
             }
+
             float x, y;
             mPointerController->getPosition(&x, &y);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
             if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
-                float minX, minY, maxX, maxY;
-                if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-                    if (x <= minX) {
-                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
-                    } else if (x >= maxX) {
-                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
-                    }
-                    if (y <= minY) {
-                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
-                    } else if (y >= maxY) {
-                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
-                    }
-                }
+                motionEventEdgeFlags = calculateEdgeFlagsUsingPointerBounds(
+                        mPointerController, x, y);
             }
         } else {
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
         }
 
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
 
         if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) {
             vscroll = mAccumulator.relWheel;
@@ -1449,7 +1491,7 @@
 
     int32_t metaState = mContext->getGlobalMetaState();
     int32_t pointerId = 0;
-    getDispatcher()->notifyMotion(when, getDeviceId(), mSources, policyFlags,
+    getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
             motionEventAction, 0, metaState, motionEventEdgeFlags,
             1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
 
@@ -1459,7 +1501,7 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        getDispatcher()->notifyMotion(when, getDeviceId(), mSources, policyFlags,
+        getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
                 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
     }
@@ -1476,7 +1518,9 @@
 void CursorInputMapper::fadePointer() {
     { // acquire lock
         AutoMutex _l(mLock);
-        mPointerController->fade();
+        if (mPointerController != NULL) {
+            mPointerController->fade();
+        }
     } // release lock
 }
 
@@ -1496,7 +1540,7 @@
 }
 
 uint32_t TouchInputMapper::getSources() {
-    return mSources;
+    return mTouchSource;
 }
 
 void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
@@ -1507,38 +1551,33 @@
 
         // Ensure surface information is up to date so that orientation changes are
         // noticed immediately.
-        configureSurfaceLocked();
+        if (!configureSurfaceLocked()) {
+            return;
+        }
 
-        info->addMotionRange(AMOTION_EVENT_AXIS_X, mLocked.orientedRanges.x);
-        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mLocked.orientedRanges.y);
+        info->addMotionRange(mLocked.orientedRanges.x);
+        info->addMotionRange(mLocked.orientedRanges.y);
 
         if (mLocked.orientedRanges.havePressure) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
-                    mLocked.orientedRanges.pressure);
+            info->addMotionRange(mLocked.orientedRanges.pressure);
         }
 
         if (mLocked.orientedRanges.haveSize) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_SIZE,
-                    mLocked.orientedRanges.size);
+            info->addMotionRange(mLocked.orientedRanges.size);
         }
 
         if (mLocked.orientedRanges.haveTouchSize) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
-                    mLocked.orientedRanges.touchMajor);
-            info->addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR,
-                    mLocked.orientedRanges.touchMinor);
+            info->addMotionRange(mLocked.orientedRanges.touchMajor);
+            info->addMotionRange(mLocked.orientedRanges.touchMinor);
         }
 
         if (mLocked.orientedRanges.haveToolSize) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_TOOL_MAJOR,
-                    mLocked.orientedRanges.toolMajor);
-            info->addMotionRange(AMOTION_EVENT_AXIS_TOOL_MINOR,
-                    mLocked.orientedRanges.toolMinor);
+            info->addMotionRange(mLocked.orientedRanges.toolMajor);
+            info->addMotionRange(mLocked.orientedRanges.toolMinor);
         }
 
         if (mLocked.orientedRanges.haveOrientation) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION,
-                    mLocked.orientedRanges.orientation);
+            info->addMotionRange(mLocked.orientedRanges.orientation);
         }
     } // release lock
 }
@@ -1552,6 +1591,7 @@
         dumpRawAxes(dump);
         dumpCalibration(dump);
         dumpSurfaceLocked(dump);
+
         dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
         dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale);
         dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale);
@@ -1564,7 +1604,10 @@
         dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mLocked.toolSizeAreaBias);
         dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale);
         dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale);
-        dump.appendFormat(INDENT4 "OrientationSCale: %0.3f\n", mLocked.orientationScale);
+        dump.appendFormat(INDENT4 "OrientationScale: %0.3f\n", mLocked.orientationScale);
+
+        dump.appendFormat(INDENT3 "Last Touch:\n");
+        dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
     } // release lock
 }
 
@@ -1598,10 +1641,10 @@
     // Configure sources.
     switch (mParameters.deviceType) {
     case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
-        mSources = AINPUT_SOURCE_TOUCHSCREEN;
+        mTouchSource = AINPUT_SOURCE_TOUCHSCREEN;
         break;
     case Parameters::DEVICE_TYPE_TOUCH_PAD:
-        mSources = AINPUT_SOURCE_TOUCHPAD;
+        mTouchSource = AINPUT_SOURCE_TOUCHPAD;
         break;
     default:
         assert(false);
@@ -1634,17 +1677,20 @@
             deviceTypeString)) {
         if (deviceTypeString == "touchScreen") {
             mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
-        } else if (deviceTypeString != "touchPad") {
+        } else if (deviceTypeString == "touchPad") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+        } else {
             LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
         }
     }
-    bool isTouchScreen = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
 
-    mParameters.orientationAware = isTouchScreen;
+    mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
     getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
             mParameters.orientationAware);
 
-    mParameters.associatedDisplayId = mParameters.orientationAware || isTouchScreen ? 0 : -1;
+    mParameters.associatedDisplayId = mParameters.orientationAware
+            || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
+            ? 0 : -1;
 }
 
 void TouchInputMapper::dumpParameters(String8& dump) {
@@ -1711,15 +1757,23 @@
     int32_t height = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
 
     if (mParameters.associatedDisplayId >= 0) {
-        bool wantSize = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
-        bool wantOrientation = mParameters.orientationAware;
-
         // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
         if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
-                wantSize ? &width : NULL, wantSize ? &height : NULL,
-                wantOrientation ? &orientation : NULL)) {
+                &mLocked.associatedDisplayWidth, &mLocked.associatedDisplayHeight,
+                &mLocked.associatedDisplayOrientation)) {
             return false;
         }
+
+        // A touch screen inherits the dimensions of the display.
+        if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+            width = mLocked.associatedDisplayWidth;
+            height = mLocked.associatedDisplayHeight;
+        }
+
+        // The device inherits the orientation of the display if it is orientation aware.
+        if (mParameters.orientationAware) {
+            orientation = mLocked.associatedDisplayOrientation;
+        }
     }
 
     bool orientationChanged = mLocked.surfaceOrientation != orientation;
@@ -1729,7 +1783,7 @@
 
     bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
     if (sizeChanged) {
-        LOGI("Device reconfigured: id=%d, name='%s', display size is now %dx%d",
+        LOGI("Device reconfigured: id=%d, name='%s', surface size is now %dx%d",
                 getDeviceId(), getDeviceName().string(), width, height);
 
         mLocked.surfaceWidth = width;
@@ -1741,6 +1795,11 @@
         mLocked.xPrecision = 1.0f / mLocked.xScale;
         mLocked.yPrecision = 1.0f / mLocked.yScale;
 
+        mLocked.orientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
+        mLocked.orientedRanges.x.source = mTouchSource;
+        mLocked.orientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
+        mLocked.orientedRanges.y.source = mTouchSource;
+
         configureVirtualKeysLocked();
 
         // Scale factor for terms that are not oriented in a particular axis.
@@ -1754,11 +1813,16 @@
         // TouchMajor and TouchMinor factors.
         if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) {
             mLocked.orientedRanges.haveTouchSize = true;
+
+            mLocked.orientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
+            mLocked.orientedRanges.touchMajor.source = mTouchSource;
             mLocked.orientedRanges.touchMajor.min = 0;
             mLocked.orientedRanges.touchMajor.max = diagonalSize;
             mLocked.orientedRanges.touchMajor.flat = 0;
             mLocked.orientedRanges.touchMajor.fuzz = 0;
+
             mLocked.orientedRanges.touchMinor = mLocked.orientedRanges.touchMajor;
+            mLocked.orientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
         }
 
         // ToolMajor and ToolMinor factors.
@@ -1802,11 +1866,16 @@
             }
 
             mLocked.orientedRanges.haveToolSize = true;
+
+            mLocked.orientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
+            mLocked.orientedRanges.toolMajor.source = mTouchSource;
             mLocked.orientedRanges.toolMajor.min = 0;
             mLocked.orientedRanges.toolMajor.max = diagonalSize;
             mLocked.orientedRanges.toolMajor.flat = 0;
             mLocked.orientedRanges.toolMajor.fuzz = 0;
+
             mLocked.orientedRanges.toolMinor = mLocked.orientedRanges.toolMajor;
+            mLocked.orientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
         }
 
         // Pressure factors.
@@ -1835,6 +1904,9 @@
             }
 
             mLocked.orientedRanges.havePressure = true;
+
+            mLocked.orientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
+            mLocked.orientedRanges.pressure.source = mTouchSource;
             mLocked.orientedRanges.pressure.min = 0;
             mLocked.orientedRanges.pressure.max = 1.0;
             mLocked.orientedRanges.pressure.flat = 0;
@@ -1851,6 +1923,9 @@
             }
 
             mLocked.orientedRanges.haveSize = true;
+
+            mLocked.orientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
+            mLocked.orientedRanges.size.source = mTouchSource;
             mLocked.orientedRanges.size.min = 0;
             mLocked.orientedRanges.size.max = 1.0;
             mLocked.orientedRanges.size.flat = 0;
@@ -1867,6 +1942,8 @@
                 }
             }
 
+            mLocked.orientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+            mLocked.orientedRanges.orientation.source = mTouchSource;
             mLocked.orientedRanges.orientation.min = - M_PI_2;
             mLocked.orientedRanges.orientation.max = M_PI_2;
             mLocked.orientedRanges.orientation.flat = 0;
@@ -2381,8 +2458,10 @@
 
     uint32_t policyFlags = 0;
     if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
-        // Hide the pointer on an initial down.
-        getContext()->fadePointer();
+        if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+            // If this is a touch screen, hide the pointer on an initial down.
+            getContext()->fadePointer();
+        }
 
         // Initial downs on external touch devices should wake the device.
         // We don't do this for internal touch screens to prevent them from waking
@@ -2396,7 +2475,7 @@
     // Process touches and virtual keys.
     TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
     if (touchResult == DISPATCH_TOUCH) {
-        detectGestures(when);
+        suppressSwipeOntoVirtualKeys(when);
         dispatchTouches(when, policyFlags);
     }
 
@@ -2523,7 +2602,7 @@
     return touchResult;
 }
 
-void TouchInputMapper::detectGestures(nsecs_t when) {
+void TouchInputMapper::suppressSwipeOntoVirtualKeys(nsecs_t when) {
     // Disable all virtual key touches that happen within a short time interval of the
     // most recent touch.  The idea is to filter out stray virtual key presses when
     // interacting with the touch screen.
@@ -2648,7 +2727,7 @@
         AutoMutex _l(mLock);
 
         // Walk through the the active pointers and map touch screen coordinates (TouchData) into
-        // display coordinates (PointerCoords) and adjust for display orientation.
+        // display or surface coordinates (PointerCoords) and adjust for display orientation.
         for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) {
             uint32_t id = idBits.firstMarkedBit();
             idBits.clearBit(id);
@@ -2869,7 +2948,7 @@
         yPrecision = mLocked.orientedYPrecision;
     } // release lock
 
-    getDispatcher()->notifyMotion(when, getDeviceId(), mSources, policyFlags,
+    getDispatcher()->notifyMotion(when, getDeviceId(), mTouchSource, policyFlags,
             motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
             pointerCount, pointerIds, pointerCoords,
             xPrecision, yPrecision, mDownTime);
@@ -3860,9 +3939,11 @@
 
     for (size_t i = 0; i < mAxes.size(); i++) {
         const Axis& axis = mAxes.valueAt(i);
-        info->addMotionRange(axis.axisInfo.axis, axis.min, axis.max, axis.flat, axis.fuzz);
+        info->addMotionRange(axis.axisInfo.axis, AINPUT_SOURCE_JOYSTICK,
+                axis.min, axis.max, axis.flat, axis.fuzz);
         if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
-            info->addMotionRange(axis.axisInfo.highAxis, axis.min, axis.max, axis.flat, axis.fuzz);
+            info->addMotionRange(axis.axisInfo.highAxis, AINPUT_SOURCE_JOYSTICK,
+                    axis.min, axis.max, axis.flat, axis.fuzz);
         }
     }
 }
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index b9e3494..68002ca 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -389,7 +389,7 @@
 
 class KeyboardInputMapper : public InputMapper {
 public:
-    KeyboardInputMapper(InputDevice* device, uint32_t sources, int32_t keyboardType);
+    KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
     virtual ~KeyboardInputMapper();
 
     virtual uint32_t getSources();
@@ -414,7 +414,7 @@
         int32_t scanCode;
     };
 
-    uint32_t mSources;
+    uint32_t mSource;
     int32_t mKeyboardType;
 
     // Immutable configuration parameters.
@@ -493,7 +493,7 @@
 
     struct Accumulator {
         enum {
-            FIELD_BTN_MOUSE = 1,
+            FIELD_BUTTONS = 1,
             FIELD_REL_X = 2,
             FIELD_REL_Y = 4,
             FIELD_REL_WHEEL = 8,
@@ -502,7 +502,9 @@
 
         uint32_t fields;
 
-        bool btnMouse;
+        uint32_t buttonDown;
+        uint32_t buttonUp;
+
         int32_t relX;
         int32_t relY;
         int32_t relWheel;
@@ -513,7 +515,7 @@
         }
     } mAccumulator;
 
-    int32_t mSources;
+    int32_t mSource;
     float mXScale;
     float mYScale;
     float mXPrecision;
@@ -527,7 +529,7 @@
     sp<PointerControllerInterface> mPointerController;
 
     struct LockedState {
-        bool down;
+        uint32_t buttonState;
         nsecs_t downTime;
     } mLocked;
 
@@ -629,7 +631,7 @@
     };
 
     // Input sources supported by the device.
-    int32_t mSources;
+    uint32_t mTouchSource; // sources when reporting touch data
 
     // Immutable configuration parameters.
     struct Parameters {
@@ -745,6 +747,10 @@
         int32_t surfaceOrientation;
         int32_t surfaceWidth, surfaceHeight;
 
+        // The associated display orientation and width and height set by configureSurfaceLocked().
+        int32_t associatedDisplayOrientation;
+        int32_t associatedDisplayWidth, associatedDisplayHeight;
+
         // Translation and scaling factors, orientation-independent.
         float xScale;
         float xPrecision;
@@ -870,7 +876,7 @@
     void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch,
             BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
             int32_t motionEventAction);
-    void detectGestures(nsecs_t when);
+    void suppressSwipeOntoVirtualKeys(nsecs_t when);
 
     bool isPointInsideSurfaceLocked(int32_t x, int32_t y);
     const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y);
@@ -900,7 +906,7 @@
             FIELD_ABS_X = 2,
             FIELD_ABS_Y = 4,
             FIELD_ABS_PRESSURE = 8,
-            FIELD_ABS_TOOL_WIDTH = 16
+            FIELD_ABS_TOOL_WIDTH = 16,
         };
 
         uint32_t fields;
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index e28dd7d..e1dab5c 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -31,10 +31,6 @@
 
 namespace android {
 
-enum {
-    POINTER_BUTTON_1 = 1 << 0,
-};
-
 /**
  * Interface for tracking a single (mouse) pointer.
  *
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index f7e1890..67a2e21 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -405,7 +405,8 @@
         String8 name;
         uint32_t classes;
         PropertyMap configuration;
-        KeyedVector<int, RawAbsoluteAxisInfo> axes;
+        KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
+        KeyedVector<int, bool> relativeAxes;
         KeyedVector<int32_t, int32_t> keyCodeStates;
         KeyedVector<int32_t, int32_t> scanCodeStates;
         KeyedVector<int32_t, int32_t> switchStates;
@@ -460,7 +461,7 @@
         device->configuration.addAll(configuration);
     }
 
-    void addAxis(int32_t deviceId, int axis,
+    void addAbsoluteAxis(int32_t deviceId, int axis,
             int32_t minValue, int32_t maxValue, int flat, int fuzz) {
         Device* device = getDevice(deviceId);
 
@@ -470,7 +471,12 @@
         info.maxValue = maxValue;
         info.flat = flat;
         info.fuzz = fuzz;
-        device->axes.add(axis, info);
+        device->absoluteAxes.add(axis, info);
+    }
+
+    void addRelativeAxis(int32_t deviceId, int32_t axis) {
+        Device* device = getDevice(deviceId);
+        device->relativeAxes.add(axis, true);
     }
 
     void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
@@ -560,9 +566,9 @@
             RawAbsoluteAxisInfo* outAxisInfo) const {
         Device* device = getDevice(deviceId);
         if (device) {
-            ssize_t index = device->axes.indexOfKey(axis);
+            ssize_t index = device->absoluteAxes.indexOfKey(axis);
             if (index >= 0) {
-                *outAxisInfo = device->axes.valueAt(index);
+                *outAxisInfo = device->absoluteAxes.valueAt(index);
                 return OK;
             }
         }
@@ -570,6 +576,10 @@
     }
 
     virtual bool hasRelativeAxis(int32_t deviceId, int axis) const {
+        Device* device = getDevice(deviceId);
+        if (device) {
+            return device->relativeAxes.indexOfKey(axis) >= 0;
+        }
         return false;
     }
 
@@ -1487,13 +1497,15 @@
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
-            int32_t rangeType, float min, float max, float flat, float fuzz) {
-        const InputDeviceInfo::MotionRange* range = info.getMotionRange(rangeType);
-        ASSERT_TRUE(range != NULL) << "Range: " << rangeType;
-        ASSERT_NEAR(min, range->min, EPSILON) << "Range: " << rangeType;
-        ASSERT_NEAR(max, range->max, EPSILON) << "Range: " << rangeType;
-        ASSERT_NEAR(flat, range->flat, EPSILON) << "Range: " << rangeType;
-        ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Range: " << rangeType;
+            int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
+        const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
+        ASSERT_TRUE(range != NULL) << "Axis: " << axis << " Source: " << source;
+        ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
+        ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
+        ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
+        ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
+        ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
+        ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
     }
 
     static void assertPointerCoords(const PointerCoords& coords,
@@ -2001,10 +2013,10 @@
     mapper->populateDeviceInfo(&info);
 
     // Initially there may not be a valid motion range.
-    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X));
-    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y));
-    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
-            0.0f, 1.0f, 0.0f, 0.0f));
+    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
+    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
+            AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
 
     // When the bounds are set, then there should be a valid motion range.
     mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
@@ -2012,11 +2024,14 @@
     InputDeviceInfo info2;
     mapper->populateDeviceInfo(&info2);
 
-    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X,
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
+            AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
             1, 800 - 1, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y,
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
+            AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE,
             2, 480 - 1, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
+            AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE,
             0.0f, 1.0f, 0.0f, 0.0f));
 }
 
@@ -2028,11 +2043,14 @@
     InputDeviceInfo info;
     mapper->populateDeviceInfo(&info);
 
-    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X,
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
+            AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
             -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
-    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y,
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
+            AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL,
             -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
-    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
+            AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TRACKBALL,
             0.0f, 1.0f, 0.0f, 0.0f));
 }
 
@@ -2385,14 +2403,18 @@
 
 void SingleTouchInputMapperTest::prepareAxes(int axes) {
     if (axes & POSITION) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_X,
+                RAW_X_MIN, RAW_X_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_Y,
+                RAW_Y_MIN, RAW_Y_MAX, 0, 0);
     }
     if (axes & PRESSURE) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_PRESSURE, RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_PRESSURE,
+                RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
     }
     if (axes & TOOL) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TOOL_WIDTH,
+                RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
     }
 }
 
@@ -3040,33 +3062,37 @@
 
 void MultiTouchInputMapperTest::prepareAxes(int axes) {
     if (axes & POSITION) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_X,
+                RAW_X_MIN, RAW_X_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_Y,
+                RAW_Y_MIN, RAW_Y_MAX, 0, 0);
     }
     if (axes & TOUCH) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR,
+                RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
         if (axes & MINOR) {
-            mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR,
+            mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR,
                     RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
         }
     }
     if (axes & TOOL) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR,
+                RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
         if (axes & MINOR) {
-            mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR,
+            mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR,
                     RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0);
         }
     }
     if (axes & ORIENTATION) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_ORIENTATION,
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_ORIENTATION,
                 RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0);
     }
     if (axes & PRESSURE) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_PRESSURE,
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_PRESSURE,
                 RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
     }
     if (axes & ID) {
-        mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TRACKING_ID,
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TRACKING_ID,
                 RAW_ID_MIN, RAW_ID_MAX, 0, 0);
     }
 }
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index bd4e787..80dddc2 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -1103,12 +1103,11 @@
     env->SetIntField(deviceObj, gInputDeviceClassInfo.mSources, deviceInfo.getSources());
     env->SetIntField(deviceObj, gInputDeviceClassInfo.mKeyboardType, deviceInfo.getKeyboardType());
 
-    const KeyedVector<int, InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
+    const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     for (size_t i = 0; i < ranges.size(); i++) {
-        int rangeType = ranges.keyAt(i);
-        const InputDeviceInfo::MotionRange& range = ranges.valueAt(i);
+        const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
         env->CallVoidMethod(deviceObj, gInputDeviceClassInfo.addMotionRange,
-                rangeType, range.min, range.max, range.flat, range.fuzz);
+                range.axis, range.source, range.min, range.max, range.flat, range.fuzz);
         if (env->ExceptionCheck()) {
             return NULL;
         }
@@ -1321,7 +1320,7 @@
             "<init>", "()V");
 
     GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
-            "addMotionRange", "(IFFFF)V");
+            "addMotionRange", "(IIFFFF)V");
 
     GET_FIELD_ID(gInputDeviceClassInfo.mId, gInputDeviceClassInfo.clazz,
             "mId", "I");