Add keycodes and meta-key modifiers to support external keyboards.

Added new key maps for external keyboards.  These maps are intended to
be shared across devices by inheriting the "keyboards.mk" product
makefile as part of the device's product definition.

One of the trickier changes here was to unwind some code in
MetaKeyKeyListener that assumed that only the low 8 bits of the meta key
state were actually used.  The new code abandons bitshifts in favor
of simple conditionals that are probably easier to read anyways.
The special meta key state constants used by MetaKeyKeyListener
are now (@hide) defined in KeyEvent now so as to make it clearer that they
share the same code space even if those codes are not valid for KeyEvents.

The EventHub now takes care of detecting the appropriate key layout
map and key character map when the device is added and sets system
properties accordingly.  This avoids having duplicate code in
KeyCharacterMap to probe for the appropriate key character map
although the current probing mechanism has been preserved for legacy
reasons just in case.

Added support for tracking caps lock, num lock and scroll lock and
turning their corresponding LEDs on and off as needed.

The key character map format will need to be updated to correctly support
PC style external keyboard semantics related to modifier keys.
That will come in a later change so caps lock doesn't actually do
anything right now except turn the shiny LEDs on and off...

Added a list of symbolic key names to KeyEvent and improved the toString()
output for debug diagnosis.  Having this list in a central place in the
framework also allows us to remove it from Monkey so there is one less
thing to maintain when we add new keycodes.

Bug: 2912307
Change-Id: If8c25e8d50a7c29bbf5d663c94284f5f86de5da4
diff --git a/api/current.xml b/api/current.xml
index b51242b..1f3a6b7 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -195848,6 +195848,17 @@
 <parameter name="keyCode" type="int">
 </parameter>
 </method>
+<method name="isNumLockLatched"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isPrintingKey"
  return="boolean"
  abstract="false"
@@ -196530,6 +196541,39 @@
  visibility="public"
 >
 </method>
+<method name="isCapsLockLatched"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isCtrlPressed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFunctionPressed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isLongPress"
  return="boolean"
  abstract="false"
@@ -196541,6 +196585,17 @@
  visibility="public"
 >
 </method>
+<method name="isMetaPressed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isModifierKey"
  return="boolean"
  abstract="false"
@@ -196565,6 +196620,17 @@
  visibility="public"
 >
 </method>
+<method name="isScrollLockLatched"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isShiftPressed"
  return="boolean"
  abstract="false"
@@ -196986,6 +197052,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_BREAK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="121"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_BUTTON_A"
  type="int"
  transient="false"
@@ -197184,6 +197261,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_CAPS_LOCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="115"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_CLEAR"
  type="int"
  transient="false"
@@ -197206,6 +197294,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_CTRL_LEFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="113"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_CTRL_RIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="114"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_D"
  type="int"
  transient="false"
@@ -197338,6 +197448,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_ESCAPE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="111"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_EXPLORER"
  type="int"
  transient="false"
@@ -197360,6 +197481,138 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_F1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="131"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F10"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="140"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F11"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="141"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F12"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="142"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="132"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="133"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="134"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F5"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="135"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F6"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="136"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F7"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="137"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F8"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="138"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F9"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="139"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_FOCUS"
  type="int"
  transient="false"
@@ -197371,6 +197624,39 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_FORWARD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="125"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_FORWARD_DEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="112"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_FUNCTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="119"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_G"
  type="int"
  transient="false"
@@ -197437,6 +197723,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_INSERT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="124"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_J"
  type="int"
  transient="false"
@@ -197492,6 +197789,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MEDIA_CLOSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_MEDIA_EJECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="129"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MEDIA_FAST_FORWARD"
  type="int"
  transient="false"
@@ -197514,6 +197833,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MEDIA_PAUSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="127"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_MEDIA_PLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="126"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MEDIA_PLAY_PAUSE"
  type="int"
  transient="false"
@@ -197536,6 +197877,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MEDIA_RECORD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="130"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MEDIA_REWIND"
  type="int"
  transient="false"
@@ -197569,6 +197921,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_META_LEFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="117"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_META_RIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="118"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MINUS"
  type="int"
  transient="false"
@@ -197580,6 +197954,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MOVE_END"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="123"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_MOVE_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="122"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MUTE"
  type="int"
  transient="false"
@@ -197624,6 +198020,237 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_NUMPAD_0"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="145"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="146"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="147"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="148"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_5"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="149"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_6"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="150"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_7"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="151"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_8"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="152"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_9"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="153"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_ADD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="157"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_COMMA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="159"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_DIVIDE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="154"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_DOT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="158"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_ENTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="160"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_EQUALS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="161"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_LEFT_PAREN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="162"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_MULTIPLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="155"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_RIGHT_PAREN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="163"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_SUBTRACT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="156"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUM_LOCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="143"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_O"
  type="int"
  transient="false"
@@ -197767,6 +198394,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_SCROLL_LOCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="116"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_SEARCH"
  type="int"
  transient="false"
@@ -197888,6 +198526,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_SYSRQ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="120"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_T"
  type="int"
  transient="false"
@@ -198053,6 +198702,116 @@
  visibility="public"
 >
 </field>
+<field name="META_CAPS_LOCK_LATCHED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1048576"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_CTRL_LEFT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8192"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_CTRL_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4096"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_CTRL_RIGHT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_FUNCTION_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_META_LEFT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="131072"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_META_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="65536"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_META_RIGHT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="262144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_NUM_LOCK_LATCHED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2097152"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_SCROLL_LOCK_LATCHED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4194304"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="META_SHIFT_LEFT_ON"
  type="int"
  transient="false"
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 54ac906..dd25eaa 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1145,12 +1145,9 @@
         float h2 = isLevelBoundary(point) ?
                     getSecondaryHorizontal(point) - 0.5f : h1;
 
-        int caps = TextKeyListener.getMetaState(editingBuffer,
-                                                KeyEvent.META_SHIFT_ON) |
-                   TextKeyListener.getMetaState(editingBuffer,
-                                                TextKeyListener.META_SELECTING);
-        int fn = TextKeyListener.getMetaState(editingBuffer,
-                                              KeyEvent.META_ALT_ON);
+        int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
+                   TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
+        int fn = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_ALT_ON);
         int dist = 0;
 
         if (caps != 0 || fn != 0) {
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 220e023..0d012d6 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -30,12 +30,12 @@
 
 public class ArrowKeyMovementMethod implements MovementMethod {
     private boolean isCap(Spannable buffer) {
-        return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
+        return ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SHIFT_ON) == 1) ||
                 (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
     }
 
     private boolean isAlt(Spannable buffer) {
-        return MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_ALT_ON) == 1;
+        return MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_ALT_ON) == 1;
     }
 
     private boolean up(TextView widget, Spannable buffer) {
diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java
index 584e83f..1e1812a 100644
--- a/core/java/android/text/method/DialerKeyListener.java
+++ b/core/java/android/text/method/DialerKeyListener.java
@@ -56,7 +56,7 @@
          * Prefer number if no meta key is active, or if it produces something
          * valid and the meta lookup does not.
          */
-        if ((meta & (KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)) == 0) {
+        if ((meta & (MetaKeyKeyListener.META_ALT_ON | MetaKeyKeyListener.META_SHIFT_ON)) == 0) {
             if (number != 0) {
                 return number;
             }
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index 61ec67f..e7b36d7 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -16,9 +16,12 @@
 
 package android.text.method;
 
+import android.text.Editable;
+import android.text.NoCopySpan;
+import android.text.Spannable;
+import android.text.Spanned;
 import android.view.KeyEvent;
 import android.view.View;
-import android.text.*;
 
 /**
  * This base class encapsulates the behavior for handling the meta keys
@@ -28,38 +31,58 @@
  */
 
 public abstract class MetaKeyKeyListener {
+    /**
+     * Flag that indicates that the SHIFT key is on.
+     * Value equals {@link KeyEvent#META_SHIFT_ON}.
+     */
     public static final int META_SHIFT_ON = KeyEvent.META_SHIFT_ON;
+    /**
+     * Flag that indicates that the ALT key is on.
+     * Value equals {@link KeyEvent#META_ALT_ON}.
+     */
     public static final int META_ALT_ON = KeyEvent.META_ALT_ON;
+    /**
+     * Flag that indicates that the SYM key is on.
+     * Value equals {@link KeyEvent#META_SYM_ON}.
+     */
     public static final int META_SYM_ON = KeyEvent.META_SYM_ON;
-
-    private static final int LOCKED_SHIFT = 8;
     
-    public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << LOCKED_SHIFT;
-    public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << LOCKED_SHIFT;
-    public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << LOCKED_SHIFT;
+    /**
+     * Flag that indicates that the SHIFT key is locked in CAPS mode.
+     */
+    public static final int META_CAP_LOCKED = KeyEvent.META_CAP_LOCKED;
+    /**
+     * Flag that indicates that the ALT key is locked.
+     */
+    public static final int META_ALT_LOCKED = KeyEvent.META_ALT_LOCKED;
+    /**
+     * Flag that indicates that the SYM key is locked.
+     */
+    public static final int META_SYM_LOCKED = KeyEvent.META_SYM_LOCKED;
 
     /**
      * @hide pending API review
      */
-    public static final int META_SELECTING = 1 << 16;
+    public static final int META_SELECTING = KeyEvent.META_SELECTING;
 
-    private static final int USED_SHIFT = 24;
-    
-    private static final long META_CAP_USED = ((long)KeyEvent.META_SHIFT_ON) << USED_SHIFT;
-    private static final long META_ALT_USED = ((long)KeyEvent.META_ALT_ON) << USED_SHIFT;
-    private static final long META_SYM_USED = ((long)KeyEvent.META_SYM_ON) << USED_SHIFT;
+    private static final int META_SHIFT_ON_AND_LOCKED = META_SHIFT_ON | META_CAP_LOCKED;
+    private static final int META_ALT_ON_AND_LOCKED = META_ALT_ON | META_ALT_LOCKED;
+    private static final int META_SYM_ON_AND_LOCKED = META_SYM_ON | META_SYM_LOCKED;
 
-    private static final int PRESSED_SHIFT = 32;
+    // These bits are privately used by the meta key key listener.
+    // They are deliberately assigned values outside of the representable range of an 'int'
+    // so as not to conflict with any meta key states publicly defined by KeyEvent.
+    private static final long META_CAP_USED = 1L << 32;
+    private static final long META_ALT_USED = 1L << 33;
+    private static final long META_SYM_USED = 1L << 34;
     
-    private static final long META_CAP_PRESSED = ((long)KeyEvent.META_SHIFT_ON) << PRESSED_SHIFT;
-    private static final long META_ALT_PRESSED = ((long)KeyEvent.META_ALT_ON) << PRESSED_SHIFT;
-    private static final long META_SYM_PRESSED = ((long)KeyEvent.META_SYM_ON) << PRESSED_SHIFT;
-
-    private static final int RELEASED_SHIFT = 40;
+    private static final long META_CAP_PRESSED = 1L << 40;
+    private static final long META_ALT_PRESSED = 1L << 41;
+    private static final long META_SYM_PRESSED = 1L << 42;
     
-    private static final long META_CAP_RELEASED = ((long)KeyEvent.META_SHIFT_ON) << RELEASED_SHIFT;
-    private static final long META_ALT_RELEASED = ((long)KeyEvent.META_ALT_ON) << RELEASED_SHIFT;
-    private static final long META_SYM_RELEASED = ((long)KeyEvent.META_SYM_ON) << RELEASED_SHIFT;
+    private static final long META_CAP_RELEASED = 1L << 48;
+    private static final long META_ALT_RELEASED = 1L << 49;
+    private static final long META_SYM_RELEASED = 1L << 50;
 
     private static final long META_SHIFT_MASK = META_SHIFT_ON
             | META_CAP_LOCKED | META_CAP_USED
@@ -306,15 +329,14 @@
      * (arrow keys, for example) and you handle a key.
      */
     public static long resetLockedMeta(long state) {
-        state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
-        state = resetLock(state, META_ALT_ON, META_ALT_MASK);
-        state = resetLock(state, META_SYM_ON, META_SYM_MASK);
-        return state;
-    }
-
-    private static long resetLock(long state, int what, long mask) {
-        if ((state&(((long)what)<<LOCKED_SHIFT)) != 0) {
-            state &= ~mask;
+        if ((state & META_CAP_LOCKED) != 0) {
+            state &= ~META_SHIFT_MASK;
+        }
+        if ((state & META_ALT_LOCKED) != 0) {
+            state &= ~META_ALT_MASK;
+        }
+        if ((state & META_SYM_LOCKED) != 0) {
+            state &= ~META_SYM_MASK;
         }
         return state;
     }
@@ -332,9 +354,27 @@
      *         or locked meta key.
      */
     public static final int getMetaState(long state) {
-        return getActive(state, META_SHIFT_ON, META_SHIFT_ON, META_CAP_LOCKED) |
-               getActive(state, META_ALT_ON, META_ALT_ON, META_ALT_LOCKED) |
-               getActive(state, META_SYM_ON, META_SYM_ON, META_SYM_LOCKED);
+        int result = 0;
+
+        if ((state & META_CAP_LOCKED) != 0) {
+            result |= META_CAP_LOCKED;
+        } else if ((state & META_SHIFT_ON) != 0) {
+            result |= META_SHIFT_ON;
+        }
+
+        if ((state & META_ALT_LOCKED) != 0) {
+            result |= META_ALT_LOCKED;
+        } else if ((state & META_ALT_ON) != 0) {
+            result |= META_ALT_ON;
+        }
+
+        if ((state & META_SYM_LOCKED) != 0) {
+            result |= META_SYM_LOCKED;
+        } else if ((state & META_SYM_ON) != 0) {
+            result |= META_SYM_ON;
+        }
+
+        return result;
     }
 
     /**
@@ -348,29 +388,25 @@
     public static final int getMetaState(long state, int meta) {
         switch (meta) {
             case META_SHIFT_ON:
-                return getActive(state, meta, 1, 2);
+                if ((state & META_CAP_LOCKED) != 0) return 2;
+                if ((state & META_SHIFT_ON) != 0) return 1;
+                return 0;
 
             case META_ALT_ON:
-                return getActive(state, meta, 1, 2);
+                if ((state & META_ALT_LOCKED) != 0) return 2;
+                if ((state & META_ALT_ON) != 0) return 1;
+                return 0;
 
             case META_SYM_ON:
-                return getActive(state, meta, 1, 2);
+                if ((state & META_SYM_LOCKED) != 0) return 2;
+                if ((state & META_SYM_ON) != 0) return 1;
+                return 0;
 
             default:
                 return 0;
         }
     }
 
-    private static int getActive(long state, int meta, int on, int lock) {
-        if ((state&(meta<<LOCKED_SHIFT)) != 0) {
-            return lock;
-        } else if ((state&meta) != 0) {
-            return on;
-        } else {
-            return 0;
-        }
-    }
-
     /**
      * Call this method after you handle a keypress so that the meta
      * state will be reset to unshifted (if it is not still down)
@@ -378,17 +414,23 @@
      * the current state, returns the new state.
      */
     public static long adjustMetaAfterKeypress(long state) {
-        state = adjust(state, META_SHIFT_ON, META_SHIFT_MASK);
-        state = adjust(state, META_ALT_ON, META_ALT_MASK);
-        state = adjust(state, META_SYM_ON, META_SYM_MASK);
-        return state;
-    }
+        if ((state & META_CAP_PRESSED) != 0) {
+            state = (state & ~META_SHIFT_MASK) | META_SHIFT_ON | META_CAP_USED;
+        } else if ((state & META_CAP_RELEASED) != 0) {
+            state &= ~META_SHIFT_MASK;
+        }
 
-    private static long adjust(long state, int what, long mask) {
-        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
-            return (state&~mask) | what | ((long)what)<<USED_SHIFT;
-        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
-            return state & ~mask;
+        if ((state & META_ALT_PRESSED) != 0) {
+            state = (state & ~META_ALT_MASK) | META_ALT_ON | META_ALT_USED;
+        } else if ((state & META_ALT_RELEASED) != 0) {
+            state &= ~META_ALT_MASK;
+        }
+
+        if ((state & META_SYM_PRESSED) != 0) {
+            state = (state & ~META_SYM_MASK) | META_SYM_ON | META_SYM_USED;
+        } else if ((state & META_SYM_RELEASED) != 0) {
+            state &= ~META_SYM_MASK;
+        }
         return state;
     }
 
@@ -397,32 +439,36 @@
      */
     public static long handleKeyDown(long state, int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            return press(state, META_SHIFT_ON, META_SHIFT_MASK);
+            return press(state, META_SHIFT_ON, META_SHIFT_MASK,
+                    META_CAP_LOCKED, META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
                 || keyCode == KeyEvent.KEYCODE_NUM) {
-            return press(state, META_ALT_ON, META_ALT_MASK);
+            return press(state, META_ALT_ON, META_ALT_MASK,
+                    META_ALT_LOCKED, META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_SYM) {
-            return press(state, META_SYM_ON, META_SYM_MASK);
+            return press(state, META_SYM_ON, META_SYM_MASK,
+                    META_SYM_LOCKED, META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED);
         }
-
         return state;
     }
 
-    private static long press(long state, int what, long mask) {
-        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
-            ; // repeat before use
-        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
-            state = (state&~mask) | what | (((long)what) << LOCKED_SHIFT);
-        else if ((state&(((long)what)<<USED_SHIFT)) != 0)
-            ; // repeat after use
-        else if ((state&(((long)what)<<LOCKED_SHIFT)) != 0)
-            state = state&~mask;
-        else
-            state = state | what | (((long)what)<<PRESSED_SHIFT);
+    private static long press(long state, int what, long mask,
+            long locked, long pressed, long released, long used) {
+        if ((state & pressed) != 0) {
+            // repeat before use
+        } else if ((state & released) != 0) {
+            state = (state &~ mask) | what | locked;
+        } else if ((state & used) != 0) {
+            // repeat after use
+        } else if ((state & locked) != 0) {
+            state &= ~mask;
+        } else {
+            state |= what | pressed;
+        }
         return state;
     }
 
@@ -431,39 +477,52 @@
      */
     public static long handleKeyUp(long state, int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            return release(state, META_SHIFT_ON, META_SHIFT_MASK);
+            return release(state, META_SHIFT_ON, META_SHIFT_MASK,
+                    META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
                 || keyCode == KeyEvent.KEYCODE_NUM) {
-            return release(state, META_ALT_ON, META_ALT_MASK);
+            return release(state, META_ALT_ON, META_ALT_MASK,
+                    META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_SYM) {
-            return release(state, META_SYM_ON, META_SYM_MASK);
+            return release(state, META_SYM_ON, META_SYM_MASK,
+                    META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED);
         }
-
         return state;
     }
 
-    private static long release(long state, int what, long mask) {
-        if ((state&(((long)what)<<USED_SHIFT)) != 0)
-            state = state&~mask;
-        else if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
-            state = state | what | (((long)what)<<RELEASED_SHIFT);
+    private static long release(long state, int what, long mask,
+            long pressed, long released, long used) {
+        if ((state & used) != 0) {
+            state &= ~mask;
+        } else if ((state & pressed) != 0) {
+            state |= what | released;
+        }
         return state;
     }
 
+    /**
+     * Clears the state of the specified meta key if it is locked.
+     * @param state the meta key state
+     * @param which meta keys to clear, may be a combination of {@link #META_SHIFT_ON},
+     * {@link #META_ALT_ON} or {@link #META_SYM_ON}.
+     */
     public long clearMetaKeyState(long state, int which) {
-        if ((which&META_SHIFT_ON) != 0)
-            state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
-        if ((which&META_ALT_ON) != 0)
-            state = resetLock(state, META_ALT_ON, META_ALT_MASK);
-        if ((which&META_SYM_ON) != 0)
-            state = resetLock(state, META_SYM_ON, META_SYM_MASK);
+        if ((which & META_SHIFT_ON_AND_LOCKED) == META_SHIFT_ON_AND_LOCKED) {
+            state &= ~META_SHIFT_MASK;
+        }
+        if ((which & META_ALT_ON_AND_LOCKED) == META_ALT_ON_AND_LOCKED) {
+            state &= ~META_ALT_MASK;
+        }
+        if ((which & META_SYM_ON_AND_LOCKED) == META_SYM_ON_AND_LOCKED) {
+            state &= ~META_SYM_MASK;
+        }
         return state;
     }
-    
+
     /**
      * The meta key has been pressed but has not yet been used.
      */
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index 3b98fc3..9eaab17 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -144,7 +144,7 @@
                 if (ds[0].mFarEnough) {
                     ds[0].mUsed = true;
                     boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                                   KeyEvent.META_SHIFT_ON) == 1) ||
+                                   MetaKeyKeyListener.META_SHIFT_ON) == 1) ||
                                    (MetaKeyKeyListener.getMetaState(buffer,
                                     MetaKeyKeyListener.META_SELECTING) != 0);
                     float dx;
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0e5ece1..0e75682 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -42,6 +42,13 @@
  * Meta states describe the pressed state of key modifiers
  * such as {@link #META_SHIFT_ON} or {@link #META_ALT_ON}.
  * </p><p>
+ * Key codes typically correspond one-to-one with individual keys on an input device.
+ * Many keys and key combinations serve quite different functions on different
+ * input devices so care must be taken when interpreting them.  Always use the
+ * {@link KeyCharacterMap} associated with the input device when mapping keys
+ * to characters.  Be aware that there may be multiple key input devices active
+ * at the same time and each will have its own key character map.
+ * </p><p>
  * When interacting with an IME, the framework may deliver key events
  * with the special action {@link #ACTION_MULTIPLE} that either specifies
  * that single repeated key code or a sequence of characters to insert.
@@ -209,7 +216,7 @@
     /** Key code constant: Enter key. */
     public static final int KEYCODE_ENTER           = 66;
     /** Key code constant: Backspace key.
-     * Deletes characters before the insertion point. */
+     * Deletes characters before the insertion point, unlike {@link #KEYCODE_FORWARD_DEL}. */
     public static final int KEYCODE_DEL             = 67;
     /** Key code constant: '`' (backtick) key. */
     public static final int KEYCODE_GRAVE           = 68;
@@ -331,6 +338,125 @@
     /** Key code constant: Mode Button key.
      * On a game controller, the button labeled Mode. */
     public static final int KEYCODE_BUTTON_MODE     = 110;
+    /** Key code constant: Escape key. */
+    public static final int KEYCODE_ESCAPE          = 111;
+    /** Key code constant: Forward Delete key.
+     * Deletes characters ahead of the insertion point, unlike {@link #KEYCODE_DEL}. */
+    public static final int KEYCODE_FORWARD_DEL     = 112;
+    /** Key code constant: Left Control modifier key. */
+    public static final int KEYCODE_CTRL_LEFT       = 113;
+    /** Key code constant: Right Control modifier key. */
+    public static final int KEYCODE_CTRL_RIGHT      = 114;
+    /** Key code constant: Caps Lock modifier key. */
+    public static final int KEYCODE_CAPS_LOCK       = 115;
+    /** Key code constant: Scroll Lock key. */
+    public static final int KEYCODE_SCROLL_LOCK     = 116;
+    /** Key code constant: Left Meta modifier key. */
+    public static final int KEYCODE_META_LEFT       = 117;
+    /** Key code constant: Right Meta modifier key. */
+    public static final int KEYCODE_META_RIGHT      = 118;
+    /** Key code constant: Function modifier key. */
+    public static final int KEYCODE_FUNCTION        = 119;
+    /** Key code constant: System Request / Print Screen key. */
+    public static final int KEYCODE_SYSRQ           = 120;
+    /** Key code constant: Break / Pause key. */
+    public static final int KEYCODE_BREAK           = 121;
+    /** Key code constant: Home Movement key.
+     * Used for scrolling or moving the cursor around to the start of a line
+     * or to the top of a list. */
+    public static final int KEYCODE_MOVE_HOME       = 122;
+    /** Key code constant: End Movement key.
+     * Used for scrolling or moving the cursor around to the end of a line
+     * or to the bottom of a list. */
+    public static final int KEYCODE_MOVE_END        = 123;
+    /** Key code constant: Insert key.
+     * Toggles insert / overwrite edit mode. */
+    public static final int KEYCODE_INSERT          = 124;
+    /** Key code constant: Forward key.
+     * Navigates forward in the history stack.  Complement of {@link #KEYCODE_BACK}. */
+    public static final int KEYCODE_FORWARD         = 125;
+    /** Key code constant: Play media key. */
+    public static final int KEYCODE_MEDIA_PLAY      = 126;
+    /** Key code constant: Pause media key. */
+    public static final int KEYCODE_MEDIA_PAUSE     = 127;
+    /** Key code constant: Close media key.
+     * May be used to close a CD tray, for example. */
+    public static final int KEYCODE_MEDIA_CLOSE     = 128;
+    /** Key code constant: Eject media key.
+     * May be used to eject a CD tray, for example. */
+    public static final int KEYCODE_MEDIA_EJECT     = 129;
+    /** Key code constant: Record media key. */
+    public static final int KEYCODE_MEDIA_RECORD    = 130;
+    /** Key code constant: F1 key. */
+    public static final int KEYCODE_F1              = 131;
+    /** Key code constant: F2 key. */
+    public static final int KEYCODE_F2              = 132;
+    /** Key code constant: F3 key. */
+    public static final int KEYCODE_F3              = 133;
+    /** Key code constant: F4 key. */
+    public static final int KEYCODE_F4              = 134;
+    /** Key code constant: F5 key. */
+    public static final int KEYCODE_F5              = 135;
+    /** Key code constant: F6 key. */
+    public static final int KEYCODE_F6              = 136;
+    /** Key code constant: F7 key. */
+    public static final int KEYCODE_F7              = 137;
+    /** Key code constant: F8 key. */
+    public static final int KEYCODE_F8              = 138;
+    /** Key code constant: F9 key. */
+    public static final int KEYCODE_F9              = 139;
+    /** Key code constant: F10 key. */
+    public static final int KEYCODE_F10             = 140;
+    /** Key code constant: F11 key. */
+    public static final int KEYCODE_F11             = 141;
+    /** Key code constant: F12 key. */
+    public static final int KEYCODE_F12             = 142;
+    /** Key code constant: Num Lock modifier key.
+     * This is the Num Lock key; it is different from {@link #KEYCODE_NUM}.
+     * This key generally modifies the behavior of other keys on the numeric keypad. */
+    public static final int KEYCODE_NUM_LOCK        = 143;
+    /** Key code constant: Numeric keypad '0' key. */
+    public static final int KEYCODE_NUMPAD_0        = 144;
+    /** Key code constant: Numeric keypad '1' key. */
+    public static final int KEYCODE_NUMPAD_1        = 145;
+    /** Key code constant: Numeric keypad '2' key. */
+    public static final int KEYCODE_NUMPAD_2        = 146;
+    /** Key code constant: Numeric keypad '3' key. */
+    public static final int KEYCODE_NUMPAD_3        = 147;
+    /** Key code constant: Numeric keypad '4' key. */
+    public static final int KEYCODE_NUMPAD_4        = 148;
+    /** Key code constant: Numeric keypad '5' key. */
+    public static final int KEYCODE_NUMPAD_5        = 149;
+    /** Key code constant: Numeric keypad '6' key. */
+    public static final int KEYCODE_NUMPAD_6        = 150;
+    /** Key code constant: Numeric keypad '7' key. */
+    public static final int KEYCODE_NUMPAD_7        = 151;
+    /** Key code constant: Numeric keypad '8' key. */
+    public static final int KEYCODE_NUMPAD_8        = 152;
+    /** Key code constant: Numeric keypad '9' key. */
+    public static final int KEYCODE_NUMPAD_9        = 153;
+    /** Key code constant: Numeric keypad '/' key (for division). */
+    public static final int KEYCODE_NUMPAD_DIVIDE   = 154;
+    /** Key code constant: Numeric keypad '*' key (for multiplication). */
+    public static final int KEYCODE_NUMPAD_MULTIPLY = 155;
+    /** Key code constant: Numeric keypad '-' key (for subtraction). */
+    public static final int KEYCODE_NUMPAD_SUBTRACT = 156;
+    /** Key code constant: Numeric keypad '+' key (for addition). */
+    public static final int KEYCODE_NUMPAD_ADD      = 157;
+    /** Key code constant: Numeric keypad '.' key (for decimals or digit grouping). */
+    public static final int KEYCODE_NUMPAD_DOT      = 158;
+    /** Key code constant: Numeric keypad ',' key (for decimals or digit grouping). */
+    public static final int KEYCODE_NUMPAD_COMMA    = 159;
+    /** Key code constant: Numeric keypad Enter key. */
+    public static final int KEYCODE_NUMPAD_ENTER    = 160;
+    /** Key code constant: Numeric keypad '=' key. */
+    public static final int KEYCODE_NUMPAD_EQUALS   = 161;
+    /** Key code constant: Numeric keypad '(' key. */
+    public static final int KEYCODE_NUMPAD_LEFT_PAREN = 162;
+    /** Key code constant: Numeric keypad ')' key. */
+    public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163;
+
+    private static final int LAST_KEYCODE           = KEYCODE_NUMPAD_RIGHT_PAREN;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
@@ -339,7 +465,6 @@
     //  external/webkit/WebKit/android/plugins/ANPKeyCodes.h
     //  tools/puppet_master/PuppetMaster/nav_keys.py
     //  frameworks/base/core/res/res/values/attrs.xml
-    //  commands/monkey/Monkey.java
     //  emulator?
     //
     //  Also Android currently does not reserve code ranges for vendor-
@@ -347,9 +472,213 @@
     //  MUST contribute a patch to the open source project to define
     //  those new codes.  This is intended to maintain a consistent
     //  set of key code definitions across all Android devices.
-   
-    private static final int LAST_KEYCODE           = KEYCODE_BUTTON_MODE;
-    
+
+    // 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",
+    };
+
+    // Symbolic names of all metakeys in bit order from least significant to most significant.
+    // Accordingly there are exactly 32 values in this table.
+    private static final String[] META_SYMBOLIC_NAMES = new String[] {
+        "META_SHIFT_ON",
+        "META_ALT_ON",
+        "META_SYM_ON",
+        "META_FUNCTION_ON",
+        "META_ALT_LEFT_ON",
+        "META_ALT_RIGHT_ON",
+        "META_SHIFT_LEFT_ON",
+        "META_SHIFT_RIGHT_ON",
+        "META_CAP_LOCKED",
+        "META_ALT_LOCKED",
+        "META_SYM_LOCKED",
+        "0x00000800",
+        "META_CTRL_ON",
+        "META_CTRL_LEFT_ON",
+        "META_CTRL_RIGHT_ON",
+        "0x00008000",
+        "META_META_ON",
+        "META_META_LEFT_ON",
+        "META_META_RIGHT_ON",
+        "0x00080000",
+        "META_CAPS_LOCK_LATCHED",
+        "META_NUM_LOCK_LATCHED",
+        "META_SCROLL_LOCK_LATCHED",
+        "0x00800000",
+        "0x01000000",
+        "0x02000000",
+        "0x04000000",
+        "0x08000000",
+        "0x10000000",
+        "0x20000000",
+        "0x40000000",
+        "0x80000000",
+    };
+
     /**
      * @deprecated There are now more than MAX_KEYCODE keycodes.
      * Use {@link #getMaxKeyCode()} instead.
@@ -377,6 +706,35 @@
     public static final int ACTION_MULTIPLE         = 2;
 
     /**
+     * SHIFT key locked in CAPS mode.
+     * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API.
+     * @hide
+     */
+    public static final int META_CAP_LOCKED = 0x100;
+
+    /**
+     * ALT key locked.
+     * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API.
+     * @hide
+     */
+    public static final int META_ALT_LOCKED = 0x200;
+
+    /**
+     * SYM key locked.
+     * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API.
+     * @hide
+     */
+    public static final int META_SYM_LOCKED = 0x400;
+
+    /**
+     * Text is in selection mode.
+     * Reserved for use by {@link MetaKeyKeyListener} for a private unpublished constant
+     * in its API that is currently being retained for legacy reasons.
+     * @hide
+     */
+    public static final int META_SELECTING = 0x800;
+
+    /**
      * <p>This mask is used to check whether one of the ALT meta keys is pressed.</p>
      *
      * @see #isAltPressed()
@@ -441,6 +799,97 @@
     public static final int META_SYM_ON = 0x4;
 
     /**
+     * <p>This mask is used to check whether the FUNCTION meta key is pressed.</p>
+     *
+     * @see #isFunctionPressed()
+     * @see #getMetaState()
+     */
+    public static final int META_FUNCTION_ON = 0x8;
+
+    /**
+     * <p>This mask is used to check whether one of the CTRL meta keys is pressed.</p>
+     *
+     * @see #isCtrlPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_CTRL_LEFT
+     * @see #KEYCODE_CTRL_RIGHT
+     */
+    public static final int META_CTRL_ON = 0x1000;
+
+    /**
+     * <p>This mask is used to check whether the left CTRL meta key is pressed.</p>
+     *
+     * @see #isCtrlPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_CTRL_LEFT
+     */
+    public static final int META_CTRL_LEFT_ON = 0x2000;
+
+    /**
+     * <p>This mask is used to check whether the right CTRL meta key is pressed.</p>
+     *
+     * @see #isCtrlPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_CTRL_RIGHT
+     */
+    public static final int META_CTRL_RIGHT_ON = 0x4000;
+
+    /**
+     * <p>This mask is used to check whether one of the META meta keys is pressed.</p>
+     *
+     * @see #isMetaPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_META_LEFT
+     * @see #KEYCODE_META_RIGHT
+     */
+    public static final int META_META_ON = 0x10000;
+
+    /**
+     * <p>This mask is used to check whether the left META meta key is pressed.</p>
+     *
+     * @see #isMetaPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_META_LEFT
+     */
+    public static final int META_META_LEFT_ON = 0x20000;
+
+    /**
+     * <p>This mask is used to check whether the right META meta key is pressed.</p>
+     *
+     * @see #isMetaPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_META_RIGHT
+     */
+    public static final int META_META_RIGHT_ON = 0x40000;
+
+    /**
+     * <p>This mask is used to check whether the CAPS LOCK meta key is latched.</p>
+     *
+     * @see #isCapsLockLatched()
+     * @see #getMetaState()
+     * @see #KEYCODE_CAPS_LOCK
+     */
+    public static final int META_CAPS_LOCK_LATCHED = 0x100000;
+
+    /**
+     * <p>This mask is used to check whether the NUM LOCK meta key is latched.</p>
+     *
+     * @see #isNumLockLatched()
+     * @see #getMetaState()
+     * @see #KEYCODE_NUM_LOCK
+     */
+    public static final int META_NUM_LOCK_LATCHED = 0x200000;
+
+    /**
+     * <p>This mask is used to check whether the SCROLL LOCK meta key is latched.</p>
+     *
+     * @see #isScrollLockLatched()
+     * @see #getMetaState()
+     * @see #KEYCODE_SCROLL_LOCK
+     */
+    public static final int META_SCROLL_LOCK_LATCHED = 0x400000;
+
+    /**
      * This mask is set if the device woke because of this key event.
      */
     public static final int FLAG_WOKE_HERE = 0x1;
@@ -603,6 +1052,17 @@
         boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
     }
 
+    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.");
+        }
+    }
+
     /**
      * Create a new key event.
      * 
@@ -939,9 +1399,29 @@
      * @see #isAltPressed()
      * @see #isShiftPressed()
      * @see #isSymPressed()
+     * @see #isCtrlPressed()
+     * @see #isMetaPressed()
+     * @see #isFunctionPressed()
+     * @see #isCapsLockLatched()
+     * @see #isNumLockLatched()
+     * @see #isScrollLockLatched()
      * @see #META_ALT_ON
+     * @see #META_ALT_LEFT_ON
+     * @see #META_ALT_RIGHT_ON
      * @see #META_SHIFT_ON
+     * @see #META_SHIFT_LEFT_ON
+     * @see #META_SHIFT_RIGHT_ON
      * @see #META_SYM_ON
+     * @see #META_FUNCTION_ON
+     * @see #META_CTRL_ON
+     * @see #META_CTRL_LEFT_ON
+     * @see #META_CTRL_RIGHT_ON
+     * @see #META_META_ON
+     * @see #META_META_LEFT_ON
+     * @see #META_META_RIGHT_ON
+     * @see #META_CAPS_LOCK_LATCHED
+     * @see #META_NUM_LOCK_LATCHED
+     * @see #META_SCROLL_LOCK_LATCHED
      */
     public final int getMetaState() {
         return mMetaState;
@@ -961,13 +1441,28 @@
      *
      * @return whether the provided keyCode is one of
      * {@link #KEYCODE_SHIFT_LEFT} {@link #KEYCODE_SHIFT_RIGHT},
-     * {@link #KEYCODE_ALT_LEFT}, {@link #KEYCODE_ALT_RIGHT}
-     * or {@link #KEYCODE_SYM}.
+     * {@link #KEYCODE_ALT_LEFT}, {@link #KEYCODE_ALT_RIGHT},
+     * {@link #KEYCODE_SYM}, {@link #KEYCODE_NUM}, {@link #KEYCODE_FUNCTION},
+     * {@link #KEYCODE_CTRL_LEFT}, {@link #KEYCODE_CTRL_RIGHT},
+     * {@link #KEYCODE_META_LEFT}, or {@link #KEYCODE_META_RIGHT}.
      */
     public static boolean isModifierKey(int keyCode) {
-        return keyCode == KEYCODE_SHIFT_LEFT || keyCode == KEYCODE_SHIFT_RIGHT
-                || keyCode == KEYCODE_ALT_LEFT || keyCode == KEYCODE_ALT_RIGHT
-                || keyCode == KEYCODE_SYM;
+        switch (keyCode) {
+            case KEYCODE_SHIFT_LEFT:
+            case KEYCODE_SHIFT_RIGHT:
+            case KEYCODE_ALT_LEFT:
+            case KEYCODE_ALT_RIGHT:
+            case KEYCODE_SYM:
+            case KEYCODE_NUM:
+            case KEYCODE_FUNCTION:
+            case KEYCODE_CTRL_LEFT:
+            case KEYCODE_CTRL_RIGHT:
+            case KEYCODE_META_LEFT:
+            case KEYCODE_META_RIGHT:
+                return true;
+            default:
+                return false;
+        }
     }
 
     /**
@@ -1009,6 +1504,80 @@
     }
 
     /**
+     * <p>Returns the pressed state of the CTRL meta key.</p>
+     *
+     * @return true if the CTRL key is pressed, false otherwise
+     *
+     * @see #KEYCODE_CTRL_LEFT
+     * @see #KEYCODE_CTRL_RIGHT
+     * @see #META_CTRL_ON
+     */
+    public final boolean isCtrlPressed() {
+        return (mMetaState & META_CTRL_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the pressed state of the META meta key.</p>
+     *
+     * @return true if the META key is pressed, false otherwise
+     *
+     * @see #KEYCODE_META_LEFT
+     * @see #KEYCODE_META_RIGHT
+     * @see #META_META_ON
+     */
+    public final boolean isMetaPressed() {
+        return (mMetaState & META_META_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the pressed state of the FUNCTION meta key.</p>
+     *
+     * @return true if the FUNCTION key is pressed, false otherwise
+     *
+     * @see #KEYCODE_FUNCTION
+     * @see #META_FUNCTION_ON
+     */
+    public final boolean isFunctionPressed() {
+        return (mMetaState & META_FUNCTION_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the latched state of the CAPS LOCK meta key.</p>
+     *
+     * @return true if the CAPS LOCK key is latched, false otherwise
+     *
+     * @see #KEYCODE_CAPS_LOCK
+     * @see #META_CAPS_LOCK_LATCHED
+     */
+    public final boolean isCapsLockLatched() {
+        return (mMetaState & META_CAPS_LOCK_LATCHED) != 0;
+    }
+
+    /**
+     * <p>Returns the latched state of the NUM LOCK meta key.</p>
+     *
+     * @return true if the NUM LOCK key is latched, false otherwise
+     *
+     * @see #KEYCODE_NUM_LOCK
+     * @see #META_NUM_LOCK_LATCHED
+     */
+    public final boolean isNumLockLatched() {
+        return (mMetaState & META_NUM_LOCK_LATCHED) != 0;
+    }
+
+    /**
+     * <p>Returns the latched state of the SCROLL LOCK meta key.</p>
+     *
+     * @return true if the SCROLL LOCK key is latched, false otherwise
+     *
+     * @see #KEYCODE_SCROLL_LOCK
+     * @see #META_SCROLL_LOCK_LATCHED
+     */
+    public final boolean isScrollLockLatched() {
+        return (mMetaState & META_SCROLL_LOCK_LATCHED) != 0;
+    }
+
+    /**
      * Retrieve the action of this key event.  May be either
      * {@link #ACTION_DOWN}, {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
      * 
@@ -1392,12 +1961,118 @@
             }
         }
     }
-    
+
+    @Override
     public String toString() {
-        return "KeyEvent{action=" + mAction + " code=" + mKeyCode
-            + " repeat=" + mRepeatCount
-            + " meta=" + mMetaState + " scancode=" + mScanCode
-            + " mFlags=" + mFlags + "}";
+        return "KeyEvent{action=" + actionToString(mAction)
+                + " keycode=" + keyCodeToString(mKeyCode)
+                + " scancode=" + mScanCode
+                + " meta=" + metaStateToString(mMetaState)
+                + " flags=0x" + Integer.toHexString(mFlags)
+                + " repeat=" + mRepeatCount
+                + " device=" + mDeviceId
+                + " source=0x" + Integer.toHexString(mSource)
+                + "}";
+    }
+
+    /**
+     * Returns a string that represents the symbolic name of the specified action
+     * such as "ACTION_DOWN", or "35" (if unknown).
+     *
+     * @param action The action.
+     * @return The symbolic name of the specified action.
+     * @hide
+     */
+    public static String actionToString(int action) {
+        switch (action) {
+            case ACTION_DOWN:
+                return "ACTION_DOWN";
+            case ACTION_UP:
+                return "ACTION_UP";
+            case ACTION_MULTIPLE:
+                return "ACTION_MULTIPLE";
+            default:
+                return Integer.toString(action);
+        }
+    }
+
+    /**
+     * Returns a string that represents the symbolic name of the specified keycode
+     * such as "KEYCODE_A", "KEYCODE_DPAD_UP", or "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);
+    }
+
+    /**
+     * Gets a keycode by its symbolic name such as "KEYCODE_A" or "1001" (if unknown).
+     *
+     * @param symbolicName The symbolic name of the keycode.
+     * @return The keycode or -1 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;
+        for (int i = 0; i < count; i++) {
+            if (symbolicName.equals(KEYCODE_SYMBOLIC_NAMES[i])) {
+                return i;
+            }
+        }
+
+        try {
+            return Integer.parseInt(symbolicName,10);
+        } catch (NumberFormatException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * 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).
+     *
+     * @param metaState The meta state.
+     * @return The symbolic name of the specified combined meta state flags.
+     * @hide
+     */
+    public static String metaStateToString(int metaState) {
+        if (metaState == 0) {
+            return "0";
+        }
+        StringBuilder result = null;
+        int i = 0;
+        while (metaState != 0) {
+            final boolean isSet = (metaState & 1) != 0;
+            metaState >>>= 1; // unsigned shift!
+            if (isSet) {
+                final String name = META_SYMBOLIC_NAMES[i];
+                if (result == null) {
+                    if (metaState == 0) {
+                        return name;
+                    }
+                    result = new StringBuilder(name);
+                } else {
+                    result.append('|');
+                    result.append(name);
+                }
+            }
+            i += 1;
+        }
+        return result.toString();
     }
 
     public static final Parcelable.Creator<KeyEvent> CREATOR
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 9411474..195d689 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1550,8 +1550,54 @@
     @Override
     public String toString() {
         return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this))
-            + " action=" + mAction + " x=" + getX()
-            + " y=" + getY() + " pressure=" + getPressure() + " size=" + getSize() + "}";
+            + " action=" + actionToString(mAction)
+            + " x=" + getX()
+            + " y=" + getY()
+            + " pressure=" + getPressure()
+            + " size=" + getSize()
+            + " touchMajor=" + getTouchMajor()
+            + " touchMinor=" + getTouchMinor()
+            + " toolMajor=" + getToolMajor()
+            + " toolMinor=" + getToolMinor()
+            + " orientation=" + getOrientation()
+            + " meta=" + KeyEvent.metaStateToString(mMetaState)
+            + " pointerCount=" + getPointerCount()
+            + " historySize=" + getHistorySize()
+            + " flags=0x" + Integer.toHexString(mFlags)
+            + " edgeFlags=0x" + Integer.toHexString(mEdgeFlags)
+            + " device=" + mDeviceId
+            + " source=0x" + Integer.toHexString(mSource)
+            + "}";
+    }
+
+    /**
+     * Returns a string that represents the symbolic name of the specified action
+     * such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or "35" (if unknown).
+     *
+     * @param action The action.
+     * @return The symbolic name of the specified action.
+     * @hide
+     */
+    public static String actionToString(int action) {
+        switch (action) {
+            case ACTION_DOWN:
+                return "ACTION_DOWN";
+            case ACTION_UP:
+                return "ACTION_UP";
+            case ACTION_CANCEL:
+                return "ACTION_CANCEL";
+            case ACTION_MOVE:
+                return "ACTION_MOVE";
+        }
+        int index = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
+        switch (action & ACTION_MASK) {
+            case ACTION_POINTER_DOWN:
+                return "ACTION_POINTER_DOWN(" + index + ")";
+            case ACTION_POINTER_UP:
+                return "ACTION_POINTER_UP(" + index + ")";
+            default:
+                return Integer.toString(action);
+        }
     }
 
     public static final Parcelable.Creator<MotionEvent> CREATOR
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ef6d6cb..578191a 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1056,6 +1056,59 @@
         <enum name="KEYCODE_BUTTON_START" value="108" />
         <enum name="KEYCODE_BUTTON_SELECT" value="109" />
         <enum name="KEYCODE_BUTTON_MODE" value="110" />
+        <enum name="KEYCODE_ESCAPE" value="111" />
+        <enum name="KEYCODE_FORWARD_DEL" value="112" />
+        <enum name="KEYCODE_CTRL_LEFT" value="113" />
+        <enum name="KEYCODE_CTRL_RIGHT" value="114" />
+        <enum name="KEYCODE_CAPS_LOCK" value="115" />
+        <enum name="KEYCODE_SCROLL_LOCK" value="116" />
+        <enum name="KEYCODE_META_LEFT" value="117" />
+        <enum name="KEYCODE_META_RIGHT" value="118" />
+        <enum name="KEYCODE_FUNCTION" value="119" />
+        <enum name="KEYCODE_SYSRQ" value="120" />
+        <enum name="KEYCODE_BREAK" value="121" />
+        <enum name="KEYCODE_MOVE_HOME" value="122" />
+        <enum name="KEYCODE_MOVE_END" value="123" />
+        <enum name="KEYCODE_INSERT" value="124" />
+        <enum name="KEYCODE_FORWARD" value="125" />
+        <enum name="KEYCODE_MEDIA_PLAY" value="126" />
+        <enum name="KEYCODE_MEDIA_PAUSE" value="127" />
+        <enum name="KEYCODE_MEDIA_CLOSE" value="128" />
+        <enum name="KEYCODE_MEDIA_EJECT" value="129" />
+        <enum name="KEYCODE_MEDIA_RECORD" value="130" />
+        <enum name="KEYCODE_F1" value="131" />
+        <enum name="KEYCODE_F2" value="132" />
+        <enum name="KEYCODE_F3" value="133" />
+        <enum name="KEYCODE_F4" value="134" />
+        <enum name="KEYCODE_F5" value="135" />
+        <enum name="KEYCODE_F6" value="136" />
+        <enum name="KEYCODE_F7" value="137" />
+        <enum name="KEYCODE_F8" value="138" />
+        <enum name="KEYCODE_F9" value="139" />
+        <enum name="KEYCODE_F10" value="140" />
+        <enum name="KEYCODE_F11" value="141" />
+        <enum name="KEYCODE_F12" value="142" />
+        <enum name="NUM_LOCK" value="143" />
+        <enum name="NUMPAD_0" value="144" />
+        <enum name="NUMPAD_1" value="145" />
+        <enum name="NUMPAD_2" value="146" />
+        <enum name="NUMPAD_3" value="147" />
+        <enum name="NUMPAD_4" value="148" />
+        <enum name="NUMPAD_5" value="149" />
+        <enum name="NUMPAD_6" value="150" />
+        <enum name="NUMPAD_7" value="151" />
+        <enum name="NUMPAD_8" value="152" />
+        <enum name="NUMPAD_9" value="153" />
+        <enum name="NUMPAD_DIVIDE" value="154" />
+        <enum name="NUMPAD_MULTIPLY" value="155" />
+        <enum name="NUMPAD_SUBTRACT" value="156" />
+        <enum name="NUMPAD_ADD" value="157" />
+        <enum name="NUMPAD_DOT" value="158" />
+        <enum name="NUMPAD_COMMA" value="159" />
+        <enum name="NUMPAD_ENTER" value="160" />
+        <enum name="NUMPAD_EQUALS" value="161" />
+        <enum name="NUMPAD_LEFT_PAREN" value="162" />
+        <enum name="NUMPAD_RIGHT_PAREN" value="163" />
     </attr>
 
     <!-- ***************************************************************** -->
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
new file mode 100644
index 0000000..7a0eae0
--- /dev/null
+++ b/data/keyboards/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This makefile builds the key character map binary files.  (*.kcm.bin)
+
+LOCAL_PATH := $(call my-dir)
+include $(LOCAL_PATH)/common.mk
+
+$(foreach file,$(keycharmaps), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_MODULE_TAGS := optional) \
+    $(eval include $(BUILD_KEY_CHAR_MAP)) \
+)
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
new file mode 100644
index 0000000..f3c52a7
--- /dev/null
+++ b/data/keyboards/Generic.kcm
@@ -0,0 +1,100 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Generic key character map for full alphabetic US English PC style external keyboards.
+#
+# This file is intentionally very generic and is intended to support a broad rang of keyboards.
+# Do not edit the generic key character map to support a specific keyboard; instead, create
+# a new key character map file with the required keyboard configuration.
+#
+
+[type=QWERTY]
+
+# keycode               display number  base    caps    fn      caps_fn
+
+A                       'A'     '2'     'a'     'A'     'a'     'A'
+B                       'B'     '2'     'b'     'B'     'b'     'B'
+C                       'C'     '2'     'c'     'C'     'c'     'C'
+D                       'D'     '3'     'd'     'D'     'd'     'D'
+E                       'E'     '3'     'e'     'E'     'e'     'E'
+F                       'F'     '3'     'f'     'F'     'f'     'F'
+G                       'G'     '4'     'g'     'G'     'g'     'G'
+H                       'H'     '4'     'h'     'H'     'h'     'H'
+I                       'I'     '4'     'i'     'I'     'i'     'I'
+J                       'J'     '5'     'j'     'J'     'j'     'J'
+K                       'K'     '5'     'k'     'K'     'k'     'K'
+L                       'L'     '5'     'l'     'L'     'l'     'L'
+M                       'M'     '6'     'm'     'M'     'm'     'M'
+N                       'N'     '6'     'n'     'N'     'n'     'N'
+O                       'O'     '6'     'o'     'O'     'o'     'O'
+P                       'P'     '7'     'p'     'P'     'p'     'P'
+Q                       'Q'     '7'     'q'     'Q'     'q'     'Q'
+R                       'R'     '7'     'r'     'R'     'r'     'R'
+S                       'S'     '7'     's'     'S'     's'     'S'
+T                       'T'     '8'     't'     'T'     't'     'T'
+U                       'U'     '8'     'u'     'U'     'u'     'U'
+V                       'V'     '8'     'v'     'V'     'v'     'V'
+W                       'W'     '9'     'w'     'W'     'w'     'W'
+X                       'X'     '9'     'x'     'X'     'x'     'X'
+Y                       'Y'     '9'     'y'     'Y'     'y'     'Y'
+Z                       'Z'     '9'     'z'     'Z'     'z'     'Z'
+
+0                       '0'     '0'     '0'     ')'     '0'     ')'
+1                       '1'     '1'     '1'     '!'     '1'     '!'
+2                       '2'     '2'     '2'     '@'     '2'     '@'
+3                       '3'     '3'     '3'     '#'     '3'     '#'
+4                       '4'     '4'     '4'     '$'     '4'     '$'
+5                       '5'     '5'     '5'     '%'     '5'     '%'
+6                       '6'     '6'     '6'     '^'     '6'     '^'
+7                       '7'     '7'     '7'     '&'     '7'     '&'
+8                       '8'     '8'     '8'     '*'     '8'     '*'
+9                       '9'     '9'     '9'     '('     '9'     '('
+
+SPACE                   0x20    0x20    0x20    0x20    0x20    0x20
+ENTER                   0xa     0xa     0xa     0xa     0xa     0xa
+TAB                     0x9     0x9     0x9     0x9     0x9     0x9
+
+COMMA                   ','     ','     ','     '<'     ','     '<'
+PERIOD                  '.'     '.'     '.'     '>'     '.'     '>'
+SLASH                   '/'     '/'     '/'     '?'     '/'     '?'
+GRAVE                   '`'     '`'     '`'     '~'     '`'     '~'
+MINUS                   '-'     '-'     '-'     '_'     '-'     '_'
+EQUALS                  '='     '='     '='     '+'     '='     '+'
+LEFT_BRACKET            '['     '['     '['     '{'     '['     '{'
+RIGHT_BRACKET           ']'     ']'     ']'     '}'     ']'     '}'
+BACKSLASH               '\'     '\'     '\'     '|'     '\'     '|'
+SEMICOLON               ';'     ';'     ';'     ':'     ';'     ':'
+APOSTROPHE              '''     '''     '''     '"'     '''     '"'
+
+NUMPAD_0                '0'     '0'     '0'     '0'     '0'     '0'
+NUMPAD_1                '1'     '1'     '1'     '1'     '1'     '1'
+NUMPAD_2                '2'     '2'     '2'     '2'     '2'     '2'
+NUMPAD_3                '3'     '3'     '3'     '3'     '3'     '3'
+NUMPAD_4                '4'     '4'     '4'     '4'     '4'     '4'
+NUMPAD_5                '5'     '5'     '5'     '5'     '5'     '5'
+NUMPAD_6                '6'     '6'     '6'     '6'     '6'     '6'
+NUMPAD_7                '7'     '7'     '7'     '7'     '7'     '7'
+NUMPAD_8                '8'     '8'     '8'     '8'     '8'     '8'
+NUMPAD_9                '9'     '9'     '9'     '9'     '9'     '9'
+NUMPAD_LEFT_PAREN       '('     '('     '('     '('     '('     '('
+NUMPAD_RIGHT_PAREN      ')'     ')'     ')'     ')'     ')'     ')'
+NUMPAD_DIVIDE           '/'     '/'     '/'     '/'     '/'     '/'
+NUMPAD_MULTIPLY         '*'     '*'     '*'     '*'     '*'     '*'
+NUMPAD_SUBTRACT         '-'     '-'     '-'     '-'     '-'     '-'
+NUMPAD_ADD              '+'     '+'     '+'     '+'     '+'     '+'
+NUMPAD_DOT              '.'     '.'     '.'     '.'     '.'     '.'
+NUMPAD_COMMA            ','     ','     ','     ','     ','     ','
+NUMPAD_EQUALS           '='     '='     '='     '='     '='     '='
+NUMPAD_ENTER            0xa     0xa     0xa     0xa     0xa     0xa
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
new file mode 100644
index 0000000..818397b
--- /dev/null
+++ b/data/keyboards/Generic.kl
@@ -0,0 +1,384 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Generic key layout file for full alphabetic US English PC style external keyboards.
+#
+# This file is intentionally very generic and is intended to support a broad rang of keyboards.
+# Do not edit the generic key layout to support a specific keyboard; instead, create
+# a new key layout file with the required keyboard configuration.
+#
+
+key 1     ESCAPE
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 13    EQUALS
+key 14    DEL
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 26    LEFT_BRACKET
+key 27    RIGHT_BRACKET
+key 28    ENTER
+key 29    CTRL_LEFT
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+key 41    GRAVE
+key 42    SHIFT_LEFT
+key 43    BACKSLASH
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+key 55    NUMPAD_MULTIPLY
+key 56    ALT_LEFT
+key 57    SPACE
+key 58    CAPS_LOCK
+key 59    F1
+key 60    F2
+key 61    F3
+key 62    F4
+key 63    F5
+key 64    F6
+key 65    F7
+key 66    F8
+key 67    F9
+key 68    F10
+key 69    NUM_LOCK
+key 70    SCROLL_LOCK
+key 71    NUMPAD_7
+key 72    NUMPAD_8
+key 73    NUMPAD_9
+key 74    NUMPAD_SUBTRACT
+key 75    NUMPAD_4
+key 76    NUMPAD_5
+key 77    NUMPAD_6
+key 78    NUMPAD_ADD
+key 79    NUMPAD_1
+key 80    NUMPAD_2
+key 81    NUMPAD_3
+key 82    NUMPAD_0
+key 83    NUMPAD_DOT
+# key 84 (undefined)
+# key 85 "KEY_ZENKAKUHANKAKU"
+# key 86 "KEY_102ND"
+key 87    F11
+key 88    F12
+# key 89 "KEY_RO"
+# key 90 "KEY_KATAKANA"
+# key 91 "KEY_HIRAGANA"
+# key 92 "KEY_HENKAN"
+# key 93 "KEY_KATAKANAHIRAGANA"
+# key 94 "KEY_MUHENKAN"
+key 95    NUMPAD_COMMA
+key 96    NUMPAD_ENTER
+key 97    CTRL_RIGHT
+key 98    NUMPAD_DIVIDE
+key 99    SYSRQ
+key 100   ALT_RIGHT
+# key 101 "KEY_LINEFEED"
+key 102   MOVE_HOME
+key 103   DPAD_UP
+key 104   PAGE_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 107   MOVE_END
+key 108   DPAD_DOWN
+key 109   PAGE_DOWN
+key 110   INSERT
+key 111   FORWARD_DEL
+# key 112 "KEY_MACRO"
+key 113   MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+key 116   POWER             WAKE
+key 117   NUMPAD_EQUALS
+# key 118 "KEY_KPPLUSMINUS"
+key 119   BREAK
+# key 120 (undefined)
+key 121   NUMPAD_COMMA
+# key 122 "KEY_HANGEUL"
+# key 123 "KEY_HANJA"
+# key 124 "KEY_YEN"
+key 125   META_LEFT
+key 126   META_RIGHT
+key 127   MENU              WAKE_DROPPED
+key 128   MEDIA_STOP
+# key 129 "KEY_AGAIN"
+# key 130 "KEY_PROPS"
+# key 131 "KEY_UNDO"
+# key 132 "KEY_FRONT"
+# key 133 "KEY_COPY"
+# key 134 "KEY_OPEN"
+# key 135 "KEY_PASTE"
+# key 136 "KEY_FIND"
+# key 137 "KEY_CUT"
+# key 138 "KEY_HELP"
+key 139   MENU              WAKE_DROPPED
+# key 140 "KEY_CALC"
+# key 141 "KEY_SETUP"
+# key 142 "KEY_SLEEP"
+# key 143 "KEY_WAKEUP"
+# key 144 "KEY_FILE"
+# key 145 "KEY_SENDFILE"
+# key 146 "KEY_DELETEFILE"
+# key 147 "KEY_XFER"
+# key 148 "KEY_PROG1"
+# key 149 "KEY_PROG2"
+key 150   EXPLORER
+# key 151 "KEY_MSDOS"
+# key 152 "KEY_COFFEE"
+# key 153 "KEY_DIRECTION"
+# key 154 "KEY_CYCLEWINDOWS"
+key 155   ENVELOPE
+# key 156 "KEY_BOOKMARKS"
+# key 157 "KEY_COMPUTER"
+key 158   BACK              WAKE_DROPPED
+key 159   FORWARD
+key 160   MEDIA_CLOSE
+key 161   MEDIA_EJECT
+key 162   MEDIA_EJECT
+key 163   MEDIA_NEXT
+key 164   MEDIA_PLAY_PAUSE
+key 165   MEDIA_PREVIOUS
+key 166   MEDIA_STOP
+key 167   MEDIA_RECORD
+key 168   MEDIA_REWIND
+key 169   CALL
+# key 170 "KEY_ISO"
+# key 171 "KEY_CONFIG"
+key 172   HOME
+# key 173 "KEY_REFRESH"
+# key 174 "KEY_EXIT"
+# key 175 "KEY_MOVE"
+# key 176 "KEY_EDIT"
+key 177   PAGE_UP
+key 178   PAGE_DOWN
+key 179   NUMPAD_LEFT_PAREN
+key 180   NUMPAD_RIGHT_PAREN
+# key 181 "KEY_NEW"
+# key 182 "KEY_REDO"
+# key 183   F13
+# key 184   F14
+# key 185   F15
+# key 186   F16
+# key 187   F17
+# key 188   F18
+# key 189   F19
+# key 190   F20
+# key 191   F21
+# key 192   F22
+# key 193   F23
+# key 194   F24
+# key 195 (undefined)
+# key 196 (undefined)
+# key 197 (undefined)
+# key 198 (undefined)
+# key 199 (undefined)
+key 200   MEDIA_PLAY
+key 201   MEDIA_PAUSE
+# key 202 "KEY_PROG3"
+# key 203 "KEY_PROG4"
+# key 204 (undefined)
+# key 205 "KEY_SUSPEND"
+# key 206 "KEY_CLOSE"
+key 207   MEDIA_PLAY
+key 208   MEDIA_FAST_FORWARD
+# key 209 "KEY_BASSBOOST"
+# key 210 "KEY_PRINT"
+# key 211 "KEY_HP"
+key 212   CAMERA
+# key 213 "KEY_SOUND"
+# key 214 "KEY_QUESTION"
+key 215   ENVELOPE
+# key 216 "KEY_CHAT"
+key 217   SEARCH
+# key 218 "KEY_CONNECT"
+# key 219 "KEY_FINANCE"
+# key 220 "KEY_SPORT"
+# key 221 "KEY_SHOP"
+# key 222 "KEY_ALTERASE"
+# key 223 "KEY_CANCEL"
+# key 224 "KEY_BRIGHTNESSDOWN"
+# key 225 "KEY_BRIGHTNESSUP"
+key 226   HEADSETHOOK
+key 227   STAR
+key 228   POUND
+key 229   SOFT_LEFT
+key 230   SOFT_RIGHT
+key 231   CALL
+key 232   DPAD_CENTER
+key 233   HEADSETHOOK
+# key 234 "KEY_0_5" or "KEY_SAVE"
+# key 235 "KEY_2_5" or "KEY_DOCUMENTS"
+# key 236 "KEY_SWITCHVIDEOMODE" or "KEY_BATTERY"
+# key 237 "KEY_KBDILLUMTOGGLE"
+# key 238 "KEY_KBDILLUMDOWN"
+# key 239 "KEY_KBDILLUMUP"
+# key 240 "KEY_UNKNOWN"
+
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 306   BUTTON_C
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 309   BUTTON_Z
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+key 312   BUTTON_L2
+key 313   BUTTON_R2
+key 314   BUTTON_SELECT
+key 315   BUTTON_START
+key 316   BUTTON_MODE
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+
+# key 352 "KEY_OK"
+# key 353 "KEY_SELECT"
+# key 354 "KEY_GOTO"
+# key 355 "KEY_CLEAR"
+# key 356 "KEY_POWER2"
+# key 357 "KEY_OPTION"
+# key 358 "KEY_INFO"
+# key 359 "KEY_TIME"
+# key 360 "KEY_VENDOR"
+# key 361 "KEY_ARCHIVE"
+# key 362 "KEY_PROGRAM"
+# key 363 "KEY_CHANNEL"
+# key 364 "KEY_FAVORITES"
+# key 365 "KEY_EPG"
+# key 366 "KEY_PVR"
+# key 367 "KEY_MHP"
+# key 368 "KEY_LANGUAGE"
+# key 369 "KEY_TITLE"
+# key 370 "KEY_SUBTITLE"
+# key 371 "KEY_ANGLE"
+# key 372 "KEY_ZOOM"
+# key 373 "KEY_MODE"
+# key 374 "KEY_KEYBOARD"
+# key 375 "KEY_SCREEN"
+# key 376 "KEY_PC"
+# key 377 "KEY_TV"
+# key 378 "KEY_TV2"
+# key 379 "KEY_VCR"
+# key 380 "KEY_VCR2"
+# key 381 "KEY_SAT"
+# key 382 "KEY_SAT2"
+# key 383 "KEY_CD"
+# key 384 "KEY_TAPE"
+# key 385 "KEY_RADIO"
+# key 386 "KEY_TUNER"
+# key 387 "KEY_PLAYER"
+# key 388 "KEY_TEXT"
+# key 389 "KEY_DVD"
+# key 390 "KEY_AUX"
+# key 391 "KEY_MP3"
+# key 392 "KEY_AUDIO"
+# key 393 "KEY_VIDEO"
+# key 394 "KEY_DIRECTORY"
+# key 395 "KEY_LIST"
+# key 396 "KEY_MEMO"
+# key 397 "KEY_CALENDAR"
+# key 398 "KEY_RED"
+# key 399 "KEY_GREEN"
+# key 400 "KEY_YELLOW"
+# key 401 "KEY_BLUE"
+# key 402 "KEY_CHANNELUP"
+# key 403 "KEY_CHANNELDOWN"
+# key 404 "KEY_FIRST"
+# key 405 "KEY_LAST"
+# key 406 "KEY_AB"
+# key 407 "KEY_NEXT"
+# key 408 "KEY_RESTART"
+# key 409 "KEY_SLOW"
+# key 410 "KEY_SHUFFLE"
+# key 411 "KEY_BREAK"
+# key 412 "KEY_PREVIOUS"
+# key 413 "KEY_DIGITS"
+# key 414 "KEY_TEEN"
+# key 415 "KEY_TWEN"
+
+
+# key 448 "KEY_DEL_EOL"
+# key 449 "KEY_DEL_EOS"
+# key 450 "KEY_INS_LINE"
+# key 451 "KEY_DEL_LINE"
+
+
+key 464   FUNCTION
+key 465   ESCAPE            FUNCTION
+key 466   F1                FUNCTION
+key 467   F2                FUNCTION
+key 468   F3                FUNCTION
+key 469   F4                FUNCTION
+key 470   F5                FUNCTION
+key 471   F6                FUNCTION
+key 472   F7                FUNCTION
+key 473   F8                FUNCTION
+key 474   F9                FUNCTION
+key 475   F10               FUNCTION
+key 476   F11               FUNCTION
+key 477   F12               FUNCTION
+key 478   1                 FUNCTION
+key 479   2                 FUNCTION
+key 480   D                 FUNCTION
+key 481   E                 FUNCTION
+key 482   F                 FUNCTION
+key 483   S                 FUNCTION
+key 484   B                 FUNCTION
+
+
+# key 497 KEY_BRL_DOT1
+# key 498 KEY_BRL_DOT2
+# key 499 KEY_BRL_DOT3
+# key 500 KEY_BRL_DOT4
+# key 501 KEY_BRL_DOT5
+# key 502 KEY_BRL_DOT6
+# key 503 KEY_BRL_DOT7
+# key 504 KEY_BRL_DOT8
diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm
new file mode 100644
index 0000000..0f31683
--- /dev/null
+++ b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm
@@ -0,0 +1,71 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[type=QWERTY]
+
+# keycode       display number  base    caps    fn      caps_fn
+
+A               'A'     '2'     'a'     'A'     'a'     'A'
+B               'B'     '2'     'b'     'B'     'b'     'B'
+C               'C'     '2'     'c'     'C'     'c'     'C'
+D               'D'     '3'     'd'     'D'     'd'     'D'
+E               'E'     '3'     'e'     'E'     'e'     'E'
+F               'F'     '3'     'f'     'F'     'f'     'F'
+G               'G'     '4'     'g'     'G'     'g'     'G'
+H               'H'     '4'     'h'     'H'     'h'     'H'
+I               'I'     '4'     'i'     'I'     'i'     'I'
+J               'J'     '5'     'j'     'J'     'j'     'J'
+K               'K'     '5'     'k'     'K'     'k'     'K'
+L               'L'     '5'     'l'     'L'     'l'     'L'
+M               'M'     '6'     'm'     'M'     'm'     'M'
+N               'N'     '6'     'n'     'N'     'n'     'N'
+O               'O'     '6'     'o'     'O'     'o'     'O'
+P               'P'     '7'     'p'     'P'     'p'     'P'
+Q               'Q'     '7'     'q'     'Q'     'q'     'Q'
+R               'R'     '7'     'r'     'R'     'r'     'R'
+S               'S'     '7'     's'     'S'     's'     'S'
+T               'T'     '8'     't'     'T'     't'     'T'
+U               'U'     '8'     'u'     'U'     'u'     'U'
+V               'V'     '8'     'v'     'V'     'v'     'V'
+W               'W'     '9'     'w'     'W'     'w'     'W'
+X               'X'     '9'     'x'     'X'     'x'     'X'
+Y               'Y'     '9'     'y'     'Y'     'y'     'Y'
+Z               'Z'     '9'     'z'     'Z'     'z'     'Z'
+
+0               '0'     '0'     '0'     ')'     '0'     ')'
+1               '1'     '1'     '1'     '!'     '1'     '!'
+2               '2'     '2'     '2'     '@'     '2'     '@'
+3               '3'     '3'     '3'     '#'     '3'     '#'
+4               '4'     '4'     '4'     '$'     '4'     '$'
+5               '5'     '5'     '5'     '%'     '5'     '%'
+6               '6'     '6'     '6'     '^'     '6'     '^'
+7               '7'     '7'     '7'     '&'     '7'     '&'
+8               '8'     '8'     '8'     '*'     '8'     '*'
+9               '9'     '9'     '9'     '('     '9'     '('
+
+SPACE           0x20    0x20    0x20    0x20    0x20    0x20
+ENTER           0xa     0xa     0xa     0xa     0xa     0xa
+TAB             0x9     0x9     0x9     0x9     0x9     0x9
+
+COMMA           ','     ','     ','     '<'     ','     '<'
+PERIOD          '.'     '.'     '.'     '>'     '.'     '>'
+SLASH           '/'     '/'     '/'     '?'     '/'     '?'
+GRAVE           '`'     '`'     '`'     '~'     '`'     '~'
+MINUS           '-'     '-'     '-'     '_'     '-'     '_'
+EQUALS          '='     '='     '='     '+'     '='     '+'
+LEFT_BRACKET    '['     '['     '['     '{'     '['     '{'
+RIGHT_BRACKET   ']'     ']'     ']'     '}'     ']'     '}'
+BACKSLASH       '\'     '\'     '\'     '|'     '\'     '|'
+SEMICOLON       ';'     ';'     ';'     ':'     ';'     ':'
+APOSTROPHE      '''     '''     '''     '"'     '''     '"'
diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl
new file mode 100644
index 0000000..1298d53
--- /dev/null
+++ b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl
@@ -0,0 +1,96 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+key 1     BACK
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 13    EQUALS
+key 14    DEL
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 26    LEFT_BRACKET
+key 27    RIGHT_BRACKET
+key 28    ENTER
+key 29    CTRL_LEFT
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+key 41    GRAVE
+key 42    SHIFT_LEFT
+key 43    BACKSLASH
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+key 57    SPACE
+key 58    CAPS_LOCK
+key 59    F1
+key 60    F2
+key 61    F3
+key 62    F4
+key 63    F5
+key 64    F6
+key 65    F7
+key 66    F8
+key 67    F9
+key 97    CTRL_RIGHT
+key 102   HOME
+key 103   DPAD_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 107   MOVE_END
+key 110   INSERT
+key 111   FORWARD_DEL
+key 113   MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+key 125   MENU
+key 127   SEARCH
+key 163   MEDIA_NEXT
+key 164   MEDIA_PLAY_PAUSE
+key 165   MEDIA_PREVIOUS
+key 166   MEDIA_STOP
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
new file mode 100644
index 0000000..7a58b07
--- /dev/null
+++ b/data/keyboards/common.mk
@@ -0,0 +1,24 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is the list of keylayouts and key character maps to build.
+# Used by Android.mk and keyboards.mk.
+
+keylayouts := \
+    Generic.kl \
+    Motorola_Bluetooth_Wireless_Keyboard.kl
+
+keycharmaps := \
+    Generic.kcm \
+    Motorola_Bluetooth_Wireless_Keyboard.kcm
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
new file mode 100644
index 0000000..665c8bf
--- /dev/null
+++ b/data/keyboards/keyboards.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Warning: this is actually a product definition, to be inherited from
+
+include $(LOCAL_PATH)/common.mk
+
+PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\
+    frameworks/base/data/keyboards/$(file):system/usr/keylayout/$(file))
+
+PRODUCT_PACKAGES := $(keycharmaps)
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index d78e35f..1431964 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -187,6 +187,9 @@
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
             uint8_t* outFlags) const = 0;
 
+    virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
+
     virtual void dump(String8& dump) = 0;
 };
 
@@ -198,9 +201,9 @@
     status_t errorCheck() const;
 
     virtual uint32_t getDeviceClasses(int32_t deviceId) const;
-    
+
     virtual String8 getDeviceName(int32_t deviceId) const;
-    
+
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const;
 
@@ -218,6 +221,9 @@
 
     virtual bool getEvent(RawEvent* outEvent);
 
+    virtual bool hasLed(int32_t deviceId, int32_t led) const;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+
     virtual void dump(String8& dump);
 
 protected:
@@ -240,7 +246,10 @@
         uint32_t        classes;
         uint8_t*        keyBitmask;
         KeyLayoutMap*   layoutMap;
-        String8         keylayoutFilename;
+        String8         keyMapName;
+        bool            defaultKeyMap;
+        String8         keyLayoutFilename;
+        String8         keyCharacterMapFilename;
         int             fd;
         device_t*       next;
         
@@ -250,13 +259,19 @@
 
     device_t* getDeviceLocked(int32_t deviceId) const;
     bool hasKeycodeLocked(device_t* device, int keycode) const;
-    
+
     int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
     int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
     int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
     bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
+    void configureKeyMap(device_t* device);
+    bool probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
+    void selectKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
+    void setKeyboardProperties(device_t* device, bool firstKeyboard);
+    void clearKeyboardProperties(device_t* device, bool firstKeyboard);
+
     // Protect all internal state.
     mutable Mutex   mLock;
     
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8c6018b..1355bab 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -76,7 +76,7 @@
  */
 enum {
     /* These flags originate in RawEvents and are generally set in the key map.
-     * See also labels for policy flags in KeycodeLabels.h. */
+     * NOTE: If you edit these flags, also edit labels in KeycodeLabels.h. */
 
     POLICY_FLAG_WAKE = 0x00000001,
     POLICY_FLAG_WAKE_DROPPED = 0x00000002,
@@ -87,6 +87,7 @@
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
     POLICY_FLAG_VIRTUAL = 0x00000100,
+    POLICY_FLAG_FUNCTION = 0x00000200,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
 
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index c15e382..a1b59e6 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -419,9 +419,18 @@
         Vector<KeyDown> keyDowns; // keys that are down
         int32_t metaState;
         nsecs_t downTime; // time of most recent key down
+
+        struct LedState {
+            bool avail; // led is available
+            bool on;    // we think the led is currently on
+        };
+        LedState capsLockLedState;
+        LedState numLockLedState;
+        LedState scrollLockLedState;
     } mLocked;
 
     void initializeLocked();
+    void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
 
     bool isKeyboardOrGamepadKey(int32_t scanCode);
 
@@ -429,6 +438,10 @@
             uint32_t policyFlags);
 
     ssize_t findKeyDownLocked(int32_t scanCode);
+
+    void updateLedStateLocked(bool reset);
+    void updateLedStateForModifierLocked(LockedState::LedState& ledState, int32_t led,
+            int32_t modifier, bool reset);
 };
 
 
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index f71d9cd..ef2b6b3 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -135,6 +135,59 @@
     { "BUTTON_START", 108 },
     { "BUTTON_SELECT", 109 },
     { "BUTTON_MODE", 110 },
+    { "ESCAPE", 111 },
+    { "FORWARD_DEL", 112 },
+    { "CTRL_LEFT", 113 },
+    { "CTRL_RIGHT", 114 },
+    { "CAPS_LOCK", 115 },
+    { "SCROLL_LOCK", 116 },
+    { "META_LEFT", 117 },
+    { "META_RIGHT", 118 },
+    { "FUNCTION", 119 },
+    { "SYSRQ", 120 },
+    { "BREAK", 121 },
+    { "MOVE_HOME", 122 },
+    { "MOVE_END", 123 },
+    { "INSERT", 124 },
+    { "FORWARD", 125 },
+    { "MEDIA_PLAY", 126 },
+    { "MEDIA_PAUSE", 127 },
+    { "MEDIA_CLOSE", 128 },
+    { "MEDIA_EJECT", 129 },
+    { "MEDIA_RECORD", 130 },
+    { "F1", 131 },
+    { "F2", 132 },
+    { "F3", 133 },
+    { "F4", 134 },
+    { "F5", 135 },
+    { "F6", 136 },
+    { "F7", 137 },
+    { "F8", 138 },
+    { "F9", 139 },
+    { "F10", 140 },
+    { "F11", 141 },
+    { "F12", 142 },
+    { "NUM_LOCK", 143 },
+    { "NUMPAD_0", 144 },
+    { "NUMPAD_1", 145 },
+    { "NUMPAD_2", 146 },
+    { "NUMPAD_3", 147 },
+    { "NUMPAD_4", 148 },
+    { "NUMPAD_5", 149 },
+    { "NUMPAD_6", 150 },
+    { "NUMPAD_7", 151 },
+    { "NUMPAD_8", 152 },
+    { "NUMPAD_9", 153 },
+    { "NUMPAD_DIVIDE", 154 },
+    { "NUMPAD_MULTIPLY", 155 },
+    { "NUMPAD_SUBTRACT", 156 },
+    { "NUMPAD_ADD", 157 },
+    { "NUMPAD_DOT", 158 },
+    { "NUMPAD_COMMA", 159 },
+    { "NUMPAD_ENTER", 160 },
+    { "NUMPAD_EQUALS", 161 },
+    { "NUMPAD_LEFT_PAREN", 162 },
+    { "NUMPAD_RIGHT_PAREN", 163 },
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
@@ -142,7 +195,7 @@
     { NULL, 0 }
 };
 
-// See also policy flags in Input.h.
+// NOTE: If you edit these flags, also edit policy flags in Input.h.
 static const KeycodeLabel FLAGS[] = {
     { "WAKE", 0x00000001 },
     { "WAKE_DROPPED", 0x00000002 },
@@ -153,6 +206,7 @@
     { "MENU", 0x00000040 },
     { "LAUNCHER", 0x00000080 },
     { "VIRTUAL", 0x00000100 },
+    { "FUNCTION", 0x00000200 },
     { NULL, 0 }
 };
 
diff --git a/include/utils/String8.h b/include/utils/String8.h
index ef0b51a..cef8eca 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -157,9 +157,12 @@
     inline  size_t              size() const;
     inline  size_t              length() const;
     inline  size_t              bytes() const;
+    inline  bool                isEmpty() const;
     
     inline  const SharedBuffer* sharedBuffer() const;
     
+            void                clear();
+
             void                setTo(const String8& other);
             status_t            setTo(const char* other);
             status_t            setTo(const char* other, size_t numChars);
@@ -345,6 +348,11 @@
     return length();
 }
 
+inline bool String8::isEmpty() const
+{
+    return length() == 0;
+}
+
 inline size_t String8::bytes() const
 {
     return SharedBuffer::sizeFromData(mString)-1;
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 944731d..97a7528 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -94,7 +94,7 @@
 
 EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
     : id(_id), path(_path), name(name), classes(0)
-    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
+    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), defaultKeyMap(false), fd(-1), next(NULL) {
 }
 
 EventHub::device_t::~device_t() {
@@ -103,7 +103,7 @@
 }
 
 EventHub::EventHub(void)
-    : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
+    : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(-1)
     , mDevicesById(0), mNumDevicesById(0)
     , mOpeningDevices(0), mClosingDevices(0)
     , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false)
@@ -325,6 +325,39 @@
     mExcludedDevices.push_back(name);
 }
 
+bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
+    AutoMutex _l(mLock);
+    device_t* device = getDeviceLocked(deviceId);
+    if (device) {
+        uint8_t bitmask[sizeof_bit_array(LED_MAX + 1)];
+        memset(bitmask, 0, sizeof(bitmask));
+        if (ioctl(device->fd, EVIOCGBIT(EV_LED, sizeof(bitmask)), bitmask) >= 0) {
+            if (test_bit(led, bitmask)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
+    AutoMutex _l(mLock);
+    device_t* device = getDeviceLocked(deviceId);
+    if (device) {
+        struct input_event ev;
+        ev.time.tv_sec = 0;
+        ev.time.tv_usec = 0;
+        ev.type = EV_LED;
+        ev.code = led;
+        ev.value = on ? 1 : 0;
+
+        ssize_t nWrite;
+        do {
+            nWrite = write(device->fd, &ev, sizeof(struct input_event));
+        } while (nWrite == -1 && errno == EINTR);
+    }
+}
+
 EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
 {
     if (deviceId == 0) deviceId = mFirstKeyboardId;
@@ -760,54 +793,42 @@
 #endif
 
     if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
-        char tmpfn[sizeof(name)];
-        char keylayoutFilename[300];
-
         // a more descriptive name
         device->name = name;
 
-        // replace all the spaces with underscores
-        strcpy(tmpfn, name);
-        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
-            *p = '_';
+        // Configure the keymap for the device.
+        configureKeyMap(device);
 
-        // find the .kl file we need for this device
-        const char* root = getenv("ANDROID_ROOT");
-        snprintf(keylayoutFilename, sizeof(keylayoutFilename),
-                 "%s/usr/keylayout/%s.kl", root, tmpfn);
-        bool defaultKeymap = false;
-        if (access(keylayoutFilename, R_OK)) {
-            snprintf(keylayoutFilename, sizeof(keylayoutFilename),
-                     "%s/usr/keylayout/%s", root, "qwerty.kl");
-            defaultKeymap = true;
-        }
-        status_t status = device->layoutMap->load(keylayoutFilename);
-        if (status) {
-            LOGE("Error %d loading key layout.", status);
-        }
-
-        // tell the world about the devname (the descriptive name)
-        if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
+        // Tell the world about the devname (the descriptive name)
+        if (!mHaveFirstKeyboard && !device->defaultKeyMap && strstr(name, "-keypad")) {
             // the built-in keyboard has a well-known device ID of 0,
             // this device better not go away.
             mHaveFirstKeyboard = true;
             mFirstKeyboardId = device->id;
-            property_set("hw.keyboards.0.devname", name);
+            setKeyboardProperties(device, true);
         } else {
             // ensure mFirstKeyboardId is set to -something-.
-            if (mFirstKeyboardId == 0) {
+            if (mFirstKeyboardId == -1) {
                 mFirstKeyboardId = device->id;
+                setKeyboardProperties(device, true);
             }
         }
-        char propName[100];
-        sprintf(propName, "hw.keyboards.%u.devname", device->id);
-        property_set(propName, name);
+        setKeyboardProperties(device, false);
+
+        // Load the keylayout.
+        if (!device->keyLayoutFilename.isEmpty()) {
+            status_t status = device->layoutMap->load(device->keyLayoutFilename);
+            if (status) {
+                LOGE("Error %d loading key layout file '%s'.", status,
+                        device->keyLayoutFilename.string());
+            }
+        }
 
         // 'Q' key support = cheap test of whether this is an alpha-capable kbd
         if (hasKeycodeLocked(device, AKEYCODE_Q)) {
             device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
         }
-        
+
         // See if this device has a DPAD.
         if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
                 hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
@@ -816,7 +837,7 @@
                 hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
             device->classes |= INPUT_DEVICE_CLASS_DPAD;
         }
-        
+
         // See if this device has a gamepad.
         for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) {
             if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
@@ -825,8 +846,9 @@
             }
         }
 
-        LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
-                device->id, name, propName, keylayoutFilename);
+        LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n",
+                device->id, name,
+                device->keyLayoutFilename.string(), device->keyCharacterMapFilename.string());
     }
 
     // If the device isn't recognized as something we handle, don't monitor it.
@@ -852,6 +874,109 @@
     return 0;
 }
 
+void EventHub::configureKeyMap(device_t* device) {
+    // As an initial key map name, try using the device name.
+    String8 keyMapName(device->name);
+    char* p = keyMapName.lockBuffer(keyMapName.size());
+    while (*p) {
+        if (*p == ' ') *p = '_';
+        p++;
+    }
+    keyMapName.unlockBuffer();
+
+    if (probeKeyMap(device, keyMapName, false)) return;
+
+    // TODO Consider allowing the user to configure a specific key map somehow.
+
+    // Try the Generic key map.
+    // TODO Apply some additional heuristics here to figure out what kind of
+    //      generic key map to use (US English, etc.).
+    keyMapName.setTo("Generic");
+    if (probeKeyMap(device, keyMapName, true)) return;
+
+    // Fall back on the old style catchall qwerty key map.
+    keyMapName.setTo("qwerty");
+    if (probeKeyMap(device, keyMapName, true)) return;
+
+    // Give up!
+    keyMapName.setTo("unknown");
+    selectKeyMap(device, keyMapName, true);
+    LOGE("Could not determine key map for device '%s'.", device->name.string());
+}
+
+bool EventHub::probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap) {
+    const char* root = getenv("ANDROID_ROOT");
+
+    // TODO Consider also looking somewhere in a writeable partition like /data for a
+    //      custom keymap supplied by the user for this device.
+    bool haveKeyLayout = !device->keyLayoutFilename.isEmpty();
+    if (!haveKeyLayout) {
+        device->keyLayoutFilename.setTo(root);
+        device->keyLayoutFilename.append("/usr/keylayout/");
+        device->keyLayoutFilename.append(keyMapName);
+        device->keyLayoutFilename.append(".kl");
+        if (access(device->keyLayoutFilename.string(), R_OK)) {
+            device->keyLayoutFilename.clear();
+        } else {
+            haveKeyLayout = true;
+        }
+    }
+
+    bool haveKeyCharacterMap = !device->keyCharacterMapFilename.isEmpty();
+    if (!haveKeyCharacterMap) {
+        device->keyCharacterMapFilename.setTo(root);
+        device->keyCharacterMapFilename.append("/usr/keychars/");
+        device->keyCharacterMapFilename.append(keyMapName);
+        device->keyCharacterMapFilename.append(".kcm.bin");
+        if (access(device->keyCharacterMapFilename.string(), R_OK)) {
+            device->keyCharacterMapFilename.clear();
+        } else {
+            haveKeyCharacterMap = true;
+        }
+    }
+
+    if (haveKeyLayout || haveKeyCharacterMap) {
+        selectKeyMap(device, keyMapName, defaultKeyMap);
+    }
+    return haveKeyLayout && haveKeyCharacterMap;
+}
+
+void EventHub::selectKeyMap(device_t* device,
+        const String8& keyMapName, bool defaultKeyMap) {
+    if (device->keyMapName.isEmpty()) {
+        device->keyMapName.setTo(keyMapName);
+        device->defaultKeyMap = defaultKeyMap;
+    }
+}
+
+void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) {
+    int32_t id = firstKeyboard ? 0 : device->id;
+
+    char propName[100];
+    sprintf(propName, "hw.keyboards.%u.devname", id);
+    property_set(propName, device->name.string());
+    sprintf(propName, "hw.keyboards.%u.keymap", id);
+    property_set(propName, device->keyMapName.string());
+    sprintf(propName, "hw.keyboards.%u.klfile", id);
+    property_set(propName, device->keyLayoutFilename.string());
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    property_set(propName, device->keyCharacterMapFilename.string());
+}
+
+void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) {
+    int32_t id = firstKeyboard ? 0 : device->id;
+
+    char propName[100];
+    sprintf(propName, "hw.keyboards.%u.devname", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.keymap", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.klfile", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    property_set(propName, "");
+}
+
 bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
 {
     if (device->keyBitmask == NULL || device->layoutMap == NULL) {
@@ -909,13 +1034,10 @@
             if (device->id == mFirstKeyboardId) {
                 LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
                         device->path.string(), mFirstKeyboardId);
-                mFirstKeyboardId = 0;
-                property_set("hw.keyboards.0.devname", NULL);
+                mFirstKeyboardId = -1;
+                clearKeyboardProperties(device, true);
             }
-            // clear the property
-            char propName[100];
-            sprintf(propName, "hw.keyboards.%u.devname", device->id);
-            property_set(propName, NULL);
+            clearKeyboardProperties(device, false);
             return 0;
         }
     }
@@ -1014,7 +1136,11 @@
                 }
                 dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
                 dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
-                dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", device->keylayoutFilename.string());
+                dump.appendFormat(INDENT3 "KeyMapName: %s\n", device->keyMapName.string());
+                dump.appendFormat(INDENT3 "KeyLayoutFilename: %s\n",
+                        device->keyLayoutFilename.string());
+                dump.appendFormat(INDENT3 "KeyCharacterMapFilename: %s\n",
+                        device->keyCharacterMapFilename.string());
             }
         }
     } // release lock
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 0560bb8..6225121 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -70,32 +70,15 @@
     return value ? "true" : "false";
 }
 
-
-int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
-    int32_t mask;
-    switch (keyCode) {
-    case AKEYCODE_ALT_LEFT:
-        mask = AMETA_ALT_LEFT_ON;
-        break;
-    case AKEYCODE_ALT_RIGHT:
-        mask = AMETA_ALT_RIGHT_ON;
-        break;
-    case AKEYCODE_SHIFT_LEFT:
-        mask = AMETA_SHIFT_LEFT_ON;
-        break;
-    case AKEYCODE_SHIFT_RIGHT:
-        mask = AMETA_SHIFT_RIGHT_ON;
-        break;
-    case AKEYCODE_SYM:
-        mask = AMETA_SYM_ON;
-        break;
-    default:
-        return oldMetaState;
+int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+    int32_t newMetaState;
+    if (down) {
+        newMetaState = oldMetaState | mask;
+    } else {
+        newMetaState = oldMetaState &
+                ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
     }
 
-    int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask
-            & ~ (AMETA_ALT_ON | AMETA_SHIFT_ON);
-
     if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
         newMetaState |= AMETA_ALT_ON;
     }
@@ -104,9 +87,58 @@
         newMetaState |= AMETA_SHIFT_ON;
     }
 
+    if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
+        newMetaState |= AMETA_CTRL_ON;
+    }
+
+    if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
+        newMetaState |= AMETA_META_ON;
+    }
     return newMetaState;
 }
 
+int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+    if (down) {
+        return oldMetaState;
+    } else {
+        return oldMetaState ^ mask;
+    }
+}
+
+int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
+    int32_t mask;
+    switch (keyCode) {
+    case AKEYCODE_ALT_LEFT:
+        return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_ALT_RIGHT:
+        return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_SHIFT_LEFT:
+        return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_SHIFT_RIGHT:
+        return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_SYM:
+        return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
+    case AKEYCODE_FUNCTION:
+        return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
+    case AKEYCODE_CTRL_LEFT:
+        return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_CTRL_RIGHT:
+        return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_META_LEFT:
+        return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_META_RIGHT:
+        return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_CAPS_LOCK:
+        return toggleLockedMetaState(AMETA_CAPS_LOCK_LATCHED, down, oldMetaState);
+    case AKEYCODE_NUM_LOCK:
+        return toggleLockedMetaState(AMETA_NUM_LOCK_LATCHED, down, oldMetaState);
+    case AKEYCODE_SCROLL_LOCK:
+        return toggleLockedMetaState(AMETA_SCROLL_LOCK_LATCHED, down, oldMetaState);
+    default:
+        return oldMetaState;
+    }
+}
+
 static const int32_t keyCodeRotationMap[][4] = {
         // key codes enumerated counter-clockwise with the original (unrotated) key first
         // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
@@ -842,6 +874,17 @@
 void KeyboardInputMapper::initializeLocked() {
     mLocked.metaState = AMETA_NONE;
     mLocked.downTime = 0;
+
+    initializeLedStateLocked(mLocked.capsLockLedState, LED_CAPSL);
+    initializeLedStateLocked(mLocked.numLockLedState, LED_NUML);
+    initializeLedStateLocked(mLocked.scrollLockLedState, LED_SCROLLL);
+
+    updateLedStateLocked(true);
+}
+
+void KeyboardInputMapper::initializeLedStateLocked(LockedState::LedState& ledState, int32_t led) {
+    ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
+    ledState.on = false;
 }
 
 uint32_t KeyboardInputMapper::getSources() {
@@ -966,6 +1009,7 @@
         if (oldMetaState != newMetaState) {
             mLocked.metaState = newMetaState;
             metaStateChanged = true;
+            updateLedStateLocked(false);
         }
 
         downTime = mLocked.downTime;
@@ -975,6 +1019,9 @@
         getContext()->updateGlobalMetaState();
     }
 
+    if (policyFlags & POLICY_FLAG_FUNCTION) {
+        newMetaState |= AMETA_FUNCTION_ON;
+    }
     getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
@@ -1010,6 +1057,26 @@
     } // release lock
 }
 
+void KeyboardInputMapper::updateLedStateLocked(bool reset) {
+    updateLedStateForModifierLocked(mLocked.capsLockLedState, LED_CAPSL,
+            AMETA_CAPS_LOCK_LATCHED, reset);
+    updateLedStateForModifierLocked(mLocked.numLockLedState, LED_NUML,
+            AMETA_NUM_LOCK_LATCHED, reset);
+    updateLedStateForModifierLocked(mLocked.scrollLockLedState, LED_SCROLLL,
+            AMETA_SCROLL_LOCK_LATCHED, reset);
+}
+
+void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState& ledState,
+        int32_t led, int32_t modifier, bool reset) {
+    if (ledState.avail) {
+        bool desiredState = (mLocked.metaState & modifier) != 0;
+        if (ledState.on != desiredState) {
+            getEventHub()->setLedState(getDeviceId(), led, desiredState);
+            ledState.on = desiredState;
+        }
+    }
+}
+
 
 // --- TrackballInputMapper ---
 
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp
index e891181..870a45c 100644
--- a/libs/ui/KeyCharacterMap.cpp
+++ b/libs/ui/KeyCharacterMap.cpp
@@ -156,26 +156,38 @@
 KeyCharacterMap*
 KeyCharacterMap::load(int id)
 {
-    KeyCharacterMap* rv = NULL;
+    KeyCharacterMap* map;
     char path[PATH_MAX];
     char propName[100];
     char dev[PROPERTY_VALUE_MAX];
-    char tmpfn[PROPERTY_VALUE_MAX];
+    char fn[PROPERTY_VALUE_MAX];
     int err;
+
+    // Check whether the EventHub has set a key character map filename for us already.
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    err = property_get(propName, fn, "");
+    if (err > 0) {
+        map = try_file(fn);
+        if (map) {
+            return map;
+        }
+        LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, fn);
+    }
+
+    // Try using the device name.
     const char* root = getenv("ANDROID_ROOT");
 
     sprintf(propName, "hw.keyboards.%u.devname", id);
     err = property_get(propName, dev, "");
     if (err > 0) {
         // replace all the spaces with underscores
-        strcpy(tmpfn, dev);
-        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
+        strcpy(fn, dev);
+        for (char *p = strchr(fn, ' '); p && *p; p = strchr(p + 1, ' '))
             *p = '_';
-        snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn);
-        //LOGD("load: dev='%s' path='%s'\n", dev, path);
-        rv = try_file(path);
-        if (rv != NULL) {
-            return rv;
+        snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, fn);
+        map = try_file(path);
+        if (map) {
+            return map;
         }
         LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev);
     } else {
@@ -183,14 +195,14 @@
     }
 
     snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root);
-    rv = try_file(path);
-    if (rv == NULL) {
-        LOGE("Can't find any keycharmaps (also tried %s)", path);
-        return NULL;
+    map = try_file(path);
+    if (map) {
+        LOGW("Using default keymap: %s", path);
+        return map;
     }
-    LOGW("Using default keymap: %s", path);
 
-    return rv;
+    LOGE("Can't find any keycharmaps (also tried %s)", path);
+    return NULL;
 }
 
 KeyCharacterMap*
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index 1c4f80c..6358fc4 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -292,6 +292,11 @@
     SharedBuffer::bufferFromData(mString)->release();
 }
 
+void String8::clear() {
+    SharedBuffer::bufferFromData(mString)->release();
+    mString = getEmptyString();
+}
+
 void String8::setTo(const String8& other)
 {
     SharedBuffer::bufferFromData(other.mString)->acquire();
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 5580700..d190e49 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -93,7 +93,37 @@
     AMETA_SHIFT_RIGHT_ON = 0x80,
 
     /* This mask is used to check whether the SYM meta key is pressed. */
-    AMETA_SYM_ON = 0x04
+    AMETA_SYM_ON = 0x04,
+
+    /* This mask is used to check whether the FUNCTION meta key is pressed. */
+    AMETA_FUNCTION_ON = 0x08,
+
+    /* This mask is used to check whether one of the CTRL meta keys is pressed. */
+    AMETA_CTRL_ON = 0x1000,
+
+    /* This mask is used to check whether the left CTRL meta key is pressed. */
+    AMETA_CTRL_LEFT_ON = 0x2000,
+
+    /* This mask is used to check whether the right CTRL meta key is pressed. */
+    AMETA_CTRL_RIGHT_ON = 0x4000,
+
+    /* This mask is used to check whether one of the META meta keys is pressed. */
+    AMETA_META_ON = 0x10000,
+
+    /* This mask is used to check whether the left META meta key is pressed. */
+    AMETA_META_LEFT_ON = 0x20000,
+
+    /* This mask is used to check whether the right META meta key is pressed. */
+    AMETA_META_RIGHT_ON = 0x40000,
+
+    /* This mask is used to check whether the CAPS LOCK meta key is latched. */
+    AMETA_CAPS_LOCK_LATCHED = 0x100000,
+
+    /* This mask is used to check whether the NUM LOCK meta key is latched. */
+    AMETA_NUM_LOCK_LATCHED = 0x200000,
+
+    /* This mask is used to check whether the SCROLL LOCK meta key is latched. */
+    AMETA_SCROLL_LOCK_LATCHED = 0x400000,
 };
 
 /*
diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h
index 496eccc0..3fcf977 100644
--- a/native/include/android/keycodes.h
+++ b/native/include/android/keycodes.h
@@ -154,6 +154,60 @@
     AKEYCODE_BUTTON_START    = 108,
     AKEYCODE_BUTTON_SELECT   = 109,
     AKEYCODE_BUTTON_MODE     = 110,
+    AKEYCODE_ESCAPE          = 111,
+    AKEYCODE_FORWARD_DEL     = 112,
+    AKEYCODE_CTRL_LEFT       = 113,
+    AKEYCODE_CTRL_RIGHT      = 114,
+    AKEYCODE_CAPS_LOCK       = 115,
+    AKEYCODE_SCROLL_LOCK     = 116,
+    AKEYCODE_META_LEFT       = 117,
+    AKEYCODE_META_RIGHT      = 118,
+    AKEYCODE_FUNCTION        = 119,
+    AKEYCODE_SYSRQ           = 120,
+    AKEYCODE_BREAK           = 121,
+    AKEYCODE_MOVE_HOME       = 122,
+    AKEYCODE_MOVE_END        = 123,
+    AKEYCODE_INSERT          = 124,
+    AKEYCODE_FORWARD         = 125,
+    AKEYCODE_MEDIA_PLAY      = 126,
+    AKEYCODE_MEDIA_PAUSE     = 127,
+    AKEYCODE_MEDIA_CLOSE     = 128,
+    AKEYCODE_MEDIA_EJECT     = 129,
+    AKEYCODE_MEDIA_RECORD    = 130,
+    AKEYCODE_F1              = 131,
+    AKEYCODE_F2              = 132,
+    AKEYCODE_F3              = 133,
+    AKEYCODE_F4              = 134,
+    AKEYCODE_F5              = 135,
+    AKEYCODE_F6              = 136,
+    AKEYCODE_F7              = 137,
+    AKEYCODE_F8              = 138,
+    AKEYCODE_F9              = 139,
+    AKEYCODE_F10             = 140,
+    AKEYCODE_F11             = 141,
+    AKEYCODE_F12             = 142,
+    AKEYCODE_NUM_LOCK        = 143,
+    AKEYCODE_NUMPAD_0        = 144,
+    AKEYCODE_NUMPAD_1        = 145,
+    AKEYCODE_NUMPAD_2        = 146,
+    AKEYCODE_NUMPAD_3        = 147,
+    AKEYCODE_NUMPAD_4        = 148,
+    AKEYCODE_NUMPAD_5        = 149,
+    AKEYCODE_NUMPAD_6        = 150,
+    AKEYCODE_NUMPAD_7        = 151,
+    AKEYCODE_NUMPAD_8        = 152,
+    AKEYCODE_NUMPAD_9        = 153,
+    AKEYCODE_NUMPAD_DIVIDE   = 154,
+    AKEYCODE_NUMPAD_MULTIPLY = 155,
+    AKEYCODE_NUMPAD_SUBTRACT = 156,
+    AKEYCODE_NUMPAD_ADD      = 157,
+    AKEYCODE_NUMPAD_DOT      = 158,
+    AKEYCODE_NUMPAD_COMMA    = 159,
+    AKEYCODE_NUMPAD_ENTER    = 160,
+    AKEYCODE_NUMPAD_EQUALS   = 161,
+    AKEYCODE_NUMPAD_LEFT_PAREN = 162,
+    AKEYCODE_NUMPAD_RIGHT_PAREN = 163,
+
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.