Merge "Add support for arbitrary axes in MotionEvents."
diff --git a/api/11.xml b/api/11.xml
index 6a1f909..76669e3 100644
--- a/api/11.xml
+++ b/api/11.xml
@@ -207458,22 +207458,22 @@
 </method>
 <method name="getDeviceId"
  return="int"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
 </method>
 <method name="getSource"
  return="int"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
diff --git a/api/current.xml b/api/current.xml
index e56bff8..9a38459 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -210585,7 +210585,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="rangeType" type="int">
+<parameter name="axis" type="int">
 </parameter>
 </method>
 <method name="getName"
@@ -210675,7 +210675,7 @@
  value="8"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210686,7 +210686,7 @@
  value="2"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210697,7 +210697,7 @@
  value="3"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210708,7 +210708,7 @@
  value="6"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210719,7 +210719,7 @@
  value="7"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210730,7 +210730,7 @@
  value="4"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210741,7 +210741,7 @@
  value="5"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210752,7 +210752,7 @@
  value="0"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -210763,7 +210763,7 @@
  value="1"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -211042,22 +211042,22 @@
 </method>
 <method name="getDeviceId"
  return="int"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
 </method>
 <method name="getSource"
  return="int"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -211847,6 +211847,17 @@
 <parameter name="c" type="int">
 </parameter>
 </method>
+<method name="getDeviceId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDisplayLabel"
  return="char"
  abstract="false"
@@ -212020,6 +212031,17 @@
  visibility="public"
 >
 </method>
+<method name="getSource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getUnicodeChar"
  return="int"
  abstract="false"
@@ -212276,6 +212298,19 @@
 <parameter name="metaState" type="int">
 </parameter>
 </method>
+<method name="setSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="int">
+</parameter>
+</method>
 <method name="startTracking"
  return="void"
  abstract="false"
@@ -216532,6 +216567,45 @@
  visibility="public"
 >
 </method>
+<method name="getAxisValue"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="axis" type="int">
+</parameter>
+</method>
+<method name="getAxisValue"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="axis" type="int">
+</parameter>
+<parameter name="pointerIndex" type="int">
+</parameter>
+</method>
+<method name="getDeviceId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDownTime"
  return="long"
  abstract="false"
@@ -216576,6 +216650,38 @@
  visibility="public"
 >
 </method>
+<method name="getHistoricalAxisValue"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="axis" type="int">
+</parameter>
+<parameter name="pos" type="int">
+</parameter>
+</method>
+<method name="getHistoricalAxisValue"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="axis" type="int">
+</parameter>
+<parameter name="pointerIndex" type="int">
+</parameter>
+<parameter name="pos" type="int">
+</parameter>
+</method>
 <method name="getHistoricalEventTime"
  return="long"
  abstract="false"
@@ -217013,6 +217119,17 @@
 <parameter name="pointerIndex" type="int">
 </parameter>
 </method>
+<method name="getSource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getToolMajor"
  return="float"
  abstract="false"
@@ -217321,7 +217438,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="android.view.MotionEvent">
+<parameter name="other" type="android.view.MotionEvent">
 </parameter>
 </method>
 <method name="obtainNoHistory"
@@ -217334,7 +217451,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="android.view.MotionEvent">
+<parameter name="other" type="android.view.MotionEvent">
 </parameter>
 </method>
 <method name="offsetLocation"
@@ -217404,6 +217521,19 @@
 <parameter name="y" type="float">
 </parameter>
 </method>
+<method name="setSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="int">
+</parameter>
+</method>
 <method name="transform"
  return="void"
  abstract="false"
@@ -217630,6 +217760,105 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_ORIENTATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_PRESSURE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_SIZE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_TOOL_MAJOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_TOOL_MINOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_TOUCH_MAJOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_TOUCH_MINOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_X"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_Y"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CREATOR"
  type="android.os.Parcelable.Creator"
  transient="false"
@@ -217712,6 +217941,68 @@
  visibility="public"
 >
 </constructor>
+<constructor name="MotionEvent.PointerCoords"
+ type="android.view.MotionEvent.PointerCoords"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.view.MotionEvent.PointerCoords">
+</parameter>
+</constructor>
+<method name="clear"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="copyFrom"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.view.MotionEvent.PointerCoords">
+</parameter>
+</method>
+<method name="getAxisValue"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="axis" type="int">
+</parameter>
+</method>
+<method name="setAxisValue"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="axis" type="int">
+</parameter>
+<parameter name="value" type="float">
+</parameter>
+</method>
 <field name="orientation"
  type="float"
  transient="false"
@@ -265821,7 +266112,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index e799f76..7abbce6 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -20,6 +20,7 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.SparseArray;
 
 /**
  * Describes the capabilities of a particular input device.
@@ -41,9 +42,9 @@
     private String mName;
     private int mSources;
     private int mKeyboardType;
-    
-    private MotionRange[] mMotionRanges;
-    
+
+    private final SparseArray<MotionRange> mMotionRanges = new SparseArray<MotionRange>();
+
     /**
      * A mask for input source classes.
      * 
@@ -181,70 +182,85 @@
     public static final int SOURCE_ANY = 0xffffff00;
 
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#x}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_X}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_X} instead.
      */
-    public static final int MOTION_RANGE_X = 0;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_X = MotionEvent.AXIS_X;
+
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#y}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_Y}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_Y} instead.
      */
-    public static final int MOTION_RANGE_Y = 1;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_Y = MotionEvent.AXIS_Y;
+
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#pressure}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_PRESSURE}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_PRESSURE} instead.
      */
-    public static final int MOTION_RANGE_PRESSURE = 2;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_PRESSURE = MotionEvent.AXIS_PRESSURE;
+
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#size}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_SIZE}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_SIZE} instead.
      */
-    public static final int MOTION_RANGE_SIZE = 3;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_SIZE = MotionEvent.AXIS_SIZE;
+
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#touchMajor}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MAJOR}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MAJOR} instead.
      */
-    public static final int MOTION_RANGE_TOUCH_MAJOR = 4;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_TOUCH_MAJOR = MotionEvent.AXIS_TOUCH_MAJOR;
+
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#touchMinor}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MINOR}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MINOR} instead.
      */
-    public static final int MOTION_RANGE_TOUCH_MINOR = 5;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_TOUCH_MINOR = MotionEvent.AXIS_TOUCH_MINOR;
+
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#toolMajor}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MAJOR}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_TOOL_MAJOR} instead.
      */
-    public static final int MOTION_RANGE_TOOL_MAJOR = 6;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_TOOL_MAJOR = MotionEvent.AXIS_TOOL_MAJOR;
+
     /**
-     * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#toolMinor}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MINOR}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_TOOL_MINOR} instead.
      */
-    public static final int MOTION_RANGE_TOOL_MINOR = 7;
-    
+    @Deprecated
+    public static final int MOTION_RANGE_TOOL_MINOR = MotionEvent.AXIS_TOOL_MINOR;
+
     /**
-     * Constant for retrieving the range of values for
-     * {@link MotionEvent.PointerCoords#orientation}.
+     * Constant for retrieving the range of values for {@link MotionEvent#AXIS_ORIENTATION}.
      * 
      * @see #getMotionRange
+     * @deprecated Use {@link MotionEvent#AXIS_ORIENTATION} instead.
      */
-    public static final int MOTION_RANGE_ORIENTATION = 8;
-    
-    private static final int MOTION_RANGE_LAST = MOTION_RANGE_ORIENTATION;
+    @Deprecated
+    public static final int MOTION_RANGE_ORIENTATION = MotionEvent.AXIS_ORIENTATION;
     
     /**
      * There is no keyboard.
@@ -261,10 +277,9 @@
      * The keyboard supports a complement of alphabetic keys.
      */
     public static final int KEYBOARD_TYPE_ALPHABETIC = 2;
-    
+
     // Called by native code.
     private InputDevice() {
-        mMotionRanges = new MotionRange[MOTION_RANGE_LAST + 1];
     }
 
     /**
@@ -335,72 +350,69 @@
     public KeyCharacterMap getKeyCharacterMap() {
         return KeyCharacterMap.load(mId);
     }
-    
+
     /**
-     * Gets information about the range of values for a particular {@link MotionEvent}
-     * coordinate.
-     * @param rangeType The motion range constant.
-     * @return The range of values, or null if the requested coordinate is not
+     * Gets information about the range of values for a particular {@link MotionEvent} axis.
+     * @param axis The axis constant.
+     * @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
      */
-    public MotionRange getMotionRange(int rangeType) {
-        if (rangeType < 0 || rangeType > MOTION_RANGE_LAST) {
-            throw new IllegalArgumentException("Requested range is out of bounds.");
-        }
-        
-        return mMotionRanges[rangeType];
+    public MotionRange getMotionRange(int axis) {
+        return mMotionRanges.get(axis);
     }
-    
-    private void addMotionRange(int rangeType, float min, float max, float flat, float fuzz) {
-        if (rangeType >= 0 && rangeType <= MOTION_RANGE_LAST) {
-            MotionRange range = new MotionRange(min, max, flat, fuzz);
-            mMotionRanges[rangeType] = range;
-        }
+
+    // Called by native code.
+    private void addMotionRange(int axis, float min, float max, float flat, float fuzz) {
+        mMotionRanges.append(axis, new MotionRange(min, max, flat, fuzz));
     }
-    
+
     /**
-     * Provides information about the range of values for a particular {@link MotionEvent}
-     * coordinate.
+     * Provides information about the range of values for a particular {@link MotionEvent} axis.
+     *
+     * @see InputDevice#getMotionRange(int)
      */
     public static final class MotionRange {
         private float mMin;
         private float mMax;
         private float mFlat;
         private float mFuzz;
-        
+
         private MotionRange(float min, float max, float flat, float fuzz) {
             mMin = min;
             mMax = max;
             mFlat = flat;
             mFuzz = fuzz;
         }
-        
+
         /**
-         * Gets the minimum value for the coordinate.
-         * @return The minimum value.
+         * Gets the minimum value for the axis.
+         * @return The (inclusive) minimum value.
          */
         public float getMin() {
             return mMin;
         }
-        
+
         /**
-         * Gets the maximum value for the coordinate.
-         * @return The minimum value.
+         * Gets the maximum value for the axis.
+         * @return The (inclusive) maximum value.
          */
         public float getMax() {
             return mMax;
         }
-        
+
         /**
-         * Gets the range of the coordinate (difference between maximum and minimum).
+         * Gets the range of the axis (difference between maximum and minimum plus one).
          * @return The range of values.
          */
         public float getRange() {
-            return mMax - mMin;
+            return mMax - mMin + 1;
         }
-        
+
         /**
-         * Gets the extent of the center flat position with respect to this coordinate.
+         * Gets the extent of the center flat position with respect to this axis.
          * For example, a flat value of 8 means that the center position is between -8 and +8.
          * This value is mainly useful for calibrating self-centering devices.
          * @return The extent of the center flat position.
@@ -408,9 +420,9 @@
         public float getFlat() {
             return mFlat;
         }
-        
+
         /**
-         * Gets the error tolerance for input device measurements with respect to this coordinate.
+         * Gets the error tolerance for input device measurements with respect to this axis.
          * For example, a value of 2 indicates that the measured value may be up to +/- 2 units
          * away from the actual value due to noise and device sensitivity limitations.
          * @return The error tolerance.
@@ -419,7 +431,7 @@
             return mFuzz;
         }
     }
-    
+
     public static final Parcelable.Creator<InputDevice> CREATOR
             = new Parcelable.Creator<InputDevice>() {
         public InputDevice createFromParcel(Parcel in) {
@@ -438,15 +450,13 @@
         mName = in.readString();
         mSources = in.readInt();
         mKeyboardType = in.readInt();
-        
+
         for (;;) {
-            int rangeType = in.readInt();
-            if (rangeType < 0) {
+            int axis = in.readInt();
+            if (axis < 0) {
                 break;
             }
-            
-            addMotionRange(rangeType,
-                    in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
+            addMotionRange(axis, in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
         }
     }
 
@@ -456,25 +466,25 @@
         out.writeString(mName);
         out.writeInt(mSources);
         out.writeInt(mKeyboardType);
-        
-        for (int i = 0; i <= MOTION_RANGE_LAST; i++) {
-            MotionRange range = mMotionRanges[i];
-            if (range != null) {
-                out.writeInt(i);
-                out.writeFloat(range.mMin);
-                out.writeFloat(range.mMax);
-                out.writeFloat(range.mFlat);
-                out.writeFloat(range.mFuzz);
-            }
+
+        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);
+            out.writeFloat(range.mMin);
+            out.writeFloat(range.mMax);
+            out.writeFloat(range.mFlat);
+            out.writeFloat(range.mFuzz);
         }
         out.writeInt(-1);
     }
-    
+
     @Override
     public int describeContents() {
         return 0;
     }
-    
+
     @Override
     public String toString() {
         StringBuilder description = new StringBuilder();
@@ -493,29 +503,32 @@
                 break;
         }
         description.append("\n");
-        
-        description.append("  Sources:");
+
+        description.append("  Sources: ").append(Integer.toHexString(mSources)).append(" (");
         appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard");
         appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
         appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHSCREEN, "touchscreen");
         appendSourceDescriptionIfApplicable(description, SOURCE_MOUSE, "mouse");
         appendSourceDescriptionIfApplicable(description, SOURCE_TRACKBALL, "trackball");
         appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad");
-        description.append("\n");
-        
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_X, "x");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_Y, "y");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_PRESSURE, "pressure");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_SIZE, "size");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOUCH_MAJOR, "touchMajor");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOUCH_MINOR, "touchMinor");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOOL_MAJOR, "toolMajor");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOOL_MINOR, "toolMinor");
-        appendRangeDescriptionIfApplicable(description, MOTION_RANGE_ORIENTATION, "orientation");
-        
+        appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK, "joystick");
+        appendSourceDescriptionIfApplicable(description, SOURCE_GAMEPAD, "gamepad");
+        description.append(" )\n");
+
+        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);
+            description.append(" max=").append(range.mMax);
+            description.append(" flat=").append(range.mFlat);
+            description.append(" fuzz=").append(range.mFuzz);
+            description.append("\n");
+        }
         return description.toString();
     }
-    
+
     private void appendSourceDescriptionIfApplicable(StringBuilder description, int source,
             String sourceName) {
         if ((mSources & source) == source) {
@@ -523,17 +536,4 @@
             description.append(sourceName);
         }
     }
-    
-    private void appendRangeDescriptionIfApplicable(StringBuilder description,
-            int rangeType, String rangeName) {
-        MotionRange range = mMotionRanges[rangeType];
-        if (range != null) {
-            description.append("  Range[").append(rangeName);
-            description.append("]: min=").append(range.mMin);
-            description.append(" max=").append(range.mMax);
-            description.append(" flat=").append(range.mFlat);
-            description.append(" fuzz=").append(range.mFuzz);
-            description.append("\n");
-        }
-    }
 }
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 184e0fc..f6aeb39 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -24,11 +24,6 @@
  */
 public abstract class InputEvent implements Parcelable {
     /** @hide */
-    protected int mDeviceId;
-    /** @hide */
-    protected int mSource;
-    
-    /** @hide */
     protected static final int PARCEL_TOKEN_MOTION_EVENT = 1;
     /** @hide */
     protected static final int PARCEL_TOKEN_KEY_EVENT = 2;
@@ -45,55 +40,37 @@
      * @return The device id.
      * @see InputDevice#getDevice
      */
-    public final int getDeviceId() {
-        return mDeviceId;
-    }
-    
+    public abstract int getDeviceId();
+
     /**
      * Gets the device that this event came from.
      * 
      * @return The device, or null if unknown.
      */
     public final InputDevice getDevice() {
-        return InputDevice.getDevice(mDeviceId);
+        return InputDevice.getDevice(getDeviceId());
     }
-    
+
     /**
      * Gets the source of the event.
      * 
      * @return The event source or {@link InputDevice#SOURCE_UNKNOWN} if unknown.
      * @see InputDevice#getSourceInfo
      */
-    public final int getSource() {
-        return mSource;
-    }
-    
+    public abstract int getSource();
+
     /**
      * Modifies the source of the event.
-     * @param source The source.
-     * 
+     *
+     * @param source The new source.
      * @hide
      */
-    public final void setSource(int source) {
-        mSource = source;
-    }
-    
+    public abstract void setSource(int source);
+
     public int describeContents() {
         return 0;
     }
-    
-    /** @hide */
-    protected final void readBaseFromParcel(Parcel in) {
-        mDeviceId = in.readInt();
-        mSource = in.readInt();
-    }
-    
-    /** @hide */
-    protected final void writeBaseToParcel(Parcel out) {
-        out.writeInt(mDeviceId);
-        out.writeInt(mSource);
-    }
-    
+
     public static final Parcelable.Creator<InputEvent> CREATOR
             = new Parcelable.Creator<InputEvent>() {
         public InputEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 766969a..3f6a04b 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1180,6 +1180,8 @@
     private KeyEvent mNext;
     private boolean mRecycled;
 
+    private int mDeviceId;
+    private int mSource;
     private int mMetaState;
     private int mAction;
     private int mKeyCode;
@@ -1651,6 +1653,23 @@
         return native_hasDefaultAction(mKeyCode);
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public final int getDeviceId() {
+        return mDeviceId;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getSource() {
+        return mSource;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void setSource(int source) {
+        mSource = source;
+    }
 
     /**
      * <p>Returns the state of the meta keys.</p>
@@ -2651,8 +2670,8 @@
     }
     
     private KeyEvent(Parcel in) {
-        readBaseFromParcel(in);
-        
+        mDeviceId = in.readInt();
+        mSource = in.readInt();
         mAction = in.readInt();
         mKeyCode = in.readInt();
         mRepeatCount = in.readInt();
@@ -2665,9 +2684,9 @@
 
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(PARCEL_TOKEN_KEY_EVENT);
-        
-        writeBaseToParcel(out);
-        
+
+        out.writeInt(mDeviceId);
+        out.writeInt(mSource);
         out.writeInt(mAction);
         out.writeInt(mKeyCode);
         out.writeInt(mRepeatCount);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 5db4895..6673be2 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -102,7 +102,7 @@
  * </p>
  */
 public final class MotionEvent extends InputEvent implements Parcelable {
-    private static final long MS_PER_NS = 1000000;
+    private static final long NS_PER_MS = 1000000;
     private static final boolean TRACK_RECYCLED_LOCATION = false;
     
     /**
@@ -261,123 +261,241 @@
      */
     public static final int EDGE_RIGHT = 0x00000008;
 
-    /*
-     * Offset for the sample's X coordinate.
+    /**
+     * Constant used to identify the X axis of a motion event.
+     *
+     * The interpretation of the X axis varies by input source.
+     * It may represent the X position of the center of the touch contact area,
+     * a relative horizontal displacement of a trackball or joystick, or something else.
+     *
+     * @see #getX(int)
+     * @see #getHistoricalX(int, int)
+     * @see MotionEvent.PointerCoords#x
+     * @see InputDevice#getMotionRange
      */
-    static private final int SAMPLE_X = 0;
-    
-    /*
-     * Offset for the sample's Y coordinate.
-     */
-    static private final int SAMPLE_Y = 1;
-    
-    /*
-     * Offset for the sample's pressure.
-     */
-    static private final int SAMPLE_PRESSURE = 2;
-    
-    /*
-     * Offset for the sample's size
-     */
-    static private final int SAMPLE_SIZE = 3;
-    
-    /*
-     * Offset for the sample's touch major axis length.
-     */
-    static private final int SAMPLE_TOUCH_MAJOR = 4;
+    public static final int AXIS_X = 0;
 
-    /*
-     * Offset for the sample's touch minor axis length.
+    /**
+     * Constant used to identify the Y axis of a motion event.
+     * 
+     * The interpretation of the Y axis varies by input source.
+     * It may represent the Y position of the center of the touch contact area,
+     * a relative vertical displacement of a trackball or joystick, or something else.
+     *
+     * @see #getY(int)
+     * @see #getHistoricalY(int, int)
+     * @see MotionEvent.PointerCoords#y
+     * @see InputDevice#getMotionRange
      */
-    static private final int SAMPLE_TOUCH_MINOR = 5;
-    
-    /*
-     * Offset for the sample's tool major axis length.
-     */
-    static private final int SAMPLE_TOOL_MAJOR = 6;
+    public static final int AXIS_Y = 1;
 
-    /*
-     * Offset for the sample's tool minor axis length.
+    /**
+     * Constant used to identify the Pressure axis of a motion event.
+     *
+     * The pressure axis specifies a normalized value that describes the approximate
+     * pressure applied to the device by a finger or other tool.
+     * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
+     * although values higher than 1 may be generated depending on the calibration of
+     * the input device.
+     *
+     * @see #getPressure(int)
+     * @see #getHistoricalPressure(int, int)
+     * @see MotionEvent.PointerCoords#pressure
+     * @see InputDevice#getMotionRange
      */
-    static private final int SAMPLE_TOOL_MINOR = 7;
-    
-    /*
-     * Offset for the sample's orientation.
-     */
-    static private final int SAMPLE_ORIENTATION = 8;
+    public static final int AXIS_PRESSURE = 2;
 
-    /*
-     * Number of data items for each sample.
+    /**
+     * Constant used to identify the Size axis of a motion event.
+     *
+     * The size axis specifies a normalized value that describes the approximate size
+     * of the pointer touch area in relation to the maximum detectable size for the device.
+     * It represents some approximation of the area of the screen being
+     * pressed; the actual value in pixels corresponding to the
+     * touch is normalized with the device specific range of values
+     * and scaled to a value between 0 and 1. The value of size can be used to
+     * determine fat touch events.
+     *
+     * To obtain calibrated size information in terms of pixels, use
+     * {@link #AXIS_TOUCH_MAJOR} or {@link #AXIS_TOOL_MAJOR} instead.
+     *
+     * @see #getSize(int)
+     * @see #getHistoricalSize(int, int)
+     * @see MotionEvent.PointerCoords#size
+     * @see InputDevice#getMotionRange
      */
-    static private final int NUM_SAMPLE_DATA = 9;
-    
-    /*
-     * Minimum number of pointers for which to reserve space when allocating new
-     * motion events.  This is explicitly not a bound on the maximum number of pointers.
+    public static final int AXIS_SIZE = 3;
+
+    /**
+     * Constant used to identify the TouchMajor axis of a motion event.
+     *
+     * The touch major axis specifies the length of the major axis of an ellipse that
+     * describes the touch area at the point of contact.
+     * If the device is a touch screen, the length is reported in pixels, otherwise it is
+     * reported in device-specific units.
+     *
+     * @see #getTouchMajor(int)
+     * @see #getHistoricalTouchMajor(int, int)
+     * @see MotionEvent.PointerCoords#touchMajor
+     * @see InputDevice#getMotionRange
      */
-    static private final int BASE_AVAIL_POINTERS = 5;
-    
-    /*
-     * Minimum number of samples for which to reserve space when allocating new motion events.
+    public static final int AXIS_TOUCH_MAJOR = 4;
+
+    /**
+     * Constant used to identify the TouchMinor axis of a motion event.
+     *
+     * The touch major axis specifies the length of the minor axis of an ellipse that
+     * describes the touch area at the point of contact.
+     * If the device is a touch screen, the length is reported in pixels, otherwise it is
+     * reported in device-specific units.
+     *
+     * @see #getTouchMinor(int)
+     * @see #getHistoricalTouchMinor(int, int)
+     * @see MotionEvent.PointerCoords#touchMinor
+     * @see InputDevice#getMotionRange
      */
-    static private final int BASE_AVAIL_SAMPLES = 8;
-    
+    public static final int AXIS_TOUCH_MINOR = 5;
+
+    /**
+     * Constant used to identify the ToolMajor axis of a motion event.
+     *
+     * The tool major axis specifies the length of the major axis of an ellipse that
+     * describes the size of the approaching tool.
+     * The tool area represents the estimated size of the finger or pen that is
+     * touching the device independent of its actual touch area at the point of contact.
+     * If the device is a touch screen, the length is reported in pixels, otherwise it is
+     * reported in device-specific units.
+     *
+     * @see #getToolMajor(int)
+     * @see #getHistoricalToolMajor(int, int)
+     * @see MotionEvent.PointerCoords#toolMajor
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_TOOL_MAJOR = 6;
+
+    /**
+     * Constant used to identify the ToolMinor axis of a motion event.
+     *
+     * The tool minor axis specifies the length of the major axis of an ellipse that
+     * describes the size of the approaching tool.
+     * The tool area represents the estimated size of the finger or pen that is
+     * touching the device independent of its actual touch area at the point of contact.
+     * If the device is a touch screen, the length is reported in pixels, otherwise it is
+     * reported in device-specific units.
+     *
+     * @see #getToolMinor(int)
+     * @see #getHistoricalToolMinor(int, int)
+     * @see MotionEvent.PointerCoords#toolMinor
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_TOOL_MINOR = 7;
+
+    /**
+     * Constant used to identify the Orientation axis of a motion event.
+     *
+     * The orientation axis specifies the orientation of the touch area and tool area in
+     * radians clockwise from vertical relative to the vertical plane of the device.
+     * An angle of 0 degrees indicates that the major axis of contact is oriented
+     * 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/2 radians (finger pointing fully left) to PI/2 radians
+     * (finger pointing fully right).
+     *
+     * @see #getOrientation(int)
+     * @see #getHistoricalOrientation(int, int)
+     * @see MotionEvent.PointerCoords#orientation
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_ORIENTATION = 8;
+
+    // Private value for history pos that obtains the current sample.
+    private static final int HISTORY_CURRENT = -0x80000000;
+
     private static final int MAX_RECYCLED = 10;
     private static final Object gRecyclerLock = new Object();
     private static int gRecyclerUsed;
     private static MotionEvent gRecyclerTop;
 
-    private long mDownTimeNano;
-    private int mAction;
-    private float mXOffset;
-    private float mYOffset;
-    private float mXPrecision;
-    private float mYPrecision;
-    private int mEdgeFlags;
-    private int mMetaState;
-    private int mFlags;
-    
-    private int mNumPointers;
-    private int mNumSamples;
-    
-    private int mLastDataSampleIndex;
-    private int mLastEventTimeNanoSampleIndex;
-    
-    // Array of mNumPointers size of identifiers for each pointer of data.
-    private int[] mPointerIdentifiers;
-    
-    // Array of (mNumSamples * mNumPointers * NUM_SAMPLE_DATA) size of event data.
-    // Samples are ordered from oldest to newest.
-    private float[] mDataSamples;
-    
-    // Array of mNumSamples size of event time stamps in nanoseconds.
-    // Samples are ordered from oldest to newest.
-    private long[] mEventTimeNanoSamples;
+    // Shared temporary objects used when translating coordinates supplied by
+    // the caller into single element PointerCoords and pointer id arrays.
+    // Must lock gTmpPointerCoords prior to use.
+    private static final PointerCoords[] gTmpPointerCoords =
+            new PointerCoords[] { new PointerCoords() };
+    private static final int[] gTmpPointerIds = new int[] { 0 /*always 0*/ };
+
+    // Pointer to the native MotionEvent object that contains the actual data.
+    private int mNativePtr;
 
     private MotionEvent mNext;
     private RuntimeException mRecycledLocation;
     private boolean mRecycled;
 
-    private native void nativeTransform(Matrix matrix);
+    private static native int nativeInitialize(int nativePtr,
+            int deviceId, int source, int action, int flags, int edgeFlags, int metaState,
+            float xOffset, float yOffset, float xPrecision, float yPrecision,
+            long downTimeNanos, long eventTimeNanos,
+            int pointerCount, int[] pointerIds, PointerCoords[] pointerCoords);
+    private static native int nativeCopy(int destNativePtr, int sourceNativePtr,
+            boolean keepHistory);
+    private static native void nativeDispose(int nativePtr);
+    private static native void nativeAddBatch(int nativePtr, long eventTimeNanos,
+            PointerCoords[] pointerCoords, int metaState);
 
-    private MotionEvent(int pointerCount, int sampleCount) {
-        mPointerIdentifiers = new int[pointerCount];
-        mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
-        mEventTimeNanoSamples = new long[sampleCount];
+    private static native int nativeGetDeviceId(int nativePtr);
+    private static native int nativeGetSource(int nativePtr);
+    private static native int nativeSetSource(int nativePtr, int source);
+    private static native int nativeGetAction(int nativePtr);
+    private static native void nativeSetAction(int nativePtr, int action);
+    private static native int nativeGetFlags(int nativePtr);
+    private static native int nativeGetEdgeFlags(int nativePtr);
+    private static native void nativeSetEdgeFlags(int nativePtr, int action);
+    private static native int nativeGetMetaState(int nativePtr);
+    private static native void nativeOffsetLocation(int nativePtr, float deltaX, float deltaY);
+    private static native float nativeGetXPrecision(int nativePtr);
+    private static native float nativeGetYPrecision(int nativePtr);
+    private static native long nativeGetDownTimeNanos(int nativePtr);
+
+    private static native int nativeGetPointerCount(int nativePtr);
+    private static native int nativeGetPointerId(int nativePtr, int pointerIndex);
+    private static native int nativeFindPointerIndex(int nativePtr, int pointerId);
+
+    private static native int nativeGetHistorySize(int nativePtr);
+    private static native long nativeGetEventTimeNanos(int nativePtr, int historyPos);
+    private static native float nativeGetRawAxisValue(int nativePtr,
+            int axis, int pointerIndex, int historyPos);
+    private static native float nativeGetAxisValue(int nativePtr,
+            int axis, int pointerIndex, int historyPos);
+    private static native void nativeGetPointerCoords(int nativePtr,
+            int pointerIndex, int historyPos, PointerCoords outPointerCoords);
+
+    private static native void nativeScale(int nativePtr, float scale);
+    private static native void nativeTransform(int nativePtr, Matrix matrix);
+
+    private static native int nativeReadFromParcel(int nativePtr, Parcel parcel);
+    private static native void nativeWriteToParcel(int nativePtr, Parcel parcel);
+
+    private MotionEvent() {
     }
 
-    static private MotionEvent obtain(int pointerCount, int sampleCount) {
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mNativePtr != 0) {
+                nativeDispose(mNativePtr);
+                mNativePtr = 0;
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    static private MotionEvent obtain() {
         final MotionEvent ev;
         synchronized (gRecyclerLock) {
             ev = gRecyclerTop;
             if (ev == null) {
-                if (pointerCount < BASE_AVAIL_POINTERS) {
-                    pointerCount = BASE_AVAIL_POINTERS;
-                }
-                if (sampleCount < BASE_AVAIL_SAMPLES) {
-                    sampleCount = BASE_AVAIL_SAMPLES;
-                }
-                return new MotionEvent(pointerCount, sampleCount);
+                return new MotionEvent();
             }
             gRecyclerTop = ev.mNext;
             gRecyclerUsed -= 1;
@@ -385,23 +503,9 @@
         ev.mRecycledLocation = null;
         ev.mRecycled = false;
         ev.mNext = null;
-        
-        if (ev.mPointerIdentifiers.length < pointerCount) {
-            ev.mPointerIdentifiers = new int[pointerCount];
-        }
-        
-        if (ev.mEventTimeNanoSamples.length < sampleCount) {
-            ev.mEventTimeNanoSamples = new long[sampleCount];
-        }
-        
-        final int neededDataSamplesLength = pointerCount * sampleCount * NUM_SAMPLE_DATA;
-        if (ev.mDataSamples.length < neededDataSamplesLength) {
-            ev.mDataSamples = new float[neededDataSamplesLength];
-        }
-        
         return ev;
     }
-    
+
     /**
      * Create a new MotionEvent, filling in all of the basic values that
      * define the motion.
@@ -434,34 +538,15 @@
             int action, int pointers, int[] pointerIds, PointerCoords[] pointerCoords,
             int metaState, float xPrecision, float yPrecision, int deviceId,
             int edgeFlags, int source, int flags) {
-        MotionEvent ev = obtain(pointers, 1);
-        ev.mDeviceId = deviceId;
-        ev.mSource = source;
-        ev.mEdgeFlags = edgeFlags;
-        ev.mDownTimeNano = downTime * MS_PER_NS;
-        ev.mAction = action;
-        ev.mFlags = flags;
-        ev.mMetaState = metaState;
-        ev.mXOffset = 0;
-        ev.mYOffset = 0;
-        ev.mXPrecision = xPrecision;
-        ev.mYPrecision = yPrecision;
-        
-        ev.mNumPointers = pointers;
-        ev.mNumSamples = 1;
-        
-        ev.mLastDataSampleIndex = 0;
-        ev.mLastEventTimeNanoSampleIndex = 0;
-        
-        System.arraycopy(pointerIds, 0, ev.mPointerIdentifiers, 0, pointers);
-        
-        ev.mEventTimeNanoSamples[0] = eventTime * MS_PER_NS;
-        
-        ev.setPointerCoordsAtSampleIndex(0, pointerCoords);
-        
+        MotionEvent ev = obtain();
+        ev.mNativePtr = nativeInitialize(ev.mNativePtr,
+                deviceId, source, action, flags, edgeFlags, metaState,
+                0, 0, xPrecision, yPrecision,
+                downTime * NS_PER_MS, eventTime * NS_PER_MS,
+                pointers, pointerIds, pointerCoords);
         return ev;
     }
-    
+
     /**
      * Create a new MotionEvent, filling in all of the basic values that
      * define the motion.
@@ -496,31 +581,22 @@
     static public MotionEvent obtain(long downTime, long eventTime, int action,
             float x, float y, float pressure, float size, int metaState,
             float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
-        MotionEvent ev = obtain(1, 1);
-        ev.mDeviceId = deviceId;
-        ev.mSource = InputDevice.SOURCE_UNKNOWN;
-        ev.mEdgeFlags = edgeFlags;
-        ev.mDownTimeNano = downTime * MS_PER_NS;
-        ev.mAction = action;
-        ev.mFlags = 0;
-        ev.mMetaState = metaState;
-        ev.mXOffset = 0;
-        ev.mYOffset = 0;
-        ev.mXPrecision = xPrecision;
-        ev.mYPrecision = yPrecision;
-        
-        ev.mNumPointers = 1;
-        ev.mNumSamples = 1;
-        
-        ev.mLastDataSampleIndex = 0;
-        ev.mLastEventTimeNanoSampleIndex = 0;
-        
-        ev.mPointerIdentifiers[0] = 0;
-        
-        ev.mEventTimeNanoSamples[0] = eventTime * MS_PER_NS;
-        
-        ev.setPointerCoordsAtSampleIndex(0, x, y, pressure, size);
-        return ev;
+        synchronized (gTmpPointerCoords) {
+            final PointerCoords pc = gTmpPointerCoords[0];
+            pc.clear();
+            pc.x = x;
+            pc.y = y;
+            pc.pressure = pressure;
+            pc.size = size;
+
+            MotionEvent ev = obtain();
+            ev.mNativePtr = nativeInitialize(ev.mNativePtr,
+                    deviceId, InputDevice.SOURCE_UNKNOWN, action, 0, edgeFlags, metaState,
+                    0, 0, xPrecision, yPrecision,
+                    downTime * NS_PER_MS, eventTime * NS_PER_MS,
+                    1, gTmpPointerIds, gTmpPointerCoords);
+            return ev;
+        }
     }
 
     /**
@@ -592,31 +668,13 @@
     /**
      * Create a new MotionEvent, copying from an existing one.
      */
-    static public MotionEvent obtain(MotionEvent o) {
-        MotionEvent ev = obtain(o.mNumPointers, o.mNumSamples);
-        ev.mDeviceId = o.mDeviceId;
-        ev.mSource = o.mSource;
-        ev.mEdgeFlags = o.mEdgeFlags;
-        ev.mDownTimeNano = o.mDownTimeNano;
-        ev.mAction = o.mAction;
-        ev.mFlags = o.mFlags;
-        ev.mMetaState = o.mMetaState;
-        ev.mXOffset = o.mXOffset;
-        ev.mYOffset = o.mYOffset;
-        ev.mXPrecision = o.mXPrecision;
-        ev.mYPrecision = o.mYPrecision;
-        int numPointers = ev.mNumPointers = o.mNumPointers;
-        int numSamples = ev.mNumSamples = o.mNumSamples;
-        
-        ev.mLastDataSampleIndex = o.mLastDataSampleIndex;
-        ev.mLastEventTimeNanoSampleIndex = o.mLastEventTimeNanoSampleIndex;
-        
-        System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, numPointers);
-        
-        System.arraycopy(o.mEventTimeNanoSamples, 0, ev.mEventTimeNanoSamples, 0, numSamples);
-        
-        System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0,
-                numPointers * numSamples * NUM_SAMPLE_DATA);
+    static public MotionEvent obtain(MotionEvent other) {
+        if (other == null) {
+            throw new IllegalArgumentException("other motion event must not be null");
+        }
+
+        MotionEvent ev = obtain();
+        ev.mNativePtr = nativeCopy(ev.mNativePtr, other.mNativePtr, true /*keepHistory*/);
         return ev;
     }
 
@@ -624,32 +682,13 @@
      * Create a new MotionEvent, copying from an existing one, but not including
      * any historical point information.
      */
-    static public MotionEvent obtainNoHistory(MotionEvent o) {
-        MotionEvent ev = obtain(o.mNumPointers, 1);
-        ev.mDeviceId = o.mDeviceId;
-        ev.mSource = o.mSource;
-        ev.mEdgeFlags = o.mEdgeFlags;
-        ev.mDownTimeNano = o.mDownTimeNano;
-        ev.mAction = o.mAction;
-        o.mFlags = o.mFlags;
-        ev.mMetaState = o.mMetaState;
-        ev.mXOffset = o.mXOffset;
-        ev.mYOffset = o.mYOffset;
-        ev.mXPrecision = o.mXPrecision;
-        ev.mYPrecision = o.mYPrecision;
-        
-        int numPointers = ev.mNumPointers = o.mNumPointers;
-        ev.mNumSamples = 1;
-        
-        ev.mLastDataSampleIndex = 0;
-        ev.mLastEventTimeNanoSampleIndex = 0;
-        
-        System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, numPointers);
-        
-        ev.mEventTimeNanoSamples[0] = o.mEventTimeNanoSamples[o.mLastEventTimeNanoSampleIndex];
-        
-        System.arraycopy(o.mDataSamples, o.mLastDataSampleIndex, ev.mDataSamples, 0,
-                numPointers * NUM_SAMPLE_DATA);
+    static public MotionEvent obtainNoHistory(MotionEvent other) {
+        if (other == null) {
+            throw new IllegalArgumentException("other motion event must not be null");
+        }
+
+        MotionEvent ev = obtain();
+        ev.mNativePtr = nativeCopy(ev.mNativePtr, other.mNativePtr, false /*keepHistory*/);
         return ev;
     }
 
@@ -675,7 +714,6 @@
         synchronized (gRecyclerLock) {
             if (gRecyclerUsed < MAX_RECYCLED) {
                 gRecyclerUsed++;
-                mNumSamples = 0;
                 mNext = gRecyclerTop;
                 gRecyclerTop = this;
             }
@@ -688,23 +726,25 @@
      * @hide
      */
     public final void scale(float scale) {
-        mXOffset *= scale;
-        mYOffset *= scale;
-        mXPrecision *= scale;
-        mYPrecision *= scale;
-        
-        float[] history = mDataSamples;
-        final int length = mNumPointers * mNumSamples * NUM_SAMPLE_DATA;
-        for (int i = 0; i < length; i += NUM_SAMPLE_DATA) {
-            history[i + SAMPLE_X] *= scale;
-            history[i + SAMPLE_Y] *= scale;
-            // no need to scale pressure
-            history[i + SAMPLE_SIZE] *= scale;    // TODO: square this?
-            history[i + SAMPLE_TOUCH_MAJOR] *= scale;
-            history[i + SAMPLE_TOUCH_MINOR] *= scale;
-            history[i + SAMPLE_TOOL_MAJOR] *= scale;
-            history[i + SAMPLE_TOOL_MINOR] *= scale;
-        }
+        nativeScale(mNativePtr, scale);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getDeviceId() {
+        return nativeGetDeviceId(mNativePtr);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getSource() {
+        return nativeGetSource(mNativePtr);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void setSource(int source) {
+        nativeSetSource(mNativePtr, source);
     }
 
     /**
@@ -715,7 +755,7 @@
      * and pointer index.
      */
     public final int getAction() {
-        return mAction;
+        return nativeGetAction(mNativePtr);
     }
 
     /**
@@ -727,7 +767,7 @@
      * pointer actions.
      */
     public final int getActionMasked() {
-        return mAction & ACTION_MASK;
+        return nativeGetAction(mNativePtr) & ACTION_MASK;
     }
 
     /**
@@ -739,7 +779,8 @@
      * gone down or up.
      */
     public final int getActionIndex() {
-        return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
+        return (nativeGetAction(mNativePtr) & ACTION_POINTER_INDEX_MASK)
+                >> ACTION_POINTER_INDEX_SHIFT;
     }
 
     /**
@@ -748,7 +789,7 @@
      * @see #FLAG_WINDOW_IS_OBSCURED
      */
     public final int getFlags() {
-        return mFlags;
+        return nativeGetFlags(mNativePtr);
     }
 
     /**
@@ -756,14 +797,14 @@
      * a stream of position events.
      */
     public final long getDownTime() {
-        return mDownTimeNano / MS_PER_NS;
+        return nativeGetDownTimeNanos(mNativePtr) / NS_PER_MS;
     }
 
     /**
      * Returns the time (in ms) when this specific event was generated.
      */
     public final long getEventTime() {
-        return mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] / MS_PER_NS;
+        return nativeGetEventTimeNanos(mNativePtr, HISTORY_CURRENT) / NS_PER_MS;
     }
 
     /**
@@ -773,79 +814,110 @@
      * @hide
      */
     public final long getEventTimeNano() {
-        return mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex];
+        return nativeGetEventTimeNanos(mNativePtr, HISTORY_CURRENT);
     }
 
     /**
      * {@link #getX(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_X
      */
     public final float getX() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_X] + mXOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
     }
 
     /**
      * {@link #getY(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_Y
      */
     public final float getY() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_Y] + mYOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
     }
 
     /**
      * {@link #getPressure(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_PRESSURE
      */
     public final float getPressure() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_PRESSURE];
+        return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, 0, HISTORY_CURRENT);
     }
 
     /**
      * {@link #getSize(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_SIZE
      */
     public final float getSize() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_SIZE];
+        return nativeGetAxisValue(mNativePtr, AXIS_SIZE, 0, HISTORY_CURRENT);
     }
     
     /**
      * {@link #getTouchMajor(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_TOUCH_MAJOR
      */
     public final float getTouchMajor() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_TOUCH_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, 0, HISTORY_CURRENT);
     }
 
     /**
      * {@link #getTouchMinor(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_TOUCH_MINOR
      */
     public final float getTouchMinor() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_TOUCH_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, 0, HISTORY_CURRENT);
     }
     
     /**
      * {@link #getToolMajor(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_TOOL_MAJOR
      */
     public final float getToolMajor() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_TOOL_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, 0, HISTORY_CURRENT);
     }
 
     /**
      * {@link #getToolMinor(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_TOOL_MINOR
      */
     public final float getToolMinor() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_TOOL_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, 0, HISTORY_CURRENT);
     }
-    
+
     /**
      * {@link #getOrientation(int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @see #AXIS_ORIENTATION
      */
     public final float getOrientation() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_ORIENTATION];
+        return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, 0, HISTORY_CURRENT);
+    }
+
+    /**
+     * {@link #getAxisValue(int)} for the first pointer index (may be an
+     * arbitrary pointer identifier).
+     *
+     * @param axis The axis identifier for the axis value to retrieve.
+     *
+     * @see #AXIS_X
+     * @see #AXIS_Y
+     */
+    public final float getAxisValue(int axis) {
+        return nativeGetAxisValue(mNativePtr, axis, 0, HISTORY_CURRENT);
     }
 
     /**
@@ -853,7 +925,7 @@
      * >= 1.
      */
     public final int getPointerCount() {
-        return mNumPointers;
+        return nativeGetPointerCount(mNativePtr);
     }
     
     /**
@@ -865,7 +937,7 @@
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      */
     public final int getPointerId(int pointerIndex) {
-        return mPointerIdentifiers[pointerIndex];
+        return nativeGetPointerId(mNativePtr, pointerIndex);
     }
     
     /**
@@ -877,14 +949,7 @@
      * that pointer identifier.
      */
     public final int findPointerIndex(int pointerId) {
-        int i = mNumPointers;
-        while (i > 0) {
-            i--;
-            if (mPointerIdentifiers[i] == pointerId) {
-                return i;
-            }
-        }
-        return -1;
+        return nativeFindPointerIndex(mNativePtr, pointerId);
     }
     
     /**
@@ -895,10 +960,11 @@
      * value may have a fraction for input devices that are sub-pixel precise. 
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_X
      */
     public final float getX(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
     }
 
     /**
@@ -909,10 +975,11 @@
      * value may have a fraction for input devices that are sub-pixel precise.
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_Y
      */
     public final float getY(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT);
     }
 
     /**
@@ -925,10 +992,11 @@
      * the input device.
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_PRESSURE
      */
     public final float getPressure(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_PRESSURE];
+        return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, pointerIndex, HISTORY_CURRENT);
     }
 
     /**
@@ -942,10 +1010,11 @@
      * determine fat touch events.
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_SIZE
      */
     public final float getSize(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_SIZE];
+        return nativeGetAxisValue(mNativePtr, AXIS_SIZE, pointerIndex, HISTORY_CURRENT);
     }
     
     /**
@@ -955,10 +1024,11 @@
      * identifier for this index).
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_TOUCH_MAJOR
      */
     public final float getTouchMajor(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, pointerIndex, HISTORY_CURRENT);
     }
     
     /**
@@ -968,10 +1038,11 @@
      * identifier for this index).
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_TOUCH_MINOR
      */
     public final float getTouchMinor(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, pointerIndex, HISTORY_CURRENT);
     }
     
     /**
@@ -983,10 +1054,11 @@
      * touching the device independent of its actual touch area at the point of contact.
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_TOOL_MAJOR
      */
     public final float getToolMajor(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOOL_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, pointerIndex, HISTORY_CURRENT);
     }
     
     /**
@@ -998,10 +1070,11 @@
      * touching the device independent of its actual touch area at the point of contact.
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #AXIS_TOOL_MINOR
      */
     public final float getToolMinor(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOOL_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, pointerIndex, HISTORY_CURRENT);
     }
     
     /**
@@ -1016,12 +1089,29 @@
      * (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.
+     *
+     * @see #AXIS_ORIENTATION
      */
     public final float getOrientation(int pointerIndex) {
-        return mDataSamples[mLastDataSampleIndex
-                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_ORIENTATION];
+        return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, pointerIndex, HISTORY_CURRENT);
     }
-    
+
+    /**
+     * Returns the value of the requested axis for the given pointer <em>index</em>
+     * (use {@link #getPointerId(int)} to find the pointer identifier for this index).
+     *
+     * @param axis The axis identifier for the axis value to retrieve.
+     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
+     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * @return The value of the axis, or 0 if the axis is not available.
+     *
+     * @see #AXIS_X
+     * @see #AXIS_Y
+     */
+    public final float getAxisValue(int axis, int pointerIndex) {
+        return nativeGetAxisValue(mNativePtr, axis, pointerIndex, HISTORY_CURRENT);
+    }
+
     /**
      * Populates a {@link PointerCoords} object with pointer coordinate data for
      * the specified pointer index.
@@ -1029,10 +1119,11 @@
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      * @param outPointerCoords The pointer coordinate object to populate.
+     *
+     * @see PointerCoords
      */
     public final void getPointerCoords(int pointerIndex, PointerCoords outPointerCoords) {
-        final int sampleIndex = mLastDataSampleIndex + pointerIndex * NUM_SAMPLE_DATA;
-        getPointerCoordsAtSampleIndex(sampleIndex, outPointerCoords);
+        nativeGetPointerCoords(mNativePtr, pointerIndex, HISTORY_CURRENT, outPointerCoords);
     }
 
     /**
@@ -1046,7 +1137,7 @@
      * @see KeyEvent#getMetaState()
      */
     public final int getMetaState() {
-        return mMetaState;
+        return nativeGetMetaState(mNativePtr);
     }
 
     /**
@@ -1054,39 +1145,49 @@
      * events on the screen, this is the original location of the event
      * on the screen, before it had been adjusted for the containing window
      * and views.
+     *
+     * @see getX()
+     * @see #AXIS_X
      */
     public final float getRawX() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_X];
+        return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
     }
-    
+
     /**
      * Returns the original raw Y coordinate of this event.  For touch
      * events on the screen, this is the original location of the event
      * on the screen, before it had been adjusted for the containing window
      * and views.
+     *
+     * @see getY()
+     * @see #AXIS_Y
      */
     public final float getRawY() {
-        return mDataSamples[mLastDataSampleIndex + SAMPLE_Y];
+        return nativeGetRawAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
     }
 
     /**
      * Return the precision of the X coordinates being reported.  You can
-     * multiple this number with {@link #getX} to find the actual hardware
+     * multiply this number with {@link #getX} to find the actual hardware
      * value of the X coordinate.
      * @return Returns the precision of X coordinates being reported.
+     *
+     * @see #AXIS_X
      */
     public final float getXPrecision() {
-        return mXPrecision;
+        return nativeGetXPrecision(mNativePtr);
     }
 
     /**
      * Return the precision of the Y coordinates being reported.  You can
-     * multiple this number with {@link #getY} to find the actual hardware
+     * multiply this number with {@link #getY} to find the actual hardware
      * value of the Y coordinate.
      * @return Returns the precision of Y coordinates being reported.
+     *
+     * @see #AXIS_Y
      */
     public final float getYPrecision() {
-        return mYPrecision;
+        return nativeGetYPrecision(mNativePtr);
     }
 
     /**
@@ -1098,7 +1199,7 @@
      * @return Returns the number of historical points in the event.
      */
     public final int getHistorySize() {
-        return mLastEventTimeNanoSampleIndex;
+        return nativeGetHistorySize(mNativePtr);
     }
 
     /**
@@ -1112,81 +1213,161 @@
      * @see #getEventTime
      */
     public final long getHistoricalEventTime(int pos) {
-        return mEventTimeNanoSamples[pos] / MS_PER_NS;
+        return nativeGetEventTimeNanos(mNativePtr, pos) / NS_PER_MS;
     }
 
     /**
-     * {@link #getHistoricalX(int)} for the first pointer index (may be an
+     * {@link #getHistoricalX(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getX()
+     * @see #AXIS_X
      */
     public final float getHistoricalX(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_X, 0, pos);
     }
 
     /**
-     * {@link #getHistoricalY(int)} for the first pointer index (may be an
+     * {@link #getHistoricalY(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getY()
+     * @see #AXIS_Y
      */
     public final float getHistoricalY(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_Y, 0, pos);
     }
 
     /**
-     * {@link #getHistoricalPressure(int)} for the first pointer index (may be an
+     * {@link #getHistoricalPressure(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getPressure()
+     * @see #AXIS_PRESSURE
      */
     public final float getHistoricalPressure(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_PRESSURE];
+        return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, 0, pos);
     }
 
     /**
-     * {@link #getHistoricalSize(int)} for the first pointer index (may be an
+     * {@link #getHistoricalSize(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getSize()
+     * @see #AXIS_SIZE
      */
     public final float getHistoricalSize(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_SIZE];
+        return nativeGetAxisValue(mNativePtr, AXIS_SIZE, 0, pos);
     }
 
     /**
-     * {@link #getHistoricalTouchMajor(int)} for the first pointer index (may be an
+     * {@link #getHistoricalTouchMajor(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getTouchMajor()
+     * @see #AXIS_TOUCH_MAJOR
      */
     public final float getHistoricalTouchMajor(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, 0, pos);
     }
 
     /**
-     * {@link #getHistoricalTouchMinor(int)} for the first pointer index (may be an
+     * {@link #getHistoricalTouchMinor(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getTouchMinor()
+     * @see #AXIS_TOUCH_MINOR
      */
     public final float getHistoricalTouchMinor(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, 0, pos);
     }
     
     /**
-     * {@link #getHistoricalToolMajor(int)} for the first pointer index (may be an
+     * {@link #getHistoricalToolMajor(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getToolMajor()
+     * @see #AXIS_TOOL_MAJOR
      */
     public final float getHistoricalToolMajor(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOOL_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, 0, pos);
     }
 
     /**
-     * {@link #getHistoricalToolMinor(int)} for the first pointer index (may be an
+     * {@link #getHistoricalToolMinor(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getToolMinor()
+     * @see #AXIS_TOOL_MINOR
      */
     public final float getHistoricalToolMinor(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOOL_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, 0, pos);
     }
     
     /**
-     * {@link #getHistoricalOrientation(int)} for the first pointer index (may be an
+     * {@link #getHistoricalOrientation(int, int)} for the first pointer index (may be an
      * arbitrary pointer identifier).
+     *
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getOrientation()
+     * @see #AXIS_ORIENTATION
      */
     public final float getHistoricalOrientation(int pos) {
-        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_ORIENTATION];
+        return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, 0, pos);
     }
-    
+
+    /**
+     * {@link #getHistoricalAxisValue(int, int, int)} for the first pointer index (may be an
+     * arbitrary pointer identifier).
+     *
+     * @param axis The axis identifier for the axis value to retrieve.
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     *
+     * @see #getHistorySize
+     * @see #getAxisValue(int)
+     * @see #AXIS_X
+     * @see #AXIS_Y
+     */
+    public final float getHistoricalAxisValue(int axis, int pos) {
+        return nativeGetAxisValue(mNativePtr, axis, 0, pos);
+    }
+
     /**
      * Returns a historical X coordinate, as per {@link #getX(int)}, that
      * occurred between this event and the previous event for the given pointer.
@@ -1198,11 +1379,11 @@
      * {@link #getHistorySize}
      *
      * @see #getHistorySize
-     * @see #getX
+     * @see #getX(int)
+     * @see #AXIS_X
      */
     public final float getHistoricalX(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, pos);
     }
 
     /**
@@ -1216,11 +1397,11 @@
      * {@link #getHistorySize}
      *
      * @see #getHistorySize
-     * @see #getY
+     * @see #getY(int)
+     * @see #AXIS_Y
      */
     public final float getHistoricalY(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset;
+        return nativeGetAxisValue(mNativePtr, AXIS_Y, pointerIndex, pos);
     }
 
     /**
@@ -1234,11 +1415,11 @@
      * {@link #getHistorySize}
      * 
      * @see #getHistorySize
-     * @see #getPressure
+     * @see #getPressure(int)
+     * @see #AXIS_PRESSURE
      */
     public final float getHistoricalPressure(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_PRESSURE];
+        return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, pointerIndex, pos);
     }
 
     /**
@@ -1252,11 +1433,11 @@
      * {@link #getHistorySize}
      * 
      * @see #getHistorySize
-     * @see #getSize
+     * @see #getSize(int)
+     * @see #AXIS_SIZE
      */
     public final float getHistoricalSize(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_SIZE];
+        return nativeGetAxisValue(mNativePtr, AXIS_SIZE, pointerIndex, pos);
     }
     
     /**
@@ -1270,11 +1451,11 @@
      * {@link #getHistorySize}
      * 
      * @see #getHistorySize
-     * @see #getTouchMajor
+     * @see #getTouchMajor(int)
+     * @see #AXIS_TOUCH_MAJOR
      */
     public final float getHistoricalTouchMajor(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, pointerIndex, pos);
     }
 
     /**
@@ -1288,11 +1469,11 @@
      * {@link #getHistorySize}
      * 
      * @see #getHistorySize
-     * @see #getTouchMinor
+     * @see #getTouchMinor(int)
+     * @see #AXIS_TOUCH_MINOR
      */
     public final float getHistoricalTouchMinor(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, pointerIndex, pos);
     }
 
     /**
@@ -1306,11 +1487,11 @@
      * {@link #getHistorySize}
      * 
      * @see #getHistorySize
-     * @see #getToolMajor
+     * @see #getToolMajor(int)
+     * @see #AXIS_TOOL_MAJOR
      */
     public final float getHistoricalToolMajor(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_TOOL_MAJOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, pointerIndex, pos);
     }
 
     /**
@@ -1324,11 +1505,11 @@
      * {@link #getHistorySize}
      * 
      * @see #getHistorySize
-     * @see #getToolMinor
+     * @see #getToolMinor(int)
+     * @see #AXIS_TOOL_MINOR
      */
     public final float getHistoricalToolMinor(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_TOOL_MINOR];
+        return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, pointerIndex, pos);
     }
 
     /**
@@ -1342,11 +1523,30 @@
      * {@link #getHistorySize}
      * 
      * @see #getHistorySize
-     * @see #getOrientation
+     * @see #getOrientation(int)
+     * @see #AXIS_ORIENTATION
      */
     public final float getHistoricalOrientation(int pointerIndex, int pos) {
-        return mDataSamples[(pos * mNumPointers + pointerIndex)
-                            * NUM_SAMPLE_DATA + SAMPLE_ORIENTATION];
+        return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, pointerIndex, pos);
+    }
+
+    /**
+     * Returns the historical value of the requested axis, as per {@link #getAxisValue(int, int)},
+     * occurred between this event and the previous event for the given pointer.
+     * Only applies to ACTION_MOVE events.
+     *
+     * @param axis The axis identifier for the axis value to retrieve.
+     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
+     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     * @return The value of the axis, or 0 if the axis is not available.
+     *
+     * @see #AXIS_X
+     * @see #AXIS_Y
+     */
+    public final float getHistoricalAxisValue(int axis, int pointerIndex, int pos) {
+        return nativeGetAxisValue(mNativePtr, axis, pointerIndex, pos);
     }
 
     /**
@@ -1363,11 +1563,11 @@
      * 
      * @see #getHistorySize
      * @see #getPointerCoords
+     * @see PointerCoords
      */
     public final void getHistoricalPointerCoords(int pointerIndex, int pos,
             PointerCoords outPointerCoords) {
-        final int sampleIndex = (pos * mNumPointers + pointerIndex) * NUM_SAMPLE_DATA;
-        getPointerCoordsAtSampleIndex(sampleIndex, outPointerCoords);
+        nativeGetPointerCoords(mNativePtr, pointerIndex, pos, outPointerCoords);
     }
     
     /**
@@ -1381,10 +1581,9 @@
      * @see #EDGE_BOTTOM
      */
     public final int getEdgeFlags() {
-        return mEdgeFlags;
+        return nativeGetEdgeFlags(mNativePtr);
     }
 
-
     /**
      * Sets the bitfield indicating which edges, if any, were touched by this
      * MotionEvent.
@@ -1392,14 +1591,14 @@
      * @see #getEdgeFlags()
      */
     public final void setEdgeFlags(int flags) {
-        mEdgeFlags = flags;
+        nativeSetEdgeFlags(mNativePtr, flags);
     }
 
     /**
      * Sets this event's action.
      */
     public final void setAction(int action) {
-        mAction = action;
+        nativeSetAction(mNativePtr, action);
     }
 
     /**
@@ -1408,8 +1607,7 @@
      * @param deltaY Amount to add to the current Y coordinate of the event.
      */
     public final void offsetLocation(float deltaX, float deltaY) {
-        mXOffset += deltaX;
-        mYOffset += deltaY;
+        nativeOffsetLocation(mNativePtr, deltaX, deltaY);
     }
 
     /**
@@ -1420,10 +1618,9 @@
      * @param y New absolute Y location.
      */
     public final void setLocation(float x, float y) {
-        final float[] dataSamples = mDataSamples;
-        final int lastDataSampleIndex = mLastDataSampleIndex;
-        mXOffset = x - dataSamples[lastDataSampleIndex + SAMPLE_X];
-        mYOffset = y - dataSamples[lastDataSampleIndex + SAMPLE_Y];
+        float oldX = getX();
+        float oldY = getY();
+        nativeOffsetLocation(mNativePtr, x - oldX, y - oldY);
     }
     
     /**
@@ -1436,85 +1633,14 @@
             throw new IllegalArgumentException("matrix must not be null");
         }
 
-        nativeTransform(matrix);
-    }
-
-    private final void getPointerCoordsAtSampleIndex(int sampleIndex,
-            PointerCoords outPointerCoords) {
-        final float[] dataSamples = mDataSamples;
-        outPointerCoords.x = dataSamples[sampleIndex + SAMPLE_X] + mXOffset;
-        outPointerCoords.y = dataSamples[sampleIndex + SAMPLE_Y] + mYOffset;
-        outPointerCoords.pressure = dataSamples[sampleIndex + SAMPLE_PRESSURE];
-        outPointerCoords.size = dataSamples[sampleIndex + SAMPLE_SIZE];
-        outPointerCoords.touchMajor = dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR];
-        outPointerCoords.touchMinor = dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR];
-        outPointerCoords.toolMajor = dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR];
-        outPointerCoords.toolMinor = dataSamples[sampleIndex + SAMPLE_TOOL_MINOR];
-        outPointerCoords.orientation = dataSamples[sampleIndex + SAMPLE_ORIENTATION];
-    }
-    
-    private final void setPointerCoordsAtSampleIndex(int sampleIndex,
-            PointerCoords[] pointerCoords) {
-        final int numPointers = mNumPointers;
-        for (int i = 0; i < numPointers; i++) {
-            setPointerCoordsAtSampleIndex(sampleIndex, pointerCoords[i]);
-            sampleIndex += NUM_SAMPLE_DATA;
-        }
-    }
-    
-    private final void setPointerCoordsAtSampleIndex(int sampleIndex,
-            PointerCoords pointerCoords) {
-        final float[] dataSamples = mDataSamples;
-        dataSamples[sampleIndex + SAMPLE_X] = pointerCoords.x - mXOffset;
-        dataSamples[sampleIndex + SAMPLE_Y] = pointerCoords.y - mYOffset;
-        dataSamples[sampleIndex + SAMPLE_PRESSURE] = pointerCoords.pressure;
-        dataSamples[sampleIndex + SAMPLE_SIZE] = pointerCoords.size;
-        dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pointerCoords.touchMajor;
-        dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pointerCoords.touchMinor;
-        dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = pointerCoords.toolMajor;
-        dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = pointerCoords.toolMinor;
-        dataSamples[sampleIndex + SAMPLE_ORIENTATION] = pointerCoords.orientation;
-    }
-    
-    private final void setPointerCoordsAtSampleIndex(int sampleIndex,
-            float x, float y, float pressure, float size) {
-        final float[] dataSamples = mDataSamples;
-        dataSamples[sampleIndex + SAMPLE_X] = x - mXOffset;
-        dataSamples[sampleIndex + SAMPLE_Y] = y - mYOffset;
-        dataSamples[sampleIndex + SAMPLE_PRESSURE] = pressure;
-        dataSamples[sampleIndex + SAMPLE_SIZE] = size;
-        dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pressure;
-        dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pressure;
-        dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = size;
-        dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = size;
-        dataSamples[sampleIndex + SAMPLE_ORIENTATION] = 0;
-    }
-    
-    private final void incrementNumSamplesAndReserveStorage(int dataSampleStride) {
-        if (mNumSamples == mEventTimeNanoSamples.length) {
-            long[] newEventTimeNanoSamples = new long[mNumSamples + BASE_AVAIL_SAMPLES];
-            System.arraycopy(mEventTimeNanoSamples, 0, newEventTimeNanoSamples, 0, mNumSamples);
-            mEventTimeNanoSamples = newEventTimeNanoSamples;
-        }
-        
-        int nextDataSampleIndex = mLastDataSampleIndex + dataSampleStride;
-        if (nextDataSampleIndex + dataSampleStride > mDataSamples.length) {
-            float[] newDataSamples = new float[nextDataSampleIndex
-                                               + BASE_AVAIL_SAMPLES * dataSampleStride];
-            System.arraycopy(mDataSamples, 0, newDataSamples, 0, nextDataSampleIndex);
-            mDataSamples = newDataSamples;
-        }
-        
-        mLastEventTimeNanoSampleIndex = mNumSamples;
-        mLastDataSampleIndex = nextDataSampleIndex;
-        mNumSamples += 1;
+        nativeTransform(mNativePtr, matrix);
     }
 
     /**
      * Add a new movement to the batch of movements in this event.  The event's
      * current location, position and size is updated to the new values.
      * The current values in the event are added to a list of historical values.
-     * 
+     *
      * Only applies to {@link #ACTION_MOVE} events.
      *
      * @param eventTime The time stamp (in ms) for this data.
@@ -1526,19 +1652,22 @@
      */
     public final void addBatch(long eventTime, float x, float y,
             float pressure, float size, int metaState) {
-        incrementNumSamplesAndReserveStorage(NUM_SAMPLE_DATA);
-        
-        mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] = eventTime * MS_PER_NS;
-        setPointerCoordsAtSampleIndex(mLastDataSampleIndex, x, y, pressure, size);
-        
-        mMetaState |= metaState;
+        synchronized (gTmpPointerCoords) {
+            final PointerCoords pc = gTmpPointerCoords[0];
+            pc.clear();
+            pc.x = x;
+            pc.y = y;
+            pc.pressure = pressure;
+            pc.size = size;
+            nativeAddBatch(mNativePtr, eventTime * NS_PER_MS, gTmpPointerCoords, metaState);
+        }
     }
 
     /**
      * Add a new movement to the batch of movements in this event.  The event's
      * current location, position and size is updated to the new values.
      * The current values in the event are added to a list of historical values.
-     * 
+     *
      * Only applies to {@link #ACTION_MOVE} events.
      *
      * @param eventTime The time stamp (in ms) for this data.
@@ -1546,20 +1675,14 @@
      * @param metaState Meta key state.
      */
     public final void addBatch(long eventTime, PointerCoords[] pointerCoords, int metaState) {
-        final int dataSampleStride = mNumPointers * NUM_SAMPLE_DATA;
-        incrementNumSamplesAndReserveStorage(dataSampleStride);
-        
-        mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] = eventTime * MS_PER_NS;
-        setPointerCoordsAtSampleIndex(mLastDataSampleIndex, pointerCoords);
-        
-        mMetaState |= metaState;
+        nativeAddBatch(mNativePtr, eventTime * NS_PER_MS, pointerCoords, metaState);
     }
 
     @Override
     public String toString() {
         return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this))
             + " pointerId=" + getPointerId(0)
-            + " action=" + actionToString(mAction)
+            + " action=" + actionToString(getAction())
             + " x=" + getX()
             + " y=" + getY()
             + " pressure=" + getPressure()
@@ -1569,13 +1692,13 @@
             + " toolMajor=" + getToolMajor()
             + " toolMinor=" + getToolMinor()
             + " orientation=" + getOrientation()
-            + " meta=" + KeyEvent.metaStateToString(mMetaState)
+            + " meta=" + KeyEvent.metaStateToString(getMetaState())
             + " pointerCount=" + getPointerCount()
             + " historySize=" + getHistorySize()
-            + " flags=0x" + Integer.toHexString(mFlags)
-            + " edgeFlags=0x" + Integer.toHexString(mEdgeFlags)
-            + " device=" + mDeviceId
-            + " source=0x" + Integer.toHexString(mSource)
+            + " flags=0x" + Integer.toHexString(getFlags())
+            + " edgeFlags=0x" + Integer.toHexString(getEdgeFlags())
+            + " device=" + getDeviceId()
+            + " source=0x" + Integer.toHexString(getSource())
             + (getPointerCount() > 1 ?
                 " pointerId2=" + getPointerId(1) + " x2=" + getX(1) + " y2=" + getY(1) : "")
             + "}";
@@ -1583,7 +1706,8 @@
 
     /**
      * Returns a string that represents the symbolic name of the specified action
-     * such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or "35" (if unknown).
+     * such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or an equivalent numeric constant
+     * such as "35" if unknown.
      *
      * @param action The action.
      * @return The symbolic name of the specified action.
@@ -1611,6 +1735,39 @@
         }
     }
 
+    /**
+     * Returns a string that represents the symbolic name of the specified axis
+     * such as "AXIS_X" or an equivalent numeric constants such as "42" if unknown.
+     *
+     * @param axis The axis
+     * @return The symbolic name of the specified axis.
+     * @hide
+     */
+    public static String axisToString(int axis) {
+        switch (axis) {
+            case AXIS_X:
+                return "AXIS_X";
+            case AXIS_Y:
+                return "AXIS_Y";
+            case AXIS_PRESSURE:
+                return "AXIS_PRESSURE";
+            case AXIS_SIZE:
+                return "AXIS_SIZE";
+            case AXIS_TOUCH_MAJOR:
+                return "AXIS_TOUCH_MAJOR";
+            case AXIS_TOUCH_MINOR:
+                return "AXIS_TOUCH_MINOR";
+            case AXIS_TOOL_MAJOR:
+                return "AXIS_TOOL_MAJOR";
+            case AXIS_TOOL_MINOR:
+                return "AXIS_TOOL_MINOR";
+            case AXIS_ORIENTATION:
+                return "AXIS_ORIENTATION";
+            default:
+                return Integer.toString(axis);
+        }
+    }
+
     public static final Parcelable.Creator<MotionEvent> CREATOR
             = new Parcelable.Creator<MotionEvent>() {
         public MotionEvent createFromParcel(Parcel in) {
@@ -1625,84 +1782,16 @@
 
     /** @hide */
     public static MotionEvent createFromParcelBody(Parcel in) {
-        final int NP = in.readInt();
-        final int NS = in.readInt();
-        final int NI = NP * NS * NUM_SAMPLE_DATA;
-        
-        MotionEvent ev = obtain(NP, NS);
-        ev.mNumPointers = NP;
-        ev.mNumSamples = NS;
-        
-        ev.readBaseFromParcel(in);
-        
-        ev.mDownTimeNano = in.readLong();
-        ev.mAction = in.readInt();
-        ev.mXOffset = in.readFloat();
-        ev.mYOffset = in.readFloat();
-        ev.mXPrecision = in.readFloat();
-        ev.mYPrecision = in.readFloat();
-        ev.mEdgeFlags = in.readInt();
-        ev.mMetaState = in.readInt();
-        ev.mFlags = in.readInt();
-        
-        final int[] pointerIdentifiers = ev.mPointerIdentifiers;
-        for (int i = 0; i < NP; i++) {
-            pointerIdentifiers[i] = in.readInt();
-        }
-        
-        final long[] eventTimeNanoSamples = ev.mEventTimeNanoSamples;
-        for (int i = 0; i < NS; i++) {
-            eventTimeNanoSamples[i] = in.readLong();
-        }
-
-        final float[] dataSamples = ev.mDataSamples;
-        for (int i = 0; i < NI; i++) {
-            dataSamples[i] = in.readFloat();
-        }
-        
-        ev.mLastEventTimeNanoSampleIndex = NS - 1;
-        ev.mLastDataSampleIndex = (NS - 1) * NP * NUM_SAMPLE_DATA;
+        MotionEvent ev = obtain();
+        ev.mNativePtr = nativeReadFromParcel(ev.mNativePtr, in);
         return ev;
     }
-    
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(PARCEL_TOKEN_MOTION_EVENT);
-        
-        final int NP = mNumPointers;
-        final int NS = mNumSamples;
-        final int NI = NP * NS * NUM_SAMPLE_DATA;
-        
-        out.writeInt(NP);
-        out.writeInt(NS);
-        
-        writeBaseToParcel(out);
-        
-        out.writeLong(mDownTimeNano);
-        out.writeInt(mAction);
-        out.writeFloat(mXOffset);
-        out.writeFloat(mYOffset);
-        out.writeFloat(mXPrecision);
-        out.writeFloat(mYPrecision);
-        out.writeInt(mEdgeFlags);
-        out.writeInt(mMetaState);
-        out.writeInt(mFlags);
-        
-        final int[] pointerIdentifiers = mPointerIdentifiers;
-        for (int i = 0; i < NP; i++) {
-            out.writeInt(pointerIdentifiers[i]);
-        }
-        
-        final long[] eventTimeNanoSamples = mEventTimeNanoSamples;
-        for (int i = 0; i < NS; i++) {
-            out.writeLong(eventTimeNanoSamples[i]);
-        }
-
-        final float[] dataSamples = mDataSamples;
-        for (int i = 0; i < NI; i++) {
-            out.writeFloat(dataSamples[i]);
-        }
+        nativeWriteToParcel(mNativePtr, out);
     }
-    
+
     /**
      * Transfer object for pointer coordinates.
      * 
@@ -1713,49 +1802,87 @@
      * input devices and sources represent pointer coordinates.
      */
     public static final class PointerCoords {
+        private static final int INITIAL_PACKED_AXIS_VALUES = 8;
+        private int mPackedAxisBits; // 32bits are enough for now, can raise to 64bit when needed
+        private float[] mPackedAxisValues;
+
+        /**
+         * Creates a pointer coords object with all axes initialized to zero.
+         */
+        public PointerCoords() {
+        }
+
+        /**
+         * Creates a pointer coords object as a copy of the
+         * contents of another pointer coords object.
+         *
+         * @param other The pointer coords object to copy.
+         */
+        public PointerCoords(PointerCoords other) {
+            copyFrom(other);
+        }
+
         /**
          * The X coordinate of the pointer movement.
-         * The interpretation varies by input source and may represent the position of
-         * the center of the contact area, a relative displacement in device-specific units
-         * or something else.
+         * The interpretation of the X axis varies by input source.
+         * It may represent the X position of the center of the touch contact area,
+         * a relative horizontal displacement of a trackball or joystick, or something else.
+         *
+         * @see MotionEvent#AXIS_X
          */
         public float x;
         
         /**
          * The Y coordinate of the pointer movement.
-         * The interpretation varies by input source and may represent the position of
-         * the center of the contact area, a relative displacement in device-specific units
-         * or something else.
+         * The interpretation of the Y axis varies by input source.
+         * It may represent the Y position of the center of the touch contact area,
+         * a relative vertical displacement of a trackball or joystick, or something else.
+         *
+         * @see MotionEvent#AXIS_Y
          */
         public float y;
         
         /**
-         * A scaled value that describes the pressure applied to the pointer.
+         * A normalized value that describes the pressure applied to the device
+         * by a finger or other tool.
          * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
-         * however values higher than 1 may be generated depending on the calibration of
+         * although values higher than 1 may be generated depending on the calibration of
          * the input device.
+         *
+         * @see MotionEvent#AXIS_PRESSURE
          */
         public float pressure;
         
         /**
-         * A scaled value of the approximate size of the pointer touch area.
-         * This represents some approximation of the area of the screen being
+         * A normalized value that describes the approximate size of the pointer touch area
+         * in relation to the maximum detectable size of the device.
+         * It represents some approximation of the area of the screen being
          * pressed; the actual value in pixels corresponding to the
          * touch is normalized with the device specific range of values
          * and scaled to a value between 0 and 1. The value of size can be used to
          * determine fat touch events.
+         *
+         * @see MotionEvent#AXIS_SIZE
          */
         public float size;
         
         /**
          * The length of the major axis of an ellipse that describes the touch area at
          * the point of contact.
+         * If the device is a touch screen, the length is reported in pixels, otherwise it is
+         * reported in device-specific units.
+         *
+         * @see MotionEvent#AXIS_TOUCH_MAJOR
          */
         public float touchMajor;
         
         /**
          * The length of the minor axis of an ellipse that describes the touch area at
          * the point of contact.
+         * If the device is a touch screen, the length is reported in pixels, otherwise it is
+         * reported in device-specific units.
+         *
+         * @see MotionEvent#AXIS_TOUCH_MINOR
          */
         public float touchMinor;
         
@@ -1764,6 +1891,10 @@
          * the approaching tool.
          * The tool area represents the estimated size of the finger or pen that is
          * touching the device independent of its actual touch area at the point of contact.
+         * If the device is a touch screen, the length is reported in pixels, otherwise it is
+         * reported in device-specific units.
+         *
+         * @see MotionEvent#AXIS_TOOL_MAJOR
          */
         public float toolMajor;
         
@@ -1772,6 +1903,10 @@
          * the approaching tool.
          * The tool area represents the estimated size of the finger or pen that is
          * touching the device independent of its actual touch area at the point of contact.
+         * If the device is a touch screen, the length is reported in pixels, otherwise it is
+         * reported in device-specific units.
+         *
+         * @see MotionEvent#AXIS_TOOL_MINOR
          */
         public float toolMinor;
         
@@ -1783,27 +1918,168 @@
          * indicates that the major axis of contact is oriented to the left.
          * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
          * (finger pointing fully right).
+         *
+         * @see MotionEvent#AXIS_ORIENTATION
          */
         public float orientation;
-        
-        /*
-        private static final float PI_4 = (float) (Math.PI / 4);
-        
-        public float getTouchWidth() {
-            return Math.abs(orientation) > PI_4 ? touchMajor : touchMinor;
+
+        /**
+         * Clears the contents of this object.
+         * Resets all axes to zero.
+         */
+        public void clear() {
+            mPackedAxisBits = 0;
+
+            x = 0;
+            y = 0;
+            pressure = 0;
+            size = 0;
+            touchMajor = 0;
+            touchMinor = 0;
+            toolMajor = 0;
+            toolMinor = 0;
+            orientation = 0;
         }
-        
-        public float getTouchHeight() {
-            return Math.abs(orientation) > PI_4 ? touchMinor : touchMajor;
+
+        /**
+         * Copies the contents of another pointer coords object.
+         *
+         * @param other The pointer coords object to copy.
+         */
+        public void copyFrom(PointerCoords other) {
+            final int bits = other.mPackedAxisBits;
+            mPackedAxisBits = bits;
+            if (bits != 0) {
+                final float[] otherValues = other.mPackedAxisValues;
+                final int count = Integer.bitCount(bits);
+                float[] values = mPackedAxisValues;
+                if (values == null || count > values.length) {
+                    values = new float[otherValues.length];
+                    mPackedAxisValues = values;
+                }
+                System.arraycopy(otherValues, 0, values, 0, count);
+            }
+
+            x = other.x;
+            y = other.y;
+            pressure = other.pressure;
+            size = other.size;
+            touchMajor = other.touchMajor;
+            touchMinor = other.touchMinor;
+            toolMajor = other.toolMajor;
+            toolMinor = other.toolMinor;
+            orientation = other.orientation;
         }
-        
-        public float getToolWidth() {
-            return Math.abs(orientation) > PI_4 ? toolMajor : toolMinor;
+
+        /**
+         * Gets the value associated with the specified axis.
+         *
+         * @param axis The axis identifier for the axis value to retrieve.
+         * @return The value associated with the axis, or 0 if none.
+         *
+         * @see MotionEvent#AXIS_X
+         * @see MotionEvent#AXIS_Y
+         */
+        public float getAxisValue(int axis) {
+            switch (axis) {
+                case AXIS_X:
+                    return x;
+                case AXIS_Y:
+                    return y;
+                case AXIS_PRESSURE:
+                    return pressure;
+                case AXIS_SIZE:
+                    return size;
+                case AXIS_TOUCH_MAJOR:
+                    return touchMajor;
+                case AXIS_TOUCH_MINOR:
+                    return touchMinor;
+                case AXIS_TOOL_MAJOR:
+                    return toolMajor;
+                case AXIS_TOOL_MINOR:
+                    return toolMinor;
+                case AXIS_ORIENTATION:
+                    return orientation;
+                default: {
+                    final int bits = mPackedAxisBits;
+                    final int axisBit = 1 << axis;
+                    if ((bits & axisBit) == 0) {
+                        return 0;
+                    }
+                    final int index = Integer.bitCount(bits & (axisBit - 1));
+                    return mPackedAxisValues[index];
+                }
+            }
         }
-        
-        public float getToolHeight() {
-            return Math.abs(orientation) > PI_4 ? toolMinor : toolMajor;
+
+        /**
+         * Sets the value associated with the specified axis.
+         *
+         * @param axis The axis identifier for the axis value to assign.
+         * @param value The value to set.
+         *
+         * @see MotionEvent#AXIS_X
+         * @see MotionEvent#AXIS_Y
+         */
+        public void setAxisValue(int axis, float value) {
+            switch (axis) {
+                case AXIS_X:
+                    x = value;
+                    break;
+                case AXIS_Y:
+                    y = value;
+                    break;
+                case AXIS_PRESSURE:
+                    pressure = value;
+                    break;
+                case AXIS_SIZE:
+                    size = value;
+                    break;
+                case AXIS_TOUCH_MAJOR:
+                    touchMajor = value;
+                    break;
+                case AXIS_TOUCH_MINOR:
+                    touchMinor = value;
+                    break;
+                case AXIS_TOOL_MAJOR:
+                    toolMajor = value;
+                    break;
+                case AXIS_TOOL_MINOR:
+                    toolMinor = value;
+                    break;
+                case AXIS_ORIENTATION:
+                    orientation = value;
+                    break;
+                default: {
+                    final int bits = mPackedAxisBits;
+                    final int axisBit = 1 << axis;
+                    final int index = Integer.bitCount(bits & (axisBit - 1));
+                    float[] values = mPackedAxisValues;
+                    if ((bits & axisBit) == 0) {
+                        if (values == null) {
+                            values = new float[INITIAL_PACKED_AXIS_VALUES];
+                            mPackedAxisValues = values;
+                        } else {
+                            final int count = Integer.bitCount(bits);
+                            if (count < values.length) {
+                                if (index != count) {
+                                    System.arraycopy(values, index, values, index + 1,
+                                            count - index);
+                                }
+                            } else {
+                                float[] newValues = new float[count * 2];
+                                System.arraycopy(values, 0, newValues, 0, index);
+                                System.arraycopy(values, index, newValues, index + 1,
+                                        count - index);
+                                values = newValues;
+                                mPackedAxisValues = values;
+                            }
+                        }
+                        mPackedAxisBits = bits | axisBit;
+                    }
+                    values[index] = value;
+                }
+            }
         }
-        */
     }
 }
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index f32f0ff..99fbbe9 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -22,24 +22,10 @@
 #include <utils/Log.h>
 #include <ui/Input.h>
 #include "android_view_MotionEvent.h"
+#include "android_util_Binder.h"
 #include "android/graphics/Matrix.h"
 
-#include <math.h>
 #include "SkMatrix.h"
-#include "SkScalar.h"
-
-// Number of float items per entry in a DVM sample data array
-#define NUM_SAMPLE_DATA 9
-
-#define SAMPLE_X 0
-#define SAMPLE_Y 1
-#define SAMPLE_PRESSURE 2
-#define SAMPLE_SIZE 3
-#define SAMPLE_TOUCH_MAJOR 4
-#define SAMPLE_TOUCH_MINOR 5
-#define SAMPLE_TOOL_MAJOR 6
-#define SAMPLE_TOOL_MINOR 7
-#define SAMPLE_ORIENTATION 8
 
 
 namespace android {
@@ -52,35 +38,41 @@
     jmethodID obtain;
     jmethodID recycle;
 
-    jfieldID mDeviceId;
-    jfieldID mSource;
-    jfieldID mDownTimeNano;
-    jfieldID mAction;
-    jfieldID mXOffset;
-    jfieldID mYOffset;
-    jfieldID mXPrecision;
-    jfieldID mYPrecision;
-    jfieldID mEdgeFlags;
-    jfieldID mMetaState;
-    jfieldID mFlags;
-    jfieldID mNumPointers;
-    jfieldID mNumSamples;
-    jfieldID mPointerIdentifiers;
-    jfieldID mDataSamples;
-    jfieldID mEventTimeNanoSamples;
-    jfieldID mLastDataSampleIndex;
-    jfieldID mLastEventTimeNanoSampleIndex;
+    jfieldID mNativePtr;
 } gMotionEventClassInfo;
 
+static struct {
+    jclass clazz;
+
+    jfieldID mPackedAxisBits;
+    jfieldID mPackedAxisValues;
+    jfieldID x;
+    jfieldID y;
+    jfieldID pressure;
+    jfieldID size;
+    jfieldID touchMajor;
+    jfieldID touchMinor;
+    jfieldID toolMajor;
+    jfieldID toolMinor;
+    jfieldID orientation;
+} gPointerCoordsClassInfo;
+
 // ----------------------------------------------------------------------------
 
-jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) {
-    jint numPointers = jint(event->getPointerCount());
-    jint numHistoricalSamples = jint(event->getHistorySize());
-    jint numSamples = numHistoricalSamples + 1;
+static MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) {
+    return reinterpret_cast<MotionEvent*>(
+            env->GetIntField(eventObj, gMotionEventClassInfo.mNativePtr));
+}
 
+static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj,
+        MotionEvent* event) {
+    env->SetIntField(eventObj, gMotionEventClassInfo.mNativePtr,
+            reinterpret_cast<int>(event));
+}
+
+jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) {
     jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
-            gMotionEventClassInfo.obtain, numPointers, numSamples);
+            gMotionEventClassInfo.obtain);
     if (env->ExceptionCheck()) {
         LOGE("An exception occurred while obtaining a motion event.");
         LOGE_EX(env);
@@ -88,168 +80,25 @@
         return NULL;
     }
 
-    env->SetIntField(eventObj, gMotionEventClassInfo.mDeviceId,
-            event->getDeviceId());
-    env->SetIntField(eventObj, gMotionEventClassInfo.mSource,
-            event->getSource());
-    env->SetLongField(eventObj, gMotionEventClassInfo.mDownTimeNano,
-            event->getDownTime());
-    env->SetIntField(eventObj, gMotionEventClassInfo.mAction,
-            event->getAction());
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset,
-            event->getXOffset());
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset,
-            event->getYOffset());
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mXPrecision,
-            event->getXPrecision());
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mYPrecision,
-            event->getYPrecision());
-    env->SetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags,
-            event->getEdgeFlags());
-    env->SetIntField(eventObj, gMotionEventClassInfo.mMetaState,
-            event->getMetaState());
-    env->SetIntField(eventObj, gMotionEventClassInfo.mFlags,
-            event->getFlags());
-    env->SetIntField(eventObj, gMotionEventClassInfo.mNumPointers,
-            numPointers);
-    env->SetIntField(eventObj, gMotionEventClassInfo.mNumSamples,
-            numSamples);
-    env->SetIntField(eventObj, gMotionEventClassInfo.mLastDataSampleIndex,
-            (numSamples - 1) * numPointers * NUM_SAMPLE_DATA);
-    env->SetIntField(eventObj, gMotionEventClassInfo.mLastEventTimeNanoSampleIndex,
-            numSamples - 1);
-
-    jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mPointerIdentifiers));
-    jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mDataSamples));
-    jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mEventTimeNanoSamples));
-
-    jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL);
-    jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
-    jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical(
-            eventTimeNanoSampleArray, NULL);
-
-    const int32_t* srcPointerIdentifiers = event->getPointerIds();
-    jint* destPointerIdentifiers = pointerIdentifiers;
-    for (jint i = 0; i < numPointers; i++) {
-        *(destPointerIdentifiers++) = *(srcPointerIdentifiers++);
+    MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
+    if (!destEvent) {
+        destEvent = new MotionEvent();
+        android_view_MotionEvent_setNativePtr(env, eventObj, destEvent);
     }
 
-    const nsecs_t* srcSampleEventTimes = event->getSampleEventTimes();
-    jlong* destEventTimeNanoSamples = eventTimeNanoSamples;
-    for (jint i = 0; i < numSamples; i++) {
-        *(destEventTimeNanoSamples++) = *(srcSampleEventTimes++);
-    }
-
-    const PointerCoords* srcSamplePointerCoords = event->getSamplePointerCoords();
-    jfloat* destDataSamples = dataSamples;
-    jint numItems = numSamples * numPointers;
-    for (jint i = 0; i < numItems; i++) {
-        *(destDataSamples++) = srcSamplePointerCoords->x;
-        *(destDataSamples++) = srcSamplePointerCoords->y;
-        *(destDataSamples++) = srcSamplePointerCoords->pressure;
-        *(destDataSamples++) = srcSamplePointerCoords->size;
-        *(destDataSamples++) = srcSamplePointerCoords->touchMajor;
-        *(destDataSamples++) = srcSamplePointerCoords->touchMinor;
-        *(destDataSamples++) = srcSamplePointerCoords->toolMajor;
-        *(destDataSamples++) = srcSamplePointerCoords->toolMinor;
-        *(destDataSamples++) = srcSamplePointerCoords->orientation;
-        srcSamplePointerCoords += 1;
-    }
-
-    env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, 0);
-    env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0);
-    env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, 0);
-
-    env->DeleteLocalRef(pointerIdentifierArray);
-    env->DeleteLocalRef(dataSampleArray);
-    env->DeleteLocalRef(eventTimeNanoSampleArray);
+    destEvent->copyFrom(event, true);
     return eventObj;
 }
 
 status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
         MotionEvent* event) {
-    jint deviceId = env->GetIntField(eventObj, gMotionEventClassInfo.mDeviceId);
-    jint source = env->GetIntField(eventObj, gMotionEventClassInfo.mSource);
-    jlong downTimeNano = env->GetLongField(eventObj, gMotionEventClassInfo.mDownTimeNano);
-    jint action = env->GetIntField(eventObj, gMotionEventClassInfo.mAction);
-    jfloat xOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset);
-    jfloat yOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset);
-    jfloat xPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mXPrecision);
-    jfloat yPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mYPrecision);
-    jint edgeFlags = env->GetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags);
-    jint metaState = env->GetIntField(eventObj, gMotionEventClassInfo.mMetaState);
-    jint flags = env->GetIntField(eventObj, gMotionEventClassInfo.mFlags);
-    jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
-    jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
-
-    if (numPointers == 0) {
-        LOGE("Malformed MotionEvent: mNumPointers was zero");
-        return BAD_VALUE;
-    }
-    if (numSamples == 0) {
-        LOGE("Malformed MotionEvent: mNumSamples was zero");
+    MotionEvent* srcEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
+    if (!srcEvent) {
+        LOGE("MotionEvent was finalized");
         return BAD_VALUE;
     }
 
-    jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mPointerIdentifiers));
-    jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mDataSamples));
-    jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mEventTimeNanoSamples));
-
-    jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL);
-    jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
-    jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical(
-            eventTimeNanoSampleArray, NULL);
-
-    jfloat* srcDataSamples = dataSamples;
-    jlong* srcEventTimeNanoSamples = eventTimeNanoSamples;
-
-    jlong sampleEventTime = *(srcEventTimeNanoSamples++);
-    PointerCoords samplePointerCoords[MAX_POINTERS];
-    for (jint j = 0; j < numPointers; j++) {
-        samplePointerCoords[j].x = *(srcDataSamples++);
-        samplePointerCoords[j].y = *(srcDataSamples++);
-        samplePointerCoords[j].pressure = *(srcDataSamples++);
-        samplePointerCoords[j].size = *(srcDataSamples++);
-        samplePointerCoords[j].touchMajor = *(srcDataSamples++);
-        samplePointerCoords[j].touchMinor = *(srcDataSamples++);
-        samplePointerCoords[j].toolMajor = *(srcDataSamples++);
-        samplePointerCoords[j].toolMinor = *(srcDataSamples++);
-        samplePointerCoords[j].orientation = *(srcDataSamples++);
-    }
-
-    event->initialize(deviceId, source, action, flags, edgeFlags, metaState,
-            xOffset, yOffset, xPrecision, yPrecision, downTimeNano, sampleEventTime,
-            numPointers, pointerIdentifiers, samplePointerCoords);
-
-    for (jint i = 1; i < numSamples; i++) {
-        sampleEventTime = *(srcEventTimeNanoSamples++);
-        for (jint j = 0; j < numPointers; j++) {
-            samplePointerCoords[j].x = *(srcDataSamples++);
-            samplePointerCoords[j].y = *(srcDataSamples++);
-            samplePointerCoords[j].pressure = *(srcDataSamples++);
-            samplePointerCoords[j].size = *(srcDataSamples++);
-            samplePointerCoords[j].touchMajor = *(srcDataSamples++);
-            samplePointerCoords[j].touchMinor = *(srcDataSamples++);
-            samplePointerCoords[j].toolMajor = *(srcDataSamples++);
-            samplePointerCoords[j].toolMinor = *(srcDataSamples++);
-            samplePointerCoords[j].orientation = *(srcDataSamples++);
-        }
-        event->addSample(sampleEventTime, samplePointerCoords);
-    }
-
-    env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, JNI_ABORT);
-    env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT);
-    env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT);
-
-    env->DeleteLocalRef(eventTimeNanoSampleArray);
-    env->DeleteLocalRef(dataSampleArray);
-    env->DeleteLocalRef(pointerIdentifierArray);
+    event->copyFrom(srcEvent, true);
     return OK;
 }
 
@@ -264,85 +113,615 @@
     return OK;
 }
 
-static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
-    // Construct and transform a vector oriented at the specified clockwise angle from vertical.
-    // Coordinate system: down is increasing Y, right is increasing X.
-    SkPoint vector;
-    vector.fX = SkFloatToScalar(sinf(angleRadians));
-    vector.fY = SkFloatToScalar(- cosf(angleRadians));
-    matrix->mapVectors(& vector, 1);
+// ----------------------------------------------------------------------------
 
-    // Derive the transformed vector's clockwise angle from vertical.
-    float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(- vector.fY));
-    if (result < - M_PI_2) {
-        result += M_PI;
-    } else if (result > M_PI_2) {
-        result -= M_PI;
+static const jint HISTORY_CURRENT = -0x80000000;
+
+static bool validatePointerCount(JNIEnv* env, jint pointerCount) {
+    if (pointerCount < 1) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "pointerCount must be at least 1");
+        return false;
     }
-    return result;
+    return true;
 }
 
-static void android_view_MotionEvent_nativeTransform(JNIEnv* env,
-        jobject eventObj, jobject matrixObj) {
-    SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
+static bool validatePointerIdsArray(JNIEnv* env, jintArray pointerIdsArray,
+        size_t pointerCount) {
+    if (!pointerIdsArray) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "pointerIds array must not be null");
+        return false;
+    }
+    size_t length = size_t(env->GetArrayLength(pointerIdsArray));
+    if (length < pointerCount) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "pointerIds array must be large enough to hold all pointers");
+        return false;
+    }
+    return true;
+}
 
-    jfloat oldXOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset);
-    jfloat oldYOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset);
-    jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
-    jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
-    jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mDataSamples));
-    jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
+static bool validatePointerCoordsObjArray(JNIEnv* env, jobjectArray pointerCoordsObjArray,
+        size_t pointerCount) {
+    if (!pointerCoordsObjArray) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "pointerCoords array must not be null");
+        return false;
+    }
+    size_t length = size_t(env->GetArrayLength(pointerCoordsObjArray));
+    if (length < pointerCount) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "pointerCoords array must be large enough to hold all pointers");
+        return false;
+    }
+    return true;
+}
 
-    // The tricky part of this implementation is to preserve the value of
-    // rawX and rawY.  So we apply the transformation to the first point
-    // then derive an appropriate new X/Y offset that will preserve rawX and rawY.
-    SkPoint point;
-    jfloat rawX = dataSamples[SAMPLE_X];
-    jfloat rawY = dataSamples[SAMPLE_Y];
-    matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
-            & point);
-    jfloat newX = SkScalarToFloat(point.fX);
-    jfloat newY = SkScalarToFloat(point.fY);
-    jfloat newXOffset = newX - rawX;
-    jfloat newYOffset = newY - rawY;
+static bool validatePointerIndex(JNIEnv* env, jint pointerIndex, size_t pointerCount) {
+    if (pointerIndex < 0 || size_t(pointerIndex) >= pointerCount) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "pointerIndex out of range");
+        return false;
+    }
+    return true;
+}
 
-    dataSamples[SAMPLE_ORIENTATION] = transformAngle(matrix, dataSamples[SAMPLE_ORIENTATION]);
+static bool validateHistoryPos(JNIEnv* env, jint historyPos, size_t historySize) {
+    if (historyPos < 0 || size_t(historyPos) >= historySize) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "historyPos out of range");
+        return false;
+    }
+    return true;
+}
 
-    // Apply the transformation to all samples.
-    jfloat* currentDataSample = dataSamples;
-    jfloat* endDataSample = dataSamples + numPointers * numSamples * NUM_SAMPLE_DATA;
-    for (;;) {
-        currentDataSample += NUM_SAMPLE_DATA;
-        if (currentDataSample == endDataSample) {
-            break;
+static bool validatePointerCoords(JNIEnv* env, jobject pointerCoordsObj) {
+    if (!pointerCoordsObj) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "pointerCoords must not be null");
+        return false;
+    }
+    return true;
+}
+
+static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
+        float xOffset, float yOffset, PointerCoords* outRawPointerCoords) {
+    outRawPointerCoords->clear();
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_X,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset);
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_Y,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset);
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_PRESSURE,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_SIZE,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
+    outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_ORIENTATION,
+            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
+
+    uint32_t bits = env->GetIntField(pointerCoordsObj,
+            gPointerCoordsClassInfo.mPackedAxisBits);
+    if (bits) {
+        jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
+                gPointerCoordsClassInfo.mPackedAxisValues));
+        if (valuesArray) {
+            jfloat* values = static_cast<jfloat*>(
+                    env->GetPrimitiveArrayCritical(valuesArray, NULL));
+
+            uint32_t index = 0;
+            do {
+                uint32_t axis = __builtin_ctz(bits);
+                uint32_t axisBit = 1 << axis;
+                bits &= ~axisBit;
+                outRawPointerCoords->setAxisValue(axis, values[index++]);
+            } while (bits);
+
+            env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
+            env->DeleteLocalRef(valuesArray);
+        }
+    }
+}
+
+static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize,
+        jobject outPointerCoordsObj) {
+    jfloatArray outValuesArray = jfloatArray(env->GetObjectField(outPointerCoordsObj,
+            gPointerCoordsClassInfo.mPackedAxisValues));
+    if (outValuesArray) {
+        uint32_t size = env->GetArrayLength(outValuesArray);
+        if (minSize <= size) {
+            return outValuesArray;
+        }
+        env->DeleteLocalRef(outValuesArray);
+    }
+    uint32_t size = 8;
+    while (size < minSize) {
+        size *= 2;
+    }
+    outValuesArray = env->NewFloatArray(size);
+    env->SetObjectField(outPointerCoordsObj,
+            gPointerCoordsClassInfo.mPackedAxisValues, outValuesArray);
+    return outValuesArray;
+}
+
+static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointerCoords,
+        float xOffset, float yOffset, jobject outPointerCoordsObj) {
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset);
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset);
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_PRESSURE));
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.size,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_SIZE));
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMajor,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR));
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMinor,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR));
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMajor,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR));
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMinor,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR));
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
+            rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION));
+
+    const uint32_t unpackedAxisBits = 0
+            | (1 << AINPUT_MOTION_AXIS_X)
+            | (1 << AINPUT_MOTION_AXIS_Y)
+            | (1 << AINPUT_MOTION_AXIS_PRESSURE)
+            | (1 << AINPUT_MOTION_AXIS_SIZE)
+            | (1 << AINPUT_MOTION_AXIS_TOUCH_MAJOR)
+            | (1 << AINPUT_MOTION_AXIS_TOUCH_MINOR)
+            | (1 << AINPUT_MOTION_AXIS_TOOL_MAJOR)
+            | (1 << AINPUT_MOTION_AXIS_TOOL_MINOR)
+            | (1 << AINPUT_MOTION_AXIS_ORIENTATION);
+
+    uint32_t outBits = 0;
+    uint32_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits;
+    if (remainingBits) {
+        uint32_t packedAxesCount = __builtin_popcount(remainingBits);
+        jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
+                outPointerCoordsObj);
+        if (!outValuesArray) {
+            return; // OOM
         }
 
-        jfloat x = currentDataSample[SAMPLE_X] + oldXOffset;
-        jfloat y = currentDataSample[SAMPLE_Y] + oldYOffset;
-        matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point);
-        currentDataSample[SAMPLE_X] = SkScalarToFloat(point.fX) - newXOffset;
-        currentDataSample[SAMPLE_Y] = SkScalarToFloat(point.fY) - newYOffset;
+        jfloat* outValues = static_cast<jfloat*>(env->GetPrimitiveArrayCritical(
+                outValuesArray, NULL));
 
-        currentDataSample[SAMPLE_ORIENTATION] = transformAngle(matrix,
-                currentDataSample[SAMPLE_ORIENTATION]);
+        const float* values = rawPointerCoords->values;
+        uint32_t index = 0;
+        do {
+            uint32_t axis = __builtin_ctz(remainingBits);
+            uint32_t axisBit = 1 << axis;
+            remainingBits &= ~axisBit;
+            outBits |= axisBit;
+            outValues[index++] = rawPointerCoords->getAxisValue(axis);
+        } while (remainingBits);
+
+        env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0);
+        env->DeleteLocalRef(outValuesArray);
+    }
+    env->SetIntField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
+}
+
+
+// ----------------------------------------------------------------------------
+
+static jint android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
+        jint nativePtr,
+        jint deviceId, jint source, jint action, jint flags, jint edgeFlags, jint metaState,
+        jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
+        jlong downTimeNanos, jlong eventTimeNanos,
+        jint pointerCount, jintArray pointerIdsArray, jobjectArray pointerCoordsObjArray) {
+    if (!validatePointerCount(env, pointerCount)
+            || !validatePointerIdsArray(env, pointerIdsArray, pointerCount)
+            || !validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) {
+        return 0;
     }
 
-    env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0);
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    if (!event) {
+        event = new MotionEvent();
+    }
 
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset, newXOffset);
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset, newYOffset);
+    PointerCoords rawPointerCoords[pointerCount];
 
-    env->DeleteLocalRef(dataSampleArray);
+    for (jint i = 0; i < pointerCount; i++) {
+        jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
+        if (!pointerCoordsObj) {
+            jniThrowNullPointerException(env, "pointerCoords");
+            if (!nativePtr) {
+                delete event;
+            }
+            return 0;
+        }
+        pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]);
+        env->DeleteLocalRef(pointerCoordsObj);
+    }
+
+    int* pointerIds = static_cast<int*>(env->GetPrimitiveArrayCritical(pointerIdsArray, NULL));
+
+    event->initialize(deviceId, source, action, flags, edgeFlags, metaState,
+            xOffset, yOffset, xPrecision, yPrecision,
+            downTimeNanos, eventTimeNanos, pointerCount, pointerIds, rawPointerCoords);
+
+    env->ReleasePrimitiveArrayCritical(pointerIdsArray, pointerIds, JNI_ABORT);
+    return reinterpret_cast<jint>(event);
+}
+
+static jint android_view_MotionEvent_nativeCopy(JNIEnv* env, jclass clazz,
+        jint destNativePtr, jint sourceNativePtr, jboolean keepHistory) {
+    MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
+    if (!destEvent) {
+        destEvent = new MotionEvent();
+    }
+    MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
+    destEvent->copyFrom(sourceEvent, keepHistory);
+    return reinterpret_cast<jint>(destEvent);
+}
+
+static void android_view_MotionEvent_nativeDispose(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    delete event;
+}
+
+static void android_view_MotionEvent_nativeAddBatch(JNIEnv* env, jclass clazz,
+        jint nativePtr, jlong eventTimeNanos, jobjectArray pointerCoordsObjArray,
+        jint metaState) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) {
+        return;
+    }
+
+    PointerCoords rawPointerCoords[pointerCount];
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
+        if (!pointerCoordsObj) {
+            jniThrowNullPointerException(env, "pointerCoords");
+            return;
+        }
+        pointerCoordsToNative(env, pointerCoordsObj,
+                event->getXOffset(), event->getYOffset(), &rawPointerCoords[i]);
+        env->DeleteLocalRef(pointerCoordsObj);
+    }
+
+    event->addSample(eventTimeNanos, rawPointerCoords);
+    event->setMetaState(event->getMetaState() | metaState);
+}
+
+static jint android_view_MotionEvent_nativeGetDeviceId(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getDeviceId();
+}
+
+static jint android_view_MotionEvent_nativeGetSource(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getSource();
+}
+
+static void android_view_MotionEvent_nativeSetSource(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint source) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setSource(source);
+}
+
+static jint android_view_MotionEvent_nativeGetAction(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getAction();
+}
+
+static void android_view_MotionEvent_nativeSetAction(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint action) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setAction(action);
+}
+
+static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getFlags();
+}
+
+static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getEdgeFlags();
+}
+
+static void android_view_MotionEvent_nativeSetEdgeFlags(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint edgeFlags) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setEdgeFlags(edgeFlags);
+}
+
+static jint android_view_MotionEvent_nativeGetMetaState(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getMetaState();
+}
+
+static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz,
+        jint nativePtr, jfloat deltaX, jfloat deltaY) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->offsetLocation(deltaX, deltaY);
+}
+
+static jfloat android_view_MotionEvent_nativeGetXPrecision(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getXPrecision();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYPrecision(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getYPrecision();
+}
+
+static jlong android_view_MotionEvent_nativeGetDownTimeNanos(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getDownTime();
+}
+
+static jint android_view_MotionEvent_nativeGetPointerCount(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return jint(event->getPointerCount());
+}
+
+static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint pointerIndex) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+        return -1;
+    }
+    return event->getPointerId(pointerIndex);
+}
+
+static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint pointerId) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    for (size_t i = 0; i < pointerCount; i++) {
+        if (event->getPointerId(i) == pointerId) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz,
+        jint nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return jint(event->getHistorySize());
+}
+
+static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint historyPos) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    if (historyPos == HISTORY_CURRENT) {
+        return event->getEventTime();
+    } else {
+        size_t historySize = event->getHistorySize();
+        if (!validateHistoryPos(env, historyPos, historySize)) {
+            return 0;
+        }
+        return event->getHistoricalEventTime(historyPos);
+    }
+}
+
+static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint axis, jint pointerIndex, jint historyPos) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+        return 0;
+    }
+
+    if (historyPos == HISTORY_CURRENT) {
+        return event->getRawAxisValue(axis, pointerIndex);
+    } else {
+        size_t historySize = event->getHistorySize();
+        if (!validateHistoryPos(env, historyPos, historySize)) {
+            return 0;
+        }
+        return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
+    }
+}
+
+static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint axis, jint pointerIndex, jint historyPos) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+        return 0;
+    }
+
+    if (historyPos == HISTORY_CURRENT) {
+        return event->getAxisValue(axis, pointerIndex);
+    } else {
+        size_t historySize = event->getHistorySize();
+        if (!validateHistoryPos(env, historyPos, historySize)) {
+            return 0;
+        }
+        return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
+    }
+}
+
+static void android_view_MotionEvent_nativeGetPointerCoords(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint pointerIndex, jint historyPos, jobject outPointerCoordsObj) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)
+            || !validatePointerCoords(env, outPointerCoordsObj)) {
+        return;
+    }
+
+    const PointerCoords* rawPointerCoords;
+    if (historyPos == HISTORY_CURRENT) {
+        rawPointerCoords = event->getRawPointerCoords(pointerIndex);
+    } else {
+        size_t historySize = event->getHistorySize();
+        if (!validateHistoryPos(env, historyPos, historySize)) {
+            return;
+        }
+        rawPointerCoords = event->getHistoricalRawPointerCoords(pointerIndex, historyPos);
+    }
+    pointerCoordsFromNative(env, rawPointerCoords, event->getXOffset(), event->getYOffset(),
+            outPointerCoordsObj);
+}
+
+static void android_view_MotionEvent_nativeScale(JNIEnv* env, jclass clazz,
+        jint nativePtr, jfloat scale) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->scale(scale);
+}
+
+static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
+        jint nativePtr, jobject matrixObj) {
+    SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->transform(matrix);
+}
+
+static jint android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass clazz,
+        jint nativePtr, jobject parcelObj) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    if (!event) {
+        event = new MotionEvent();
+    }
+
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+
+    status_t status = event->readFromParcel(parcel);
+    if (!status) {
+        if (!nativePtr) {
+            delete event;
+        }
+        jniThrowRuntimeException(env, "Failed to read MotionEvent parcel.");
+        return 0;
+    }
+    return reinterpret_cast<jint>(event);
+}
+
+static void android_view_MotionEvent_nativeWriteToParcel(JNIEnv* env, jclass clazz,
+        jint nativePtr, jobject parcelObj) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+
+    status_t status = event->writeToParcel(parcel);
+    if (!status) {
+        jniThrowRuntimeException(env, "Failed to write MotionEvent parcel.");
+    }
 }
 
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMotionEventMethods[] = {
     /* name, signature, funcPtr */
+    { "nativeInitialize",
+            "(IIIIIIIFFFFJJI[I[Landroid/view/MotionEvent$PointerCoords;)I",
+            (void*)android_view_MotionEvent_nativeInitialize },
+    { "nativeCopy",
+            "(IIZ)I",
+            (void*)android_view_MotionEvent_nativeCopy },
+    { "nativeDispose",
+            "(I)V",
+            (void*)android_view_MotionEvent_nativeDispose },
+    { "nativeAddBatch",
+            "(IJ[Landroid/view/MotionEvent$PointerCoords;I)V",
+            (void*)android_view_MotionEvent_nativeAddBatch },
+    { "nativeGetDeviceId",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetDeviceId },
+    { "nativeGetSource",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetSource },
+    { "nativeSetSource",
+            "(II)I",
+            (void*)android_view_MotionEvent_nativeSetSource },
+    { "nativeGetAction",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetAction },
+    { "nativeSetAction",
+            "(II)V",
+            (void*)android_view_MotionEvent_nativeSetAction },
+    { "nativeGetFlags",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetFlags },
+    { "nativeGetEdgeFlags",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetEdgeFlags },
+    { "nativeSetEdgeFlags",
+            "(II)V",
+            (void*)android_view_MotionEvent_nativeSetEdgeFlags },
+    { "nativeGetMetaState",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetMetaState },
+    { "nativeOffsetLocation",
+            "(IFF)V",
+            (void*)android_view_MotionEvent_nativeOffsetLocation },
+    { "nativeGetXPrecision",
+            "(I)F",
+            (void*)android_view_MotionEvent_nativeGetXPrecision },
+    { "nativeGetYPrecision",
+            "(I)F",
+            (void*)android_view_MotionEvent_nativeGetYPrecision },
+    { "nativeGetDownTimeNanos",
+            "(I)J",
+            (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
+    { "nativeGetPointerCount",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetPointerCount },
+    { "nativeGetPointerId",
+            "(II)I",
+            (void*)android_view_MotionEvent_nativeGetPointerId },
+    { "nativeFindPointerIndex",
+            "(II)I",
+            (void*)android_view_MotionEvent_nativeFindPointerIndex },
+    { "nativeGetHistorySize",
+            "(I)I",
+            (void*)android_view_MotionEvent_nativeGetHistorySize },
+    { "nativeGetEventTimeNanos",
+            "(II)J",
+            (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
+    { "nativeGetRawAxisValue",
+            "(IIII)F",
+            (void*)android_view_MotionEvent_nativeGetRawAxisValue },
+    { "nativeGetAxisValue",
+            "(IIII)F",
+            (void*)android_view_MotionEvent_nativeGetAxisValue },
+    { "nativeGetPointerCoords",
+            "(IIILandroid/view/MotionEvent$PointerCoords;)V",
+            (void*)android_view_MotionEvent_nativeGetPointerCoords },
+    { "nativeScale",
+            "(IF)V",
+            (void*)android_view_MotionEvent_nativeScale },
     { "nativeTransform",
-            "(Landroid/graphics/Matrix;)V",
+            "(ILandroid/graphics/Matrix;)V",
             (void*)android_view_MotionEvent_nativeTransform },
+    { "nativeReadFromParcel",
+            "(ILandroid/os/Parcel;)I",
+            (void*)android_view_MotionEvent_nativeReadFromParcel },
+    { "nativeWriteToParcel",
+            "(ILandroid/os/Parcel;)V",
+            (void*)android_view_MotionEvent_nativeWriteToParcel },
 };
 
 #define FIND_CLASS(var, className) \
@@ -370,46 +749,36 @@
     FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
 
     GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz,
-            "obtain", "(II)Landroid/view/MotionEvent;");
+            "obtain", "()Landroid/view/MotionEvent;");
     GET_METHOD_ID(gMotionEventClassInfo.recycle, gMotionEventClassInfo.clazz,
             "recycle", "()V");
+    GET_FIELD_ID(gMotionEventClassInfo.mNativePtr, gMotionEventClassInfo.clazz,
+            "mNativePtr", "I");
 
-    GET_FIELD_ID(gMotionEventClassInfo.mDeviceId, gMotionEventClassInfo.clazz,
-            "mDeviceId", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mSource, gMotionEventClassInfo.clazz,
-            "mSource", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mDownTimeNano, gMotionEventClassInfo.clazz,
-            "mDownTimeNano", "J");
-    GET_FIELD_ID(gMotionEventClassInfo.mAction, gMotionEventClassInfo.clazz,
-            "mAction", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mXOffset, gMotionEventClassInfo.clazz,
-            "mXOffset", "F");
-    GET_FIELD_ID(gMotionEventClassInfo.mYOffset, gMotionEventClassInfo.clazz,
-            "mYOffset", "F");
-    GET_FIELD_ID(gMotionEventClassInfo.mXPrecision, gMotionEventClassInfo.clazz,
-            "mXPrecision", "F");
-    GET_FIELD_ID(gMotionEventClassInfo.mYPrecision, gMotionEventClassInfo.clazz,
-            "mYPrecision", "F");
-    GET_FIELD_ID(gMotionEventClassInfo.mEdgeFlags, gMotionEventClassInfo.clazz,
-            "mEdgeFlags", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mMetaState, gMotionEventClassInfo.clazz,
-            "mMetaState", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mFlags, gMotionEventClassInfo.clazz,
-            "mFlags", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mNumPointers, gMotionEventClassInfo.clazz,
-            "mNumPointers", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mNumSamples, gMotionEventClassInfo.clazz,
-            "mNumSamples", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mPointerIdentifiers, gMotionEventClassInfo.clazz,
-            "mPointerIdentifiers", "[I");
-    GET_FIELD_ID(gMotionEventClassInfo.mDataSamples, gMotionEventClassInfo.clazz,
-            "mDataSamples", "[F");
-    GET_FIELD_ID(gMotionEventClassInfo.mEventTimeNanoSamples, gMotionEventClassInfo.clazz,
-            "mEventTimeNanoSamples", "[J");
-    GET_FIELD_ID(gMotionEventClassInfo.mLastDataSampleIndex, gMotionEventClassInfo.clazz,
-            "mLastDataSampleIndex", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mLastEventTimeNanoSampleIndex, gMotionEventClassInfo.clazz,
-            "mLastEventTimeNanoSampleIndex", "I");
+    FIND_CLASS(gPointerCoordsClassInfo.clazz, "android/view/MotionEvent$PointerCoords");
+
+    GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisBits, gPointerCoordsClassInfo.clazz,
+            "mPackedAxisBits", "I");
+    GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisValues, gPointerCoordsClassInfo.clazz,
+            "mPackedAxisValues", "[F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.x, gPointerCoordsClassInfo.clazz,
+            "x", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.y, gPointerCoordsClassInfo.clazz,
+            "y", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.pressure, gPointerCoordsClassInfo.clazz,
+            "pressure", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.size, gPointerCoordsClassInfo.clazz,
+            "size", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.touchMajor, gPointerCoordsClassInfo.clazz,
+            "touchMajor", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.touchMinor, gPointerCoordsClassInfo.clazz,
+            "touchMinor", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.toolMajor, gPointerCoordsClassInfo.clazz,
+            "toolMajor", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.toolMinor, gPointerCoordsClassInfo.clazz,
+            "toolMinor", "F");
+    GET_FIELD_ID(gPointerCoordsClassInfo.orientation, gPointerCoordsClassInfo.clazz,
+            "orientation", "F");
 
     return 0;
 }
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 2012fcc..b7b5a8d 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -28,6 +28,10 @@
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 
+#ifdef HAVE_ANDROID_OS
+class SkMatrix;
+#endif
+
 /*
  * Additional private constants not defined in ndk/ui/input.h.
  */
@@ -79,6 +83,10 @@
 
 namespace android {
 
+#ifdef HAVE_ANDROID_OS
+class Parcel;
+#endif
+
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
@@ -162,15 +170,61 @@
  * Pointer coordinate data.
  */
 struct PointerCoords {
-    float x;
-    float y;
-    float pressure;
-    float size;
-    float touchMajor;
-    float touchMinor;
-    float toolMajor;
-    float toolMinor;
-    float orientation;
+    static const size_t MAX_AXES = 15; // 15 so that sizeof(PointerCoords) == 16 * 4 == 64
+
+    // Bitfield of axes that are present in this structure.
+    uint32_t bits; // 32bits are enough for now, can raise to 64bit when needed
+
+    // Values of axes that are stored in this structure packed in order by axis id
+    // for each axis that is present in the structure according to 'bits'.
+    float values[MAX_AXES];
+
+    inline void clear() {
+        bits = 0;
+    }
+
+    inline float getAxisValue(int32_t axis) const {
+        uint32_t axisBit = 1 << axis;
+        if (!(bits & axisBit)) {
+            return 0;
+        }
+        uint32_t index = __builtin_popcount(bits & (axisBit - 1));
+        return values[index];
+    }
+
+    inline void setAxisValue(int32_t axis, float value) {
+        uint32_t axisBit = 1 << axis;
+        uint32_t index = __builtin_popcount(bits & (axisBit - 1));
+        if (!(bits & axisBit)) {
+            uint32_t count = __builtin_popcount(bits);
+            if (count >= MAX_AXES) {
+                tooManyAxes(axis);
+                return;
+            }
+            bits |= axisBit;
+            for (uint32_t i = count; i > index; i--) {
+                values[i] = values[i - 1];
+            }
+        }
+        values[index] = value;
+    }
+
+    inline float* editAxisValue(int32_t axis) {
+        uint32_t axisBit = 1 << axis;
+        if (!(bits & axisBit)) {
+            return NULL;
+        }
+        uint32_t index = __builtin_popcount(bits & (axisBit - 1));
+        return &values[index];
+    }
+
+#ifdef HAVE_ANDROID_OS
+    status_t readFromParcel(Parcel* parcel);
+    status_t writeToParcel(Parcel* parcel) const;
+#endif
+
+private:
+    void tooManyAxes(int axis);
 };
 
 /*
@@ -185,12 +239,13 @@
     inline int32_t getDeviceId() const { return mDeviceId; }
 
     inline int32_t getSource() const { return mSource; }
-    
+
+    inline void setSource(int32_t source) { mSource = source; }
+
 protected:
     void initialize(int32_t deviceId, int32_t source);
     void initialize(const InputEvent& from);
 
-private:
     int32_t mDeviceId;
     int32_t mSource;
 };
@@ -241,7 +296,7 @@
             nsecs_t eventTime);
     void initialize(const KeyEvent& from);
 
-private:
+protected:
     int32_t mAction;
     int32_t mFlags;
     int32_t mKeyCode;
@@ -263,12 +318,18 @@
 
     inline int32_t getAction() const { return mAction; }
 
+    inline void setAction(int32_t action) { mAction = action; }
+
     inline int32_t getFlags() const { return mFlags; }
 
     inline int32_t getEdgeFlags() const { return mEdgeFlags; }
 
+    inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
+
     inline int32_t getMetaState() const { return mMetaState; }
 
+    inline void setMetaState(int32_t metaState) { mMetaState = metaState; }
+
     inline float getXOffset() const { return mXOffset; }
 
     inline float getYOffset() const { return mYOffset; }
@@ -285,48 +346,54 @@
 
     inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
 
+    const PointerCoords* getRawPointerCoords(size_t pointerIndex) const;
+
+    float getRawAxisValue(int32_t axis, size_t pointerIndex) const;
+
     inline float getRawX(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).x;
+        return getRawAxisValue(AINPUT_MOTION_AXIS_X, pointerIndex);
     }
 
     inline float getRawY(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).y;
+        return getRawAxisValue(AINPUT_MOTION_AXIS_Y, pointerIndex);
     }
 
+    float getAxisValue(int32_t axis, size_t pointerIndex) const;
+
     inline float getX(size_t pointerIndex) const {
-        return getRawX(pointerIndex) + mXOffset;
+        return getAxisValue(AINPUT_MOTION_AXIS_X, pointerIndex);
     }
 
     inline float getY(size_t pointerIndex) const {
-        return getRawY(pointerIndex) + mYOffset;
+        return getAxisValue(AINPUT_MOTION_AXIS_Y, pointerIndex);
     }
 
     inline float getPressure(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).pressure;
+        return getAxisValue(AINPUT_MOTION_AXIS_PRESSURE, pointerIndex);
     }
 
     inline float getSize(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).size;
+        return getAxisValue(AINPUT_MOTION_AXIS_SIZE, pointerIndex);
     }
 
     inline float getTouchMajor(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).touchMajor;
+        return getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, pointerIndex);
     }
 
     inline float getTouchMinor(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).touchMinor;
+        return getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, pointerIndex);
     }
 
     inline float getToolMajor(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).toolMajor;
+        return getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, pointerIndex);
     }
 
     inline float getToolMinor(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).toolMinor;
+        return getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR, pointerIndex);
     }
 
     inline float getOrientation(size_t pointerIndex) const {
-        return getCurrentPointerCoords(pointerIndex).orientation;
+        return getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, pointerIndex);
     }
 
     inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
@@ -335,48 +402,67 @@
         return mSampleEventTimes[historicalIndex];
     }
 
+    const PointerCoords* getHistoricalRawPointerCoords(
+            size_t pointerIndex, size_t historicalIndex) const;
+
+    float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
+            size_t historicalIndex) const;
+
     inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).x;
+        return getHistoricalRawAxisValue(
+                AINPUT_MOTION_AXIS_X, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).y;
+        return getHistoricalRawAxisValue(
+                AINPUT_MOTION_AXIS_Y, pointerIndex, historicalIndex);
     }
 
+    float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const;
+
     inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalRawX(pointerIndex, historicalIndex) + mXOffset;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_X, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalRawY(pointerIndex, historicalIndex) + mYOffset;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_Y, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_PRESSURE, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).size;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_SIZE, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMajor;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMinor;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMajor;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMinor;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_TOOL_MINOR, pointerIndex, historicalIndex);
     }
 
     inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const {
-        return getHistoricalPointerCoords(pointerIndex, historicalIndex).orientation;
+        return getHistoricalAxisValue(
+                AINPUT_MOTION_AXIS_ORIENTATION, pointerIndex, historicalIndex);
     }
 
     void initialize(
@@ -396,12 +482,23 @@
             const int32_t* pointerIds,
             const PointerCoords* pointerCoords);
 
+    void copyFrom(const MotionEvent* other, bool keepHistory);
+
     void addSample(
             nsecs_t eventTime,
             const PointerCoords* pointerCoords);
 
     void offsetLocation(float xOffset, float yOffset);
 
+    void scale(float scaleFactor);
+
+#ifdef HAVE_ANDROID_OS
+    void transform(const SkMatrix* matrix);
+
+    status_t readFromParcel(Parcel* parcel);
+    status_t writeToParcel(Parcel* parcel) const;
+#endif
+
     // Low-level accessors.
     inline const int32_t* getPointerIds() const { return mPointerIds.array(); }
     inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
@@ -409,7 +506,7 @@
             return mSamplePointerCoords.array();
     }
 
-private:
+protected:
     int32_t mAction;
     int32_t mFlags;
     int32_t mEdgeFlags;
@@ -422,15 +519,6 @@
     Vector<int32_t> mPointerIds;
     Vector<nsecs_t> mSampleEventTimes;
     Vector<PointerCoords> mSamplePointerCoords;
-
-    inline const PointerCoords& getCurrentPointerCoords(size_t pointerIndex) const {
-        return mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
-    }
-
-    inline const PointerCoords& getHistoricalPointerCoords(
-            size_t pointerIndex, size_t historicalIndex) const {
-        return mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
-    }
 };
 
 /*
@@ -486,11 +574,11 @@
     inline const String8 getName() const { return mName; }
     inline uint32_t getSources() const { return mSources; }
 
-    const MotionRange* getMotionRange(int32_t rangeType) const;
+    const MotionRange* getMotionRange(int32_t axis) 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);
+    void addMotionRange(int32_t axis, float min, float max, float flat, float fuzz);
+    void addMotionRange(int32_t axis, const MotionRange& range);
 
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
     inline int32_t getKeyboardType() const { return mKeyboardType; }
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 0d55f08..f9990bb 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -60,7 +60,12 @@
 	libEGL \
 	libpixelflinger \
 	libhardware \
-	libhardware_legacy
+	libhardware_legacy \
+	libskia \
+	libbinder
+
+LOCAL_C_INCLUDES := \
+    external/skia/include/core
 
 LOCAL_MODULE:= libui
 
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index b8d59e6..90b954e 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -15,6 +15,16 @@
 
 #include <ui/Input.h>
 
+#include <math.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <binder/Parcel.h>
+
+#include "SkPoint.h"
+#include "SkMatrix.h"
+#include "SkScalar.h"
+#endif
+
 namespace android {
 
 static const char* CONFIGURATION_FILE_DIR[] = {
@@ -237,6 +247,41 @@
     mEventTime = from.mEventTime;
 }
 
+
+// --- PointerCoords ---
+
+#ifdef HAVE_ANDROID_OS
+status_t PointerCoords::readFromParcel(Parcel* parcel) {
+    bits = parcel->readInt32();
+
+    uint32_t count = __builtin_popcount(bits);
+    if (count > MAX_AXES) {
+        return BAD_VALUE;
+    }
+
+    for (uint32_t i = 0; i < count; i++) {
+        values[i] = parcel->readInt32();
+    }
+    return OK;
+}
+
+status_t PointerCoords::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(bits);
+
+    uint32_t count = __builtin_popcount(bits);
+    for (uint32_t i = 0; i < count; i++) {
+        parcel->writeInt32(values[i]);
+    }
+    return OK;
+}
+#endif
+
+void PointerCoords::tooManyAxes(int axis) {
+    LOGW("Could not set value for axis %d because the PointerCoords structure is full and "
+            "cannot contain more than %d axis values.", axis, int(MAX_AXES));
+}
+
+
 // --- MotionEvent ---
 
 void MotionEvent::initialize(
@@ -272,6 +317,33 @@
     addSample(eventTime, pointerCoords);
 }
 
+void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
+    InputEvent::initialize(other->mDeviceId, other->mSource);
+    mAction = other->mAction;
+    mFlags = other->mFlags;
+    mEdgeFlags = other->mEdgeFlags;
+    mMetaState = other->mMetaState;
+    mXOffset = other->mXOffset;
+    mYOffset = other->mYOffset;
+    mXPrecision = other->mXPrecision;
+    mYPrecision = other->mYPrecision;
+    mDownTime = other->mDownTime;
+    mPointerIds = other->mPointerIds;
+
+    if (keepHistory) {
+        mSampleEventTimes = other->mSampleEventTimes;
+        mSamplePointerCoords = other->mSamplePointerCoords;
+    } else {
+        mSampleEventTimes.clear();
+        mSampleEventTimes.push(other->getEventTime());
+        mSamplePointerCoords.clear();
+        size_t pointerCount = other->getPointerCount();
+        size_t historySize = other->getHistorySize();
+        mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array()
+                + (historySize * pointerCount), pointerCount);
+    }
+}
+
 void MotionEvent::addSample(
         int64_t eventTime,
         const PointerCoords* pointerCoords) {
@@ -279,11 +351,224 @@
     mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
 }
 
+const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
+    return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
+}
+
+float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
+    return getRawPointerCoords(pointerIndex)->getAxisValue(axis);
+}
+
+float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
+    float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
+    switch (axis) {
+    case AINPUT_MOTION_AXIS_X:
+        value += mXOffset;
+        break;
+    case AINPUT_MOTION_AXIS_Y:
+        value += mYOffset;
+        break;
+    }
+    return value;
+}
+
+const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
+        size_t pointerIndex, size_t historicalIndex) const {
+    return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
+}
+
+float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
+        size_t historicalIndex) const {
+    return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+}
+
+float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
+        size_t historicalIndex) const {
+    float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+    switch (axis) {
+    case AINPUT_MOTION_AXIS_X:
+        value += mXOffset;
+        break;
+    case AINPUT_MOTION_AXIS_Y:
+        value += mYOffset;
+        break;
+    }
+    return value;
+}
+
 void MotionEvent::offsetLocation(float xOffset, float yOffset) {
     mXOffset += xOffset;
     mYOffset += yOffset;
 }
 
+static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) {
+    float* value = c.editAxisValue(axis);
+    if (value) {
+        *value *= scaleFactor;
+    }
+}
+
+void MotionEvent::scale(float scaleFactor) {
+    mXOffset *= scaleFactor;
+    mYOffset *= scaleFactor;
+    mXPrecision *= scaleFactor;
+    mYPrecision *= scaleFactor;
+
+    size_t numSamples = mSamplePointerCoords.size();
+    for (size_t i = 0; i < numSamples; i++) {
+        PointerCoords& c = mSamplePointerCoords.editItemAt(i);
+        // No need to scale pressure or size since they are normalized.
+        // No need to scale orientation since it is meaningless to do so.
+        scaleAxisValue(c, AINPUT_MOTION_AXIS_X, scaleFactor);
+        scaleAxisValue(c, AINPUT_MOTION_AXIS_Y, scaleFactor);
+        scaleAxisValue(c, AINPUT_MOTION_AXIS_TOUCH_MAJOR, scaleFactor);
+        scaleAxisValue(c, AINPUT_MOTION_AXIS_TOUCH_MINOR, scaleFactor);
+        scaleAxisValue(c, AINPUT_MOTION_AXIS_TOOL_MAJOR, scaleFactor);
+        scaleAxisValue(c, AINPUT_MOTION_AXIS_TOOL_MINOR, scaleFactor);
+    }
+}
+
+#ifdef HAVE_ANDROID_OS
+static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
+    // Construct and transform a vector oriented at the specified clockwise angle from vertical.
+    // Coordinate system: down is increasing Y, right is increasing X.
+    SkPoint vector;
+    vector.fX = SkFloatToScalar(sinf(angleRadians));
+    vector.fY = SkFloatToScalar(-cosf(angleRadians));
+    matrix->mapVectors(& vector, 1);
+
+    // Derive the transformed vector's clockwise angle from vertical.
+    float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY));
+    if (result < - M_PI_2) {
+        result += M_PI;
+    } else if (result > M_PI_2) {
+        result -= M_PI;
+    }
+    return result;
+}
+
+void MotionEvent::transform(const SkMatrix* matrix) {
+    float oldXOffset = mXOffset;
+    float oldYOffset = mYOffset;
+
+    // The tricky part of this implementation is to preserve the value of
+    // rawX and rawY.  So we apply the transformation to the first point
+    // then derive an appropriate new X/Y offset that will preserve rawX and rawY.
+    SkPoint point;
+    float rawX = getRawX(0);
+    float rawY = getRawY(0);
+    matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
+            & point);
+    float newX = SkScalarToFloat(point.fX);
+    float newY = SkScalarToFloat(point.fY);
+    float newXOffset = newX - rawX;
+    float newYOffset = newY - rawY;
+
+    mXOffset = newXOffset;
+    mYOffset = newYOffset;
+
+    // Apply the transformation to all samples.
+    size_t numSamples = mSamplePointerCoords.size();
+    for (size_t i = 0; i < numSamples; i++) {
+        PointerCoords& c = mSamplePointerCoords.editItemAt(i);
+        float* xPtr = c.editAxisValue(AINPUT_MOTION_AXIS_X);
+        float* yPtr = c.editAxisValue(AINPUT_MOTION_AXIS_Y);
+        if (xPtr && yPtr) {
+            float x = *xPtr + oldXOffset;
+            float y = *yPtr + oldYOffset;
+            matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point);
+            *xPtr = SkScalarToFloat(point.fX) - newXOffset;
+            *yPtr = SkScalarToFloat(point.fY) - newYOffset;
+        }
+
+        float* orientationPtr = c.editAxisValue(AINPUT_MOTION_AXIS_ORIENTATION);
+        if (orientationPtr) {
+            *orientationPtr = transformAngle(matrix, *orientationPtr);
+        }
+    }
+}
+
+status_t MotionEvent::readFromParcel(Parcel* parcel) {
+    size_t pointerCount = parcel->readInt32();
+    size_t sampleCount = parcel->readInt32();
+    if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) {
+        return BAD_VALUE;
+    }
+
+    mDeviceId = parcel->readInt32();
+    mSource = parcel->readInt32();
+    mAction = parcel->readInt32();
+    mFlags = parcel->readInt32();
+    mEdgeFlags = parcel->readInt32();
+    mMetaState = parcel->readInt32();
+    mXOffset = parcel->readFloat();
+    mYOffset = parcel->readFloat();
+    mXPrecision = parcel->readFloat();
+    mYPrecision = parcel->readFloat();
+    mDownTime = parcel->readInt64();
+
+    mPointerIds.clear();
+    mPointerIds.setCapacity(pointerCount);
+    mSampleEventTimes.clear();
+    mSampleEventTimes.setCapacity(sampleCount);
+    mSamplePointerCoords.clear();
+    mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        mPointerIds.push(parcel->readInt32());
+    }
+
+    while (sampleCount-- > 0) {
+        mSampleEventTimes.push(parcel->readInt64());
+        for (size_t i = 0; i < pointerCount; i++) {
+            mSamplePointerCoords.push();
+            status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
+            if (!status) {
+                return status;
+            }
+        }
+    }
+    return OK;
+}
+
+status_t MotionEvent::writeToParcel(Parcel* parcel) const {
+    size_t pointerCount = mPointerIds.size();
+    size_t sampleCount = mSampleEventTimes.size();
+
+    parcel->writeInt32(pointerCount);
+    parcel->writeInt32(sampleCount);
+
+    parcel->writeInt32(mDeviceId);
+    parcel->writeInt32(mSource);
+    parcel->writeInt32(mAction);
+    parcel->writeInt32(mFlags);
+    parcel->writeInt32(mEdgeFlags);
+    parcel->writeInt32(mMetaState);
+    parcel->writeFloat(mXOffset);
+    parcel->writeFloat(mYOffset);
+    parcel->writeFloat(mXPrecision);
+    parcel->writeFloat(mYPrecision);
+    parcel->writeInt64(mDownTime);
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        parcel->writeInt32(mPointerIds.itemAt(i));
+    }
+
+    const PointerCoords* pc = mSamplePointerCoords.array();
+    for (size_t h = 0; h < sampleCount; h++) {
+        parcel->writeInt64(mSampleEventTimes.itemAt(h));
+        for (size_t i = 0; i < pointerCount; i++) {
+            status_t status = (pc++)->writeToParcel(parcel);
+            if (!status) {
+                return status;
+            }
+        }
+    }
+    return OK;
+}
+#endif
+
+
 // --- InputDeviceInfo ---
 
 InputDeviceInfo::InputDeviceInfo() {
@@ -307,8 +592,8 @@
     mMotionRanges.clear();
 }
 
-const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const {
-    ssize_t index = mMotionRanges.indexOfKey(rangeType);
+const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t axis) const {
+    ssize_t index = mMotionRanges.indexOfKey(axis);
     return index >= 0 ? & mMotionRanges.valueAt(index) : NULL;
 }
 
@@ -316,14 +601,14 @@
     mSources |= source;
 }
 
-void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max,
+void InputDeviceInfo::addMotionRange(int32_t axis, float min, float max,
         float flat, float fuzz) {
     MotionRange range = { min, max, flat, fuzz };
-    addMotionRange(rangeType, range);
+    addMotionRange(axis, range);
 }
 
-void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) {
-    mMotionRanges.add(rangeType, range);
+void InputDeviceInfo::addMotionRange(int32_t axis, const MotionRange& range) {
+    mMotionRanges.add(axis, range);
 }
 
 } // namespace android
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
index 903fcaf..1819a8b 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -159,15 +159,17 @@
         sampleEventTimes.push(i + 10);
         for (size_t j = 0; j < pointerCount; j++) {
             samplePointerCoords.push();
-            samplePointerCoords.editTop().x = 100 * i + j;
-            samplePointerCoords.editTop().y = 200 * i + j;
-            samplePointerCoords.editTop().pressure = 0.5 * i + j;
-            samplePointerCoords.editTop().size = 0.7 * i + j;
-            samplePointerCoords.editTop().touchMajor = 1.5 * i + j;
-            samplePointerCoords.editTop().touchMinor = 1.7 * i + j;
-            samplePointerCoords.editTop().toolMajor = 2.5 * i + j;
-            samplePointerCoords.editTop().toolMinor = 2.7 * i + j;
-            samplePointerCoords.editTop().orientation = 3.5 * i + j;
+            PointerCoords& pc = samplePointerCoords.editTop();
+            pc.clear();
+            pc.setAxisValue(AINPUT_MOTION_AXIS_X, 100 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_Y, 200 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, 0.5 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_SIZE, 0.7 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, 1.5 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, 1.7 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, 2.5 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, 2.7 * i + j);
+            pc.setAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, 3.5 * i + j);
         }
     }
 
@@ -239,27 +241,27 @@
         for (size_t i = 0; i < pointerCount; i++) {
             SCOPED_TRACE(i);
             size_t offset = sampleIndex * pointerCount + i;
-            EXPECT_EQ(samplePointerCoords[offset].x,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X),
                     motionEvent->getHistoricalRawX(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].y,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y),
                     motionEvent->getHistoricalRawY(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].x + xOffset,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset,
                     motionEvent->getHistoricalX(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].y + yOffset,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset,
                     motionEvent->getHistoricalY(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].pressure,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE),
                     motionEvent->getHistoricalPressure(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].size,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_SIZE),
                     motionEvent->getHistoricalSize(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].touchMajor,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR),
                     motionEvent->getHistoricalTouchMajor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].touchMinor,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR),
                     motionEvent->getHistoricalTouchMinor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].toolMajor,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR),
                     motionEvent->getHistoricalToolMajor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].toolMinor,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR),
                     motionEvent->getHistoricalToolMinor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].orientation,
+            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION),
                     motionEvent->getHistoricalOrientation(i, sampleIndex));
         }
     }
@@ -269,17 +271,28 @@
     for (size_t i = 0; i < pointerCount; i++) {
         SCOPED_TRACE(i);
         size_t offset = lastSampleIndex * pointerCount + i;
-        EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i));
-        EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i));
-        EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i));
-        EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i));
-        EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i));
-        EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i));
-        EXPECT_EQ(samplePointerCoords[offset].touchMajor, motionEvent->getTouchMajor(i));
-        EXPECT_EQ(samplePointerCoords[offset].touchMinor, motionEvent->getTouchMinor(i));
-        EXPECT_EQ(samplePointerCoords[offset].toolMajor, motionEvent->getToolMajor(i));
-        EXPECT_EQ(samplePointerCoords[offset].toolMinor, motionEvent->getToolMinor(i));
-        EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X),
+                motionEvent->getRawX(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y),
+                motionEvent->getRawY(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset,
+                motionEvent->getX(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset,
+                motionEvent->getY(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE),
+                motionEvent->getPressure(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_SIZE),
+                motionEvent->getSize(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR),
+                motionEvent->getTouchMajor(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR),
+                motionEvent->getTouchMinor(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR),
+                motionEvent->getToolMajor(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR),
+                motionEvent->getToolMinor(i));
+        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION),
+                motionEvent->getOrientation(i));
     }
 
     status = mConsumer->sendFinishedSignal(false);
@@ -328,7 +341,8 @@
 
     const size_t pointerCount = 1;
     int32_t pointerIds[pointerCount] = { 0 };
-    PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
+    PointerCoords pointerCoords[pointerCount];
+    pointerCoords[0].clear();
 
     status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerIds, pointerCoords);
diff --git a/native/android/input.cpp b/native/android/input.cpp
index a96240c..ed26667 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -172,6 +172,11 @@
     return static_cast<const MotionEvent*>(motion_event)->getOrientation(pointer_index);
 }
 
+float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
+        int32_t axis, size_t pointer_index) {
+    return static_cast<const MotionEvent*>(motion_event)->getAxisValue(axis, pointer_index);
+}
+
 size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getHistorySize();
 }
@@ -248,6 +253,12 @@
             pointer_index, history_index);
 }
 
+float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
+        int32_t axis, size_t pointer_index, size_t history_index) {
+    return static_cast<const MotionEvent*>(motion_event)->getHistoricalAxisValue(
+            axis, pointer_index, history_index);
+}
+
 
 void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
         int ident, ALooper_callbackFunc callback, void* data) {
diff --git a/native/include/android/input.h b/native/include/android/input.h
index bad363d..0ffb8b5 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -363,23 +363,40 @@
 };
 
 /*
+ * Constants that identify each individual axis of a motion event.
+ */
+enum {
+    AINPUT_MOTION_AXIS_X = 0,
+    AINPUT_MOTION_AXIS_Y = 1,
+    AINPUT_MOTION_AXIS_PRESSURE = 2,
+    AINPUT_MOTION_AXIS_SIZE = 3,
+    AINPUT_MOTION_AXIS_TOUCH_MAJOR = 4,
+    AINPUT_MOTION_AXIS_TOUCH_MINOR = 5,
+    AINPUT_MOTION_AXIS_TOOL_MAJOR = 6,
+    AINPUT_MOTION_AXIS_TOOL_MINOR = 7,
+    AINPUT_MOTION_AXIS_ORIENTATION = 8,
+};
+
+/*
  * 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.
+ *
+ * DEPRECATION NOTICE: These constants are deprecated.  Use AINPUT_MOTION_AXIS_* constants instead.
  */
 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,
-};
+    AINPUT_MOTION_RANGE_X = AINPUT_MOTION_AXIS_X,
+    AINPUT_MOTION_RANGE_Y = AINPUT_MOTION_AXIS_Y,
+    AINPUT_MOTION_RANGE_PRESSURE = AINPUT_MOTION_AXIS_PRESSURE,
+    AINPUT_MOTION_RANGE_SIZE = AINPUT_MOTION_AXIS_SIZE,
+    AINPUT_MOTION_RANGE_TOUCH_MAJOR = AINPUT_MOTION_AXIS_TOUCH_MAJOR,
+    AINPUT_MOTION_RANGE_TOUCH_MINOR = AINPUT_MOTION_AXIS_TOUCH_MINOR,
+    AINPUT_MOTION_RANGE_TOOL_MAJOR = AINPUT_MOTION_AXIS_TOOL_MAJOR,
+    AINPUT_MOTION_RANGE_TOOL_MINOR = AINPUT_MOTION_AXIS_TOOL_MINOR,
+    AINPUT_MOTION_RANGE_ORIENTATION = AINPUT_MOTION_AXIS_ORIENTATION,
+} __attribute__ ((deprecated));
 
 
 /*
@@ -526,7 +543,7 @@
 
 /* Get the current pressure of this event for the given pointer index.
  * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
- * however values higher than 1 may be generated depending on the calibration of
+ * although values higher than 1 may be generated depending on the calibration of
  * the input device. */
 float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index);
 
@@ -568,6 +585,10 @@
  * (finger pointing fully right). */
 float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
 
+/* Get the value of the request axis for the given pointer index. */
+float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
+        int32_t axis, size_t pointer_index);
+
 /* Get the number of historical points in this event.  These are movements that
  * have occurred between this event and the previous event.  This only applies
  * to AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
@@ -614,7 +635,7 @@
 /* Get the historical pressure of this event for the given pointer index that
  * occurred between this event and the previous motion event.
  * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
- * however values higher than 1 may be generated depending on the calibration of
+ * although values higher than 1 may be generated depending on the calibration of
  * the input device. */
 float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
@@ -669,6 +690,11 @@
 float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
 
+/* Get the historical value of the request axis for the given pointer index
+ * that occurred between this event and the previous motion event. */
+float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
+        int32_t axis, size_t pointer_index, size_t history_index);
+
 
 /*
  * Input queue
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index cbfdd75..466a9b3 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -482,8 +482,10 @@
                 && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                 && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                 && mInputTargetWaitApplication != NULL) {
-            int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0].x);
-            int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0].y);
+            int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0].
+                    getAxisValue(AINPUT_MOTION_AXIS_X));
+            int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0].
+                    getAxisValue(AINPUT_MOTION_AXIS_Y));
             const InputWindow* touchedWindow = findTouchedWindowAtLocked(x, y);
             if (touchedWindow
                     && touchedWindow->inputWindowHandle != NULL
@@ -888,11 +890,15 @@
                 "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
                 "orientation=%f",
                 i, entry->pointerIds[i],
-                sample->pointerCoords[i].x, sample->pointerCoords[i].y,
-                sample->pointerCoords[i].pressure, sample->pointerCoords[i].size,
-                sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor,
-                sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor,
-                sample->pointerCoords[i].orientation);
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_X),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_Y),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_SIZE),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR),
+                sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION));
     }
 
     // Keep in mind that due to batching, it is possible for the number of samples actually
@@ -1188,8 +1194,10 @@
         /* Case 1: New splittable pointer going down. */
 
         int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-        int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x);
-        int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y);
+        int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].
+                getAxisValue(AINPUT_MOTION_AXIS_X));
+        int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].
+                getAxisValue(AINPUT_MOTION_AXIS_Y));
         const InputWindow* newTouchedWindow = NULL;
         const InputWindow* topErrorWindow = NULL;
 
@@ -2275,11 +2283,16 @@
         LOGD("  Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, "
                 "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
                 "orientation=%f",
-                i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y,
-                pointerCoords[i].pressure, pointerCoords[i].size,
-                pointerCoords[i].touchMajor, pointerCoords[i].touchMinor,
-                pointerCoords[i].toolMajor, pointerCoords[i].toolMinor,
-                pointerCoords[i].orientation);
+                i, pointerIds[i],
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_X),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_Y),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_SIZE),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR),
+                pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION));
     }
 #endif
     if (! validateMotionEvent(action, pointerCount, pointerIds)) {
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 05f40ba..577da01 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1285,24 +1285,23 @@
             }
         }
 
+        pointerCoords.clear();
+
         if (mPointerController != NULL) {
             mPointerController->move(deltaX, deltaY);
             if (downChanged) {
                 mPointerController->setButtonState(mLocked.down ? POINTER_BUTTON_1 : 0);
             }
-            mPointerController->getPosition(&pointerCoords.x, &pointerCoords.y);
+            float x, y;
+            mPointerController->getPosition(&x, &y);
+            pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_X, x);
+            pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_Y, y);
         } else {
-            pointerCoords.x = deltaX;
-            pointerCoords.y = deltaY;
+            pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_X, deltaX);
+            pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_Y, deltaY);
         }
 
-        pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;
-        pointerCoords.size = 0;
-        pointerCoords.touchMajor = 0;
-        pointerCoords.touchMinor = 0;
-        pointerCoords.toolMajor = 0;
-        pointerCoords.toolMinor = 0;
-        pointerCoords.orientation = 0;
+        pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f);
     } // release lock
 
     int32_t metaState = mContext->getGlobalMetaState();
@@ -2686,15 +2685,16 @@
 
             // Write output coords.
             PointerCoords& out = pointerCoords[outIndex];
-            out.x = x;
-            out.y = y;
-            out.pressure = pressure;
-            out.size = size;
-            out.touchMajor = touchMajor;
-            out.touchMinor = touchMinor;
-            out.toolMajor = toolMajor;
-            out.toolMinor = toolMinor;
-            out.orientation = orientation;
+            out.clear();
+            out.setAxisValue(AINPUT_MOTION_AXIS_X, x);
+            out.setAxisValue(AINPUT_MOTION_AXIS_Y, y);
+            out.setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, pressure);
+            out.setAxisValue(AINPUT_MOTION_AXIS_SIZE, size);
+            out.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, touchMajor);
+            out.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, touchMinor);
+            out.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, toolMajor);
+            out.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR, toolMinor);
+            out.setAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, orientation);
 
             pointerIds[outIndex] = int32_t(id);
 
@@ -2706,14 +2706,17 @@
         // Check edge flags by looking only at the first pointer since the flags are
         // global to the event.
         if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
-            if (pointerCoords[0].x <= 0) {
+            float x = pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X);
+            float y = pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y);
+
+            if (x <= 0) {
                 motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
-            } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) {
+            } else if (x >= mLocked.orientedSurfaceWidth) {
                 motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
             }
-            if (pointerCoords[0].y <= 0) {
+            if (y <= 0) {
                 motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
-            } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) {
+            } else if (y >= mLocked.orientedSurfaceHeight) {
                 motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
             }
         }
@@ -3853,15 +3856,9 @@
 
     if (motionAxisChanged) {
         PointerCoords pointerCoords;
-        pointerCoords.x = mAxes.x.value;
-        pointerCoords.y = mAxes.y.value;
-        pointerCoords.touchMajor = 0;
-        pointerCoords.touchMinor = 0;
-        pointerCoords.toolMajor = 0;
-        pointerCoords.toolMinor = 0;
-        pointerCoords.pressure = 0;
-        pointerCoords.size = 0;
-        pointerCoords.orientation = 0;
+        pointerCoords.clear();
+        pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_X, mAxes.x.value);
+        pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_Y, mAxes.y.value);
 
         int32_t pointerId = 0;
         getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 8ec1fd4..34d613a 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -1473,15 +1473,15 @@
             float x, float y, float pressure, float size,
             float touchMajor, float touchMinor, float toolMajor, float toolMinor,
             float orientation) {
-        ASSERT_NEAR(x, coords.x, 1);
-        ASSERT_NEAR(y, coords.y, 1);
-        ASSERT_NEAR(pressure, coords.pressure, EPSILON);
-        ASSERT_NEAR(size, coords.size, EPSILON);
-        ASSERT_NEAR(touchMajor, coords.touchMajor, 1);
-        ASSERT_NEAR(touchMinor, coords.touchMinor, 1);
-        ASSERT_NEAR(toolMajor, coords.toolMajor, 1);
-        ASSERT_NEAR(toolMinor, coords.toolMinor, 1);
-        ASSERT_NEAR(orientation, coords.orientation, EPSILON);
+        ASSERT_NEAR(x, coords.getAxisValue(AINPUT_MOTION_AXIS_X), 1);
+        ASSERT_NEAR(y, coords.getAxisValue(AINPUT_MOTION_AXIS_Y), 1);
+        ASSERT_NEAR(pressure, coords.getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), EPSILON);
+        ASSERT_NEAR(size, coords.getAxisValue(AINPUT_MOTION_AXIS_SIZE), EPSILON);
+        ASSERT_NEAR(touchMajor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), 1);
+        ASSERT_NEAR(touchMinor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), 1);
+        ASSERT_NEAR(toolMajor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), 1);
+        ASSERT_NEAR(toolMinor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), 1);
+        ASSERT_NEAR(orientation, coords.getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION), EPSILON);
     }
 };
 
@@ -2078,7 +2078,6 @@
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
-    ASSERT_NEAR(0.0f, args.pointerCoords[0].x, EPSILON);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
@@ -2893,8 +2892,8 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(50, args.pointerCoords[0].x, 1);
-    ASSERT_NEAR(75, args.pointerCoords[0].y, 1);
+    ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1);
+    ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
@@ -2915,8 +2914,8 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(50, args.pointerCoords[0].x, 1);
-    ASSERT_NEAR(75, args.pointerCoords[0].y, 1);
+    ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1);
+    ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
@@ -2928,8 +2927,8 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(75, args.pointerCoords[0].x, 1);
-    ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].y, 1);
+    ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1);
+    ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
@@ -2941,8 +2940,8 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].x, 1);
-    ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].y, 1);
+    ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1);
+    ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
@@ -2954,8 +2953,8 @@
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].x, 1);
-    ASSERT_NEAR(50, args.pointerCoords[0].y, 1);
+    ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1);
+    ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 5b329bb..427af23 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -87,7 +87,6 @@
     jfieldID mName;
     jfieldID mSources;
     jfieldID mKeyboardType;
-    jfieldID mMotionRanges;
 } gInputDeviceClassInfo;
 
 static struct {
@@ -1221,9 +1220,6 @@
     GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz,
             "mKeyboardType", "I");
 
-    GET_FIELD_ID(gInputDeviceClassInfo.mMotionRanges, gInputDeviceClassInfo.clazz,
-            "mMotionRanges", "[Landroid/view/InputDevice$MotionRange;");
-
     // Configuration
 
     FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");