Add new axes for joysticks and mouse wheels.

Added API on InputDevice to query the set of axes available.
Added API on KeyEvent and MotionEvent to convert keycodes and axes
to symbolic name strings for diagnostic purposes.
Added API on KeyEvent to query if a given key code is a gamepad button.
Added a new "axis" element to key layout files to specify the
mapping between raw absolute axis values and motion axis ids.
Expanded the axis bitfield to 64bits to allow for future growth.
Modified the Makefile for keyboard prebuilts to run the keymap
validation tool during the build.
Added layouts for two game controllers.
Added default actions for game pad button keys.
Added more tests.
Fixed a bunch of bugs.

Change-Id: I73f9166c3b3c5bcf4970845b58088ad467525525
diff --git a/api/current.xml b/api/current.xml
index 6279d8d..019ca71 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -210656,6 +210656,17 @@
  visibility="public"
 >
 </method>
+<method name="getMotionAxes"
+ return="int[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getMotionRange"
  return="android.view.InputDevice.MotionRange"
  abstract="false"
@@ -212226,6 +212237,19 @@
  visibility="public"
 >
 </method>
+<method name="isGamepadButton"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+</method>
 <method name="isLongPress"
  return="boolean"
  abstract="false"
@@ -212338,6 +212362,32 @@
  visibility="public"
 >
 </method>
+<method name="keyCodeFromString"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="symbolicName" type="java.lang.String">
+</parameter>
+</method>
+<method name="keyCodeToString"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+</method>
 <method name="metaStateHasModifiers"
  return="boolean"
  abstract="false"
@@ -216602,6 +216652,32 @@
 <parameter name="metaState" type="int">
 </parameter>
 </method>
+<method name="axisFromString"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="symbolicName" type="java.lang.String">
+</parameter>
+</method>
+<method name="axisToString"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="axis" type="int">
+</parameter>
+</method>
 <method name="findPointerIndex"
  return="int"
  abstract="false"
@@ -217841,6 +217917,226 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_GENERIC_1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_10"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="41"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_11"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="42"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_12"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="43"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_13"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="44"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_14"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="45"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_15"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="46"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_16"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="47"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="33"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="34"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="35"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_5"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="36"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_6"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="37"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_7"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="38"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_8"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="39"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GENERIC_9"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="40"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_HAT_X"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="15"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_HAT_Y"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_HSCROLL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_LTRIGGER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AXIS_ORIENTATION"
  type="int"
  transient="false"
@@ -217863,6 +218159,50 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_RTRIGGER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="18"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_RX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="12"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_RY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="13"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_RZ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="14"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AXIS_SIZE"
  type="int"
  transient="false"
@@ -217918,6 +218258,17 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_VSCROLL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AXIS_X"
  type="int"
  transient="false"
@@ -217940,6 +218291,17 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_Z"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CREATOR"
  type="android.os.Parcelable.Creator"
  transient="false"
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 7abbce6..def1161 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -44,6 +44,7 @@
     private int mKeyboardType;
 
     private final SparseArray<MotionRange> mMotionRanges = new SparseArray<MotionRange>();
+    private int[] mMotionAxes;
 
     /**
      * A mask for input source classes.
@@ -359,12 +360,31 @@
      *
      * @see MotionEvent#AXIS_X
      * @see MotionEvent#AXIS_Y
+     * @see #getSupportedAxes()
      */
     public MotionRange getMotionRange(int axis) {
         return mMotionRanges.get(axis);
     }
 
-    // Called by native code.
+    /**
+     * Gets the axis ids of all motion axes supported by this device.
+     * @return The axis ids of all motion axes supported by this device.
+     *
+     * @see #getMotionRange(int)
+     */
+    public int[] getMotionAxes() {
+        synchronized (this) {
+            if (mMotionAxes == null) {
+                final int count = mMotionRanges.size();
+                mMotionAxes = new int[count];
+                for (int i = 0; i < count; i++) {
+                    mMotionAxes[i] = mMotionRanges.keyAt(i);
+                }
+            }
+            return mMotionAxes;
+        }
+    }
+
     private void addMotionRange(int axis, float min, float max, float flat, float fuzz) {
         mMotionRanges.append(axis, new MotionRange(min, max, flat, fuzz));
     }
@@ -388,33 +408,35 @@
         }
 
         /**
-         * Gets the minimum value for the axis.
-         * @return The (inclusive) minimum value.
+         * Gets the inclusive minimum value for the axis.
+         * @return The inclusive minimum value.
          */
         public float getMin() {
             return mMin;
         }
 
         /**
-         * Gets the maximum value for the axis.
-         * @return The (inclusive) maximum value.
+         * Gets the inclusive maximum value for the axis.
+         * @return The inclusive maximum value.
          */
         public float getMax() {
             return mMax;
         }
 
         /**
-         * Gets the range of the axis (difference between maximum and minimum plus one).
+         * Gets the range of the axis (difference between maximum and minimum).
          * @return The range of values.
          */
         public float getRange() {
-            return mMax - mMin + 1;
+            return mMax - mMin;
         }
 
         /**
          * Gets the extent of the center flat position with respect to this axis.
+         * <p>
          * 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.
+         * </p>
          * @return The extent of the center flat position.
          */
         public float getFlat() {
@@ -423,8 +445,10 @@
 
         /**
          * Gets the error tolerance for input device measurements with respect to this axis.
+         * <p>
          * 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.
+         * </p>
          * @return The error tolerance.
          */
         public float getFuzz() {
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 3f6a04b..81d5a6e 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -21,6 +21,7 @@
 import android.text.method.MetaKeyKeyListener;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.KeyCharacterMap;
 import android.view.KeyCharacterMap.KeyData;
@@ -582,213 +583,214 @@
     //  those new codes.  This is intended to maintain a consistent
     //  set of key code definitions across all Android devices.
 
-    // Symbolic names of all keys indexed by keycode.
-    // There should be exactly LAST_KEYCODE + 1 entries in this table.
-    private static final String[] KEYCODE_SYMBOLIC_NAMES = new String[] {
-        "KEYCODE_UNKNOWN",
-        "KEYCODE_SOFT_LEFT",
-        "KEYCODE_SOFT_RIGHT",
-        "KEYCODE_HOME",
-        "KEYCODE_BACK",
-        "KEYCODE_CALL",
-        "KEYCODE_ENDCALL",
-        "KEYCODE_0",
-        "KEYCODE_1",
-        "KEYCODE_2",
-        "KEYCODE_3",
-        "KEYCODE_4",
-        "KEYCODE_5",
-        "KEYCODE_6",
-        "KEYCODE_7",
-        "KEYCODE_8",
-        "KEYCODE_9",
-        "KEYCODE_STAR",
-        "KEYCODE_POUND",
-        "KEYCODE_DPAD_UP",
-        "KEYCODE_DPAD_DOWN",
-        "KEYCODE_DPAD_LEFT",
-        "KEYCODE_DPAD_RIGHT",
-        "KEYCODE_DPAD_CENTER",
-        "KEYCODE_VOLUME_UP",
-        "KEYCODE_VOLUME_DOWN",
-        "KEYCODE_POWER",
-        "KEYCODE_CAMERA",
-        "KEYCODE_CLEAR",
-        "KEYCODE_A",
-        "KEYCODE_B",
-        "KEYCODE_C",
-        "KEYCODE_D",
-        "KEYCODE_E",
-        "KEYCODE_F",
-        "KEYCODE_G",
-        "KEYCODE_H",
-        "KEYCODE_I",
-        "KEYCODE_J",
-        "KEYCODE_K",
-        "KEYCODE_L",
-        "KEYCODE_M",
-        "KEYCODE_N",
-        "KEYCODE_O",
-        "KEYCODE_P",
-        "KEYCODE_Q",
-        "KEYCODE_R",
-        "KEYCODE_S",
-        "KEYCODE_T",
-        "KEYCODE_U",
-        "KEYCODE_V",
-        "KEYCODE_W",
-        "KEYCODE_X",
-        "KEYCODE_Y",
-        "KEYCODE_Z",
-        "KEYCODE_COMMA",
-        "KEYCODE_PERIOD",
-        "KEYCODE_ALT_LEFT",
-        "KEYCODE_ALT_RIGHT",
-        "KEYCODE_SHIFT_LEFT",
-        "KEYCODE_SHIFT_RIGHT",
-        "KEYCODE_TAB",
-        "KEYCODE_SPACE",
-        "KEYCODE_SYM",
-        "KEYCODE_EXPLORER",
-        "KEYCODE_ENVELOPE",
-        "KEYCODE_ENTER",
-        "KEYCODE_DEL",
-        "KEYCODE_GRAVE",
-        "KEYCODE_MINUS",
-        "KEYCODE_EQUALS",
-        "KEYCODE_LEFT_BRACKET",
-        "KEYCODE_RIGHT_BRACKET",
-        "KEYCODE_BACKSLASH",
-        "KEYCODE_SEMICOLON",
-        "KEYCODE_APOSTROPHE",
-        "KEYCODE_SLASH",
-        "KEYCODE_AT",
-        "KEYCODE_NUM",
-        "KEYCODE_HEADSETHOOK",
-        "KEYCODE_FOCUS",
-        "KEYCODE_PLUS",
-        "KEYCODE_MENU",
-        "KEYCODE_NOTIFICATION",
-        "KEYCODE_SEARCH",
-        "KEYCODE_MEDIA_PLAY_PAUSE",
-        "KEYCODE_MEDIA_STOP",
-        "KEYCODE_MEDIA_NEXT",
-        "KEYCODE_MEDIA_PREVIOUS",
-        "KEYCODE_MEDIA_REWIND",
-        "KEYCODE_MEDIA_FAST_FORWARD",
-        "KEYCODE_MUTE",
-        "KEYCODE_PAGE_UP",
-        "KEYCODE_PAGE_DOWN",
-        "KEYCODE_PICTSYMBOLS",
-        "KEYCODE_SWITCH_CHARSET",
-        "KEYCODE_BUTTON_A",
-        "KEYCODE_BUTTON_B",
-        "KEYCODE_BUTTON_C",
-        "KEYCODE_BUTTON_X",
-        "KEYCODE_BUTTON_Y",
-        "KEYCODE_BUTTON_Z",
-        "KEYCODE_BUTTON_L1",
-        "KEYCODE_BUTTON_R1",
-        "KEYCODE_BUTTON_L2",
-        "KEYCODE_BUTTON_R2",
-        "KEYCODE_BUTTON_THUMBL",
-        "KEYCODE_BUTTON_THUMBR",
-        "KEYCODE_BUTTON_START",
-        "KEYCODE_BUTTON_SELECT",
-        "KEYCODE_BUTTON_MODE",
-        "KEYCODE_ESCAPE",
-        "KEYCODE_FORWARD_DEL",
-        "KEYCODE_CTRL_LEFT",
-        "KEYCODE_CTRL_RIGHT",
-        "KEYCODE_CAPS_LOCK",
-        "KEYCODE_SCROLL_LOCK",
-        "KEYCODE_META_LEFT",
-        "KEYCODE_META_RIGHT",
-        "KEYCODE_FUNCTION",
-        "KEYCODE_SYSRQ",
-        "KEYCODE_BREAK",
-        "KEYCODE_MOVE_HOME",
-        "KEYCODE_MOVE_END",
-        "KEYCODE_INSERT",
-        "KEYCODE_FORWARD",
-        "KEYCODE_MEDIA_PLAY",
-        "KEYCODE_MEDIA_PAUSE",
-        "KEYCODE_MEDIA_CLOSE",
-        "KEYCODE_MEDIA_EJECT",
-        "KEYCODE_MEDIA_RECORD",
-        "KEYCODE_F1",
-        "KEYCODE_F2",
-        "KEYCODE_F3",
-        "KEYCODE_F4",
-        "KEYCODE_F5",
-        "KEYCODE_F6",
-        "KEYCODE_F7",
-        "KEYCODE_F8",
-        "KEYCODE_F9",
-        "KEYCODE_F10",
-        "KEYCODE_F11",
-        "KEYCODE_F12",
-        "KEYCODE_NUM_LOCK",
-        "KEYCODE_NUMPAD_0",
-        "KEYCODE_NUMPAD_1",
-        "KEYCODE_NUMPAD_2",
-        "KEYCODE_NUMPAD_3",
-        "KEYCODE_NUMPAD_4",
-        "KEYCODE_NUMPAD_5",
-        "KEYCODE_NUMPAD_6",
-        "KEYCODE_NUMPAD_7",
-        "KEYCODE_NUMPAD_8",
-        "KEYCODE_NUMPAD_9",
-        "KEYCODE_NUMPAD_DIVIDE",
-        "KEYCODE_NUMPAD_MULTIPLY",
-        "KEYCODE_MUMPAD_SUBTRACT",
-        "KEYCODE_NUMPAD_ADD",
-        "KEYCODE_NUMPAD_DOT",
-        "KEYCODE_NUMPAD_COMMA",
-        "KEYCODE_NUMPAD_ENTER",
-        "KEYCODE_NUMPAD_EQUALS",
-        "KEYCODE_NUMPAD_LEFT_PAREN",
-        "KEYCODE_NUMPAD_RIGHT_PAREN",
-        "KEYCODE_VOLUME_MUTE",
-        "KEYCODE_INFO",
-        "KEYCODE_CHANNEL_UP",
-        "KEYCODE_CHANNEL_DOWN",
-        "KEYCODE_ZOOM_IN",
-        "KEYCODE_ZOOM_OUT",
-        "KEYCODE_TV",
-        "KEYCODE_WINDOW",
-        "KEYCODE_GUIDE",
-        "KEYCODE_DVR",
-        "KEYCODE_BOOKMARK",
-        "KEYCODE_CAPTIONS",
-        "KEYCODE_SETTINGS",
-        "KEYCODE_TV_POWER",
-        "KEYCODE_TV_INPUT",
-        "KEYCODE_STB_INPUT",
-        "KEYCODE_STB_POWER",
-        "KEYCODE_AVR_POWER",
-        "KEYCODE_AVR_INPUT",
-        "KEYCODE_PROG_RED",
-        "KEYCODE_PROG_GREEN",
-        "KEYCODE_PROG_YELLOW",
-        "KEYCODE_PROG_BLUE",
-        "KEYCODE_APP_SWITCH",
-        "KEYCODE_BUTTON_1",
-        "KEYCODE_BUTTON_2",
-        "KEYCODE_BUTTON_3",
-        "KEYCODE_BUTTON_4",
-        "KEYCODE_BUTTON_5",
-        "KEYCODE_BUTTON_6",
-        "KEYCODE_BUTTON_7",
-        "KEYCODE_BUTTON_8",
-        "KEYCODE_BUTTON_9",
-        "KEYCODE_BUTTON_10",
-        "KEYCODE_BUTTON_11",
-        "KEYCODE_BUTTON_12",
-        "KEYCODE_BUTTON_13",
-        "KEYCODE_BUTTON_14",
-        "KEYCODE_BUTTON_15",
-        "KEYCODE_BUTTON_16",
+    // Symbolic names of all key codes.
+    private static final SparseArray<String> KEYCODE_SYMBOLIC_NAMES = new SparseArray<String>();
+    private static void populateKeycodeSymbolicNames() {
+        SparseArray<String> names = KEYCODE_SYMBOLIC_NAMES;
+        names.append(KEYCODE_UNKNOWN, "KEYCODE_UNKNOWN");
+        names.append(KEYCODE_SOFT_LEFT, "KEYCODE_SOFT_LEFT");
+        names.append(KEYCODE_SOFT_RIGHT, "KEYCODE_SOFT_RIGHT");
+        names.append(KEYCODE_HOME, "KEYCODE_HOME");
+        names.append(KEYCODE_BACK, "KEYCODE_BACK");
+        names.append(KEYCODE_CALL, "KEYCODE_CALL");
+        names.append(KEYCODE_ENDCALL, "KEYCODE_ENDCALL");
+        names.append(KEYCODE_0, "KEYCODE_0");
+        names.append(KEYCODE_1, "KEYCODE_1");
+        names.append(KEYCODE_2, "KEYCODE_2");
+        names.append(KEYCODE_3, "KEYCODE_3");
+        names.append(KEYCODE_4, "KEYCODE_4");
+        names.append(KEYCODE_5, "KEYCODE_5");
+        names.append(KEYCODE_6, "KEYCODE_6");
+        names.append(KEYCODE_7, "KEYCODE_7");
+        names.append(KEYCODE_8, "KEYCODE_8");
+        names.append(KEYCODE_9, "KEYCODE_9");
+        names.append(KEYCODE_STAR, "KEYCODE_STAR");
+        names.append(KEYCODE_POUND, "KEYCODE_POUND");
+        names.append(KEYCODE_DPAD_UP, "KEYCODE_DPAD_UP");
+        names.append(KEYCODE_DPAD_DOWN, "KEYCODE_DPAD_DOWN");
+        names.append(KEYCODE_DPAD_LEFT, "KEYCODE_DPAD_LEFT");
+        names.append(KEYCODE_DPAD_RIGHT, "KEYCODE_DPAD_RIGHT");
+        names.append(KEYCODE_DPAD_CENTER, "KEYCODE_DPAD_CENTER");
+        names.append(KEYCODE_VOLUME_UP, "KEYCODE_VOLUME_UP");
+        names.append(KEYCODE_VOLUME_DOWN, "KEYCODE_VOLUME_DOWN");
+        names.append(KEYCODE_POWER, "KEYCODE_POWER");
+        names.append(KEYCODE_CAMERA, "KEYCODE_CAMERA");
+        names.append(KEYCODE_CLEAR, "KEYCODE_CLEAR");
+        names.append(KEYCODE_A, "KEYCODE_A");
+        names.append(KEYCODE_B, "KEYCODE_B");
+        names.append(KEYCODE_C, "KEYCODE_C");
+        names.append(KEYCODE_D, "KEYCODE_D");
+        names.append(KEYCODE_E, "KEYCODE_E");
+        names.append(KEYCODE_F, "KEYCODE_F");
+        names.append(KEYCODE_G, "KEYCODE_G");
+        names.append(KEYCODE_H, "KEYCODE_H");
+        names.append(KEYCODE_I, "KEYCODE_I");
+        names.append(KEYCODE_J, "KEYCODE_J");
+        names.append(KEYCODE_K, "KEYCODE_K");
+        names.append(KEYCODE_L, "KEYCODE_L");
+        names.append(KEYCODE_M, "KEYCODE_M");
+        names.append(KEYCODE_N, "KEYCODE_N");
+        names.append(KEYCODE_O, "KEYCODE_O");
+        names.append(KEYCODE_P, "KEYCODE_P");
+        names.append(KEYCODE_Q, "KEYCODE_Q");
+        names.append(KEYCODE_R, "KEYCODE_R");
+        names.append(KEYCODE_S, "KEYCODE_S");
+        names.append(KEYCODE_T, "KEYCODE_T");
+        names.append(KEYCODE_U, "KEYCODE_U");
+        names.append(KEYCODE_V, "KEYCODE_V");
+        names.append(KEYCODE_W, "KEYCODE_W");
+        names.append(KEYCODE_X, "KEYCODE_X");
+        names.append(KEYCODE_Y, "KEYCODE_Y");
+        names.append(KEYCODE_Z, "KEYCODE_Z");
+        names.append(KEYCODE_COMMA, "KEYCODE_COMMA");
+        names.append(KEYCODE_PERIOD, "KEYCODE_PERIOD");
+        names.append(KEYCODE_ALT_LEFT, "KEYCODE_ALT_LEFT");
+        names.append(KEYCODE_ALT_RIGHT, "KEYCODE_ALT_RIGHT");
+        names.append(KEYCODE_SHIFT_LEFT, "KEYCODE_SHIFT_LEFT");
+        names.append(KEYCODE_SHIFT_RIGHT, "KEYCODE_SHIFT_RIGHT");
+        names.append(KEYCODE_TAB, "KEYCODE_TAB");
+        names.append(KEYCODE_SPACE, "KEYCODE_SPACE");
+        names.append(KEYCODE_SYM, "KEYCODE_SYM");
+        names.append(KEYCODE_EXPLORER, "KEYCODE_EXPLORER");
+        names.append(KEYCODE_ENVELOPE, "KEYCODE_ENVELOPE");
+        names.append(KEYCODE_ENTER, "KEYCODE_ENTER");
+        names.append(KEYCODE_DEL, "KEYCODE_DEL");
+        names.append(KEYCODE_GRAVE, "KEYCODE_GRAVE");
+        names.append(KEYCODE_MINUS, "KEYCODE_MINUS");
+        names.append(KEYCODE_EQUALS, "KEYCODE_EQUALS");
+        names.append(KEYCODE_LEFT_BRACKET, "KEYCODE_LEFT_BRACKET");
+        names.append(KEYCODE_RIGHT_BRACKET, "KEYCODE_RIGHT_BRACKET");
+        names.append(KEYCODE_BACKSLASH, "KEYCODE_BACKSLASH");
+        names.append(KEYCODE_SEMICOLON, "KEYCODE_SEMICOLON");
+        names.append(KEYCODE_APOSTROPHE, "KEYCODE_APOSTROPHE");
+        names.append(KEYCODE_SLASH, "KEYCODE_SLASH");
+        names.append(KEYCODE_AT, "KEYCODE_AT");
+        names.append(KEYCODE_NUM, "KEYCODE_NUM");
+        names.append(KEYCODE_HEADSETHOOK, "KEYCODE_HEADSETHOOK");
+        names.append(KEYCODE_FOCUS, "KEYCODE_FOCUS");
+        names.append(KEYCODE_PLUS, "KEYCODE_PLUS");
+        names.append(KEYCODE_MENU, "KEYCODE_MENU");
+        names.append(KEYCODE_NOTIFICATION, "KEYCODE_NOTIFICATION");
+        names.append(KEYCODE_SEARCH, "KEYCODE_SEARCH");
+        names.append(KEYCODE_MEDIA_PLAY_PAUSE, "KEYCODE_MEDIA_PLAY_PAUSE");
+        names.append(KEYCODE_MEDIA_STOP, "KEYCODE_MEDIA_STOP");
+        names.append(KEYCODE_MEDIA_NEXT, "KEYCODE_MEDIA_NEXT");
+        names.append(KEYCODE_MEDIA_PREVIOUS, "KEYCODE_MEDIA_PREVIOUS");
+        names.append(KEYCODE_MEDIA_REWIND, "KEYCODE_MEDIA_REWIND");
+        names.append(KEYCODE_MEDIA_FAST_FORWARD, "KEYCODE_MEDIA_FAST_FORWARD");
+        names.append(KEYCODE_MUTE, "KEYCODE_MUTE");
+        names.append(KEYCODE_PAGE_UP, "KEYCODE_PAGE_UP");
+        names.append(KEYCODE_PAGE_DOWN, "KEYCODE_PAGE_DOWN");
+        names.append(KEYCODE_PICTSYMBOLS, "KEYCODE_PICTSYMBOLS");
+        names.append(KEYCODE_SWITCH_CHARSET, "KEYCODE_SWITCH_CHARSET");
+        names.append(KEYCODE_BUTTON_A, "KEYCODE_BUTTON_A");
+        names.append(KEYCODE_BUTTON_B, "KEYCODE_BUTTON_B");
+        names.append(KEYCODE_BUTTON_C, "KEYCODE_BUTTON_C");
+        names.append(KEYCODE_BUTTON_X, "KEYCODE_BUTTON_X");
+        names.append(KEYCODE_BUTTON_Y, "KEYCODE_BUTTON_Y");
+        names.append(KEYCODE_BUTTON_Z, "KEYCODE_BUTTON_Z");
+        names.append(KEYCODE_BUTTON_L1, "KEYCODE_BUTTON_L1");
+        names.append(KEYCODE_BUTTON_R1, "KEYCODE_BUTTON_R1");
+        names.append(KEYCODE_BUTTON_L2, "KEYCODE_BUTTON_L2");
+        names.append(KEYCODE_BUTTON_R2, "KEYCODE_BUTTON_R2");
+        names.append(KEYCODE_BUTTON_THUMBL, "KEYCODE_BUTTON_THUMBL");
+        names.append(KEYCODE_BUTTON_THUMBR, "KEYCODE_BUTTON_THUMBR");
+        names.append(KEYCODE_BUTTON_START, "KEYCODE_BUTTON_START");
+        names.append(KEYCODE_BUTTON_SELECT, "KEYCODE_BUTTON_SELECT");
+        names.append(KEYCODE_BUTTON_MODE, "KEYCODE_BUTTON_MODE");
+        names.append(KEYCODE_ESCAPE, "KEYCODE_ESCAPE");
+        names.append(KEYCODE_FORWARD_DEL, "KEYCODE_FORWARD_DEL");
+        names.append(KEYCODE_CTRL_LEFT, "KEYCODE_CTRL_LEFT");
+        names.append(KEYCODE_CTRL_RIGHT, "KEYCODE_CTRL_RIGHT");
+        names.append(KEYCODE_CAPS_LOCK, "KEYCODE_CAPS_LOCK");
+        names.append(KEYCODE_SCROLL_LOCK, "KEYCODE_SCROLL_LOCK");
+        names.append(KEYCODE_META_LEFT, "KEYCODE_META_LEFT");
+        names.append(KEYCODE_META_RIGHT, "KEYCODE_META_RIGHT");
+        names.append(KEYCODE_FUNCTION, "KEYCODE_FUNCTION");
+        names.append(KEYCODE_SYSRQ, "KEYCODE_SYSRQ");
+        names.append(KEYCODE_BREAK, "KEYCODE_BREAK");
+        names.append(KEYCODE_MOVE_HOME, "KEYCODE_MOVE_HOME");
+        names.append(KEYCODE_MOVE_END, "KEYCODE_MOVE_END");
+        names.append(KEYCODE_INSERT, "KEYCODE_INSERT");
+        names.append(KEYCODE_FORWARD, "KEYCODE_FORWARD");
+        names.append(KEYCODE_MEDIA_PLAY, "KEYCODE_MEDIA_PLAY");
+        names.append(KEYCODE_MEDIA_PAUSE, "KEYCODE_MEDIA_PAUSE");
+        names.append(KEYCODE_MEDIA_CLOSE, "KEYCODE_MEDIA_CLOSE");
+        names.append(KEYCODE_MEDIA_EJECT, "KEYCODE_MEDIA_EJECT");
+        names.append(KEYCODE_MEDIA_RECORD, "KEYCODE_MEDIA_RECORD");
+        names.append(KEYCODE_F1, "KEYCODE_F1");
+        names.append(KEYCODE_F2, "KEYCODE_F2");
+        names.append(KEYCODE_F3, "KEYCODE_F3");
+        names.append(KEYCODE_F4, "KEYCODE_F4");
+        names.append(KEYCODE_F5, "KEYCODE_F5");
+        names.append(KEYCODE_F6, "KEYCODE_F6");
+        names.append(KEYCODE_F7, "KEYCODE_F7");
+        names.append(KEYCODE_F8, "KEYCODE_F8");
+        names.append(KEYCODE_F9, "KEYCODE_F9");
+        names.append(KEYCODE_F10, "KEYCODE_F10");
+        names.append(KEYCODE_F11, "KEYCODE_F11");
+        names.append(KEYCODE_F12, "KEYCODE_F12");
+        names.append(KEYCODE_NUM_LOCK, "KEYCODE_NUM_LOCK");
+        names.append(KEYCODE_NUMPAD_0, "KEYCODE_NUMPAD_0");
+        names.append(KEYCODE_NUMPAD_1, "KEYCODE_NUMPAD_1");
+        names.append(KEYCODE_NUMPAD_2, "KEYCODE_NUMPAD_2");
+        names.append(KEYCODE_NUMPAD_3, "KEYCODE_NUMPAD_3");
+        names.append(KEYCODE_NUMPAD_4, "KEYCODE_NUMPAD_4");
+        names.append(KEYCODE_NUMPAD_5, "KEYCODE_NUMPAD_5");
+        names.append(KEYCODE_NUMPAD_6, "KEYCODE_NUMPAD_6");
+        names.append(KEYCODE_NUMPAD_7, "KEYCODE_NUMPAD_7");
+        names.append(KEYCODE_NUMPAD_8, "KEYCODE_NUMPAD_8");
+        names.append(KEYCODE_NUMPAD_9, "KEYCODE_NUMPAD_9");
+        names.append(KEYCODE_NUMPAD_DIVIDE, "KEYCODE_NUMPAD_DIVIDE");
+        names.append(KEYCODE_NUMPAD_MULTIPLY, "KEYCODE_NUMPAD_MULTIPLY");
+        names.append(KEYCODE_NUMPAD_SUBTRACT, "KEYCODE_NUMPAD_SUBTRACT");
+        names.append(KEYCODE_NUMPAD_ADD, "KEYCODE_NUMPAD_ADD");
+        names.append(KEYCODE_NUMPAD_DOT, "KEYCODE_NUMPAD_DOT");
+        names.append(KEYCODE_NUMPAD_COMMA, "KEYCODE_NUMPAD_COMMA");
+        names.append(KEYCODE_NUMPAD_ENTER, "KEYCODE_NUMPAD_ENTER");
+        names.append(KEYCODE_NUMPAD_EQUALS, "KEYCODE_NUMPAD_EQUALS");
+        names.append(KEYCODE_NUMPAD_LEFT_PAREN, "KEYCODE_NUMPAD_LEFT_PAREN");
+        names.append(KEYCODE_NUMPAD_RIGHT_PAREN, "KEYCODE_NUMPAD_RIGHT_PAREN");
+        names.append(KEYCODE_VOLUME_MUTE, "KEYCODE_VOLUME_MUTE");
+        names.append(KEYCODE_INFO, "KEYCODE_INFO");
+        names.append(KEYCODE_CHANNEL_UP, "KEYCODE_CHANNEL_UP");
+        names.append(KEYCODE_CHANNEL_DOWN, "KEYCODE_CHANNEL_DOWN");
+        names.append(KEYCODE_ZOOM_IN, "KEYCODE_ZOOM_IN");
+        names.append(KEYCODE_ZOOM_OUT, "KEYCODE_ZOOM_OUT");
+        names.append(KEYCODE_TV, "KEYCODE_TV");
+        names.append(KEYCODE_WINDOW, "KEYCODE_WINDOW");
+        names.append(KEYCODE_GUIDE, "KEYCODE_GUIDE");
+        names.append(KEYCODE_DVR, "KEYCODE_DVR");
+        names.append(KEYCODE_BOOKMARK, "KEYCODE_BOOKMARK");
+        names.append(KEYCODE_CAPTIONS, "KEYCODE_CAPTIONS");
+        names.append(KEYCODE_SETTINGS, "KEYCODE_SETTINGS");
+        names.append(KEYCODE_TV_POWER, "KEYCODE_TV_POWER");
+        names.append(KEYCODE_TV_INPUT, "KEYCODE_TV_INPUT");
+        names.append(KEYCODE_STB_INPUT, "KEYCODE_STB_INPUT");
+        names.append(KEYCODE_STB_POWER, "KEYCODE_STB_POWER");
+        names.append(KEYCODE_AVR_POWER, "KEYCODE_AVR_POWER");
+        names.append(KEYCODE_AVR_INPUT, "KEYCODE_AVR_INPUT");
+        names.append(KEYCODE_PROG_RED, "KEYCODE_PROG_RED");
+        names.append(KEYCODE_PROG_GREEN, "KEYCODE_PROG_GREEN");
+        names.append(KEYCODE_PROG_YELLOW, "KEYCODE_PROG_YELLOW");
+        names.append(KEYCODE_PROG_BLUE, "KEYCODE_PROG_BLUE");
+        names.append(KEYCODE_APP_SWITCH, "KEYCODE_APP_SWITCH");
+        names.append(KEYCODE_BUTTON_1, "KEYCODE_BUTTON_1");
+        names.append(KEYCODE_BUTTON_2, "KEYCODE_BUTTON_2");
+        names.append(KEYCODE_BUTTON_3, "KEYCODE_BUTTON_3");
+        names.append(KEYCODE_BUTTON_4, "KEYCODE_BUTTON_4");
+        names.append(KEYCODE_BUTTON_5, "KEYCODE_BUTTON_5");
+        names.append(KEYCODE_BUTTON_6, "KEYCODE_BUTTON_6");
+        names.append(KEYCODE_BUTTON_7, "KEYCODE_BUTTON_7");
+        names.append(KEYCODE_BUTTON_8, "KEYCODE_BUTTON_8");
+        names.append(KEYCODE_BUTTON_9, "KEYCODE_BUTTON_9");
+        names.append(KEYCODE_BUTTON_10, "KEYCODE_BUTTON_10");
+        names.append(KEYCODE_BUTTON_11, "KEYCODE_BUTTON_11");
+        names.append(KEYCODE_BUTTON_12, "KEYCODE_BUTTON_12");
+        names.append(KEYCODE_BUTTON_13, "KEYCODE_BUTTON_13");
+        names.append(KEYCODE_BUTTON_14, "KEYCODE_BUTTON_14");
+        names.append(KEYCODE_BUTTON_15, "KEYCODE_BUTTON_15");
+        names.append(KEYCODE_BUTTON_16, "KEYCODE_BUTTON_16");
     };
 
     // Symbolic names of all metakeys in bit order from least significant to most significant.
@@ -1250,14 +1252,7 @@
     }
 
     static {
-        if (META_SYMBOLIC_NAMES.length != 32) {
-            throw new IllegalStateException(
-                    "META_SYMBOLIC_NAMES array should contain exactly 32 entries.");
-        }
-        if (KEYCODE_SYMBOLIC_NAMES.length != LAST_KEYCODE + 1) {
-            throw new IllegalStateException(
-                    "KEYCODE_SYMBOLIC_NAMES array is out of sync with the keycode constants.");
-        }
+        populateKeycodeSymbolicNames();
     }
 
     private KeyEvent() {
@@ -1653,6 +1648,49 @@
         return native_hasDefaultAction(mKeyCode);
     }
 
+    /**
+     * Returns true if the specified keycode is a gamepad button.
+     * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
+     */
+    public static final boolean isGamepadButton(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_BUTTON_A:
+            case KeyEvent.KEYCODE_BUTTON_B:
+            case KeyEvent.KEYCODE_BUTTON_C:
+            case KeyEvent.KEYCODE_BUTTON_X:
+            case KeyEvent.KEYCODE_BUTTON_Y:
+            case KeyEvent.KEYCODE_BUTTON_Z:
+            case KeyEvent.KEYCODE_BUTTON_L1:
+            case KeyEvent.KEYCODE_BUTTON_R1:
+            case KeyEvent.KEYCODE_BUTTON_L2:
+            case KeyEvent.KEYCODE_BUTTON_R2:
+            case KeyEvent.KEYCODE_BUTTON_THUMBL:
+            case KeyEvent.KEYCODE_BUTTON_THUMBR:
+            case KeyEvent.KEYCODE_BUTTON_START:
+            case KeyEvent.KEYCODE_BUTTON_SELECT:
+            case KeyEvent.KEYCODE_BUTTON_MODE:
+            case KeyEvent.KEYCODE_BUTTON_1:
+            case KeyEvent.KEYCODE_BUTTON_2:
+            case KeyEvent.KEYCODE_BUTTON_3:
+            case KeyEvent.KEYCODE_BUTTON_4:
+            case KeyEvent.KEYCODE_BUTTON_5:
+            case KeyEvent.KEYCODE_BUTTON_6:
+            case KeyEvent.KEYCODE_BUTTON_7:
+            case KeyEvent.KEYCODE_BUTTON_8:
+            case KeyEvent.KEYCODE_BUTTON_9:
+            case KeyEvent.KEYCODE_BUTTON_10:
+            case KeyEvent.KEYCODE_BUTTON_11:
+            case KeyEvent.KEYCODE_BUTTON_12:
+            case KeyEvent.KEYCODE_BUTTON_13:
+            case KeyEvent.KEYCODE_BUTTON_14:
+            case KeyEvent.KEYCODE_BUTTON_15:
+            case KeyEvent.KEYCODE_BUTTON_16:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     public final int getDeviceId() {
@@ -2554,7 +2592,7 @@
 
     /**
      * Returns a string that represents the symbolic name of the specified action
-     * such as "ACTION_DOWN", or "35" (if unknown).
+     * such as "ACTION_DOWN", or an equivalent numeric constant such as "35" if unknown.
      *
      * @param action The action.
      * @return The symbolic name of the specified action.
@@ -2575,52 +2613,51 @@
 
     /**
      * Returns a string that represents the symbolic name of the specified keycode
-     * such as "KEYCODE_A", "KEYCODE_DPAD_UP", or "1001" (if unknown).
+     * such as "KEYCODE_A", "KEYCODE_DPAD_UP", or an equivalent numeric constant
+     * such as "1001" if unknown.
      *
      * @param keyCode The key code.
      * @return The symbolic name of the specified keycode.
      *
      * @see KeyCharacterMap#getDisplayLabel
-     * @hide
      */
     public static String keyCodeToString(int keyCode) {
-        if (keyCode >= 0 && keyCode < KEYCODE_SYMBOLIC_NAMES.length) {
-            return KEYCODE_SYMBOLIC_NAMES[keyCode];
-        }
-        return Integer.toString(keyCode);
+        String symbolicName = KEYCODE_SYMBOLIC_NAMES.get(keyCode);
+        return symbolicName != null ? symbolicName : Integer.toString(keyCode);
     }
 
     /**
-     * Gets a keycode by its symbolic name such as "KEYCODE_A" or "1001" (if unknown).
+     * Gets a keycode by its symbolic name such as "KEYCODE_A" or an equivalent
+     * numeric constant such as "1001".
      *
      * @param symbolicName The symbolic name of the keycode.
-     * @return The keycode or -1 if not found.
+     * @return The keycode or {@link #KEYCODE_UNKNOWN} if not found.
      * @see #keycodeToString
-     * @hide
      */
     public static int keyCodeFromString(String symbolicName) {
         if (symbolicName == null) {
             throw new IllegalArgumentException("symbolicName must not be null");
         }
 
-        final int count = KEYCODE_SYMBOLIC_NAMES.length;
+        final int count = KEYCODE_SYMBOLIC_NAMES.size();
         for (int i = 0; i < count; i++) {
-            if (symbolicName.equals(KEYCODE_SYMBOLIC_NAMES[i])) {
+            if (symbolicName.equals(KEYCODE_SYMBOLIC_NAMES.valueAt(i))) {
                 return i;
             }
         }
 
         try {
-            return Integer.parseInt(symbolicName,10);
+            return Integer.parseInt(symbolicName, 10);
         } catch (NumberFormatException ex) {
-            return -1;
+            return KEYCODE_UNKNOWN;
         }
     }
 
     /**
      * Returns a string that represents the symbolic name of the specified combined meta
      * key modifier state flags such as "0", "META_SHIFT_ON",
-     * "META_ALT_ON|META_SHIFT_ON" or "0x10000000" (if unknown).
+     * "META_ALT_ON|META_SHIFT_ON" or an equivalent numeric constant such as "0x10000000"
+     * if unknown.
      *
      * @param metaState The meta state.
      * @return The symbolic name of the specified combined meta state flags.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6673be2..a1326d2 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.util.SparseArray;
 
 /**
  * Object used to report movement (mouse, pen, finger, trackball) events.  This
@@ -263,10 +264,22 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen, reports the absolute X screen position of the center of
+     * the touch contact area.  The units are display pixels.
+     * <li>For a touch pad, reports the absolute X surface position of the center of the touch
+     * contact area.  The units are device-specific because the touch pad is not necessarily
+     * associated with a display.  Use {@link InputDevice#getMotionRange(int)} to query
+     * the effective range of values.
+     * <li>For a mouse, reports the absolute X screen position of the mouse pointer.
+     * The units are display pixels.
+     * <li>For a trackball, reports the relative horizontal displacement of the trackball.
+     * The value is normalized to a range from -1.0 (left) to 1.0 (right).
+     * <li>For a joystick, reports the absolute X position of the joystick.
+     * The value is normalized to a range from -1.0 (left) to 1.0 (right).
+     * </ul>
+     * </p>
      *
      * @see #getX(int)
      * @see #getHistoricalX(int, int)
@@ -277,10 +290,21 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen, reports the absolute Y screen position of the center of
+     * the touch contact area.  The units are display pixels.
+     * <li>For a touch pad, reports the absolute Y surface position of the center of the touch
+     * contact area.  The units are device-dependent; use {@link InputDevice#getMotionRange(int)}
+     * to query the effective range of values.
+     * <li>For a mouse, reports the absolute Y screen position of the mouse pointer.
+     * The units are display pixels.
+     * <li>For a trackball, reports the relative vertical displacement of the trackball.
+     * The value is normalized to a range from -1.0 (up) to 1.0 (down).
+     * <li>For a joystick, reports the absolute Y position of the joystick.
+     * The value is normalized to a range from -1.0 (up or far) to 1.0 (down or near).
+     * </ul>
+     * </p>
      *
      * @see #getY(int)
      * @see #getHistoricalY(int, int)
@@ -291,12 +315,18 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen or touch pad, reports the approximate pressure applied to the device
+     * by a finger or other tool.  The value is normalized to a range 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.
+     * <li>For a trackball, the value is set to 1 if the trackball button is pressed
+     * or 0 otherwise.
+     * <li>For a mouse, the value is set to 1 if the primary mouse button is pressed
+     * or 0 otherwise.
+     * </ul>
+     * </p>
      *
      * @see #getPressure(int)
      * @see #getHistoricalPressure(int, int)
@@ -307,17 +337,16 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen or touch pad, reports the approximate size of the contact area in
+     * relation to the maximum detectable size for the device.  The value is normalized
+     * to a range from 0 (smallest detectable size) to 1 (largest detectable size),
+     * although it is not a linear scale.  This value is of very limited use.
+     * To obtain calibrated size information, use
+     * {@link #AXIS_TOUCH_MAJOR} or {@link #AXIS_TOOL_MAJOR}.
+     * </ul>
+     * </p>
      *
      * @see #getSize(int)
      * @see #getHistoricalSize(int, int)
@@ -328,11 +357,17 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen, reports the length of the major axis of an ellipse that
+     * represents the touch area at the point of contact.
+     * The units are display pixels.
+     * <li>For a touch pad, reports the length of the major axis of an ellipse that
+     * represents the touch area at the point of contact.
+     * The units are device-dependent; use {@link InputDevice#getMotionRange(int)}
+     * to query the effective range of values.
+     * </ul>
+     * </p>
      *
      * @see #getTouchMajor(int)
      * @see #getHistoricalTouchMajor(int, int)
@@ -343,11 +378,19 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen, reports the length of the minor axis of an ellipse that
+     * represents the touch area at the point of contact.
+     * The units are display pixels.
+     * <li>For a touch pad, reports the length of the minor axis of an ellipse that
+     * represents the touch area at the point of contact.
+     * The units are device-dependent; use {@link InputDevice#getMotionRange(int)}
+     * to query the effective range of values.
+     * </ul>
+     * </p><p>
+     * When the touch is circular, the major and minor axis lengths will be equal to one another.
+     * </p>
      *
      * @see #getTouchMinor(int)
      * @see #getHistoricalTouchMinor(int, int)
@@ -358,13 +401,21 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen, reports the length of the major axis of an ellipse that
+     * represents the size of the approaching finger or tool used to make contact.
+     * <li>For a touch pad, reports the length of the major axis of an ellipse that
+     * represents the size of the approaching finger or tool used to make contact.
+     * The units are device-dependent; use {@link InputDevice#getMotionRange(int)}
+     * to query the effective range of values.
+     * </ul>
+     * </p><p>
+     * When the touch is circular, the major and minor axis lengths will be equal to one another.
+     * </p><p>
+     * The tool size may be larger than the touch size since the tool may not be fully
+     * in contact with the touch sensor.
+     * </p>
      *
      * @see #getToolMajor(int)
      * @see #getHistoricalToolMajor(int, int)
@@ -375,13 +426,21 @@
 
     /**
      * 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.
+     * <p>
+     * <ul>
+     * <li>For a touch screen, reports the length of the minor axis of an ellipse that
+     * represents the size of the approaching finger or tool used to make contact.
+     * <li>For a touch pad, reports the length of the minor axis of an ellipse that
+     * represents the size of the approaching finger or tool used to make contact.
+     * The units are device-dependent; use {@link InputDevice#getMotionRange(int)}
+     * to query the effective range of values.
+     * </ul>
+     * </p><p>
+     * When the touch is circular, the major and minor axis lengths will be equal to one another.
+     * </p><p>
+     * The tool size may be larger than the touch size since the tool may not be fully
+     * in contact with the touch sensor.
+     * </p>
      *
      * @see #getToolMinor(int)
      * @see #getHistoricalToolMinor(int, int)
@@ -392,15 +451,18 @@
 
     /**
      * 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
+     * <p>
+     * <ul>
+     * <li>For a touch screen or touch pad, reports the orientation of the finger
+     * or tool in radians relative to the vertical plane of the device.
+     * An angle of 0 radians 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).
+     * </ul>
+     * </p>
      *
      * @see #getOrientation(int)
      * @see #getHistoricalOrientation(int, int)
@@ -409,6 +471,399 @@
      */
     public static final int AXIS_ORIENTATION = 8;
 
+    /**
+     * Constant used to identify the Vertical Scroll axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a mouse, reports the relative movement of vertical scroll wheel.
+     * The value is normalized to a range from -1.0 (up) to 1.0 (down).
+     * </ul>
+     * </p><p>
+     * This axis should be used to scroll views vertically.
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_VSCROLL = 9;
+
+    /**
+     * Constant used to identify the Horizontal Scroll axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a mouse, reports the relative movement of horizontal scroll wheel.
+     * The value is normalized to a range from -1.0 (left) to 1.0 (right).
+     * </ul>
+     * </p><p>
+     * This axis should be used to scroll views horizontally.
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_HSCROLL = 10;
+
+    /**
+     * Constant used to identify the Z axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute Z position of the joystick.
+     * The value is normalized to a range from -1.0 (high) to 1.0 (low).
+     * <em>On game pads with two analog joysticks, this axis is often reinterpreted
+     * to report the absolute X position of the second joystick instead.</em>
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_Z = 11;
+
+    /**
+     * Constant used to identify the X Rotation axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute rotation angle about the X axis.
+     * The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_RX = 12;
+
+    /**
+     * Constant used to identify the Y Rotation axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute rotation angle about the Y axis.
+     * The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_RY = 13;
+
+    /**
+     * Constant used to identify the Z Rotation axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute rotation angle about the Z axis.
+     * The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise).
+     * <em>On game pads with two analog joysticks, this axis is often reinterpreted
+     * to report the absolute Y position of the second joystick instead.</em>
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_RZ = 14;
+
+    /**
+     * Constant used to identify the Hat X axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute X position of the directional hat control.
+     * The value is normalized to a range from -1.0 (left) to 1.0 (right).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_HAT_X = 15;
+
+    /**
+     * Constant used to identify the Hat Y axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute Y position of the directional hat control.
+     * The value is normalized to a range from -1.0 (up) to 1.0 (down).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_HAT_Y = 16;
+
+    /**
+     * Constant used to identify the Left Trigger axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute position of the left trigger control.
+     * The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_LTRIGGER = 17;
+
+    /**
+     * Constant used to identify the Right Trigger axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute position of the right trigger control.
+     * The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_RTRIGGER = 18;
+
+    /**
+     * Constant used to identify the Generic 1 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_1 = 32;
+
+    /**
+     * Constant used to identify the Generic 2 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_2 = 33;
+
+    /**
+     * Constant used to identify the Generic 3 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_3 = 34;
+
+    /**
+     * Constant used to identify the Generic 4 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_4 = 35;
+
+    /**
+     * Constant used to identify the Generic 5 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_5 = 36;
+
+    /**
+     * Constant used to identify the Generic 6 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_6 = 37;
+
+    /**
+     * Constant used to identify the Generic 7 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_7 = 38;
+
+    /**
+     * Constant used to identify the Generic 8 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_8 = 39;
+
+    /**
+     * Constant used to identify the Generic 9 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_9 = 40;
+
+    /**
+     * Constant used to identify the Generic 10 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_10 = 41;
+
+    /**
+     * Constant used to identify the Generic 11 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_11 = 42;
+
+    /**
+     * Constant used to identify the Generic 12 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_12 = 43;
+
+    /**
+     * Constant used to identify the Generic 13 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_13 = 44;
+
+    /**
+     * Constant used to identify the Generic 14 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_14 = 45;
+
+    /**
+     * Constant used to identify the Generic 15 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_15 = 46;
+
+    /**
+     * Constant used to identify the Generic 16 axis of a motion event.
+     * The interpretation of a generic axis is device-specific.
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GENERIC_16 = 47;
+
+    // NOTE: If you add a new axis here you must also add it to:
+    //  native/include/android/input.h
+    //  frameworks/base/include/ui/KeycodeLabels.h
+
+    // Symbolic names of all axes.
+    private static final SparseArray<String> AXIS_SYMBOLIC_NAMES = new SparseArray<String>();
+    private static void populateAxisSymbolicNames() {
+        SparseArray<String> names = AXIS_SYMBOLIC_NAMES;
+        names.append(AXIS_X, "AXIS_X");
+        names.append(AXIS_Y, "AXIS_Y");
+        names.append(AXIS_PRESSURE, "AXIS_PRESSURE");
+        names.append(AXIS_SIZE, "AXIS_SIZE");
+        names.append(AXIS_TOUCH_MAJOR, "AXIS_TOUCH_MAJOR");
+        names.append(AXIS_TOUCH_MINOR, "AXIS_TOUCH_MINOR");
+        names.append(AXIS_TOOL_MAJOR, "AXIS_TOOL_MAJOR");
+        names.append(AXIS_TOOL_MINOR, "AXIS_TOOL_MINOR");
+        names.append(AXIS_ORIENTATION, "AXIS_ORIENTATION");
+        names.append(AXIS_VSCROLL, "AXIS_VSCROLL");
+        names.append(AXIS_HSCROLL, "AXIS_HSCROLL");
+        names.append(AXIS_Z, "AXIS_Z");
+        names.append(AXIS_RX, "AXIS_RX");
+        names.append(AXIS_RY, "AXIS_RY");
+        names.append(AXIS_RZ, "AXIS_RZ");
+        names.append(AXIS_HAT_X, "AXIS_HAT_X");
+        names.append(AXIS_HAT_Y, "AXIS_HAT_Y");
+        names.append(AXIS_LTRIGGER, "AXIS_LTRIGGER");
+        names.append(AXIS_RTRIGGER, "AXIS_RTRIGGER");
+        names.append(AXIS_GENERIC_1, "AXIS_GENERIC_1");
+        names.append(AXIS_GENERIC_2, "AXIS_GENERIC_2");
+        names.append(AXIS_GENERIC_3, "AXIS_GENERIC_3");
+        names.append(AXIS_GENERIC_4, "AXIS_GENERIC_4");
+        names.append(AXIS_GENERIC_5, "AXIS_GENERIC_5");
+        names.append(AXIS_GENERIC_6, "AXIS_GENERIC_6");
+        names.append(AXIS_GENERIC_7, "AXIS_GENERIC_7");
+        names.append(AXIS_GENERIC_8, "AXIS_GENERIC_8");
+        names.append(AXIS_GENERIC_9, "AXIS_GENERIC_9");
+        names.append(AXIS_GENERIC_10, "AXIS_GENERIC_10");
+        names.append(AXIS_GENERIC_11, "AXIS_GENERIC_11");
+        names.append(AXIS_GENERIC_12, "AXIS_GENERIC_12");
+        names.append(AXIS_GENERIC_13, "AXIS_GENERIC_13");
+        names.append(AXIS_GENERIC_14, "AXIS_GENERIC_14");
+        names.append(AXIS_GENERIC_15, "AXIS_GENERIC_15");
+        names.append(AXIS_GENERIC_16, "AXIS_GENERIC_16");
+    }
+
+    static {
+        populateAxisSymbolicNames();
+    }
+
     // Private value for history pos that obtains the current sample.
     private static final int HISTORY_CURRENT = -0x80000000;
 
@@ -1081,7 +1536,7 @@
      * Returns the orientation of the touch area and tool area in radians clockwise from vertical
      * for the given pointer <em>index</em> (use {@link #getPointerId(int)} to find the pointer
      * identifier for this index).
-     * An angle of 0 degrees indicates that the major axis of contact is oriented
+     * An angle of 0 radians 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.
@@ -1737,34 +2192,40 @@
 
     /**
      * 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.
+     * such as "AXIS_X" or an equivalent numeric constant 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);
+        String symbolicName = AXIS_SYMBOLIC_NAMES.get(axis);
+        return symbolicName != null ? symbolicName : Integer.toString(axis);
+    }
+
+    /**
+     * Gets an axis by its symbolic name such as "KEYCODE_A" or an
+     * equivalent numeric constant such as "1001".
+     *
+     * @param symbolicName The symbolic name of the axis.
+     * @return The axis or -1 if not found.
+     * @see #keycodeToString
+     */
+    public static int axisFromString(String symbolicName) {
+        if (symbolicName == null) {
+            throw new IllegalArgumentException("symbolicName must not be null");
+        }
+
+        final int count = AXIS_SYMBOLIC_NAMES.size();
+        for (int i = 0; i < count; i++) {
+            if (symbolicName.equals(AXIS_SYMBOLIC_NAMES.valueAt(i))) {
+                return i;
+            }
+        }
+
+        try {
+            return Integer.parseInt(symbolicName, 10);
+        } catch (NumberFormatException ex) {
+            return -1;
         }
     }
 
@@ -1803,7 +2264,7 @@
      */
     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 long mPackedAxisBits;
         private float[] mPackedAxisValues;
 
         /**
@@ -1823,20 +2284,14 @@
         }
 
         /**
-         * The X coordinate of the pointer movement.
-         * 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.
+         * The X component of the pointer movement.
          *
          * @see MotionEvent#AXIS_X
          */
         public float x;
         
         /**
-         * The Y coordinate of the pointer movement.
-         * 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.
+         * The Y component of the pointer movement.
          *
          * @see MotionEvent#AXIS_Y
          */
@@ -1912,7 +2367,7 @@
         
         /**
          * The orientation of the touch area and tool area in radians clockwise from vertical.
-         * An angle of 0 degrees indicates that the major axis of contact is oriented
+         * An angle of 0 radians 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.
@@ -1947,11 +2402,11 @@
          * @param other The pointer coords object to copy.
          */
         public void copyFrom(PointerCoords other) {
-            final int bits = other.mPackedAxisBits;
+            final long bits = other.mPackedAxisBits;
             mPackedAxisBits = bits;
             if (bits != 0) {
                 final float[] otherValues = other.mPackedAxisValues;
-                final int count = Integer.bitCount(bits);
+                final int count = Long.bitCount(bits);
                 float[] values = mPackedAxisValues;
                 if (values == null || count > values.length) {
                     values = new float[otherValues.length];
@@ -2001,12 +2456,15 @@
                 case AXIS_ORIENTATION:
                     return orientation;
                 default: {
-                    final int bits = mPackedAxisBits;
-                    final int axisBit = 1 << axis;
+                    if (axis < 0 || axis > 63) {
+                        throw new IllegalArgumentException("Axis out of range.");
+                    }
+                    final long bits = mPackedAxisBits;
+                    final long axisBit = 1L << axis;
                     if ((bits & axisBit) == 0) {
                         return 0;
                     }
-                    final int index = Integer.bitCount(bits & (axisBit - 1));
+                    final int index = Long.bitCount(bits & (axisBit - 1L));
                     return mPackedAxisValues[index];
                 }
             }
@@ -2051,16 +2509,19 @@
                     orientation = value;
                     break;
                 default: {
-                    final int bits = mPackedAxisBits;
-                    final int axisBit = 1 << axis;
-                    final int index = Integer.bitCount(bits & (axisBit - 1));
+                    if (axis < 0 || axis > 63) {
+                        throw new IllegalArgumentException("Axis out of range.");
+                    }
+                    final long bits = mPackedAxisBits;
+                    final long axisBit = 1L << axis;
+                    final int index = Long.bitCount(bits & (axisBit - 1L));
                     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);
+                            final int count = Long.bitCount(bits);
                             if (count < values.length) {
                                 if (index != count) {
                                     System.arraycopy(values, index, values, index + 1,
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index ec1a373..39f99b8 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2496,7 +2496,6 @@
 
         // Deliver the event to the view.
         if (mView.dispatchGenericMotionEvent(event)) {
-            ensureTouchMode(false);
             if (isJoystick) {
                 updateJoystickDirection(event, false);
             }
@@ -2525,8 +2524,16 @@
         final int metaState = event.getMetaState();
         final int deviceId = event.getDeviceId();
         final int source = event.getSource();
-        final int xDirection = joystickAxisValueToDirection(event.getX());
-        final int yDirection = joystickAxisValueToDirection(event.getY());
+
+        int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X));
+        if (xDirection == 0) {
+            xDirection = joystickAxisValueToDirection(event.getX());
+        }
+
+        int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+        if (yDirection == 0) {
+            yDirection = joystickAxisValueToDirection(event.getY());
+        }
 
         if (xDirection != mLastJoystickXDirection) {
             if (mLastJoystickXKeyCode != 0) {
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 0b765fd..4a6857c 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -327,7 +327,10 @@
                 .append(" ToolMajor=").append(coords.toolMajor, 3)
                 .append(" ToolMinor=").append(coords.toolMinor, 3)
                 .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
-                .append("deg").toString());
+                .append("deg")
+                .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
+                .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
+                .toString());
     }
 
     public void addTouchEvent(MotionEvent event) {
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 069e40b..5ba1cff 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -207,8 +207,7 @@
     outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
             env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
 
-    uint32_t bits = env->GetIntField(pointerCoordsObj,
-            gPointerCoordsClassInfo.mPackedAxisBits);
+    uint64_t bits = env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits);
     if (bits) {
         jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
                 gPointerCoordsClassInfo.mPackedAxisValues));
@@ -219,7 +218,7 @@
             uint32_t index = 0;
             do {
                 uint32_t axis = __builtin_ctz(bits);
-                uint32_t axisBit = 1 << axis;
+                uint64_t axisBit = 1LL << axis;
                 bits &= ~axisBit;
                 outRawPointerCoords->setAxisValue(axis, values[index++]);
             } while (bits);
@@ -272,21 +271,21 @@
     env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
             rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
 
-    const uint32_t unpackedAxisBits = 0
-            | (1 << AMOTION_EVENT_AXIS_X)
-            | (1 << AMOTION_EVENT_AXIS_Y)
-            | (1 << AMOTION_EVENT_AXIS_PRESSURE)
-            | (1 << AMOTION_EVENT_AXIS_SIZE)
-            | (1 << AMOTION_EVENT_AXIS_TOUCH_MAJOR)
-            | (1 << AMOTION_EVENT_AXIS_TOUCH_MINOR)
-            | (1 << AMOTION_EVENT_AXIS_TOOL_MAJOR)
-            | (1 << AMOTION_EVENT_AXIS_TOOL_MINOR)
-            | (1 << AMOTION_EVENT_AXIS_ORIENTATION);
+    const uint64_t unpackedAxisBits = 0
+            | (1LL << AMOTION_EVENT_AXIS_X)
+            | (1LL << AMOTION_EVENT_AXIS_Y)
+            | (1LL << AMOTION_EVENT_AXIS_PRESSURE)
+            | (1LL << AMOTION_EVENT_AXIS_SIZE)
+            | (1LL << AMOTION_EVENT_AXIS_TOUCH_MAJOR)
+            | (1LL << AMOTION_EVENT_AXIS_TOUCH_MINOR)
+            | (1LL << AMOTION_EVENT_AXIS_TOOL_MAJOR)
+            | (1LL << AMOTION_EVENT_AXIS_TOOL_MINOR)
+            | (1LL << AMOTION_EVENT_AXIS_ORIENTATION);
 
-    uint32_t outBits = 0;
-    uint32_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits;
+    uint64_t outBits = 0;
+    uint64_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits;
     if (remainingBits) {
-        uint32_t packedAxesCount = __builtin_popcount(remainingBits);
+        uint32_t packedAxesCount = __builtin_popcountll(remainingBits);
         jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
                 outPointerCoordsObj);
         if (!outValuesArray) {
@@ -300,7 +299,7 @@
         uint32_t index = 0;
         do {
             uint32_t axis = __builtin_ctz(remainingBits);
-            uint32_t axisBit = 1 << axis;
+            uint64_t axisBit = 1LL << axis;
             remainingBits &= ~axisBit;
             outBits |= axisBit;
             outValues[index++] = rawPointerCoords->getAxisValue(axis);
@@ -309,7 +308,7 @@
         env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0);
         env->DeleteLocalRef(outValuesArray);
     }
-    env->SetIntField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
+    env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
 }
 
 
@@ -758,7 +757,7 @@
     FIND_CLASS(gPointerCoordsClassInfo.clazz, "android/view/MotionEvent$PointerCoords");
 
     GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisBits, gPointerCoordsClassInfo.clazz,
-            "mPackedAxisBits", "I");
+            "mPackedAxisBits", "J");
     GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisValues, gPointerCoordsClassInfo.clazz,
             "mPackedAxisValues", "[F");
     GET_FIELD_ID(gPointerCoordsClassInfo.x, gPointerCoordsClassInfo.clazz,
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
index 8cba52d..a66a884 100644
--- a/data/keyboards/Android.mk
+++ b/data/keyboards/Android.mk
@@ -17,3 +17,21 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(LOCAL_PATH)/common.mk
+
+# Validate all key maps.
+include $(CLEAR_VARS)
+
+validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
+files := \
+    $(foreach file,$(keylayouts),frameworks/base/data/keyboards/$(file)) \
+    $(foreach file,$(keycharmaps),frameworks/base/data/keyboards/$(file)) \
+    $(foreach file,$(keyconfigs),frameworks/base/data/keyboards/$(file))
+
+LOCAL_MODULE := validate_framework_keymaps
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := validatekeymaps
+
+validate_framework_keymaps: $(files)
+	$(hide) $(validatekeymaps) $(files)
+
+include $(BUILD_PHONY_PACKAGE)
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 14d7c80..51a8b27 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -545,3 +545,129 @@
     meta:                               fallback HOME
     alt:                                fallback MENU
 }
+
+### Gamepad buttons ###
+
+key BUTTON_A {
+    base:                               fallback BACK
+}
+
+key BUTTON_B {
+    base:                               fallback BACK
+}
+
+key BUTTON_C {
+    base:                               fallback BACK
+}
+
+key BUTTON_X {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_Y {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_Z {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_L1 {
+    base:                               none
+}
+
+key BUTTON_R1 {
+    base:                               none
+}
+
+key BUTTON_L2 {
+    base:                               none
+}
+
+key BUTTON_R2 {
+    base:                               none
+}
+
+key BUTTON_THUMBL {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_THUMBR {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_START {
+    base:                               fallback HOME
+}
+
+key BUTTON_SELECT {
+    base:                               fallback MENU
+}
+
+key BUTTON_MODE {
+    base:                               fallback MENU
+}
+
+key BUTTON_1 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_2 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_3 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_4 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_5 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_6 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_7 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_8 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_9 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_10 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_11 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_12 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_13 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_14 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_15 {
+    base:                               fallback DPAD_CENTER
+}
+
+key BUTTON_16 {
+    base:                               fallback DPAD_CENTER
+}
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 0aefc31..6d925d6 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -261,7 +261,6 @@
 # key 239 "KEY_KBDILLUMUP"
 # key 240 "KEY_UNKNOWN"
 
-
 key 288   BUTTON_1
 key 289   BUTTON_2
 key 290   BUTTON_3
@@ -400,3 +399,16 @@
 # key 502 KEY_BRL_DOT6
 # key 503 KEY_BRL_DOT7
 # key 504 KEY_BRL_DOT8
+
+
+# Joystick and game controller axes.
+# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x03 RX
+axis 0x04 RY
+axis 0x05 RZ
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+ 
\ No newline at end of file
diff --git a/data/keyboards/Vendor_046d_Product_c216.kl b/data/keyboards/Vendor_046d_Product_c216.kl
new file mode 100644
index 0000000..6743323
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c216.kl
@@ -0,0 +1,37 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Logitech Dual Action Controller
+#
+
+key 0x120    BUTTON_A
+key 0x123    BUTTON_B
+key 0x121    BUTTON_X
+key 0x122    BUTTON_Y
+key 0x124    BUTTON_L1
+key 0x125    BUTTON_R1
+key 0x126    BUTTON_L2
+key 0x127    BUTTON_R2
+key 0x128    BUTTON_SELECT
+key 0x129    BUTTON_START
+key 0x12a    BUTTON_THUMBL
+key 0x12b    BUTTON_THUMBR
+
+axis 0x00    X
+axis 0x01    Y
+axis 0x02    Z
+axis 0x05    RZ
+axis 0x10    HAT_X
+axis 0x11    HAT_Y
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
new file mode 100644
index 0000000..f8ac6a3
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -0,0 +1,76 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+#
+
+key 0x124    DPAD_UP
+key 0x125    DPAD_RIGHT
+key 0x126    DPAD_DOWN
+key 0x127    DPAD_LEFT
+
+key 0x120    BUTTON_SELECT
+key 0x123    BUTTON_START
+key 0x12f    BUTTON_A
+key 0x12c    BUTTON_B
+key 0x12e    BUTTON_X
+key 0x12d    BUTTON_Y
+key 0x12a    BUTTON_L1
+key 0x12b    BUTTON_R1
+key 0x128    BUTTON_L2
+key 0x129    BUTTON_R2
+key 0x121    BUTTON_THUMBL
+key 0x122    BUTTON_THUMBR
+
+# PS key
+key 0x2d0    BUTTON_1
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# DPAD
+# axis 0x2c   -HAT_Y
+# axis 0x2d   +HAT_X
+# axis 0x2e   +HAT_Y
+# axis 0x2f   -HAT_X
+
+# L2 trigger
+axis 0x30   LTRIGGER
+
+# R2 trigger
+axis 0x31   RTRIGGER
+
+# L1 trigger
+# axis 0x32
+
+# R1 trigger
+# axis 0x33
+
+# Triangle
+# axis 0x34
+
+# Circle
+# axis 0x35
+
+# Cross
+# axis 0x36
+
+# Square
+# axis 0x37
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
index 56c287a..5b367b9 100644
--- a/data/keyboards/common.mk
+++ b/data/keyboards/common.mk
@@ -19,7 +19,9 @@
     Generic.kl \
     AVRCP.kl \
     qwerty.kl \
+    Vendor_046d_Product_c216.kl \
     Vendor_046d_Product_c532.kl \
+    Vendor_054c_Product_0268.kl \
     Vendor_05ac_Product_0239.kl \
     Vendor_22b8_Product_093d.kl
 
diff --git a/include/ui/Input.h b/include/ui/Input.h
index cb9327e..86ce098 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -170,10 +170,10 @@
  * Pointer coordinate data.
  */
 struct PointerCoords {
-    enum { MAX_AXES = 15 }; // 15 so that sizeof(PointerCoords) == 16 * 4 == 64
+    enum { MAX_AXES = 14 }; // 14 so that sizeof(PointerCoords) == 64
 
     // Bitfield of axes that are present in this structure.
-    uint32_t bits; // 32bits are enough for now, can raise to 64bit when needed
+    uint64_t bits;
 
     // 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'.
@@ -183,41 +183,9 @@
         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 status_t 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 NO_MEMORY;
-            }
-            bits |= axisBit;
-            for (uint32_t i = count; i > index; i--) {
-                values[i] = values[i - 1];
-            }
-        }
-        values[index] = value;
-        return OK;
-    }
-
-    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];
-    }
+    float getAxisValue(int32_t axis) const;
+    status_t setAxisValue(int32_t axis, float value);
+    float* editAxisValue(int32_t axis);
 
 #ifdef HAVE_ANDROID_OS
     status_t readFromParcel(Parcel* parcel);
diff --git a/include/ui/KeyLayoutMap.h b/include/ui/KeyLayoutMap.h
index f0a6d00..904c8f3 100644
--- a/include/ui/KeyLayoutMap.h
+++ b/include/ui/KeyLayoutMap.h
@@ -25,7 +25,7 @@
 namespace android {
 
 /**
- * Describes a mapping from keyboard scan codes to Android key codes.
+ * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes.
  */
 class KeyLayoutMap {
 public:
@@ -33,8 +33,10 @@
 
     static status_t load(const String8& filename, KeyLayoutMap** outMap);
 
-    status_t map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
-    status_t findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
+    status_t mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
+    status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
+
+    status_t mapAxis(int32_t scanCode, int32_t* axis) const;
 
 private:
     struct Key {
@@ -42,7 +44,8 @@
         uint32_t flags;
     };
 
-    KeyedVector<int32_t,Key> mKeys;
+    KeyedVector<int32_t, Key> mKeys;
+    KeyedVector<int32_t, int32_t> mAxes;
 
     KeyLayoutMap();
 
@@ -57,6 +60,7 @@
 
     private:
         status_t parseKey();
+        status_t parseAxis();
     };
 };
 
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
index 50296e2..54bc968 100644
--- a/include/ui/Keyboard.h
+++ b/include/ui/Keyboard.h
@@ -111,6 +111,18 @@
 extern uint32_t getKeyFlagByLabel(const char* label);
 
 /**
+ * Gets a axis by its short form label, eg. "X".
+ * Returns -1 if unknown.
+ */
+extern int32_t getAxisByLabel(const char* label);
+
+/**
+ * Gets a axis label by its id.
+ * Returns NULL if unknown.
+ */
+extern const char* getAxisLabel(int32_t axisId);
+
+/**
  * Updates a meta state field when a key is pressed or released.
  */
 extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index dbccf29..bdfbf7c 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -250,4 +250,47 @@
     { NULL, 0 }
 };
 
+static const KeycodeLabel AXES[] = {
+    { "X", 0 },
+    { "Y", 1 },
+    { "PRESSURE", 2 },
+    { "SIZE", 3 },
+    { "TOUCH_MAJOR", 4 },
+    { "TOUCH_MINOR", 5 },
+    { "TOOL_MAJOR", 6 },
+    { "TOOL_MINOR", 7 },
+    { "ORIENTATION", 8 },
+    { "VSCROLL", 9 },
+    { "HSCROLL", 10 },
+    { "Z", 11 },
+    { "RX", 12 },
+    { "RY", 13 },
+    { "RZ", 14 },
+    { "HAT_X", 15 },
+    { "HAT_Y", 16 },
+    { "LTRIGGER", 17 },
+    { "RTRIGGER", 18 },
+    { "GENERIC_1", 32 },
+    { "GENERIC_2", 33 },
+    { "GENERIC_3", 34 },
+    { "GENERIC_4", 35 },
+    { "GENERIC_5", 36 },
+    { "GENERIC_6", 37 },
+    { "GENERIC_7", 38 },
+    { "GENERIC_8", 39 },
+    { "GENERIC_9", 40 },
+    { "GENERIC_10", 41 },
+    { "GENERIC_11", 42 },
+    { "GENERIC_12", 43 },
+    { "GENERIC_13", 44 },
+    { "GENERIC_14", 45 },
+    { "GENERIC_15", 46 },
+    { "GENERIC_16", 47 },
+
+    // NOTE: If you add a new axis here you must also add it to several other files.
+    //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
+
+    { NULL, -1 }
+};
+
 #endif // _UI_KEYCODE_LABELS_H
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index e3107d5..a80320e 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -250,11 +250,59 @@
 
 // --- PointerCoords ---
 
+float PointerCoords::getAxisValue(int32_t axis) const {
+    if (axis < 0 || axis > 63) {
+        return 0;
+    }
+
+    uint64_t axisBit = 1LL << axis;
+    if (!(bits & axisBit)) {
+        return 0;
+    }
+    uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
+    return values[index];
+}
+
+status_t PointerCoords::setAxisValue(int32_t axis, float value) {
+    if (axis < 0 || axis > 63) {
+        return NAME_NOT_FOUND;
+    }
+
+    uint64_t axisBit = 1LL << axis;
+    uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
+    if (!(bits & axisBit)) {
+        uint32_t count = __builtin_popcountll(bits);
+        if (count >= MAX_AXES) {
+            tooManyAxes(axis);
+            return NO_MEMORY;
+        }
+        bits |= axisBit;
+        for (uint32_t i = count; i > index; i--) {
+            values[i] = values[i - 1];
+        }
+    }
+    values[index] = value;
+    return OK;
+}
+
+float* PointerCoords::editAxisValue(int32_t axis) {
+    if (axis < 0 || axis > 63) {
+        return NULL;
+    }
+
+    uint64_t axisBit = 1LL << axis;
+    if (!(bits & axisBit)) {
+        return NULL;
+    }
+    uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
+    return &values[index];
+}
+
 #ifdef HAVE_ANDROID_OS
 status_t PointerCoords::readFromParcel(Parcel* parcel) {
-    bits = parcel->readInt32();
+    bits = parcel->readInt64();
 
-    uint32_t count = __builtin_popcount(bits);
+    uint32_t count = __builtin_popcountll(bits);
     if (count > MAX_AXES) {
         return BAD_VALUE;
     }
@@ -266,9 +314,9 @@
 }
 
 status_t PointerCoords::writeToParcel(Parcel* parcel) const {
-    parcel->writeInt32(bits);
+    parcel->writeInt64(bits);
 
-    uint32_t count = __builtin_popcount(bits);
+    uint32_t count = __builtin_popcountll(bits);
     for (uint32_t i = 0; i < count; i++) {
         parcel->writeInt32(values[i]);
     }
diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp
index 56bc26f..2ed0e66 100644
--- a/libs/ui/KeyLayoutMap.cpp
+++ b/libs/ui/KeyLayoutMap.cpp
@@ -82,11 +82,11 @@
     return status;
 }
 
-status_t KeyLayoutMap::map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
+status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
     ssize_t index = mKeys.indexOfKey(scanCode);
     if (index < 0) {
 #if DEBUG_MAPPING
-        LOGD("map: scanCode=%d ~ Failed.", scanCode);
+        LOGD("mapKey: scanCode=%d ~ Failed.", scanCode);
 #endif
         *keyCode = AKEYCODE_UNKNOWN;
         *flags = 0;
@@ -98,12 +98,12 @@
     *flags = k.flags;
 
 #if DEBUG_MAPPING
-    LOGD("map: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
+    LOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
 #endif
     return NO_ERROR;
 }
 
-status_t KeyLayoutMap::findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
+status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
     const size_t N = mKeys.size();
     for (size_t i=0; i<N; i++) {
         if (mKeys.valueAt(i).keyCode == keyCode) {
@@ -113,6 +113,25 @@
     return NO_ERROR;
 }
 
+status_t KeyLayoutMap::mapAxis(int32_t scanCode, int32_t* axis) const {
+    ssize_t index = mAxes.indexOfKey(scanCode);
+    if (index < 0) {
+#if DEBUG_MAPPING
+        LOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
+#endif
+        *axis = -1;
+        return NAME_NOT_FOUND;
+    }
+
+    *axis = mAxes.valueAt(index);
+
+#if DEBUG_MAPPING
+    LOGD("mapAxis: scanCode=%d ~ Result axis=%d.", scanCode, *axis);
+#endif
+    return NO_ERROR;
+}
+
+
 // --- KeyLayoutMap::Parser ---
 
 KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
@@ -137,6 +156,10 @@
                 mTokenizer->skipDelimiters(WHITESPACE);
                 status_t status = parseKey();
                 if (status) return status;
+            } else if (keywordToken == "axis") {
+                mTokenizer->skipDelimiters(WHITESPACE);
+                status_t status = parseAxis();
+                if (status) return status;
             } else {
                 LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
                         keywordToken.string());
@@ -162,12 +185,12 @@
     char* end;
     int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
     if (*end) {
-        LOGE("%s: Expected scan code number, got '%s'.", mTokenizer->getLocation().string(),
+        LOGE("%s: Expected key scan code number, got '%s'.", mTokenizer->getLocation().string(),
                 scanCodeToken.string());
         return BAD_VALUE;
     }
     if (mMap->mKeys.indexOfKey(scanCode) >= 0) {
-        LOGE("%s: Duplicate entry for scan code '%s'.", mTokenizer->getLocation().string(),
+        LOGE("%s: Duplicate entry for key scan code '%s'.", mTokenizer->getLocation().string(),
                 scanCodeToken.string());
         return BAD_VALUE;
     }
@@ -189,12 +212,12 @@
         String8 flagToken = mTokenizer->nextToken(WHITESPACE);
         uint32_t flag = getKeyFlagByLabel(flagToken.string());
         if (!flag) {
-            LOGE("%s: Expected flag label, got '%s'.", mTokenizer->getLocation().string(),
+            LOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
                     flagToken.string());
             return BAD_VALUE;
         }
         if (flags & flag) {
-            LOGE("%s: Duplicate flag '%s'.", mTokenizer->getLocation().string(),
+            LOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
                     flagToken.string());
             return BAD_VALUE;
         }
@@ -211,4 +234,35 @@
     return NO_ERROR;
 }
 
+status_t KeyLayoutMap::Parser::parseAxis() {
+    String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
+    char* end;
+    int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
+    if (*end) {
+        LOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
+                scanCodeToken.string());
+        return BAD_VALUE;
+    }
+    if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
+        LOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
+                scanCodeToken.string());
+        return BAD_VALUE;
+    }
+
+    mTokenizer->skipDelimiters(WHITESPACE);
+    String8 axisToken = mTokenizer->nextToken(WHITESPACE);
+    int32_t axis = getAxisByLabel(axisToken.string());
+    if (axis < 0) {
+        LOGE("%s: Expected axis label, got '%s'.", mTokenizer->getLocation().string(),
+                axisToken.string());
+        return BAD_VALUE;
+    }
+
+#if DEBUG_PARSER
+    LOGD("Parsed axis: scanCode=%d, axis=%d.", scanCode, axis);
+#endif
+    mMap->mAxes.add(scanCode, axis);
+    return NO_ERROR;
+}
+
 };
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
index 6faa600..8b6300a 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/ui/Keyboard.cpp
@@ -217,7 +217,7 @@
     return NAME_NOT_FOUND;
 }
 
-static int lookupLabel(const char* literal, const KeycodeLabel *list) {
+static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) {
     while (list->literal) {
         if (strcmp(literal, list->literal) == 0) {
             return list->value;
@@ -227,12 +227,30 @@
     return list->value;
 }
 
+static const char* lookupLabelByValue(int value, const KeycodeLabel *list) {
+    while (list->literal) {
+        if (list->value == value) {
+            return list->literal;
+        }
+        list++;
+    }
+    return NULL;
+}
+
 int32_t getKeyCodeByLabel(const char* label) {
-    return int32_t(lookupLabel(label, KEYCODES));
+    return int32_t(lookupValueByLabel(label, KEYCODES));
 }
 
 uint32_t getKeyFlagByLabel(const char* label) {
-    return uint32_t(lookupLabel(label, FLAGS));
+    return uint32_t(lookupValueByLabel(label, FLAGS));
+}
+
+int32_t getAxisByLabel(const char* label) {
+    return int32_t(lookupValueByLabel(label, AXES));
+}
+
+const char* getAxisLabel(int32_t axisId) {
+    return lookupLabelByValue(axisId, AXES);
 }
 
 static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/ui/tests/InputEvent_test.cpp
index 7b15c38..b77489e 100644
--- a/libs/ui/tests/InputEvent_test.cpp
+++ b/libs/ui/tests/InputEvent_test.cpp
@@ -18,6 +18,9 @@
 #include <gtest/gtest.h>
 #include <binder/Parcel.h>
 
+#include <math.h>
+#include <SkMatrix.h>
+
 namespace android {
 
 class BaseTest : public testing::Test {
@@ -35,7 +38,7 @@
     PointerCoords coords;
     coords.clear();
 
-    ASSERT_EQ(0U, coords.bits);
+    ASSERT_EQ(0ULL, coords.bits);
 }
 
 TEST_F(PointerCoordsTest, AxisValues) {
@@ -54,7 +57,7 @@
 
     // Set first axis.
     ASSERT_EQ(OK, coords.setAxisValue(1, 5));
-    ASSERT_EQ(0x00000002U, coords.bits);
+    ASSERT_EQ(0x00000002ULL, coords.bits);
     ASSERT_EQ(5, coords.values[0]);
 
     ASSERT_EQ(0, coords.getAxisValue(0))
@@ -64,7 +67,7 @@
 
     // Set an axis with a higher id than all others.  (appending value at the end)
     ASSERT_EQ(OK, coords.setAxisValue(3, 2));
-    ASSERT_EQ(0x0000000aU, coords.bits);
+    ASSERT_EQ(0x0000000aULL, coords.bits);
     ASSERT_EQ(5, coords.values[0]);
     ASSERT_EQ(2, coords.values[1]);
 
@@ -79,7 +82,7 @@
 
     // Set an axis with an id lower than all others.  (prepending value at beginning)
     ASSERT_EQ(OK, coords.setAxisValue(0, 4));
-    ASSERT_EQ(0x0000000bU, coords.bits);
+    ASSERT_EQ(0x0000000bULL, coords.bits);
     ASSERT_EQ(4, coords.values[0]);
     ASSERT_EQ(5, coords.values[1]);
     ASSERT_EQ(2, coords.values[2]);
@@ -104,7 +107,7 @@
 
     // Set an axis with an id between the others.  (inserting value in the middle)
     ASSERT_EQ(OK, coords.setAxisValue(2, 1));
-    ASSERT_EQ(0x0000000fU, coords.bits);
+    ASSERT_EQ(0x0000000fULL, coords.bits);
     ASSERT_EQ(4, coords.values[0]);
     ASSERT_EQ(7, coords.values[1]);
     ASSERT_EQ(1, coords.values[2]);
@@ -121,7 +124,7 @@
 
     // Set an existing axis value in place.
     ASSERT_EQ(OK, coords.setAxisValue(1, 6));
-    ASSERT_EQ(0x0000000fU, coords.bits);
+    ASSERT_EQ(0x0000000fULL, coords.bits);
     ASSERT_EQ(4, coords.values[0]);
     ASSERT_EQ(6, coords.values[1]);
     ASSERT_EQ(1, coords.values[2]);
@@ -140,15 +143,15 @@
     for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) {
         ASSERT_EQ(OK, coords.setAxisValue(axis, axis));
     }
-    ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits));
+    ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
 
     // Try to set one more axis beyond maximum number.
     // Ensure bits are unchanged.
     ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100));
-    ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits));
+    ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
 }
 
-TEST_F(PointerCoordsTest, ReadAndWriteParcel) {
+TEST_F(PointerCoordsTest, Parcel) {
     Parcel parcel;
 
     PointerCoords inCoords;
@@ -160,7 +163,7 @@
     parcel.setDataPosition(0);
     outCoords.readFromParcel(&parcel);
 
-    ASSERT_EQ(0U, outCoords.bits);
+    ASSERT_EQ(0ULL, outCoords.bits);
 
     // Round trip with some values.
     parcel.freeData();
@@ -213,16 +216,22 @@
 // --- MotionEventTest ---
 
 class MotionEventTest : public BaseTest {
+protected:
+    static const nsecs_t ARBITRARY_DOWN_TIME;
+    static const nsecs_t ARBITRARY_EVENT_TIME;
+    static const float X_OFFSET;
+    static const float Y_OFFSET;
+
+    void initializeEventWithHistory(MotionEvent* event);
+    void assertEqualsEventWithHistory(const MotionEvent* event);
 };
 
-TEST_F(MotionEventTest, Properties) {
-    MotionEvent event;
+const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
+const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
+const float MotionEventTest::X_OFFSET = 1.0f;
+const float MotionEventTest::Y_OFFSET = 1.1f;
 
-    // Initialize, add samples and get properties.
-    const nsecs_t ARBITRARY_DOWN_TIME = 1;
-    const nsecs_t ARBITRARY_EVENT_TIME = 2;
-    const float X_OFFSET = 1.0f;
-    const float Y_OFFSET = 1.1f;
+void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
     int32_t pointerIds[] = { 1, 2 };
     PointerCoords pointerCoords[2];
     pointerCoords[0].clear();
@@ -245,7 +254,7 @@
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
-    event.initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
+    event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
             AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
             AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON,
             X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
@@ -270,7 +279,7 @@
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
-    event.addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
+    event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
 
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
@@ -290,128 +299,284 @@
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
-    event.addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+    event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+}
 
-    ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event.getType());
-    ASSERT_EQ(2, event.getDeviceId());
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event.getSource());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event.getAction());
-    ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event.getFlags());
-    ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event.getEdgeFlags());
-    ASSERT_EQ(AMETA_ALT_ON, event.getMetaState());
-    ASSERT_EQ(X_OFFSET, event.getXOffset());
-    ASSERT_EQ(Y_OFFSET, event.getYOffset());
-    ASSERT_EQ(2.0f, event.getXPrecision());
-    ASSERT_EQ(2.1f, event.getYPrecision());
-    ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime());
+void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
+    // Check properties.
+    ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
+    ASSERT_EQ(2, event->getDeviceId());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
+    ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
+    ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
+    ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
+    ASSERT_EQ(X_OFFSET, event->getXOffset());
+    ASSERT_EQ(Y_OFFSET, event->getYOffset());
+    ASSERT_EQ(2.0f, event->getXPrecision());
+    ASSERT_EQ(2.1f, event->getYPrecision());
+    ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime());
 
-    ASSERT_EQ(2U, event.getPointerCount());
-    ASSERT_EQ(1, event.getPointerId(0));
-    ASSERT_EQ(2, event.getPointerId(1));
+    ASSERT_EQ(2U, event->getPointerCount());
+    ASSERT_EQ(1, event->getPointerId(0));
+    ASSERT_EQ(2, event->getPointerId(1));
 
-    ASSERT_EQ(2U, event.getHistorySize());
+    ASSERT_EQ(2U, event->getHistorySize());
 
-    // Get data.
-    ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getHistoricalEventTime(0));
-    ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event.getHistoricalEventTime(1));
-    ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event.getEventTime());
+    // Check data.
+    ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0));
+    ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
+    ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
 
-    ASSERT_EQ(11, event.getHistoricalRawPointerCoords(0, 0)->
+    ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
             getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(21, event.getHistoricalRawPointerCoords(1, 0)->
+    ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
             getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(111, event.getHistoricalRawPointerCoords(0, 1)->
+    ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
             getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(121, event.getHistoricalRawPointerCoords(1, 1)->
+    ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
             getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(211, event.getRawPointerCoords(0)->
+    ASSERT_EQ(211, event->getRawPointerCoords(0)->
             getAxisValue(AMOTION_EVENT_AXIS_Y));
-    ASSERT_EQ(221, event.getRawPointerCoords(1)->
+    ASSERT_EQ(221, event->getRawPointerCoords(1)->
             getAxisValue(AMOTION_EVENT_AXIS_Y));
 
-    ASSERT_EQ(11, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
-    ASSERT_EQ(21, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
-    ASSERT_EQ(111, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
-    ASSERT_EQ(121, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
-    ASSERT_EQ(211, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
-    ASSERT_EQ(221, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+    ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
+    ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
+    ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
+    ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
+    ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
+    ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
 
-    ASSERT_EQ(10, event.getHistoricalRawX(0, 0));
-    ASSERT_EQ(20, event.getHistoricalRawX(1, 0));
-    ASSERT_EQ(110, event.getHistoricalRawX(0, 1));
-    ASSERT_EQ(120, event.getHistoricalRawX(1, 1));
-    ASSERT_EQ(210, event.getRawX(0));
-    ASSERT_EQ(220, event.getRawX(1));
+    ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
+    ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
+    ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
+    ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
+    ASSERT_EQ(210, event->getRawX(0));
+    ASSERT_EQ(220, event->getRawX(1));
 
-    ASSERT_EQ(11, event.getHistoricalRawY(0, 0));
-    ASSERT_EQ(21, event.getHistoricalRawY(1, 0));
-    ASSERT_EQ(111, event.getHistoricalRawY(0, 1));
-    ASSERT_EQ(121, event.getHistoricalRawY(1, 1));
-    ASSERT_EQ(211, event.getRawY(0));
-    ASSERT_EQ(221, event.getRawY(1));
+    ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
+    ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
+    ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
+    ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
+    ASSERT_EQ(211, event->getRawY(0));
+    ASSERT_EQ(221, event->getRawY(1));
 
-    ASSERT_EQ(X_OFFSET + 10, event.getHistoricalX(0, 0));
-    ASSERT_EQ(X_OFFSET + 20, event.getHistoricalX(1, 0));
-    ASSERT_EQ(X_OFFSET + 110, event.getHistoricalX(0, 1));
-    ASSERT_EQ(X_OFFSET + 120, event.getHistoricalX(1, 1));
-    ASSERT_EQ(X_OFFSET + 210, event.getX(0));
-    ASSERT_EQ(X_OFFSET + 220, event.getX(1));
+    ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
+    ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
+    ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
+    ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
+    ASSERT_EQ(X_OFFSET + 210, event->getX(0));
+    ASSERT_EQ(X_OFFSET + 220, event->getX(1));
 
-    ASSERT_EQ(Y_OFFSET + 11, event.getHistoricalY(0, 0));
-    ASSERT_EQ(Y_OFFSET + 21, event.getHistoricalY(1, 0));
-    ASSERT_EQ(Y_OFFSET + 111, event.getHistoricalY(0, 1));
-    ASSERT_EQ(Y_OFFSET + 121, event.getHistoricalY(1, 1));
-    ASSERT_EQ(Y_OFFSET + 211, event.getY(0));
-    ASSERT_EQ(Y_OFFSET + 221, event.getY(1));
+    ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
+    ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
+    ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
+    ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
+    ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
+    ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
 
-    ASSERT_EQ(12, event.getHistoricalPressure(0, 0));
-    ASSERT_EQ(22, event.getHistoricalPressure(1, 0));
-    ASSERT_EQ(112, event.getHistoricalPressure(0, 1));
-    ASSERT_EQ(122, event.getHistoricalPressure(1, 1));
+    ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
+    ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
+    ASSERT_EQ(112, event->getHistoricalPressure(0, 1));
+    ASSERT_EQ(122, event->getHistoricalPressure(1, 1));
+    ASSERT_EQ(212, event->getPressure(0));
+    ASSERT_EQ(222, event->getPressure(1));
+
+    ASSERT_EQ(13, event->getHistoricalSize(0, 0));
+    ASSERT_EQ(23, event->getHistoricalSize(1, 0));
+    ASSERT_EQ(113, event->getHistoricalSize(0, 1));
+    ASSERT_EQ(123, event->getHistoricalSize(1, 1));
+    ASSERT_EQ(213, event->getSize(0));
+    ASSERT_EQ(223, event->getSize(1));
+
+    ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0));
+    ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0));
+    ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1));
+    ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1));
+    ASSERT_EQ(214, event->getTouchMajor(0));
+    ASSERT_EQ(224, event->getTouchMajor(1));
+
+    ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0));
+    ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0));
+    ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1));
+    ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1));
+    ASSERT_EQ(215, event->getTouchMinor(0));
+    ASSERT_EQ(225, event->getTouchMinor(1));
+
+    ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0));
+    ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0));
+    ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1));
+    ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1));
+    ASSERT_EQ(216, event->getToolMajor(0));
+    ASSERT_EQ(226, event->getToolMajor(1));
+
+    ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0));
+    ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0));
+    ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1));
+    ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1));
+    ASSERT_EQ(217, event->getToolMinor(0));
+    ASSERT_EQ(227, event->getToolMinor(1));
+
+    ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
+    ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
+    ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
+    ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
+    ASSERT_EQ(218, event->getOrientation(0));
+    ASSERT_EQ(228, event->getOrientation(1));
+}
+
+TEST_F(MotionEventTest, Properties) {
+    MotionEvent event;
+
+    // Initialize, add samples and check properties.
+    initializeEventWithHistory(&event);
+    ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
+
+    // Set source.
+    event.setSource(AINPUT_SOURCE_JOYSTICK);
+    ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
+
+    // Set action.
+    event.setAction(AMOTION_EVENT_ACTION_CANCEL);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
+
+    // Set meta state.
+    event.setMetaState(AMETA_CTRL_ON);
+    ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState());
+}
+
+TEST_F(MotionEventTest, CopyFrom_KeepHistory) {
+    MotionEvent event;
+    initializeEventWithHistory(&event);
+
+    MotionEvent copy;
+    copy.copyFrom(&event, true /*keepHistory*/);
+
+    ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
+}
+
+TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) {
+    MotionEvent event;
+    initializeEventWithHistory(&event);
+
+    MotionEvent copy;
+    copy.copyFrom(&event, false /*keepHistory*/);
+
+    ASSERT_EQ(event.getPointerCount(), copy.getPointerCount());
+    ASSERT_EQ(0U, copy.getHistorySize());
+
+    ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0));
+    ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1));
+
+    ASSERT_EQ(event.getEventTime(), copy.getEventTime());
+
+    ASSERT_EQ(event.getX(0), copy.getX(0));
+}
+
+TEST_F(MotionEventTest, OffsetLocation) {
+    MotionEvent event;
+    initializeEventWithHistory(&event);
+
+    event.offsetLocation(5.0f, -2.0f);
+
+    ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset());
+    ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset());
+}
+
+TEST_F(MotionEventTest, Scale) {
+    MotionEvent event;
+    initializeEventWithHistory(&event);
+
+    event.scale(2.0f);
+
+    ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
+    ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
+
+    ASSERT_EQ(210 * 2, event.getRawX(0));
+    ASSERT_EQ(211 * 2, event.getRawY(0));
+    ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
+    ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
     ASSERT_EQ(212, event.getPressure(0));
-    ASSERT_EQ(222, event.getPressure(1));
-
-    ASSERT_EQ(13, event.getHistoricalSize(0, 0));
-    ASSERT_EQ(23, event.getHistoricalSize(1, 0));
-    ASSERT_EQ(113, event.getHistoricalSize(0, 1));
-    ASSERT_EQ(123, event.getHistoricalSize(1, 1));
     ASSERT_EQ(213, event.getSize(0));
-    ASSERT_EQ(223, event.getSize(1));
-
-    ASSERT_EQ(14, event.getHistoricalTouchMajor(0, 0));
-    ASSERT_EQ(24, event.getHistoricalTouchMajor(1, 0));
-    ASSERT_EQ(114, event.getHistoricalTouchMajor(0, 1));
-    ASSERT_EQ(124, event.getHistoricalTouchMajor(1, 1));
-    ASSERT_EQ(214, event.getTouchMajor(0));
-    ASSERT_EQ(224, event.getTouchMajor(1));
-
-    ASSERT_EQ(15, event.getHistoricalTouchMinor(0, 0));
-    ASSERT_EQ(25, event.getHistoricalTouchMinor(1, 0));
-    ASSERT_EQ(115, event.getHistoricalTouchMinor(0, 1));
-    ASSERT_EQ(125, event.getHistoricalTouchMinor(1, 1));
-    ASSERT_EQ(215, event.getTouchMinor(0));
-    ASSERT_EQ(225, event.getTouchMinor(1));
-
-    ASSERT_EQ(16, event.getHistoricalToolMajor(0, 0));
-    ASSERT_EQ(26, event.getHistoricalToolMajor(1, 0));
-    ASSERT_EQ(116, event.getHistoricalToolMajor(0, 1));
-    ASSERT_EQ(126, event.getHistoricalToolMajor(1, 1));
-    ASSERT_EQ(216, event.getToolMajor(0));
-    ASSERT_EQ(226, event.getToolMajor(1));
-
-    ASSERT_EQ(17, event.getHistoricalToolMinor(0, 0));
-    ASSERT_EQ(27, event.getHistoricalToolMinor(1, 0));
-    ASSERT_EQ(117, event.getHistoricalToolMinor(0, 1));
-    ASSERT_EQ(127, event.getHistoricalToolMinor(1, 1));
-    ASSERT_EQ(217, event.getToolMinor(0));
-    ASSERT_EQ(227, event.getToolMinor(1));
-
-    ASSERT_EQ(18, event.getHistoricalOrientation(0, 0));
-    ASSERT_EQ(28, event.getHistoricalOrientation(1, 0));
-    ASSERT_EQ(118, event.getHistoricalOrientation(0, 1));
-    ASSERT_EQ(128, event.getHistoricalOrientation(1, 1));
+    ASSERT_EQ(214 * 2, event.getTouchMajor(0));
+    ASSERT_EQ(215 * 2, event.getTouchMinor(0));
+    ASSERT_EQ(216 * 2, event.getToolMajor(0));
+    ASSERT_EQ(217 * 2, event.getToolMinor(0));
     ASSERT_EQ(218, event.getOrientation(0));
-    ASSERT_EQ(228, event.getOrientation(1));
+}
+
+TEST_F(MotionEventTest, Parcel) {
+    Parcel parcel;
+
+    MotionEvent inEvent;
+    initializeEventWithHistory(&inEvent);
+    MotionEvent outEvent;
+
+    // Round trip.
+    inEvent.writeToParcel(&parcel);
+    parcel.setDataPosition(0);
+    outEvent.readFromParcel(&parcel);
+
+    ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
+}
+
+TEST_F(MotionEventTest, Transform) {
+    // Generate some points on a circle.
+    // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle
+    // of ARC * i degrees clockwise relative to the Y axis.
+    // The geometrical representation is irrelevant to the test, it's just easy to generate
+    // and check rotation.  We set the orientation to the same angle.
+    // Coordinate system: down is increasing Y, right is increasing X.
+    const float PI_180 = float(M_PI / 180);
+    const float RADIUS = 10;
+    const float ARC = 36;
+    const float ROTATION = ARC * 2;
+
+    const size_t pointerCount = 11;
+    int pointerIds[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        float angle = float(i * ARC * PI_180);
+        pointerIds[i] = i;
+        pointerCoords[i].clear();
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
+    }
+    MotionEvent event;
+    event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+    float originalRawX = 0 + 3;
+    float originalRawY = -RADIUS + 2;
+
+    // Check original raw X and Y assumption.
+    ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
+    ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
+
+    // Now translate the motion event so the circle's origin is at (0,0).
+    event.offsetLocation(-3, -2);
+
+    // Offsetting the location should preserve the raw X and Y of the first point.
+    ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
+    ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
+
+    // Apply a rotation about the origin by ROTATION degrees clockwise.
+    SkMatrix matrix;
+    matrix.setRotate(ROTATION);
+    event.transform(&matrix);
+
+    // Check the points.
+    for (size_t i = 0; i < pointerCount; i++) {
+        float angle = float((i * ARC + ROTATION) * PI_180);
+        ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001);
+        ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001);
+        ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1);
+    }
+
+    // Applying the transformation should preserve the raw X and Y of the first point.
+    ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
+    ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
 } // namespace android
diff --git a/native/include/android/input.h b/native/include/android/input.h
index ee05020..d531489 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -322,6 +322,7 @@
 
 /*
  * Constants that identify each individual axis of a motion event.
+ * Refer to the documentation on the MotionEvent class for descriptions of each axis.
  */
 enum {
     AMOTION_EVENT_AXIS_X = 0,
@@ -333,6 +334,35 @@
     AMOTION_EVENT_AXIS_TOOL_MAJOR = 6,
     AMOTION_EVENT_AXIS_TOOL_MINOR = 7,
     AMOTION_EVENT_AXIS_ORIENTATION = 8,
+    AMOTION_EVENT_AXIS_VSCROLL = 9,
+    AMOTION_EVENT_AXIS_HSCROLL = 10,
+    AMOTION_EVENT_AXIS_Z = 11,
+    AMOTION_EVENT_AXIS_RX = 12,
+    AMOTION_EVENT_AXIS_RY = 13,
+    AMOTION_EVENT_AXIS_RZ = 14,
+    AMOTION_EVENT_AXIS_HAT_X = 15,
+    AMOTION_EVENT_AXIS_HAT_Y = 16,
+    AMOTION_EVENT_AXIS_LTRIGGER = 17,
+    AMOTION_EVENT_AXIS_RTRIGGER = 18,
+    AMOTION_EVENT_AXIS_GENERIC_1 = 32,
+    AMOTION_EVENT_AXIS_GENERIC_2 = 33,
+    AMOTION_EVENT_AXIS_GENERIC_3 = 34,
+    AMOTION_EVENT_AXIS_GENERIC_4 = 35,
+    AMOTION_EVENT_AXIS_GENERIC_5 = 36,
+    AMOTION_EVENT_AXIS_GENERIC_6 = 37,
+    AMOTION_EVENT_AXIS_GENERIC_7 = 38,
+    AMOTION_EVENT_AXIS_GENERIC_8 = 39,
+    AMOTION_EVENT_AXIS_GENERIC_9 = 40,
+    AMOTION_EVENT_AXIS_GENERIC_10 = 41,
+    AMOTION_EVENT_AXIS_GENERIC_11 = 42,
+    AMOTION_EVENT_AXIS_GENERIC_12 = 43,
+    AMOTION_EVENT_AXIS_GENERIC_13 = 44,
+    AMOTION_EVENT_AXIS_GENERIC_14 = 45,
+    AMOTION_EVENT_AXIS_GENERIC_15 = 46,
+    AMOTION_EVENT_AXIS_GENERIC_16 = 47,
+
+    // NOTE: If you add a new axis here you must also add it to several other files.
+    //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
 };
 
 /*
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 066daa8..c921425 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -117,6 +117,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
+import android.view.KeyCharacterMap.FallbackAction;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.media.IAudioService;
@@ -1453,7 +1454,7 @@
             }
 
             // Check for fallback actions.
-            if (kcm.getFallbackAction(keyCode, metaState, mFallbackAction)) {
+            if (getFallbackAction(kcm, keyCode, metaState, mFallbackAction)) {
                 if (DEBUG_FALLBACK) {
                     Slog.d(TAG, "Fallback: keyCode=" + mFallbackAction.keyCode
                             + " metaState=" + Integer.toHexString(mFallbackAction.metaState));
@@ -1485,6 +1486,16 @@
         return null;
     }
 
+    private boolean getFallbackAction(KeyCharacterMap kcm, int keyCode, int metaState,
+            FallbackAction outFallbackAction) {
+        // Consult the key character map for specific fallback actions.
+        // For example, map NUMPAD_1 to MOVE_HOME when NUMLOCK is not pressed.
+        if (kcm.getFallbackAction(keyCode, metaState, outFallbackAction)) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * A home key -> launch home action was detected.  Take the appropriate action
      * given the situation with the keyguard.
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 38a896f..f79d106 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -127,9 +127,7 @@
         mOpened(false), mNeedToSendFinishedDeviceScan(false),
         mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
-#ifdef EV_SW
     memset(mSwitches, 0, sizeof(mSwitches));
-#endif
 }
 
 EventHub::~EventHub(void) {
@@ -229,7 +227,7 @@
     }
 
     Vector<int32_t> scanCodes;
-    device->keyMap.keyLayoutMap->findScanCodes(keyCode, &scanCodes);
+    device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
 
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
@@ -253,7 +251,6 @@
 }
 
 int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
-#ifdef EV_SW
     if (sw >= 0 && sw <= SW_MAX) {
         AutoMutex _l(mLock);
 
@@ -262,7 +259,6 @@
             return getSwitchStateLocked(device, sw);
         }
     }
-#endif
     return AKEY_STATE_UNKNOWN;
 }
 
@@ -297,7 +293,8 @@
     for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
         scanCodes.clear();
 
-        status_t err = device->keyMap.keyLayoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes);
+        status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(
+                keyCodes[codeIndex], &scanCodes);
         if (! err) {
             // check the possible scan codes identified by the layout map against the
             // map of codes actually emitted by the driver
@@ -312,14 +309,14 @@
     return true;
 }
 
-status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
+status_t EventHub::mapKey(int32_t deviceId, int scancode,
         int32_t* outKeycode, uint32_t* outFlags) const
 {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     
     if (device && device->keyMap.haveKeyLayout()) {
-        status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags);
+        status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags);
         if (err == NO_ERROR) {
             return NO_ERROR;
         }
@@ -329,7 +326,7 @@
         device = getDeviceLocked(mBuiltInKeyboardId);
         
         if (device && device->keyMap.haveKeyLayout()) {
-            status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags);
+            status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags);
             if (err == NO_ERROR) {
                 return NO_ERROR;
             }
@@ -341,6 +338,34 @@
     return NAME_NOT_FOUND;
 }
 
+status_t EventHub::mapAxis(int32_t deviceId, int scancode,
+        int32_t* outAxis) const
+{
+    AutoMutex _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+
+    if (device && device->keyMap.haveKeyLayout()) {
+        status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis);
+        if (err == NO_ERROR) {
+            return NO_ERROR;
+        }
+    }
+
+    if (mBuiltInKeyboardId != -1) {
+        device = getDeviceLocked(mBuiltInKeyboardId);
+
+        if (device && device->keyMap.haveKeyLayout()) {
+            status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis);
+            if (err == NO_ERROR) {
+                return NO_ERROR;
+            }
+        }
+    }
+
+    *outAxis = -1;
+    return NAME_NOT_FOUND;
+}
+
 void EventHub::addExcludedDevice(const char* deviceName)
 {
     AutoMutex _l(mLock);
@@ -488,7 +513,7 @@
                 if (iev.type == EV_KEY) {
                     outEvent->keyCode = AKEYCODE_UNKNOWN;
                     if (device->keyMap.haveKeyLayout()) {
-                        status_t err = device->keyMap.keyLayoutMap->map(iev.code,
+                        status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
                                 &outEvent->keyCode, &outEvent->flags);
                         LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
                                 iev.code, outEvent->keyCode, outEvent->flags, err);
@@ -731,86 +756,79 @@
     loadConfiguration(device);
 
     // Figure out the kinds of events the device reports.
-    
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
+    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask);
 
-    LOGV("Getting keys...");
-    if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
-        //LOGI("MAP\n");
-        //for (int i = 0; i < sizeof(key_bitmask); i++) {
-        //    LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
-        //}
+    uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
+    memset(abs_bitmask, 0, sizeof(abs_bitmask));
+    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
 
-        // See if this is a keyboard.  Ignore everything in the button range except for
-        // joystick and gamepad buttons which are also considered keyboards.
-        if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
-                || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK),
-                        sizeof_bit_array(BTN_DIGI))
-                || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
-                        sizeof_bit_array(KEY_MAX + 1))) {
-            device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+    uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
+    memset(rel_bitmask, 0, sizeof(rel_bitmask));
+    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask);
 
-            device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
-            if (device->keyBitmask != NULL) {
-                memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
-            } else {
-                delete device;
-                LOGE("out of memory allocating key bitmask");
-                return -1;
-            }
+    uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
+    memset(sw_bitmask, 0, sizeof(sw_bitmask));
+    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask);
+
+    // See if this is a keyboard.  Ignore everything in the button range except for
+    // joystick and gamepad buttons which are handled like keyboards for the most part.
+    bool haveKeyboardKeys = containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
+            || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
+                    sizeof_bit_array(KEY_MAX + 1));
+    bool haveGamepadButtons =containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK),
+                sizeof_bit_array(BTN_DIGI));
+    if (haveKeyboardKeys || haveGamepadButtons) {
+        device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+        device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
+        if (device->keyBitmask != NULL) {
+            memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
+        } else {
+            delete device;
+            LOGE("out of memory allocating key bitmask");
+            return -1;
         }
     }
-    
+
     // See if this is a cursor device such as a trackball or mouse.
-    if (test_bit(BTN_MOUSE, key_bitmask)) {
-        uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
-        memset(rel_bitmask, 0, sizeof(rel_bitmask));
-        LOGV("Getting relative controllers...");
-        if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {
-            if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
-                device->classes |= INPUT_DEVICE_CLASS_CURSOR;
-            }
-        }
+    if (test_bit(BTN_MOUSE, key_bitmask)
+            && test_bit(REL_X, rel_bitmask)
+            && test_bit(REL_Y, rel_bitmask)) {
+        device->classes |= INPUT_DEVICE_CLASS_CURSOR;
     }
 
     // See if this is a touch pad.
-    uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
-    memset(abs_bitmask, 0, sizeof(abs_bitmask));
-    LOGV("Getting absolute controllers...");
-    if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {
-        // Is this a new modern multi-touch driver?
-        if (test_bit(ABS_MT_POSITION_X, abs_bitmask)
-                && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
+    // Is this a new modern multi-touch driver?
+    if (test_bit(ABS_MT_POSITION_X, abs_bitmask)
+            && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
+        // Some joysticks such as the PS3 controller report axes that conflict
+        // with the ABS_MT range.  Try to confirm that the device really is
+        // a touch screen.
+        if (test_bit(BTN_TOUCH, key_bitmask) || !haveGamepadButtons) {
             device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
-
-        // Is this an old style single-touch driver?
-        } else if (test_bit(BTN_TOUCH, key_bitmask)
-                && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
-            device->classes |= INPUT_DEVICE_CLASS_TOUCH;
         }
+    // Is this an old style single-touch driver?
+    } else if (test_bit(BTN_TOUCH, key_bitmask)
+            && test_bit(ABS_X, abs_bitmask)
+            && test_bit(ABS_Y, abs_bitmask)) {
+        device->classes |= INPUT_DEVICE_CLASS_TOUCH;
     }
 
-#ifdef EV_SW
     // figure out the switches this device reports
-    uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
-    memset(sw_bitmask, 0, sizeof(sw_bitmask));
-    bool hasSwitches = false;
-    if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
-        for (int i=0; i<EV_SW; i++) {
-            //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
-            if (test_bit(i, sw_bitmask)) {
-                hasSwitches = true;
-                if (mSwitches[i] == 0) {
-                    mSwitches[i] = device->id;
-                }
+    bool haveSwitches = false;
+    for (int i=0; i<EV_SW; i++) {
+        //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
+        if (test_bit(i, sw_bitmask)) {
+            haveSwitches = true;
+            if (mSwitches[i] == 0) {
+                mSwitches[i] = device->id;
             }
         }
     }
-    if (hasSwitches) {
+    if (haveSwitches) {
         device->classes |= INPUT_DEVICE_CLASS_SWITCH;
     }
-#endif
 
     if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
         // Load the virtual keys for the touch screen, if any.
@@ -862,12 +880,10 @@
 
     // See if this device is a joystick.
     // Ignore touchscreens because they use the same absolute axes for other purposes.
+    // Assumes that joysticks always have buttons and the keymap has been loaded.
     if (device->classes & INPUT_DEVICE_CLASS_GAMEPAD
             && !(device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
-        if (test_bit(ABS_X, abs_bitmask)
-                || test_bit(ABS_Y, abs_bitmask)
-                || test_bit(ABS_HAT0X, abs_bitmask)
-                || test_bit(ABS_HAT0Y, abs_bitmask)) {
+        if (containsNonZeroByte(abs_bitmask, 0, sizeof_bit_array(ABS_MAX + 1))) {
             device->classes |= INPUT_DEVICE_CLASS_JOYSTICK;
         }
     }
@@ -950,7 +966,7 @@
     }
     
     Vector<int32_t> scanCodes;
-    device->keyMap.keyLayoutMap->findScanCodes(keycode, &scanCodes);
+    device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
     const size_t N = scanCodes.size();
     for (size_t i=0; i<N && i<=KEY_MAX; i++) {
         int32_t sc = scanCodes.itemAt(i);
@@ -972,13 +988,11 @@
                  device->path.string(), device->identifier.name.string(), device->id,
                  device->fd, device->classes);
 
-#ifdef EV_SW
             for (int j=0; j<EV_SW; j++) {
                 if (mSwitches[j] == device->id) {
                     mSwitches[j] = 0;
                 }
             }
-#endif
 
             if (device->id == mBuiltInKeyboardId) {
                 LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 52993f7..35712f5 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -167,9 +167,12 @@
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const = 0;
 
-    virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
+    virtual status_t mapKey(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const = 0;
 
+    virtual status_t mapAxis(int32_t deviceId, int scancode,
+            int32_t* outAxis) const = 0;
+
     // exclude a particular device from opening
     // this can be used to ignore input devices for sensors
     virtual void addExcludedDevice(const char* deviceName) = 0;
@@ -221,9 +224,12 @@
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const;
 
-    virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
+    virtual status_t mapKey(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const;
 
+    virtual status_t mapAxis(int32_t deviceId, int scancode,
+            int32_t* outAxis) const;
+
     virtual void addExcludedDevice(const char* deviceName);
 
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
@@ -314,9 +320,7 @@
     List<String8> mExcludedDevices;
 
     // device ids that report particular switches.
-#ifdef EV_SW
     int32_t mSwitches[SW_MAX + 1];
-#endif
 
     static const int INPUT_BUFFER_SIZE = 64;
     struct input_event mInputBufferData[INPUT_BUFFER_SIZE];
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 8f38cb2..47cfa05 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -584,15 +584,15 @@
     dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
     if (!deviceInfo.getMotionRanges().isEmpty()) {
         dump.append(INDENT2 "Motion Ranges:\n");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor");
-        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_X, "X");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_Y, "Y");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_PRESSURE, "Pressure");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_SIZE, "Size");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_TOUCH_MAJOR, "TouchMajor");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_TOUCH_MINOR, "TouchMinor");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_TOOL_MAJOR, "ToolMajor");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_TOOL_MINOR, "ToolMinor");
+        dumpMotionRange(dump, deviceInfo, AMOTION_EVENT_AXIS_ORIENTATION, "Orientation");
     }
 
     size_t numMappers = mMappers.size();
@@ -1061,14 +1061,21 @@
     if (mParameters.mode == Parameters::MODE_POINTER) {
         float minX, minY, maxX, maxY;
         if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-            info->addMotionRange(AINPUT_MOTION_RANGE_X, minX, maxX, 0.0f, 0.0f);
-            info->addMotionRange(AINPUT_MOTION_RANGE_Y, minY, maxY, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_X, minX, maxX, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_Y, minY, maxY, 0.0f, 0.0f);
         }
     } else {
-        info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale);
-        info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
+        info->addMotionRange(AMOTION_EVENT_AXIS_X, -1.0f, 1.0f, 0.0f, mXScale);
+        info->addMotionRange(AMOTION_EVENT_AXIS_Y, -1.0f, 1.0f, 0.0f, mYScale);
     }
-    info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, 0.0f, 1.0f, 0.0f, 0.0f);
+    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, 0.0f, 1.0f, 0.0f, 0.0f);
+
+    if (mHaveVWheel) {
+        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, -1.0f, 1.0f, 0.0f, 0.0f);
+    }
+    if (mHaveHWheel) {
+        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, -1.0f, 1.0f, 0.0f, 0.0f);
+    }
 }
 
 void CursorInputMapper::dump(String8& dump) {
@@ -1076,8 +1083,14 @@
         AutoMutex _l(mLock);
         dump.append(INDENT2 "Cursor Input Mapper:\n");
         dumpParameters(dump);
+        dump.appendFormat(INDENT3 "XScale: %0.3f\n", mXScale);
+        dump.appendFormat(INDENT3 "YScale: %0.3f\n", mYScale);
         dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
         dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+        dump.appendFormat(INDENT3 "HaveVWheel: %s\n", toString(mHaveVWheel));
+        dump.appendFormat(INDENT3 "HaveHWheel: %s\n", toString(mHaveHWheel));
+        dump.appendFormat(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
+        dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
         dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
         dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
     } // release lock
@@ -1107,6 +1120,9 @@
         mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
         break;
     }
+
+    mVWheelScale = 1.0f;
+    mHWheelScale = 1.0f;
 }
 
 void CursorInputMapper::configureParameters() {
@@ -1200,6 +1216,14 @@
             mAccumulator.fields |= Accumulator::FIELD_REL_Y;
             mAccumulator.relY = rawEvent->value;
             break;
+        case REL_WHEEL:
+            mAccumulator.fields |= Accumulator::FIELD_REL_WHEEL;
+            mAccumulator.relWheel = rawEvent->value;
+            break;
+        case REL_HWHEEL:
+            mAccumulator.fields |= Accumulator::FIELD_REL_HWHEEL;
+            mAccumulator.relHWheel = rawEvent->value;
+            break;
         }
         break;
 
@@ -1302,6 +1326,13 @@
         }
 
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f);
+
+        if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) {
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, mAccumulator.relWheel);
+        }
+        if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, mAccumulator.relHWheel);
+        }
     } // release lock
 
     int32_t metaState = mContext->getGlobalMetaState();
@@ -1350,35 +1381,35 @@
         // noticed immediately.
         configureSurfaceLocked();
 
-        info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x);
-        info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y);
+        info->addMotionRange(AMOTION_EVENT_AXIS_X, mLocked.orientedRanges.x);
+        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mLocked.orientedRanges.y);
 
         if (mLocked.orientedRanges.havePressure) {
-            info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE,
+            info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
                     mLocked.orientedRanges.pressure);
         }
 
         if (mLocked.orientedRanges.haveSize) {
-            info->addMotionRange(AINPUT_MOTION_RANGE_SIZE,
+            info->addMotionRange(AMOTION_EVENT_AXIS_SIZE,
                     mLocked.orientedRanges.size);
         }
 
         if (mLocked.orientedRanges.haveTouchSize) {
-            info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR,
+            info->addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
                     mLocked.orientedRanges.touchMajor);
-            info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR,
+            info->addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR,
                     mLocked.orientedRanges.touchMinor);
         }
 
         if (mLocked.orientedRanges.haveToolSize) {
-            info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR,
+            info->addMotionRange(AMOTION_EVENT_AXIS_TOOL_MAJOR,
                     mLocked.orientedRanges.toolMajor);
-            info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR,
+            info->addMotionRange(AMOTION_EVENT_AXIS_TOOL_MINOR,
                     mLocked.orientedRanges.toolMinor);
         }
 
         if (mLocked.orientedRanges.haveOrientation) {
-            info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION,
+            info->addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION,
                     mLocked.orientedRanges.orientation);
         }
     } // release lock
@@ -1803,7 +1834,7 @@
         virtualKey.scanCode = virtualKeyDefinition.scanCode;
         int32_t keyCode;
         uint32_t flags;
-        if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode,
+        if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode,
                 & keyCode, & flags)) {
             LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
                     virtualKey.scanCode);
@@ -3715,7 +3746,6 @@
 
 JoystickInputMapper::JoystickInputMapper(InputDevice* device) :
         InputMapper(device) {
-    initialize();
 }
 
 JoystickInputMapper::~JoystickInputMapper() {
@@ -3728,176 +3758,219 @@
 void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    if (mAxes.x.valid) {
-        info->addMotionRange(AINPUT_MOTION_RANGE_X,
-                mAxes.x.min, mAxes.x.max, mAxes.x.flat, mAxes.x.fuzz);
-    }
-    if (mAxes.y.valid) {
-        info->addMotionRange(AINPUT_MOTION_RANGE_Y,
-                mAxes.y.min, mAxes.y.max, mAxes.y.flat, mAxes.y.fuzz);
+    for (size_t i = 0; i < mAxes.size(); i++) {
+        const Axis& axis = mAxes.valueAt(i);
+        info->addMotionRange(axis.axis, axis.min, axis.max, axis.flat, axis.fuzz);
     }
 }
 
 void JoystickInputMapper::dump(String8& dump) {
     dump.append(INDENT2 "Joystick Input Mapper:\n");
 
-    dump.append(INDENT3 "Raw Axes:\n");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y");
-
-    dump.append(INDENT3 "Normalized Axes:\n");
-    dumpNormalizedAxis(dump, mAxes.x, "X");
-    dumpNormalizedAxis(dump, mAxes.y, "Y");
-    dumpNormalizedAxis(dump, mAxes.hat0X, "Hat0X");
-    dumpNormalizedAxis(dump, mAxes.hat0Y, "Hat0Y");
-}
-
-void JoystickInputMapper::dumpNormalizedAxis(String8& dump,
-        const NormalizedAxis& axis, const char* name) {
-    if (axis.valid) {
+    dump.append(INDENT3 "Axes:\n");
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        const Axis& axis = mAxes.valueAt(i);
+        const char* label = getAxisLabel(axis.axis);
+        char name[32];
+        if (label) {
+            strncpy(name, label, sizeof(name));
+            name[sizeof(name) - 1] = '\0';
+        } else {
+            snprintf(name, sizeof(name), "%d", axis.axis);
+        }
         dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, "
-                "scale=%0.3f, center=%0.3f, precision=%0.3f, value=%0.3f\n",
+                "scale=%0.3f, offset=%0.3f\n",
                 name, axis.min, axis.max, axis.flat, axis.fuzz,
-                axis.scale, axis.center, axis.precision, axis.value);
-    } else {
-        dump.appendFormat(INDENT4 "%s: unknown range\n", name);
+                axis.scale, axis.offset);
+        dump.appendFormat(INDENT4 "  rawAxis=%d, rawMin=%d, rawMax=%d, rawFlat=%d, rawFuzz=%d\n",
+                mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+                axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz);
     }
 }
 
 void JoystickInputMapper::configure() {
     InputMapper::configure();
 
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0X, & mRawAxes.hat0X);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0Y, & mRawAxes.hat0Y);
+    // Collect all axes.
+    for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
+        RawAbsoluteAxisInfo rawAxisInfo;
+        getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo);
+        if (rawAxisInfo.valid) {
+            int32_t axisId;
+            bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisId);
+            if (!explicitlyMapped) {
+                // Axis is not explicitly mapped, will choose a generic axis later.
+                axisId = -1;
+            }
 
-    mAxes.x.configure(mRawAxes.x);
-    mAxes.y.configure(mRawAxes.y);
-    mAxes.hat0X.configure(mRawAxes.hat0X);
-    mAxes.hat0Y.configure(mRawAxes.hat0Y);
+            Axis axis;
+            if (isCenteredAxis(axisId)) {
+                float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+                float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+                axis.initialize(rawAxisInfo, axisId, explicitlyMapped,
+                        scale, offset, -1.0f, 1.0f,
+                        rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale);
+            } else {
+                float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+                axis.initialize(rawAxisInfo, axisId, explicitlyMapped,
+                        scale, 0.0f, 0.0f, 1.0f,
+                        rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale);
+            }
+
+            // To eliminate noise while the joystick is at rest, filter out small variations
+            // in axis values up front.
+            axis.filter = axis.flat * 0.25f;
+
+            mAxes.add(abs, axis);
+        }
+    }
+
+    // If there are too many axes, start dropping them.
+    // Prefer to keep explicitly mapped axes.
+    if (mAxes.size() > PointerCoords::MAX_AXES) {
+        LOGI("Joystick '%s' has %d axes but the framework only supports a maximum of %d.",
+                getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES);
+        pruneAxes(true);
+        pruneAxes(false);
+    }
+
+    // Assign generic axis ids to remaining axes.
+    int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        Axis& axis = mAxes.editValueAt(i);
+        if (axis.axis < 0) {
+            while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
+                    && haveAxis(nextGenericAxisId)) {
+                nextGenericAxisId += 1;
+            }
+
+            if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
+                axis.axis = nextGenericAxisId;
+                nextGenericAxisId += 1;
+            } else {
+                LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
+                        "have already been assigned to other axes.",
+                        getDeviceName().string(), mAxes.keyAt(i));
+                mAxes.removeItemsAt(i--);
+                numAxes -= 1;
+            }
+        }
+    }
 }
 
-void JoystickInputMapper::initialize() {
-    mAccumulator.clear();
+bool JoystickInputMapper::haveAxis(int32_t axis) {
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        if (mAxes.valueAt(i).axis == axis) {
+            return true;
+        }
+    }
+    return false;
+}
 
-    mAxes.x.resetState();
-    mAxes.y.resetState();
-    mAxes.hat0X.resetState();
-    mAxes.hat0Y.resetState();
+void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
+    size_t i = mAxes.size();
+    while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
+        if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
+            continue;
+        }
+        LOGI("Discarding joystick '%s' axis %d because there are too many axes.",
+                getDeviceName().string(), mAxes.keyAt(i));
+        mAxes.removeItemsAt(i);
+    }
+}
+
+bool JoystickInputMapper::isCenteredAxis(int32_t axis) {
+    switch (axis) {
+    case AMOTION_EVENT_AXIS_X:
+    case AMOTION_EVENT_AXIS_Y:
+    case AMOTION_EVENT_AXIS_Z:
+    case AMOTION_EVENT_AXIS_RX:
+    case AMOTION_EVENT_AXIS_RY:
+    case AMOTION_EVENT_AXIS_RZ:
+    case AMOTION_EVENT_AXIS_HAT_X:
+    case AMOTION_EVENT_AXIS_HAT_Y:
+    case AMOTION_EVENT_AXIS_ORIENTATION:
+        return true;
+    default:
+        return false;
+    }
 }
 
 void JoystickInputMapper::reset() {
     // Recenter all axes.
     nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
-    mAccumulator.clear();
-    mAccumulator.fields = Accumulator::FIELD_ALL;
-    sync(when);
 
-    // Reinitialize state.
-    initialize();
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        Axis& axis = mAxes.editValueAt(i);
+        axis.newValue = 0;
+    }
+
+    sync(when, true /*force*/);
 
     InputMapper::reset();
 }
 
 void JoystickInputMapper::process(const RawEvent* rawEvent) {
     switch (rawEvent->type) {
-    case EV_ABS:
-        switch (rawEvent->scanCode) {
-        case ABS_X:
-            mAccumulator.fields |= Accumulator::FIELD_ABS_X;
-            mAccumulator.absX = rawEvent->value;
-            break;
-        case ABS_Y:
-            mAccumulator.fields |= Accumulator::FIELD_ABS_Y;
-            mAccumulator.absY = rawEvent->value;
-            break;
-        case ABS_HAT0X:
-            mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0X;
-            mAccumulator.absHat0X = rawEvent->value;
-            break;
-        case ABS_HAT0Y:
-            mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0Y;
-            mAccumulator.absHat0Y = rawEvent->value;
-            break;
+    case EV_ABS: {
+        ssize_t index = mAxes.indexOfKey(rawEvent->scanCode);
+        if (index >= 0) {
+            Axis& axis = mAxes.editValueAt(index);
+            float newValue = rawEvent->value * axis.scale + axis.offset;
+            if (newValue != axis.newValue) {
+                axis.newValue = newValue;
+            }
         }
         break;
+    }
 
     case EV_SYN:
         switch (rawEvent->scanCode) {
         case SYN_REPORT:
-            sync(rawEvent->when);
+            sync(rawEvent->when, false /*force*/);
             break;
         }
         break;
     }
 }
 
-void JoystickInputMapper::sync(nsecs_t when) {
-    uint32_t fields = mAccumulator.fields;
-    if (fields == 0) {
-        return; // no new state changes, so nothing to do
+void JoystickInputMapper::sync(nsecs_t when, bool force) {
+    if (!force && !haveAxesChangedSignificantly()) {
+        return;
     }
 
     int32_t metaState = mContext->getGlobalMetaState();
 
-    bool motionAxisChanged = false;
-    if (fields & Accumulator::FIELD_ABS_X) {
-        if (mAxes.x.updateValue(mAccumulator.absX)) {
-            motionAxisChanged = true;
-        }
+    PointerCoords pointerCoords;
+    pointerCoords.clear();
+
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        Axis& axis = mAxes.editValueAt(i);
+        pointerCoords.setAxisValue(axis.axis, axis.newValue);
+        axis.oldValue = axis.newValue;
     }
 
-    if (fields & Accumulator::FIELD_ABS_Y) {
-        if (mAxes.y.updateValue(mAccumulator.absY)) {
-            motionAxisChanged = true;
-        }
-    }
-
-    if (motionAxisChanged) {
-        PointerCoords pointerCoords;
-        pointerCoords.clear();
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, mAxes.x.value);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, mAxes.y.value);
-
-        int32_t pointerId = 0;
-        getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
-                AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                1, &pointerId, &pointerCoords, mAxes.x.precision, mAxes.y.precision, 0);
-    }
-
-    if (fields & Accumulator::FIELD_ABS_HAT0X) {
-        if (mAxes.hat0X.updateValueAndDirection(mAccumulator.absHat0X)) {
-            notifyDirectionalAxis(mAxes.hat0X, when, metaState,
-                    AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT);
-        }
-    }
-
-    if (fields & Accumulator::FIELD_ABS_HAT0Y) {
-        if (mAxes.hat0Y.updateValueAndDirection(mAccumulator.absHat0Y)) {
-            notifyDirectionalAxis(mAxes.hat0Y, when, metaState,
-                    AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN);
-        }
-    }
-
-    mAccumulator.clear();
+    int32_t pointerId = 0;
+    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+            AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+            1, &pointerId, &pointerCoords, 0, 0, 0);
 }
 
-void JoystickInputMapper::notifyDirectionalAxis(DirectionalAxis& axis,
-        nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode) {
-    if (axis.lastKeyCode) {
-        getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
-                AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM,
-                axis.lastKeyCode, 0, metaState, when);
-        axis.lastKeyCode = 0;
+bool JoystickInputMapper::haveAxesChangedSignificantly() {
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        const Axis& axis = mAxes.valueAt(i);
+        if (axis.newValue != axis.oldValue
+                && fabs(axis.newValue - axis.oldValue) > axis.filter) {
+            return true;
+        }
     }
-    if (axis.direction) {
-        axis.lastKeyCode = axis.direction > 0 ? highKeyCode : lowKeyCode;
-        getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
-                AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM,
-                axis.lastKeyCode, 0, metaState, when);
-    }
+    return false;
 }
 
-
 } // namespace android
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 27cb8e1..cf41535 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -481,7 +481,9 @@
         enum {
             FIELD_BTN_MOUSE = 1,
             FIELD_REL_X = 2,
-            FIELD_REL_Y = 4
+            FIELD_REL_Y = 4,
+            FIELD_REL_WHEEL = 8,
+            FIELD_REL_HWHEEL = 16,
         };
 
         uint32_t fields;
@@ -489,6 +491,8 @@
         bool btnMouse;
         int32_t relX;
         int32_t relY;
+        int32_t relWheel;
+        int32_t relHWheel;
 
         inline void clear() {
             fields = 0;
@@ -500,6 +504,12 @@
     float mYScale;
     float mXPrecision;
     float mYPrecision;
+
+    bool mHaveVWheel;
+    bool mHaveHWheel;
+    float mVWheelScale;
+    float mHWheelScale;
+
     sp<PointerControllerInterface> mPointerController;
 
     struct LockedState {
@@ -985,123 +995,53 @@
     virtual void process(const RawEvent* rawEvent);
 
 private:
-    struct RawAxes {
-        RawAbsoluteAxisInfo x;
-        RawAbsoluteAxisInfo y;
-        RawAbsoluteAxisInfo hat0X;
-        RawAbsoluteAxisInfo hat0Y;
-    } mRawAxes;
+    struct Axis {
+        RawAbsoluteAxisInfo rawAxisInfo;
 
-    struct NormalizedAxis {
-        bool valid;
+        int32_t axis;  // axis id
+        bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
 
-        static const float min = -1.0f;
-        static const float max = -1.0f;
+        float scale;   // scale factor from raw to normalized values
+        float offset;  // offset to add after scaling for normalization
 
-        float scale;      // scale factor
-        float center;     // center offset after scaling
-        float precision;  // precision
-        float flat;       // size of flat region
-        float fuzz;       // error tolerance
+        float min;     // normalized inclusive minimum
+        float max;     // normalized inclusive maximum
+        float flat;    // normalized flat region size
+        float fuzz;    // normalized error tolerance
 
-        float value;      // most recent value
+        float oldValue; // previous value
+        float newValue; // most recent value
 
-        NormalizedAxis() : valid(false), scale(0), center(0), precision(0),
-                flat(0), fuzz(0), value(0) {
-        }
+        float filter;  // filter out small variations of this size
 
-        void configure(const RawAbsoluteAxisInfo& rawAxis) {
-            if (rawAxis.valid && rawAxis.getRange() != 0) {
-                valid = true;
-                scale = 2.0f / rawAxis.getRange();
-                precision = rawAxis.getRange();
-                flat = rawAxis.flat * scale;
-                fuzz = rawAxis.fuzz * scale;
-                center = float(rawAxis.minValue + rawAxis.maxValue) / rawAxis.getRange();
-            }
-        }
-
-        void resetState() {
-            value = 0;
-        }
-
-        bool updateValue(int32_t rawValue) {
-            float newValue = rawValue * scale - center;
-            if (value == newValue) {
-                return false;
-            }
-            value = newValue;
-            return true;
+        void initialize(const RawAbsoluteAxisInfo& rawAxisInfo,
+                int32_t axis, bool explicitlyMapped, float scale, float offset,
+                float min, float max, float flat, float fuzz) {
+            this->rawAxisInfo = rawAxisInfo;
+            this->axis = axis;
+            this->explicitlyMapped = explicitlyMapped;
+            this->scale = scale;
+            this->offset = offset;
+            this->min = min;
+            this->max = max;
+            this->flat = flat;
+            this->fuzz = fuzz;
+            this->filter = 0;
+            this->oldValue = 0;
+            this->newValue = 0;
         }
     };
 
-    struct DirectionalAxis : NormalizedAxis {
-        int32_t direction; // most recent direction vector: value is one of -1, 0, 1.
+    // Axes indexed by raw ABS_* axis index.
+    KeyedVector<int32_t, Axis> mAxes;
 
-        int32_t lastKeyCode;  // most recent key code produced
+    void sync(nsecs_t when, bool force);
 
-        DirectionalAxis() : lastKeyCode(0) {
-        }
+    bool haveAxis(int32_t axis);
+    void pruneAxes(bool ignoreExplicitlyMappedAxes);
+    bool haveAxesChangedSignificantly();
 
-        void resetState() {
-            NormalizedAxis::resetState();
-            direction = 0;
-            lastKeyCode = 0;
-        }
-
-        bool updateValueAndDirection(int32_t rawValue) {
-            if (!updateValue(rawValue)) {
-                return false;
-            }
-            if (value > flat) {
-                direction = 1;
-            } else if (value < -flat) {
-                direction = -1;
-            } else {
-                direction = 0;
-            }
-            return true;
-        }
-    };
-
-    struct Axes {
-        NormalizedAxis x;
-        NormalizedAxis y;
-        DirectionalAxis hat0X;
-        DirectionalAxis hat0Y;
-    } mAxes;
-
-    struct Accumulator {
-        enum {
-            FIELD_ABS_X = 1,
-            FIELD_ABS_Y = 2,
-            FIELD_ABS_HAT0X = 4,
-            FIELD_ABS_HAT0Y = 8,
-
-            FIELD_ALL = FIELD_ABS_X | FIELD_ABS_Y | FIELD_ABS_HAT0X | FIELD_ABS_HAT0Y,
-        };
-
-        uint32_t fields;
-
-        int32_t absX;
-        int32_t absY;
-        int32_t absHat0X;
-        int32_t absHat0Y;
-
-        inline void clear() {
-            fields = 0;
-        }
-    } mAccumulator;
-
-    void initialize();
-
-    void sync(nsecs_t when);
-
-    void notifyDirectionalAxis(DirectionalAxis& axis,
-            nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode);
-
-    static void dumpNormalizedAxis(String8& dump,
-            const NormalizedAxis& axis, const char* name);
+    static bool isCenteredAxis(int32_t axis);
 };
 
 } // namespace android
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 41d67ed..648250e 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -561,7 +561,7 @@
         return -1;
     }
 
-    virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
+    virtual status_t mapKey(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const {
         Device* device = getDevice(deviceId);
         if (device) {
@@ -579,6 +579,11 @@
         return NAME_NOT_FOUND;
     }
 
+    virtual status_t mapAxis(int32_t deviceId, int scancode,
+            int32_t* outAxis) const {
+        return NAME_NOT_FOUND;
+    }
+
     virtual void addExcludedDevice(const char* deviceName) {
         mExcludedDevices.add(String8(deviceName));
     }
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 097b109..8ab9b6a 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -17,6 +17,7 @@
 #include <ui/KeyCharacterMap.h>
 #include <ui/KeyLayoutMap.h>
 #include <ui/VirtualKeyMap.h>
+#include <utils/PropertyMap.h>
 #include <utils/String8.h>
 
 #include <stdio.h>
@@ -32,6 +33,7 @@
     FILETYPE_KEYLAYOUT,
     FILETYPE_KEYCHARACTERMAP,
     FILETYPE_VIRTUALKEYDEFINITION,
+    FILETYPE_INPUTDEVICECONFIGURATION,
 };
 
 
@@ -39,9 +41,9 @@
     fprintf(stderr, "Keymap Validation Tool\n\n");
     fprintf(stderr, "Usage:\n");
     fprintf(stderr,
-        " %s [*.kl] [*.kcm] [virtualkeys.*] [...]\n"
-        "   Validates the specified key layouts, key character maps \n"
-        "   or virtual key definitions.\n\n",
+        " %s [*.kl] [*.kcm] [*.idc] [virtualkeys.*] [...]\n"
+        "   Validates the specified key layouts, key character maps, \n"
+        "   input device configurations, or virtual key definitions.\n\n",
         gProgName);
 }
 
@@ -54,6 +56,9 @@
         if (strcmp(extension, ".kcm") == 0) {
             return FILETYPE_KEYCHARACTERMAP;
         }
+        if (strcmp(extension, ".idc") == 0) {
+            return FILETYPE_INPUTDEVICECONFIGURATION;
+        }
     }
 
     if (strstr(filename, "virtualkeys.")) {
@@ -92,6 +97,16 @@
         break;
     }
 
+    case FILETYPE_INPUTDEVICECONFIGURATION: {
+        PropertyMap* map;
+        status_t status = PropertyMap::load(String8(filename), &map);
+        if (status) {
+            fprintf(stderr, "Error %d parsing input device configuration file.\n\n", status);
+            return false;
+        }
+        break;
+    }
+
     case FILETYPE_VIRTUALKEYDEFINITION: {
         VirtualKeyMap* map;
         status_t status = VirtualKeyMap::load(String8(filename), &map);