Merge "Update WebView.addJavascriptInterface() to ignore null instances"
diff --git a/api/current.xml b/api/current.xml
index 290eff0..7d7bec1 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -161134,6 +161134,468 @@
 >
 </field>
 </interface>
+<class name="Ptp"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Ptp"
+ type="android.provider.Ptp"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="AUTHORITY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;ptp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Ptp.Device"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<constructor name="Ptp.Device"
+ type="android.provider.Ptp.Device"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+</method>
+<field name="CONTENT_URI"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MANUFACTURER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;manufacturer&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODEL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;model&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Ptp.Object"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<constructor name="Ptp.Object"
+ type="android.provider.Ptp.Object"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="objectID" type="long">
+</parameter>
+</method>
+<method name="getContentUriForImport"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="objectID" type="long">
+</parameter>
+<parameter name="destPath" type="java.lang.String">
+</parameter>
+</method>
+<method name="getContentUriForObjectChildren"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="objectID" type="long">
+</parameter>
+</method>
+<method name="getContentUriForStorageChildren"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="storageID" type="long">
+</parameter>
+</method>
+<field name="ASSOCIATION_DESC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;association_desc&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ASSOCIATION_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;association_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DATE_CREATED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;date_created&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DATE_MODIFIED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;date_modified&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FORMAT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;format&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IMAGE_DEPTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;image_depth&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IMAGE_HEIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;image_height&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IMAGE_WIDTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;image_width&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYWORDS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;keywords&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;parent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PROTECTION_STATUS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;protection_status&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SEQUENCE_NUMBER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sequence_number&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIZE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;size&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STORAGE_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;storage_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_FORMAT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_format&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_HEIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_height&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_SIZE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_size&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THUMB_WIDTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;thumb_width&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Ptp.Storage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<constructor name="Ptp.Storage"
+ type="android.provider.Ptp.Storage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+</method>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="deviceID" type="int">
+</parameter>
+<parameter name="storageID" type="long">
+</parameter>
+</method>
+<field name="DESCRIPTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;description&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IDENTIFIER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;identifier&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="SearchRecentSuggestions"
  extends="java.lang.Object"
  abstract="false"
@@ -246160,6 +246622,17 @@
  visibility="public"
 >
 </method>
+<method name="getCustomSelectionActionModeCallback"
+ return="android.view.ActionMode.Callback"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDefaultEditable"
  return="boolean"
  abstract="false"
@@ -246958,6 +247431,19 @@
 <parameter name="visible" type="boolean">
 </parameter>
 </method>
+<method name="setCustomSelectionActionModeCallback"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="actionModeCallback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 <method name="setEditableFactory"
  return="void"
  abstract="false"
@@ -249605,7 +250091,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0a2e031..a0a6b42 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2363,7 +2363,11 @@
      */
     public boolean onMenuOpened(int featureId, Menu menu) {
         if (featureId == Window.FEATURE_ACTION_BAR) {
-            mActionBar.dispatchMenuVisibilityChanged(true);
+            if (mActionBar != null) {
+                mActionBar.dispatchMenuVisibilityChanged(true);
+            } else {
+                Log.e(TAG, "Tried to open action bar menu with no action bar");
+            }
         }
         return true;
     }
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index d1ab8d5..abeeb74 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -110,16 +110,23 @@
 
                     IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
                             selectionArgs, sortOrder, observer, window);
-                    reply.writeNoException();
                     if (bulkCursor != null) {
-                        reply.writeStrongBinder(bulkCursor.asBinder());
-
+                        final IBinder binder = bulkCursor.asBinder();
                         if (wantsCursorMetadata) {
-                            reply.writeInt(bulkCursor.count());
-                            reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
-                                bulkCursor.getColumnNames()));
+                            final int count = bulkCursor.count();
+                            final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
+                                    bulkCursor.getColumnNames());
+
+                            reply.writeNoException();
+                            reply.writeStrongBinder(binder);
+                            reply.writeInt(count);
+                            reply.writeInt(index);
+                        } else {
+                            reply.writeNoException();
+                            reply.writeStrongBinder(binder);
                         }
                     } else {
+                        reply.writeNoException();
                         reply.writeStrongBinder(null);
                     }
 
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index 01ae1da..9893133 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -16,7 +16,6 @@
 
 package android.content.res;
 
-import android.os.MemoryFile;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -184,14 +183,9 @@
 
         @Override
         public int read() throws IOException {
-            if (mRemaining >= 0) {
-                if (mRemaining == 0) return -1;
-                int res = super.read();
-                if (res >= 0) mRemaining--;
-                return res;
-            }
-            
-            return super.read();
+            byte[] buffer = new byte[1];
+            int result = read(buffer, 0, 1);
+            return result == -1 ? -1 : buffer[0] & 0xff;
         }
 
         @Override
@@ -209,16 +203,7 @@
 
         @Override
         public int read(byte[] buffer) throws IOException {
-            if (mRemaining >= 0) {
-                if (mRemaining == 0) return -1;
-                int count = buffer.length;
-                if (count > mRemaining) count = (int)mRemaining;
-                int res = super.read(buffer, 0, count);
-                if (res >= 0) mRemaining -= res;
-                return res;
-            }
-            
-            return super.read(buffer);
+            return read(buffer, 0, buffer.length);
         }
 
         @Override
@@ -231,7 +216,6 @@
                 return res;
             }
             
-            // TODO Auto-generated method stub
             return super.skip(count);
         }
 
diff --git a/core/java/android/provider/Ptp.java b/core/java/android/provider/Ptp.java
index 2c54370..0f0919e 100644
--- a/core/java/android/provider/Ptp.java
+++ b/core/java/android/provider/Ptp.java
@@ -20,10 +20,13 @@
 import android.net.Uri;
 import android.util.Log;
 
-
 /**
  * The PTP provider supports accessing content on PTP devices.
- * @hide
+ * Currently the provider supports:
+ * - enumerating the storage units, files and directories on PTP devices
+ * - deleting files and directories on PTP devices
+ * - importing a file from PTP device into the host device's storage
+ *   and adding it to the media provider
  */
 public final class Ptp
 {
@@ -36,6 +39,8 @@
 
     /**
      * Contains list of all PTP devices
+     * The BaseColumns._ID column contains a hardware specific identifier for the attached
+     * USB device, and is not guaranteed to be persistent across USB disconnects.
      */
     public static final class Device implements BaseColumns {
 
@@ -59,7 +64,8 @@
     }
 
     /**
-     * Contains list of storage units for an PTP device
+     * Contains list of storage units for an PTP device.
+     * The BaseColumns._ID column contains the PTP StorageID for the storage unit.
      */
     public static final class Storage implements BaseColumns {
 
@@ -85,7 +91,10 @@
     }
 
     /**
-     * Contains list of objects on an PTP device
+     * Contains list of objects on a PTP device.
+     * The columns in this table correspond directly to the ObjectInfo dataset
+     * described in the PTP specification (PIMA 15740:2000).
+     * The BaseColumns._ID column contains the object's PTP ObjectHandle.
      */
     public static final class Object implements BaseColumns {
 
@@ -135,14 +144,14 @@
         public static final String STORAGE_ID = "storage_id";
 
         /**
-         * The object's format.  Can be one of the FORMAT_* symbols below,
-         * or any of the valid PTP object formats as defined in the PTP specification.
+         * The object's format.  Can be any of the valid PTP object formats
+         * as defined in the PTP specification.
          * <P>Type: INTEGER</P>
          */
         public static final String FORMAT = "format";
 
         /**
-         * The protection status of the object.  See the PROTECTION_STATUS_*symbols below.
+         * The protection status of the object.
          * <P>Type: INTEGER</P>
          */
         public static final String PROTECTION_STATUS = "protection_status";
@@ -154,8 +163,8 @@
         public static final String SIZE = "size";
 
         /**
-         * The object's thumbnail format.  Can be one of the FORMAT_* symbols below,
-         * or any of the valid PTP object formats as defined in the PTP specification.
+         * The object's thumbnail format.  Can be any of the valid PTP object formats
+         * as defined in the PTP specification.
          * <P>Type: INTEGER</P>
          */
         public static final String THUMB_FORMAT = "thumb_format";
@@ -211,7 +220,6 @@
 
         /**
          * The association type for a container object.
-         * For folders this is typically {@link #ASSOCIATION_TYPE_GENERIC_FOLDER}
          * <P>Type: INTEGER</P>
          */
         public static final String ASSOCIATION_TYPE = "association_type";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4ea4a16..ed71da2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2576,12 +2576,13 @@
         public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
 
         /**
-         * A positive value indicates the frequency of SamplingProfiler
-         * taking snapshots in hertz. Zero value means SamplingProfiler is disabled.
+         * A positive value indicates how often the SamplingProfiler
+         * should take snapshots. Zero value means SamplingProfiler
+         * is disabled.
          *
          * @hide
          */
-        public static final String SAMPLING_PROFILER_HZ = "sampling_profiler_hz";
+        public static final String SAMPLING_PROFILER_MS = "sampling_profiler_ms";
 
         /**
          * Settings classname to launch when Settings is clicked from All
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 2f7482c..a6fd2f1 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -663,6 +663,25 @@
     }
 
     /**
+     * @param wp
+     */
+    private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
+        final int previousTop     = fmi.top;
+        final int previousAscent  = fmi.ascent;
+        final int previousDescent = fmi.descent;
+        final int previousBottom  = fmi.bottom;
+        final int previousLeading = fmi.leading;
+
+        wp.getFontMetricsInt(fmi);
+
+        fmi.top     = Math.min(fmi.top,     previousTop);
+        fmi.ascent  = Math.min(fmi.ascent,  previousAscent);
+        fmi.descent = Math.max(fmi.descent, previousDescent);
+        fmi.bottom  = Math.max(fmi.bottom,  previousBottom);
+        fmi.leading = Math.max(fmi.leading, previousLeading);
+    }
+
+    /**
      * Utility function for measuring and rendering text.  The text must
      * not include a tab or emoji.
      *
@@ -703,7 +722,7 @@
         }
 
         if (fmi != null) {
-            wp.getFontMetricsInt(fmi);
+            expandMetricsFromPaint(fmi, wp);
         }
 
         if (c != null) {
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 5f3184d..dcde6ef 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2611,6 +2611,10 @@
             captureKeyLog("captureDispatchKeyEvent", event);
         }
 
+        // Make sure the fallback event policy sees all keys that will be delivered to the
+        // view hierarchy.
+        mFallbackEventHandler.preDispatchKeyEvent(event);
+
         // Deliver the key to the view hierarchy.
         if (mView.dispatchKeyEvent(event)) {
             finishKeyEvent(event, sendDone, true);
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index dd71b3f..7340486 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -85,6 +85,16 @@
      * @param thumb Drawable representing the thumb
      */
     public void setThumb(Drawable thumb) {
+        boolean needUpdate;
+        // This way, calling setThumb again with the same bitmap will result in
+        // it recalcuating mThumbOffset (if for example it the bounds of the
+        // drawable changed)
+        if (mThumb != null && thumb != mThumb) {
+            mThumb.setCallback(null);
+            needUpdate = true;
+        } else {
+            needUpdate = false;
+        }
         if (thumb != null) {
             thumb.setCallback(this);
 
@@ -92,9 +102,25 @@
             // such that the thumb will hang halfway off either edge of the
             // progress bar.
             mThumbOffset = thumb.getIntrinsicWidth() / 2;
+
+            // If we're updating get the new states
+            if (needUpdate &&
+                    (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth()
+                        || thumb.getIntrinsicHeight() != mThumb.getIntrinsicHeight())) {
+                requestLayout();
+            }
         }
         mThumb = thumb;
         invalidate();
+        if (needUpdate) {
+            updateThumbPos(getWidth(), getHeight());
+            if (thumb.isStateful()) {
+                // Note that if the states are different this won't work.
+                // For now, let's consider that an app bug.
+                int[] state = getDrawableState();
+                thumb.setState(state);
+            }
+        }
     }
 
     /**
@@ -191,6 +217,10 @@
     
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        updateThumbPos(w, h);
+    }
+
+    private void updateThumbPos(int w, int h) {
         Drawable d = getCurrentDrawable();
         Drawable thumb = mThumb;
         int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 43434d3..ced8e9b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -89,6 +89,7 @@
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.ActionMode;
+import android.view.ActionMode.Callback;
 import android.view.ContextMenu;
 import android.view.DragEvent;
 import android.view.Gravity;
@@ -314,6 +315,7 @@
     Drawable mSelectHandleCenter;
 
     private int mLastDownPositionX, mLastDownPositionY;
+    private Callback mCustomSelectionActionModeCallback;
 
     /*
      * Kick-start the font cache for the zygote process (to pay the cost of
@@ -6869,7 +6871,7 @@
                 }
 
                 if (mSelectAllOnFocus) {
-                    Selection.setSelection((Spannable) mText, 0, mText.length());
+                    selectAll();
                 }
 
                 mTouchFocusSelected = true;
@@ -7401,10 +7403,8 @@
             return false;
         }
 
-        if (mText.length() > 0 && hasSelection()) {
-            if (mText instanceof Editable && mInput != null) {
-                return true;
-            }
+        if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mInput != null) {
+            return true;
         }
 
         return false;
@@ -7524,13 +7524,17 @@
         return (int) (range & 0x00000000FFFFFFFFL);
     }
 
+    private void selectAll() {
+        Selection.setSelection((Spannable) mText, 0, mText.length());
+    }
+
     private void selectCurrentWord() {
         if (hasPasswordTransformationMethod()) {
             // selectCurrentWord is not available on a password field and would return an
             // arbitrary 10-charater selection around pressed position. Select all instead.
             // Note that cut/copy menu entries are not available for passwords.
             // This is however useful to delete or paste to replace the entire content.
-            Selection.setSelection((Spannable) mText, 0, mText.length());
+            selectAll();
             return;
         }
 
@@ -7862,16 +7866,36 @@
     }
 
     /**
-     * Provides the callback used to start a selection action mode.
+     * If provided, this ActionMode.Callback will be used to create the ActionMode when text
+     * selection is initiated in this View.
      *
-     * @return A callback instance that will be used to start selection mode, or null if selection
-     * mode is not available.
+     * The standard implementation populates the menu with a subset of Select All, Cut, Copy and
+     * Paste actions, depending on what this View supports.
+     *
+     * A custom implementation can add new entries in the default menu in its
+     * {@link ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The default actions
+     * can also be removed from the menu using {@link Menu#removeItem(int)} and passing
+     * {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy} or
+     * {@link android.R.id#paste} ids as parameters.
+     *
+     * Action click events should be handled by the custom implementation of
+     * {@link ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}.
+     *
+     * Note that text selection mode is not started when a TextView receives focus and the
+     * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in
+     * that case, to allow for quick replacement.
      */
-    private ActionMode.Callback getActionModeCallback() {
-        if (canSelectText()) {
-            return new SelectionActionModeCallback();
-        }
-        return null;
+    public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
+        mCustomSelectionActionModeCallback = actionModeCallback;
+    }
+
+    /**
+     * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null.
+     *
+     * @return The current custom selection callback.
+     */
+    public ActionMode.Callback getCustomSelectionActionModeCallback() {
+        return mCustomSelectionActionModeCallback;
     }
 
     /**
@@ -7884,7 +7908,8 @@
             return false;
         }
 
-        ActionMode.Callback actionModeCallback = getActionModeCallback();
+        selectCurrentWord();
+        ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
         if (actionModeCallback != null) {
             mSelectionActionMode = startActionMode(actionModeCallback);
             return mSelectionActionMode != null;
@@ -7950,6 +7975,12 @@
         sLastCutOrCopyTime = SystemClock.uptimeMillis();
     }
 
+    /**
+     * An ActionMode Callback class that is used to provide actions while in text selection mode.
+     *
+     * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending
+     * on which of these this TextView supports.
+     */
     private class SelectionActionModeCallback implements ActionMode.Callback {
 
         @Override
@@ -7968,48 +7999,47 @@
             mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle));
             mode.setSubtitle(null);
 
-            boolean atLeastOne = false;
-
             if (canSelectText()) {
-                selectCurrentWord();
-
                 menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
                     setAlphabeticShortcut('a').
                     setShowAsAction(
                             MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-                atLeastOne = true;
             }
 
             if (canCut()) {
                 menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
-                    setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCutDrawable, 0)).
+                    setIcon(styledAttributes.getResourceId(
+                            R.styleable.Theme_actionModeCutDrawable, 0)).
                     setAlphabeticShortcut('x').
                     setShowAsAction(
                             MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-                atLeastOne = true;
             }
 
             if (canCopy()) {
                 menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
-                    setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCopyDrawable, 0)).
+                    setIcon(styledAttributes.getResourceId(
+                            R.styleable.Theme_actionModeCopyDrawable, 0)).
                     setAlphabeticShortcut('c').
                     setShowAsAction(
                             MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-                atLeastOne = true;
             }
 
             if (canPaste()) {
                 menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
-                        setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModePasteDrawable, 0)).
+                        setIcon(styledAttributes.getResourceId(
+                                R.styleable.Theme_actionModePasteDrawable, 0)).
                         setAlphabeticShortcut('v').
                         setShowAsAction(
                                 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-                atLeastOne = true;
             }
 
             styledAttributes.recycle();
 
-            if (atLeastOne) {
+            if (mCustomSelectionActionModeCallback != null) {
+                mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu);
+            }
+
+            if (menu.hasVisibleItems() || mode.getCustomView() != null) {
                 getSelectionController().show();
                 return true;
             } else {
@@ -8019,15 +8049,23 @@
 
         @Override
         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            if (mCustomSelectionActionModeCallback != null) {
+                return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu);
+            }
             return true;
         }
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            if (mCustomSelectionActionModeCallback != null &&
+                 mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) {
+                return true;
+            }
+
             final int itemId = item.getItemId();
 
             if (itemId == ID_SELECT_ALL) {
-                Selection.setSelection((Spannable) mText, 0, mText.length());
+                selectAll();
                 // Update controller positions after selection change.
                 if (hasSelectionController()) {
                     getSelectionController().show();
@@ -8070,6 +8108,9 @@
 
         @Override
         public void onDestroyActionMode(ActionMode mode) {
+            if (mCustomSelectionActionModeCallback != null) {
+                mCustomSelectionActionModeCallback.onDestroyActionMode(mode);
+            }
             Selection.setSelection((Spannable) mText, getSelectionStart());
             hideSelectionModifierCursorController();
             mSelectionActionMode = null;
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 0891acc..101dd91 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -95,7 +95,7 @@
      *
      * msg.arg1 == 0 : STATUS_SUCCESSFUL
      *             1 : STATUS_BINDING_UNSUCCESSFUL
-     * msg.arg2 == token parameter
+     * msg.obj  == the AsyncChannel
      * msg.replyTo == dstMessenger if successful
      */
     public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
@@ -136,7 +136,7 @@
      *
      * msg.arg1 == 0 : STATUS_SUCCESSFUL
      *               : All other values signify failure and the channel state is indeterminate
-     * msg.arg2 == token parameter
+     * msg.obj  == the AsyncChannel
      * msg.replyTo = messenger disconnecting or null if it was never connected.
      */
     public static final int CMD_CHANNEL_DISCONNECTED = -5;
@@ -179,14 +179,12 @@
      * @param dstPackageName is the destination package name
      * @param dstClassName is the fully qualified class name (i.e. contains
      *            package name)
-     * @param token unique id for this connection
      */
     private void connectSrcHandlerToPackage(
-            Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName,
-            int token) {
+            Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
         if (DBG) log("connect srcHandler to dst Package & class E");
 
-        mConnection = new AsyncChannelConnection(token);
+        mConnection = new AsyncChannelConnection();
 
         /* Initialize the source information */
         mSrcContext = srcContext;
@@ -205,7 +203,7 @@
         intent.setClassName(dstPackageName, dstClassName);
         boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
         if (!result) {
-            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL, token);
+            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL);
         }
 
         if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
@@ -216,7 +214,7 @@
      *
      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
      *      msg.arg1 = status
-     *      msg.arg2 = token
+     *      msg.obj = the AsyncChannel
      *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
@@ -224,10 +222,9 @@
      * @param dstPackageName is the destination package name
      * @param dstClassName is the fully qualified class name (i.e. contains
      *            package name)
-     * @param token returned in msg.arg2
      */
     public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
-            String dstClassName, int token) {
+            String dstClassName) {
         if (DBG) log("connect srcHandler to dst Package & class E");
 
         final class ConnectAsync implements Runnable {
@@ -235,25 +232,21 @@
             Handler mSrcHdlr;
             String mDstPackageName;
             String mDstClassName;
-            int mToken;
 
             ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
-                    String dstClassName, int token) {
+                    String dstClassName) {
                 mSrcCtx = srcContext;
                 mSrcHdlr = srcHandler;
                 mDstPackageName = dstPackageName;
                 mDstClassName = dstClassName;
-                mToken = token;
             }
 
             public void run() {
-                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName,
-                        mToken);
+                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName);
             }
         }
 
-        ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName,
-                token);
+        ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName);
         new Thread(ca).start();
 
         if (DBG) log("connect srcHandler to dst Package & class X");
@@ -264,15 +257,14 @@
      *
      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
      *      msg.arg1 = status
-     *      msg.arg2 = token
+     *      msg.obj = the AsyncChannel
      *
      * @param srcContext
      * @param srcHandler
      * @param klass is the class to send messages to.
-     * @param token returned in msg.arg2
      */
-    public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) {
-        connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token);
+    public void connect(Context srcContext, Handler srcHandler, Class<?> klass) {
+        connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName());
     }
 
     /**
@@ -280,14 +272,13 @@
      *
      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
      *      msg.arg1 = status
-     *      msg.arg2 = token
+     *      msg.obj = the AsyncChannel
      *
      * @param srcContext
      * @param srcHandler
      * @param dstMessenger
-     * @param token returned in msg.arg2
      */
-    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) {
+    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
         if (DBG) log("connect srcHandler to the dstMessenger  E");
 
         // Initialize source fields
@@ -301,7 +292,7 @@
         if (DBG) log("tell source we are half connected");
 
         // Tell source we are half connected
-        replyHalfConnected(STATUS_SUCCESSFUL, token);
+        replyHalfConnected(STATUS_SUCCESSFUL);
 
         if (DBG) log("connect srcHandler to the dstMessenger X");
     }
@@ -311,16 +302,15 @@
      *
      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
      *      msg.arg1 = status
-     *      msg.arg2 = token
+     *      msg.obj = the AsyncChannel
      *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
      *            messages
      * @param dstHandler is the hander to send messages to.
-     * @param token returned in msg.arg2
      */
-    public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) {
-        connect(srcContext, srcHandler, new Messenger(dstHandler), token);
+    public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) {
+        connect(srcContext, srcHandler, new Messenger(dstHandler));
     }
 
     /**
@@ -328,14 +318,13 @@
      *
      * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
      *      msg.arg1 = status
-     *      msg.arg2 = token
+     *      msg.obj = the AsyncChannel
      *
      * @param srcAsyncService
      * @param dstMessenger
-     * @param token returned in msg.arg2
      */
-    public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) {
-        connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token);
+    public void connect(AsyncService srcAsyncService, Messenger dstMessenger) {
+        connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger);
     }
 
     /**
@@ -351,15 +340,14 @@
     /**
      * Disconnect
      */
-    public void disconnect(int token) {
+    public void disconnect() {
         if (mConnection != null) {
-            mConnection.setToken(token);
             mSrcContext.unbindService(mConnection);
         }
         if (mSrcHandler != null) {
             Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
             msg.arg1 = STATUS_SUCCESSFUL;
-            msg.arg2 = token;
+            msg.obj = this;
             msg.replyTo = mDstMessenger;
             mSrcHandler.sendMessage(msg);
         }
@@ -727,10 +715,10 @@
      *
      * @param status to be stored in msg.arg1
      */
-    private void replyHalfConnected(int status, int token) {
+    private void replyHalfConnected(int status) {
         Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
         msg.arg1 = status;
-        msg.arg2 = token;
+        msg.obj = this;
         msg.replyTo = mDstMessenger;
         mSrcHandler.sendMessage(msg);
     }
@@ -739,28 +727,18 @@
      * ServiceConnection to receive call backs.
      */
     class AsyncChannelConnection implements ServiceConnection {
-        private int mToken;
-
-        AsyncChannelConnection(int token) {
-            mToken = token;
-        }
-
-        /**
-         * @param token
-         */
-        public void setToken(int token) {
-            mToken = token;
+        AsyncChannelConnection() {
         }
 
         public void onServiceConnected(ComponentName className, IBinder service) {
             mDstMessenger = new Messenger(service);
-            replyHalfConnected(STATUS_SUCCESSFUL, mToken);
+            replyHalfConnected(STATUS_SUCCESSFUL);
         }
 
         public void onServiceDisconnected(ComponentName className) {
             Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
             msg.arg1 = STATUS_SUCCESSFUL;
-            msg.arg2 = mToken;
+            msg.obj = AsyncChannel.this;
             msg.replyTo = mDstMessenger;
             mSrcHandler.sendMessage(msg);
         }
diff --git a/core/res/res/drawable-mdpi/scrubber_control_holo.png b/core/res/res/drawable-mdpi/scrubber_control_holo.png
index 135b2aa..8457833 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_holo.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png
new file mode 100644
index 0000000..8582b13
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png
new file mode 100644
index 0000000..6ad876e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
index 7b48cf9..baf70cd 100644
--- a/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
index 7c84ac9..6f31497 100644
--- a/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
index 90172a5..b117bb8 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
@@ -15,11 +15,14 @@
 -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
     <item android:id="@android:id/background"
-          android:drawable="@android:drawable/scrubber_track_holo_dark" />
-    <item android:id="@android:id/secondaryProgress"
-          android:drawable="@android:drawable/scrubber_track_holo_dark" />
-    <item android:id="@android:id/progress"
-          android:drawable="@android:drawable/scrubber_track_holo_dark" />
+            android:drawable="@android:drawable/scrubber_track_holo_dark" />
+    <item android:id="@android:id/secondaryProgress">
+        <scale android:scaleWidth="100%"
+                android:drawable="@android:drawable/scrubber_secondary_holo" />
+    </item>
+    <item android:id="@android:id/progress">
+        <scale android:scaleWidth="100%"
+                android:drawable="@android:drawable/scrubber_primary_holo" />
+    </item>
 </layer-list>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
index 5fc9697..6cd08ea 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
@@ -15,11 +15,14 @@
 -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
     <item android:id="@android:id/background"
-          android:drawable="@android:drawable/scrubber_track_holo_light" />
-    <item android:id="@android:id/secondaryProgress"
-          android:drawable="@android:drawable/scrubber_track_holo_light" />
-    <item android:id="@android:id/progress"
-          android:drawable="@android:drawable/scrubber_track_holo_light" />
+            android:drawable="@android:drawable/scrubber_track_holo_light" />
+    <item android:id="@android:id/secondaryProgress">
+        <scale android:scaleWidth="100%"
+                android:drawable="@android:drawable/scrubber_secondary_holo" />
+    </item>
+    <item android:id="@android:id/progress">
+        <scale android:scaleWidth="100%"
+                android:drawable="@android:drawable/scrubber_primary_holo" />
+    </item>
 </layer-list>
diff --git a/core/res/res/values-id-rID/arrays.xml b/core/res/res/values-in-rID/arrays.xml
similarity index 100%
rename from core/res/res/values-id-rID/arrays.xml
rename to core/res/res/values-in-rID/arrays.xml
diff --git a/core/res/res/values-id-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml
similarity index 100%
rename from core/res/res/values-id-rID/donottranslate-cldr.xml
rename to core/res/res/values-in-rID/donottranslate-cldr.xml
diff --git a/core/res/res/values-id/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml
similarity index 100%
rename from core/res/res/values-id/donottranslate-cldr.xml
rename to core/res/res/values-in/donottranslate-cldr.xml
diff --git a/core/res/res/values-id/strings.xml b/core/res/res/values-in/strings.xml
similarity index 100%
rename from core/res/res/values-id/strings.xml
rename to core/res/res/values-in/strings.xml
diff --git a/core/res/res/values-he-rIL/arrays.xml b/core/res/res/values-iw-rIL/arrays.xml
similarity index 100%
rename from core/res/res/values-he-rIL/arrays.xml
rename to core/res/res/values-iw-rIL/arrays.xml
diff --git a/core/res/res/values-he/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml
similarity index 100%
rename from core/res/res/values-he/donottranslate-cldr.xml
rename to core/res/res/values-iw/donottranslate-cldr.xml
diff --git a/core/res/res/values-he/strings.xml b/core/res/res/values-iw/strings.xml
similarity index 100%
rename from core/res/res/values-he/strings.xml
rename to core/res/res/values-iw/strings.xml
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index fef3c13..c67bbb7 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -37,4 +37,7 @@
     <dimen name="thumbnail_width">160dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
     <dimen name="thumbnail_height">100dp</dimen>
+    <!-- The standard size (both width and height) of an application icon that
+         will be displayed in the app launcher and elsewhere. -->
+    <dimen name="app_icon_size">64dip</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index eafa9b6..0f5ff05 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1806,7 +1806,7 @@
 
     <!-- Do not translate.  WebView User Agent string -->
     <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
-        AppleWebKit/534.12 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.12</string>
+        AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.13</string>
     <!-- Do not translate.  WebView User Agent targeted content -->
     <string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
 
diff --git a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
index 2b0e4af..5e3252c 100755
--- a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
+++ b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
@@ -4,7 +4,7 @@
     <ssid>opennet</ssid>
     <security>NONE</security>
   </accesspoint>
-    <accesspoint>
+  <accesspoint>
     <ssid>GoogleGuest</ssid>
     <security>NONE</security>
   </accesspoint>
@@ -14,6 +14,16 @@
     <password>androidwifi</password>
   </accesspoint>
   <accesspoint>
+    <ssid>securenetstatic</ssid>
+    <security>PSK</security>
+    <password>androidwifi</password>
+    <ip>192.168.14.2</ip>
+    <gateway>192.168.14.1</gateway>
+    <networkprefixlength>24</networkprefixlength>
+    <dns1>192.168.14.1</dns1>
+    <dns2>192.168.1.9</dns2>
+  </accesspoint>
+  <accesspoint>
     <ssid>botnet</ssid>
     <security>EAP</security>
     <eap>PEAP</eap>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index 863fbe6..21f1bfc 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -25,11 +25,18 @@
 
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
-
+import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.util.Log;
+
 import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 
@@ -38,7 +45,8 @@
  * The configurations of an access point is included in tag
  * <accesspoint></accesspoint>. The supported configuration includes: ssid,
  * security, eap, phase2, identity, password, anonymousidentity, cacert, usercert,
- * in which each is included in the corresponding tags. All access points have to be
+ * in which each is included in the corresponding tags. Static IP setting is also supported.
+ * Tags that can be used include: ip, gateway, netmask, dns1, dns2. All access points have to be
  * enclosed in tags of <resources></resources>.
  *
  * The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2.
@@ -52,6 +60,9 @@
  *   <password>abcdefgh</password>
  *   </accesspoint>
  * </resources>
+ *
+ * Note:ssid and security have to be the first two tags
+ *      for static ip setting, tag "ip" should be listed before other fields: dns, gateway, netmask.
  */
 public class AccessPointParserHelper {
     private static final String KEYSTORE_SPACE = "keystore://";
@@ -93,9 +104,11 @@
         boolean security = false;
         boolean password = false;
         boolean ip = false;
-        boolean subnetmask = false;
         boolean gateway = false;
-        boolean dns = false;
+        boolean networkprefix = false;
+        boolean netmask = false;
+        boolean dns1 = false;
+        boolean dns2 = false;
         boolean eap = false;
         boolean phase2 = false;
         boolean identity = false;
@@ -104,6 +117,8 @@
         boolean usercert = false;
         WifiConfiguration config = null;
         int securityType = NONE;
+        LinkProperties mLinkProperties = null;
+        InetAddress mInetAddr = null;
 
         @Override
         public void startElement(String uri, String localName, String tagName,
@@ -138,12 +153,37 @@
             if (tagName.equalsIgnoreCase("usercert")) {
                 usercert = true;
             }
+            if (tagName.equalsIgnoreCase("ip")) {
+                mLinkProperties = new LinkProperties();
+                ip = true;
+            }
+            if (tagName.equalsIgnoreCase("gateway")) {
+                gateway = true;
+            }
+            if (tagName.equalsIgnoreCase("networkprefixlength")) {
+                networkprefix = true;
+            }
+            if (tagName.equalsIgnoreCase("netmask")) {
+                netmask = true;
+            }
+            if (tagName.equalsIgnoreCase("dns1")) {
+                dns1 = true;
+            }
+            if (tagName.equalsIgnoreCase("dns2")) {
+                dns2 = true;
+            }
         }
 
         @Override
         public void endElement(String uri, String localName, String tagName) throws SAXException {
-            Log.v(TAG, "endElement: " + tagName);
             if (tagName.equalsIgnoreCase("accesspoint")) {
+                if (mLinkProperties != null) {
+                    config.ipAssignment = IpAssignment.STATIC;
+                    config.linkProperties = mLinkProperties;
+                } else {
+                    config.ipAssignment = IpAssignment.DHCP;
+                }
+                config.proxySettings = ProxySettings.NONE;
                 networks.add(config);
             }
         }
@@ -152,14 +192,11 @@
         public void characters(char ch[], int start, int length) throws SAXException {
             if (ssid) {
                 config.SSID = new String(ch, start, length);
-                Log.v(TAG, "ssid: " + config.SSID);
                 ssid = false;
             }
             if (security) {
                 String securityStr = (new String(ch, start, length)).toUpperCase();
-                Log.v(TAG, "security: " + securityStr);
                 securityType = getSecurityType(securityStr);
-                Log.v(TAG, "securityType = " + securityType);
                 switch (securityType) {
                     case NONE:
                         config.allowedKeyManagement.set(KeyMgmt.NONE);
@@ -175,6 +212,13 @@
                     case EAP:
                         config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
                         config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+                        // Initialize other fields.
+                        config.phase2.setValue("");
+                        config.ca_cert.setValue("");
+                        config.client_cert.setValue("");
+                        config.private_key.setValue("");
+                        config.identity.setValue("");
+                        config.anonymous_identity.setValue("");
                         break;
                     default:
                         throw new SAXException();
@@ -187,7 +231,6 @@
                 if (len == 0) {
                     throw new SAXException();
                 }
-                Log.v(TAG, "passwordStr:" + passwordStr);
                 if (securityType == WEP) {
                     if ((len == 10 || len == 26 || len == 58) &&
                             passwordStr.matches("[0-9A-Fa-f]*")) {
@@ -242,21 +285,94 @@
                 config.client_cert.setValue(KEYSTORE_SPACE);
                 usercert = false;
             }
+            if (ip) {
+                try {
+                    String ipAddr = new String(ch, start, length);
+                    if (!InetAddress.isNumeric(ipAddr)) {
+                        throw new SAXException();
+                    }
+                    mInetAddr = InetAddress.getByName(ipAddr);
+                } catch (UnknownHostException e) {
+                    throw new SAXException();
+                }
+                ip = false;
+            }
+            if (gateway) {
+                try {
+                    String gwAddr = new String(ch, start, length);
+                    if (!InetAddress.isNumeric(gwAddr)) {
+                        throw new SAXException();
+                    }
+                    mLinkProperties.setGateway(InetAddress.getByName(gwAddr));
+                } catch (UnknownHostException e) {
+                    throw new SAXException();
+                }
+                gateway = false;
+            }
+            if (networkprefix) {
+                try {
+                    int nwPrefixLength = Integer.parseInt(new String(ch, start, length));
+                    if ((nwPrefixLength < 0) || (nwPrefixLength > 32)) {
+                        throw new SAXException();
+                    }
+                    mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, nwPrefixLength));
+                } catch (NumberFormatException e) {
+                    throw new SAXException();
+                }
+                networkprefix = false;
+            }
+            if (netmask) {
+                try {
+                    String netMaskStr = new String(ch, start, length);
+                    if (!InetAddress.isNumeric(netMaskStr)) {
+                        throw new SAXException();
+                    }
+                    InetAddress netMaskAddr = InetAddress.getByName(netMaskStr);
+                    mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, netMaskAddr));
+                } catch (UnknownHostException e) {
+                    throw new SAXException();
+                }
+                netmask = false;
+            }
+            if (dns1) {
+                try {
+                    String dnsAddr = new String(ch, start, length);
+                    if (!InetAddress.isNumeric(dnsAddr)) {
+                        throw new SAXException();
+                    }
+                    mLinkProperties.addDns(InetAddress.getByName(dnsAddr));
+                } catch (UnknownHostException e) {
+                    throw new SAXException();
+                }
+                dns1 = false;
+            }
+            if (dns2) {
+                try {
+                    String dnsAddr = new String(ch, start, length);
+                    if (!InetAddress.isNumeric(dnsAddr)) {
+                        throw new SAXException();
+                    }
+                    mLinkProperties.addDns(InetAddress.getByName(dnsAddr));
+                } catch (UnknownHostException e) {
+                    throw new SAXException();
+                }
+                dns2 = false;
+            }
         }
     };
 
-    public AccessPointParserHelper() {
-    }
-
     /**
-     * Process the accesspoint.xml file
-     * @return List of WifiConfiguration
-     * @throws Exception when parsing the XML file
+     * Process the InputStream in
+     * @param in is the InputStream that can be used for XML parsing
+     * @throws Exception
      */
-    public List<WifiConfiguration> processAccessPoint(InputStream in) throws Exception {
+    public AccessPointParserHelper(InputStream in) throws Exception {
         SAXParserFactory factory = SAXParserFactory.newInstance();
         SAXParser saxParser = factory.newSAXParser();
         saxParser.parse(in, mHandler);
+    }
+
+    public List<WifiConfiguration> getNetworkConfigurations() throws Exception {
         return networks;
     }
 }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 37b9f52..2888696 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -16,10 +16,8 @@
 
 package com.android.connectivitymanagertest;
 
-import com.android.connectivitymanagertest.R;
 import android.app.Activity;
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.BroadcastReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -36,7 +34,6 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
 
-import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiInfo;
@@ -52,7 +49,7 @@
 
     public static final String LOG_TAG = "ConnectivityManagerTestActivity";
     public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
-    public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
+    public static final int WIFI_SCAN_TIMEOUT = 50 * 1000;
     public static final int SHORT_TIMEOUT = 5 * 1000;
     public static final long LONG_TIMEOUT = 50 * 1000;
     public static final int SUCCESS = 0;  // for Wifi tethering state change
@@ -61,6 +58,7 @@
     private static final String ACCESS_POINT_FILE = "accesspoints.xml";
     public ConnectivityReceiver mConnectivityReceiver = null;
     public WifiReceiver mWifiReceiver = null;
+    private AccessPointParserHelper mParseHelper = null;
     /*
      * Track network connectivity information
      */
@@ -101,7 +99,7 @@
     private class ConnectivityReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            Log.v(LOG_TAG, "ConnectivityReceiver: onReceive() is called with " + intent);
+            log("ConnectivityReceiver: onReceive() is called with " + intent);
             String action = intent.getAction();
             if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
@@ -126,9 +124,9 @@
             mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
             mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
 
-            Log.v(LOG_TAG, "mNetworkInfo: " + mNetworkInfo.toString());
+            log("mNetworkInfo: " + mNetworkInfo.toString());
             if (mOtherNetworkInfo != null) {
-                Log.v(LOG_TAG, "mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
+                log("mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
             }
             recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
             if (mOtherNetworkInfo != null) {
@@ -148,7 +146,7 @@
             } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                 mWifiNetworkInfo =
                     (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                Log.v(LOG_TAG, "mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
+                log("mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
                 if (mWifiNetworkInfo.getState() == State.CONNECTED) {
                     mBssid = intent.getStringExtra(WifiManager.EXTRA_BSSID);
                 }
@@ -181,7 +179,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Log.v(LOG_TAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
+        log("onCreate, inst=" + Integer.toHexString(hashCode()));
 
         // Create a simple layout
         LinearLayout contentView = new LinearLayout(this);
@@ -212,7 +210,7 @@
         initializeNetworkStates();
 
         if (mWifiManager.isWifiEnabled()) {
-            Log.v(LOG_TAG, "Clear Wifi before we start the test.");
+            log("Clear Wifi before we start the test.");
             removeConfiguredNetworksAndDisableWifi();
         }
         mWifiRegexs = mCM.getTetherableWifiRegexs();
@@ -220,32 +218,22 @@
 
     public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
         InputStream in = getAssets().open(ACCESS_POINT_FILE);
-        AccessPointParserHelper parseHelper = new AccessPointParserHelper();
-        return parseHelper.processAccessPoint(in);
-    }
-
-    private void printNetConfig(String[] configuration) {
-        for (int i = 0; i < configuration.length; i++) {
-            if (i == 0) {
-                Log.v(LOG_TAG, "SSID: " + configuration[0]);
-            } else {
-                Log.v(LOG_TAG, "      " + configuration[i]);
-            }
-        }
+        mParseHelper = new AccessPointParserHelper(in);
+        return mParseHelper.getNetworkConfigurations();
     }
 
     // for each network type, initialize network states to UNKNOWN, and no verification flag is set
     public void initializeNetworkStates() {
         for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
             connectivityState[networkType] =  new NetworkState();
-            Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
+            log("Initialize network state for " + networkType + ": " +
                     connectivityState[networkType].toString());
         }
     }
 
     // deposit a network state
     public void recordNetworkState(int networkType, State networkState) {
-        Log.v(LOG_TAG, "record network state for network " +  networkType +
+        log("record network state for network " +  networkType +
                 ", state is " + networkState);
         connectivityState[networkType].recordState(networkState);
     }
@@ -259,40 +247,40 @@
 
     // Validate the states recorded
     public boolean validateNetworkStates(int networkType) {
-        Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
+        log("validate network state for " + networkType + ": ");
         return connectivityState[networkType].validateStateTransition();
     }
 
     // return result from network state validation
     public String getTransitionFailureReason(int networkType) {
-        Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
+        log("get network state transition failure reason for " + networkType + ": " +
                 connectivityState[networkType].toString());
         return connectivityState[networkType].getReason();
     }
 
     private void notifyNetworkConnectivityChange() {
         synchronized(connectivityObject) {
-            Log.v(LOG_TAG, "notify network connectivity changed");
+            log("notify network connectivity changed");
             connectivityObject.notifyAll();
         }
     }
     private void notifyScanResult() {
         synchronized (this) {
-            Log.v(LOG_TAG, "notify that scan results are available");
+            log("notify that scan results are available");
             this.notify();
         }
     }
 
     private void notifyWifiState() {
         synchronized (wifiObject) {
-            Log.v(LOG_TAG, "notify wifi state changed");
+            log("notify wifi state changed");
             wifiObject.notify();
         }
     }
 
     private void notifyWifiAPState() {
         synchronized (this) {
-            Log.v(LOG_TAG, "notify wifi AP state changed");
+            log("notify wifi AP state changed");
             this.notify();
         }
     }
@@ -306,7 +294,7 @@
             for (Object obj: tethered) {
                 String str = (String)obj;
                 for (String tethRex: mWifiRegexs) {
-                    Log.v(LOG_TAG, "str: " + str +"tethRex: " + tethRex);
+                    log("str: " + str +"tethRex: " + tethRex);
                     if (str.matches(tethRex)) {
                         wifiTethered = true;
                     }
@@ -316,7 +304,7 @@
             for (Object obj: errored) {
                 String str = (String)obj;
                 for (String tethRex: mWifiRegexs) {
-                    Log.v(LOG_TAG, "error: str: " + str +"tethRex: " + tethRex);
+                    log("error: str: " + str +"tethRex: " + tethRex);
                     if (str.matches(tethRex)) {
                         wifiErrored = true;
                     }
@@ -328,7 +316,7 @@
             } else if (wifiErrored) {
                 mWifiTetherResult = FAILURE;   // wifi tethering failed
             }
-            Log.v(LOG_TAG, "mWifiTetherResult: " + mWifiTetherResult);
+            log("mWifiTetherResult: " + mWifiTetherResult);
             this.notify();
         }
     }
@@ -344,12 +332,12 @@
                     return false;
                 } else {
                     // the broadcast has been sent out. the state has been changed.
-                    Log.v(LOG_TAG, "networktype: " + networkType + " state: " +
+                    log("networktype: " + networkType + " state: " +
                             mCM.getNetworkInfo(networkType));
                     return true;
                 }
             }
-            Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
+            log("Wait for the connectivity state for network: " + networkType +
                     " to be " + expectedState.toString());
             synchronized (connectivityObject) {
                 try {
@@ -359,7 +347,7 @@
                 }
                 if ((mNetworkInfo.getType() != networkType) ||
                     (mNetworkInfo.getState() != expectedState)) {
-                    Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
+                    log("network state for " + mNetworkInfo.getType() +
                             "is: " + mNetworkInfo.getState());
                     continue;
                 }
@@ -380,7 +368,7 @@
                     return true;
                 }
             }
-            Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
+            log("Wait for wifi state to be: " + expectedState);
             synchronized (wifiObject) {
                 try {
                     wifiObject.wait(SHORT_TIMEOUT);
@@ -388,7 +376,7 @@
                     e.printStackTrace();
                 }
                 if (mWifiState != expectedState) {
-                    Log.v(LOG_TAG, "Wifi state is: " + mWifiNetworkInfo.getState());
+                    log("Wifi state is: " + mWifiState);
                     continue;
                 }
                 return true;
@@ -408,7 +396,7 @@
                     return true;
                 }
             }
-            Log.v(LOG_TAG, "Wait for wifi AP state to be: " + expectedState);
+            log("Wait for wifi AP state to be: " + expectedState);
             synchronized (wifiObject) {
                 try {
                     wifiObject.wait(SHORT_TIMEOUT);
@@ -416,7 +404,7 @@
                     e.printStackTrace();
                 }
                 if (mWifiManager.getWifiApState() != expectedState) {
-                    Log.v(LOG_TAG, "Wifi state is: " + mWifiManager.getWifiApState());
+                    log("Wifi state is: " + mWifiManager.getWifiApState());
                     continue;
                 }
                 return true;
@@ -436,7 +424,7 @@
             if ((System.currentTimeMillis() - startTime) > timeout) {
                 return mWifiTetherResult;
             }
-            Log.v(LOG_TAG, "Wait for wifi tethering result.");
+            log("Wait for wifi tethering result.");
             synchronized (this) {
                 try {
                     this.wait(SHORT_TIMEOUT);
@@ -490,64 +478,61 @@
 
         //If Wifi is not enabled, enable it
         if (!mWifiManager.isWifiEnabled()) {
-            Log.v(LOG_TAG, "Wifi is not enabled, enable it");
+            log("Wifi is not enabled, enable it");
             mWifiManager.setWifiEnabled(true);
-        }
-
-        List<ScanResult> netList = mWifiManager.getScanResults();
-        if (netList == null) {
-            Log.v(LOG_TAG, "scan results are null");
-            // if no scan results are available, start active scan
-            mWifiManager.startScanActive();
-            mScanResultIsAvailable = false;
-            long startTime = System.currentTimeMillis();
-            while (!mScanResultIsAvailable) {
-                if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
-                    return false;
-                }
-                // wait for the scan results to be available
-                synchronized (this) {
-                    // wait for the scan result to be available
-                    try {
-                        this.wait(WAIT_FOR_SCAN_RESULT);
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                    if ((mWifiManager.getScanResults() == null) ||
-                            (mWifiManager.getScanResults().size() <= 0)) {
-                        continue;
-                    }
-                    mScanResultIsAvailable = true;
-                }
+            // wait for the wifi state change before start scanning.
+            if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 2*SHORT_TIMEOUT)) {
+                log("wait for WIFI_STATE_ENABLED failed");
+                return false;
             }
         }
 
-        netList = mWifiManager.getScanResults();
-
-        for (int i = 0; i < netList.size(); i++) {
-            ScanResult sr= netList.get(i);
-            if (sr.SSID.equals(ssid)) {
-                Log.v(LOG_TAG, "found " + ssid + " in the scan result list");
-                int networkId = mWifiManager.addNetwork(config);
-                // Connect to network by disabling others.
-                mWifiManager.enableNetwork(networkId, true);
-                mWifiManager.saveConfiguration();
-                List<WifiConfiguration> wifiNetworks = mWifiManager.getConfiguredNetworks();
-                for (WifiConfiguration netConfig : wifiNetworks) {
-                    Log.v(LOG_TAG, netConfig.toString());
+        boolean foundApInScanResults = false;
+        for (int retry = 0; retry < 5; retry++) {
+            List<ScanResult> netList = mWifiManager.getScanResults();
+            if (netList != null) {
+                log("size of scan result list: " + netList.size());
+                for (int i = 0; i < netList.size(); i++) {
+                    ScanResult sr= netList.get(i);
+                    if (sr.SSID.equals(ssid)) {
+                        log("found " + ssid + " in the scan result list");
+                        log("retry: " + retry);
+                        foundApInScanResults = true;
+                        mWifiManager.connectNetwork(config);
+                        break;
+                   }
                 }
-
-                mWifiManager.reconnect();
-                break;
-           }
+            }
+            if (foundApInScanResults) {
+                return true;
+            } else {
+                // Start an active scan
+                mWifiManager.startScanActive();
+                mScanResultIsAvailable = false;
+                long startTime = System.currentTimeMillis();
+                while (!mScanResultIsAvailable) {
+                    if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
+                        log("wait for scan results timeout");
+                        return false;
+                    }
+                    // wait for the scan results to be available
+                    synchronized (this) {
+                        // wait for the scan result to be available
+                        try {
+                            this.wait(WAIT_FOR_SCAN_RESULT);
+                        } catch (InterruptedException e) {
+                            e.printStackTrace();
+                        }
+                        if ((mWifiManager.getScanResults() == null) ||
+                                (mWifiManager.getScanResults().size() <= 0)) {
+                            continue;
+                        }
+                        mScanResultIsAvailable = true;
+                    }
+                }
+            }
         }
-
-        List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
-        if (netConfList.size() <= 0) {
-            Log.v(LOG_TAG, ssid + " is not available");
-            return false;
-        }
-        return true;
+        return false;
     }
 
     /*
@@ -555,27 +540,13 @@
      */
     public boolean disconnectAP() {
         if (mWifiManager.isWifiEnabled()) {
-            //remove the current network Id
-            WifiInfo curWifi = mWifiManager.getConnectionInfo();
-            if (curWifi == null) {
-                return false;
-            }
-            int curNetworkId = curWifi.getNetworkId();
-            mWifiManager.removeNetwork(curNetworkId);
-            mWifiManager.saveConfiguration();
-
-            // remove other saved networks
-            List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
-            if (netConfList != null) {
-                Log.v(LOG_TAG, "remove configured network ids");
-                for (int i = 0; i < netConfList.size(); i++) {
-                    WifiConfiguration conf = new WifiConfiguration();
-                    conf = netConfList.get(i);
-                    mWifiManager.removeNetwork(conf.networkId);
-                }
+            // remove saved networks
+            List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
+            for (WifiConfiguration wifiConfig: wifiConfigList) {
+                log("remove wifi configuration: " + wifiConfig.toString());
+                mWifiManager.forgetNetwork(wifiConfig.networkId);
             }
         }
-        mWifiManager.saveConfiguration();
         return true;
     }
     /**
@@ -599,7 +570,7 @@
             }
             // Wait for the actions to be completed
             try {
-                Thread.sleep(5*1000);
+                Thread.sleep(SHORT_TIMEOUT);
             } catch (InterruptedException e) {}
         return true;
     }
@@ -632,7 +603,7 @@
         if (mWifiReceiver != null) {
             unregisterReceiver(mWifiReceiver);
         }
-        Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+        log("onDestroy, inst=" + Integer.toHexString(hashCode()));
     }
 
     @Override
@@ -674,4 +645,8 @@
         }
         return super.onKeyDown(keyCode, event);
     }
+
+    private void log(String message) {
+        Log.v(LOG_TAG, message);
+    }
 }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index 69eb5db..9c72102 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -17,26 +17,33 @@
 package com.android.connectivitymanagertest.functional;
 
 import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
-import com.android.connectivitymanagertest.NetworkState;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
 
 import android.R;
 import android.app.Activity;
+import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.Status;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.ConnectivityManager;
+import android.net.DhcpInfo;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
+import android.provider.Settings;
 
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Test Wi-Fi connection with different configuration
@@ -48,18 +55,25 @@
 public class WifiConnectionTest
     extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
     private static final String TAG = "WifiConnectionTest";
-    private static final boolean DEBUG = true;
-    private static final String PKG_NAME = "com.android.connectivitymanagertests";
+    private static final boolean DEBUG = false;
     private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
     private ConnectivityManagerTestActivity mAct;
+    private ConnectivityManagerTestRunner mRunner;
+    private WifiManager mWifiManager = null;
+    private Set<WifiConfiguration> enabledNetworks = null;
 
     public WifiConnectionTest() {
-        super(PKG_NAME, ConnectivityManagerTestActivity.class);
+        super(ConnectivityManagerTestActivity.class);
     }
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
+        log("before we launch the test activity, we preserve all the configured networks.");
+        mRunner = ((ConnectivityManagerTestRunner)getInstrumentation());
+        mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE);
+        enabledNetworks = getEnabledNetworks(mWifiManager.getConfiguredNetworks());
+
         mAct = getActivity();
         networks = mAct.loadNetworkConfigurations();
         if (DEBUG) {
@@ -68,30 +82,61 @@
 
         // enable Wifi and verify wpa_supplicant is started
         assertTrue("enable Wifi failed", mAct.enableWifi());
-        try {
-            Thread.sleep( 2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
-        } catch (Exception e) {
-            fail("interrupted while waiting for WPA_SUPPLICANT to start");
-        }
+        sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+                "interrupted while waiting for WPA_SUPPLICANT to start");
         WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo();
         assertNotNull(mConnection);
         assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant());
     }
 
     private void printNetworkConfigurations() {
-        Log.v(TAG, "==== print network configurations parsed from XML file ====");
-        Log.v(TAG, "number of access points: " + networks.size());
+        log("==== print network configurations parsed from XML file ====");
+        log("number of access points: " + networks.size());
         for (WifiConfiguration config : networks) {
-            Log.v(TAG, config.toString());
+            log(config.toString());
         }
     }
 
     @Override
     public void tearDown() throws Exception {
+        log("tearDown()");
         mAct.removeConfiguredNetworksAndDisableWifi();
+        reEnableNetworks(enabledNetworks);
         super.tearDown();
     }
 
+    private Set<WifiConfiguration> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) {
+        Set<WifiConfiguration> networks = new HashSet<WifiConfiguration>();
+        for (WifiConfiguration wifiConfig : configuredNetworks) {
+            if (wifiConfig.status == Status.ENABLED || wifiConfig.status == Status.CURRENT) {
+                networks.add(wifiConfig);
+                log("remembering enabled network " + wifiConfig.SSID +
+                        " status is " + wifiConfig.status);
+            }
+        }
+        return networks;
+    }
+
+    private void reEnableNetworks(Set<WifiConfiguration> enabledNetworks) {
+        if (!mWifiManager.isWifiEnabled()) {
+            log("reEnableNetworks: enable Wifi");
+            mWifiManager.setWifiEnabled(true);
+            sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+                    "interruped while waiting for wifi to be enabled");
+        }
+
+        for (WifiConfiguration config : enabledNetworks) {
+            if (DEBUG) {
+                log("recover wifi configuration: " + config.toString());
+            }
+            config.SSID = "\"" + config.SSID + "\"";
+            config.networkId = -1;
+            mWifiManager.connectNetwork(config);
+            sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+                    "interruped while connecting to " + config.SSID);
+        }
+    }
+
     /**
      * Connect to the provided Wi-Fi network
      * @param config is the network configuration
@@ -103,32 +148,40 @@
                 mAct.connectToWifiWithConfiguration(config));
 
         // step 2: verify Wifi state and network state;
-        assertTrue(mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
-                ConnectivityManagerTestActivity.SHORT_TIMEOUT));
         assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT));
+                State.CONNECTED, 2 * ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // step 3: verify the current connected network is the given SSID
+        assertNotNull("Wifi connection returns null", mAct.mWifiManager.getConnectionInfo());
         if (DEBUG) {
-            Log.v(TAG, "config.SSID = " + config.SSID);
-            Log.v(TAG, "mAct.mWifiManager.getConnectionInfo.getSSID()" +
+            log("config.SSID = " + config.SSID);
+            log("mAct.mWifiManager.getConnectionInfo.getSSID()" +
                     mAct.mWifiManager.getConnectionInfo().getSSID());
         }
         assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID()));
+    }
 
-        // Maintain the connection for 50 seconds before switching
+    private void sleep(long sometime, String errorMsg) {
         try {
-            Thread.sleep(50*1000);
-        } catch (Exception e) {
-            fail("interrupted while waiting for WPA_SUPPLICANT to start");
+            Thread.sleep(sometime);
+        } catch (InterruptedException e) {
+            fail(errorMsg);
         }
     }
 
+    private void log(String message) {
+        Log.v(TAG, message);
+    }
+
     @LargeTest
     public void testWifiConnections() {
         for (int i = 0; i < networks.size(); i++) {
+            String ssid = networks.get(i).SSID;
+            log("-- START Wi-Fi connection test to : " + ssid + " --");
             connectToWifi(networks.get(i));
-            mAct.removeConfiguredNetworksAndDisableWifi();
+            sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT,
+                    "interruped while waiting for wifi disabled.");
+            log("-- END Wi-Fi connection test to " + ssid + " -- ");
         }
     }
 }
diff --git a/data/keyboards/Logitech_USB_Receiver.kl b/data/keyboards/Vendor_046d_Product_c532.kl
similarity index 98%
rename from data/keyboards/Logitech_USB_Receiver.kl
rename to data/keyboards/Vendor_046d_Product_c532.kl
index aa7c0ee..741c2e1 100644
--- a/data/keyboards/Logitech_USB_Receiver.kl
+++ b/data/keyboards/Vendor_046d_Product_c532.kl
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 #
-# Logitech Revue keyboard
+# Logitech Revue Wireless keyboard
 #
 # Notes:
 # - The GRAVE key is emulated by the keyboard.
diff --git a/data/keyboards/Apple_Wireless_Keyboard.kl b/data/keyboards/Vendor_05ac_Product_0239.kl
similarity index 100%
rename from data/keyboards/Apple_Wireless_Keyboard.kl
rename to data/keyboards/Vendor_05ac_Product_0239.kl
diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl b/data/keyboards/Vendor_22b8_Product_093d.kl
similarity index 100%
rename from data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl
rename to data/keyboards/Vendor_22b8_Product_093d.kl
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
index 5c2a75d..56c287a 100644
--- a/data/keyboards/common.mk
+++ b/data/keyboards/common.mk
@@ -16,16 +16,20 @@
 # Used by Android.mk and keyboards.mk.
 
 keylayouts := \
-    Apple_Wireless_Keyboard.kl \
-    AVRCP.kl \
     Generic.kl \
-    Logitech_USB_Receiver.kl \
-    Motorola_Bluetooth_Wireless_Keyboard.kl \
+    AVRCP.kl \
     qwerty.kl \
-    qwerty2.kl
+    Vendor_046d_Product_c532.kl \
+    Vendor_05ac_Product_0239.kl \
+    Vendor_22b8_Product_093d.kl
 
 keycharmaps := \
     Generic.kcm \
+    Virtual.kcm \
     qwerty.kcm \
-    qwerty2.kcm \
-    Virtual.kcm
+    qwerty2.kcm
+
+keyconfigs := \
+    qwerty.idc \
+    qwerty2.idc
+
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index b32e436..564f41c 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -22,4 +22,8 @@
 PRODUCT_COPY_FILES += $(foreach file,$(keycharmaps),\
     frameworks/base/data/keyboards/$(file):system/usr/keychars/$(file))
 
-PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps)
+PRODUCT_COPY_FILES += $(foreach file,$(keyconfigs),\
+    frameworks/base/data/keyboards/$(file):system/usr/idc/$(file))
+
+PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps) $(keyconfigs)
+
diff --git a/data/keyboards/qwerty.idc b/data/keyboards/qwerty.idc
new file mode 100644
index 0000000..129b0bc
--- /dev/null
+++ b/data/keyboards/qwerty.idc
@@ -0,0 +1,23 @@
+# 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.
+
+#
+# Emulator keyboard configuration file #1.
+#
+
+keyboard.layout = qwerty
+keyboard.characterMap = qwerty
+keyboard.orientationAware = 1
+keyboard.builtIn = 1
+
diff --git a/data/keyboards/qwerty2.idc b/data/keyboards/qwerty2.idc
new file mode 100644
index 0000000..1a795c6
--- /dev/null
+++ b/data/keyboards/qwerty2.idc
@@ -0,0 +1,23 @@
+# 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.
+
+#
+# Emulator keyboard configuration file #2.
+#
+
+keyboard.layout = qwerty
+keyboard.characterMap = qwerty2
+keyboard.orientationAware = 1
+keyboard.builtIn = 1
+
diff --git a/data/keyboards/qwerty2.kl b/data/keyboards/qwerty2.kl
deleted file mode 100644
index 863a258..0000000
--- a/data/keyboards/qwerty2.kl
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Emulator keyboard layout #2.
-#
-
-key 399   GRAVE
-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 158   BACK              WAKE_DROPPED
-key 230   SOFT_RIGHT        WAKE
-key 60    SOFT_RIGHT        WAKE
-key 107   ENDCALL           WAKE_DROPPED
-key 62    ENDCALL           WAKE_DROPPED
-key 229   MENU              WAKE_DROPPED
-key 139   MENU              WAKE_DROPPED
-key 59    MENU              WAKE_DROPPED
-key 127   SEARCH            WAKE_DROPPED
-key 217   SEARCH            WAKE_DROPPED
-key 228   POUND
-key 227   STAR
-key 231   CALL              WAKE_DROPPED
-key 61    CALL              WAKE_DROPPED
-key 232   DPAD_CENTER       WAKE_DROPPED
-key 108   DPAD_DOWN         WAKE_DROPPED
-key 103   DPAD_UP           WAKE_DROPPED
-key 102   HOME              WAKE
-key 105   DPAD_LEFT         WAKE_DROPPED
-key 106   DPAD_RIGHT        WAKE_DROPPED
-key 115   VOLUME_UP         WAKE
-key 114   VOLUME_DOWN       WAKE
-key 116   POWER             WAKE
-key 212   CAMERA
-
-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 43    BACKSLASH
-
-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 14    DEL
-        
-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 28    ENTER
-        
-key 56    ALT_LEFT
-key 100   ALT_RIGHT
-key 42    SHIFT_LEFT
-key 54    SHIFT_RIGHT
-key 15    TAB
-key 57    SPACE
-key 150   EXPLORER
-key 155   ENVELOPE        
-
-key 12    MINUS
-key 13    EQUALS
-key 215   AT
-
-# On an AT keyboard: ESC, F10
-key 1     BACK              WAKE_DROPPED
-key 68    MENU              WAKE_DROPPED
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 62fbfb4..e3bb6eb 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1818,6 +1818,7 @@
         nativeGetCharArrayBounds(mNativePaint, text, index, count, bounds);
     }
     
+    @Override
     protected void finalize() throws Throwable {
         finalizer(mNativePaint);
     }
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 6c798a6..6c6c297 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -18,8 +18,11 @@
 #ifndef _RUNTIME_EVENT_HUB_H
 #define _RUNTIME_EVENT_HUB_H
 
-#include <android/input.h>
+#include <ui/Input.h>
 #include <ui/Keyboard.h>
+#include <ui/KeyLayoutMap.h>
+#include <ui/KeyCharacterMap.h>
+#include <ui/VirtualKeyMap.h>
 #include <utils/String8.h>
 #include <utils/threads.h>
 #include <utils/Log.h>
@@ -27,6 +30,7 @@
 #include <utils/List.h>
 #include <utils/Errors.h>
 #include <utils/PropertyMap.h>
+#include <utils/Vector.h>
 
 #include <linux/input.h>
 
@@ -59,8 +63,6 @@
 
 namespace android {
 
-class KeyLayoutMap;
-
 /*
  * A raw event as retrieved from the EventHub.
  */
@@ -194,6 +196,9 @@
     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 getVirtualKeyDefinitions(int32_t deviceId,
+            Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
+
     virtual void dump(String8& dump) = 0;
 };
 
@@ -230,6 +235,9 @@
     virtual bool hasLed(int32_t deviceId, int32_t led) const;
     virtual void setLedState(int32_t deviceId, int32_t led, bool on);
 
+    virtual void getVirtualKeyDefinitions(int32_t deviceId,
+            Vector<VirtualKeyDefinition>& outVirtualKeys) const;
+
     virtual void dump(String8& dump);
 
 protected:
@@ -238,78 +246,80 @@
 private:
     bool openPlatformInput(void);
 
-    int openDevice(const char *device);
-    int closeDevice(const char *device);
+    int openDevice(const char *devicePath);
+    int closeDevice(const char *devicePath);
     int scanDir(const char *dirname);
     int readNotify(int nfd);
 
     status_t mError;
 
-    struct device_t {
-        const int32_t   id;
-        const String8   path;
-        String8         name;
-        uint32_t        classes;
-        uint8_t*        keyBitmask;
-        KeyLayoutMap*   layoutMap;
-        String8         configurationFile;
-        PropertyMap*    configuration;
-        KeyMapInfo      keyMapInfo;
-        int             fd;
-        device_t*       next;
-        
-        device_t(int32_t _id, const char* _path, const char* name);
-        ~device_t();
+    struct Device {
+        Device* next;
+
+        int fd;
+        const int32_t id;
+        const String8 path;
+        const InputDeviceIdentifier identifier;
+
+        uint32_t classes;
+        uint8_t* keyBitmask;
+        String8 configurationFile;
+        PropertyMap* configuration;
+        VirtualKeyMap* virtualKeyMap;
+        KeyMap keyMap;
+
+        Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
+        ~Device();
+
+        void close();
     };
 
-    device_t* getDeviceLocked(int32_t deviceId) const;
-    bool hasKeycodeLocked(device_t* device, int keycode) const;
+    Device* getDeviceLocked(int32_t deviceId) const;
+    bool hasKeycodeLocked(Device* 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,
+    int32_t getScanCodeStateLocked(Device* device, int32_t scanCode) const;
+    int32_t getKeyCodeStateLocked(Device* device, int32_t keyCode) const;
+    int32_t getSwitchStateLocked(Device* device, int32_t sw) const;
+    bool markSupportedKeyCodesLocked(Device* device, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
-    void loadConfiguration(device_t* device);
-    void configureKeyMap(device_t* device);
-    void setKeyboardProperties(device_t* device, bool firstKeyboard);
-    void clearKeyboardProperties(device_t* device, bool firstKeyboard);
+    void loadConfiguration(Device* device);
+    status_t loadVirtualKeyMap(Device* device);
+    status_t loadKeyMap(Device* device);
+    void setKeyboardProperties(Device* device, bool builtInKeyboard);
+    void clearKeyboardProperties(Device* device, bool builtInKeyboard);
 
     // Protect all internal state.
-    mutable Mutex   mLock;
-    
-    bool            mHaveFirstKeyboard;
-    int32_t         mFirstKeyboardId; // the API is that the built-in keyboard is id 0, so map it
-    
-    struct device_ent {
-        device_t* device;
-        uint32_t seq;
-    };
-    device_ent      *mDevicesById;
-    int             mNumDevicesById;
-    
-    device_t        *mOpeningDevices;
-    device_t        *mClosingDevices;
-    
-    device_t        **mDevices;
-    struct pollfd   *mFDs;
-    int             mFDCount;
+    mutable Mutex mLock;
 
-    bool            mOpened;
-    bool            mNeedToSendFinishedDeviceScan;
-    List<String8>   mExcludedDevices;
+    // The actual id of the built-in keyboard, or -1 if none.
+    // EventHub remaps the built-in keyboard to id 0 externally as required by the API.
+    int32_t mBuiltInKeyboardId;
+
+    int32_t mNextDeviceId;
+
+    // Parallel arrays of fds and devices.
+    // First index is reserved for inotify.
+    Vector<struct pollfd> mFds;
+    Vector<Device*> mDevices;
+
+    Device *mOpeningDevices;
+    Device *mClosingDevices;
+
+    bool mOpened;
+    bool mNeedToSendFinishedDeviceScan;
+    List<String8> mExcludedDevices;
 
     // device ids that report particular switches.
 #ifdef EV_SW
-    int32_t         mSwitches[SW_MAX + 1];
+    int32_t mSwitches[SW_MAX + 1];
 #endif
 
     static const int INPUT_BUFFER_SIZE = 64;
     struct input_event mInputBufferData[INPUT_BUFFER_SIZE];
-    int32_t mInputBufferIndex;
-    int32_t mInputBufferCount;
-    int32_t mInputDeviceIndex;
+    size_t mInputBufferIndex;
+    size_t mInputBufferCount;
+    size_t mInputFdIndex;
 };
 
 }; // namespace android
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 4dc8f2a..27f65bc 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -497,6 +497,23 @@
     KeyedVector<int32_t, MotionRange> mMotionRanges;
 };
 
+/*
+ * Identifies a device.
+ */
+struct InputDeviceIdentifier {
+    inline InputDeviceIdentifier() :
+            bus(0), vendor(0), product(0), version(0) {
+    }
+
+    String8 name;
+    String8 location;
+    String8 uniqueId;
+    uint16_t bus;
+    uint16_t vendor;
+    uint16_t product;
+    uint16_t version;
+};
+
 /* Types of input device configuration files. */
 enum InputDeviceConfigurationFileType {
     INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0,     /* .idc file */
@@ -505,13 +522,28 @@
 };
 
 /*
- * Get the path of an input device configuration file, if one is available.
- * Spaces in the name are replaced with underscores.
+ * Gets the path of an input device configuration file, if one is available.
  * Considers both system provided and user installed configuration files.
  *
+ * The device identifier is used to construct several default configuration file
+ * names to try based on the device name, vendor, product, and version.
+ *
  * Returns an empty string if not found.
  */
-extern String8 getInputDeviceConfigurationFilePath(
+extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+        const InputDeviceIdentifier& deviceIdentifier,
+        InputDeviceConfigurationFileType type);
+
+/*
+ * Gets the path of an input device configuration file, if one is available.
+ * Considers both system provided and user installed configuration files.
+ *
+ * The name is case-sensitive and is used to construct the filename to resolve.
+ * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores.
+ *
+ * Returns an empty string if not found.
+ */
+extern String8 getInputDeviceConfigurationFilePathByName(
         const String8& name, InputDeviceConfigurationFileType type);
 
 } // namespace android
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index cfceaab1..8ec5421 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -35,17 +35,6 @@
 class InputDevice;
 class InputMapper;
 
-/* Describes a virtual key. */
-struct VirtualKeyDefinition {
-    int32_t scanCode;
-
-    // configured position data, specified in display coords
-    int32_t centerX;
-    int32_t centerY;
-    int32_t width;
-    int32_t height;
-};
-
 
 /*
  * Input reader policy interface.
@@ -86,10 +75,6 @@
      */
     virtual bool filterJumpyTouchEvents() = 0;
 
-    /* Gets the configured virtual key definitions for an input device. */
-    virtual void getVirtualKeyDefinitions(const String8& deviceName,
-            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
-
     /* Gets the excluded device names for the platform. */
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
 };
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
index 689607d..50296e2 100644
--- a/include/ui/Keyboard.h
+++ b/include/ui/Keyboard.h
@@ -33,30 +33,58 @@
     DEVICE_ID_VIRTUAL_KEYBOARD = -1,
 };
 
-struct KeyMapInfo {
+class KeyLayoutMap;
+class KeyCharacterMap;
+
+/**
+ * Loads the key layout map and key character map for a keyboard device.
+ */
+class KeyMap {
+public:
     String8 keyLayoutFile;
+    KeyLayoutMap* keyLayoutMap;
+
     String8 keyCharacterMapFile;
-    bool isDefaultKeyMap;
+    KeyCharacterMap* keyCharacterMap;
 
-    KeyMapInfo() : isDefaultKeyMap(false) {
+    KeyMap();
+    ~KeyMap();
+
+    status_t load(const InputDeviceIdentifier& deviceIdenfier,
+            const PropertyMap* deviceConfiguration);
+
+    inline bool haveKeyLayout() const {
+        return !keyLayoutFile.isEmpty();
     }
 
-    bool isComplete() {
-        return !keyLayoutFile.isEmpty() && !keyCharacterMapFile.isEmpty();
+    inline bool haveKeyCharacterMap() const {
+        return !keyCharacterMapFile.isEmpty();
     }
+
+    inline bool isComplete() const {
+        return haveKeyLayout() && haveKeyCharacterMap();
+    }
+
+private:
+    bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
+    status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
+    status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
+            const String8& name);
+    String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
+            const String8& name, InputDeviceConfigurationFileType type);
 };
 
 /**
- * Resolves the key map to use for a particular keyboard device.
+ * Returns true if the keyboard is eligible for use as a built-in keyboard.
  */
-extern status_t resolveKeyMap(const String8& deviceName,
-        const PropertyMap* deviceConfiguration, KeyMapInfo& outKeyMapInfo);
+extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
+        const PropertyMap* deviceConfiguration, const KeyMap* keyMap);
 
 /**
  * Sets keyboard system properties.
  */
-extern void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
-        const KeyMapInfo& keyMapInfo);
+extern void setKeyboardProperties(int32_t deviceId, const InputDeviceIdentifier& deviceIdentifier,
+        const String8& keyLayoutFile, const String8& keyCharacterMapFile);
 
 /**
  * Clears keyboard system properties.
diff --git a/include/ui/VirtualKeyMap.h b/include/ui/VirtualKeyMap.h
new file mode 100644
index 0000000..7813d9d
--- /dev/null
+++ b/include/ui/VirtualKeyMap.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_VIRTUAL_KEY_MAP_H
+#define _UI_VIRTUAL_KEY_MAP_H
+
+#include <stdint.h>
+
+#include <ui/Input.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Tokenizer.h>
+#include <utils/String8.h>
+#include <utils/Unicode.h>
+
+namespace android {
+
+/* Describes a virtual key. */
+struct VirtualKeyDefinition {
+    int32_t scanCode;
+
+    // configured position data, specified in display coords
+    int32_t centerX;
+    int32_t centerY;
+    int32_t width;
+    int32_t height;
+};
+
+
+/**
+ * Describes a collection of virtual keys on a touch screen in terms of
+ * virtual scan codes and hit rectangles.
+ */
+class VirtualKeyMap {
+public:
+    ~VirtualKeyMap();
+
+    static status_t load(const String8& filename, VirtualKeyMap** outMap);
+
+    inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const {
+        return mVirtualKeys;
+    }
+
+private:
+    class Parser {
+        VirtualKeyMap* mMap;
+        Tokenizer* mTokenizer;
+
+    public:
+        Parser(VirtualKeyMap* map, Tokenizer* tokenizer);
+        ~Parser();
+        status_t parse();
+
+    private:
+        bool consumeFieldDelimiterAndSkipWhitespace();
+        bool parseNextIntField(int32_t* outValue);
+    };
+
+    Vector<VirtualKeyDefinition> mVirtualKeys;
+
+    VirtualKeyMap();
+};
+
+} // namespace android
+
+#endif // _UI_KEY_CHARACTER_MAP_H
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 6abfb06..6b49ff5 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -47,7 +47,12 @@
     explicit                    String8(const char32_t* o);
     explicit                    String8(const char32_t* o, size_t numChars);
                                 ~String8();
-    
+
+    static inline const String8 empty();
+
+    static String8              format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
+    static String8              formatV(const char* fmt, va_list args);
+
     inline  const char*         string() const;
     inline  size_t              size() const;
     inline  size_t              length() const;
@@ -229,6 +234,10 @@
     return compare_type(lhs, rhs) < 0;
 }
 
+inline const String8 String8::empty() {
+    return String8();
+}
+
 inline const char* String8::string() const
 {
     return mString;
diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h
index 21e58e6..c7db5fb 100644
--- a/include/utils/Tokenizer.h
+++ b/include/utils/Tokenizer.h
@@ -28,7 +28,7 @@
  * A simple tokenizer for loading and parsing ASCII text files line by line.
  */
 class Tokenizer {
-    Tokenizer(const String8& filename, FileMap* fileMap, const char* buffer, size_t length);
+    Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length);
 
 public:
     ~Tokenizer();
@@ -110,7 +110,7 @@
 
     String8 mFilename;
     FileMap* mFileMap;
-    const char* mBuffer;
+    char* mBuffer;
     size_t mLength;
 
     const char* mCurrent;
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 7ca289d..ebffd34 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -42,6 +42,9 @@
     mXDivs = new int32_t[mXCount];
     mYDivs = new int32_t[mYCount];
 
+    PATCH_LOGD("    patch: xCount = %d, yCount = %d, emptyQuads = %d, vertices = %d",
+            xCount, yCount, emptyQuads, verticesCount);
+
     glGenBuffers(1, &meshBuffer);
 }
 
@@ -208,7 +211,15 @@
 
 void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
             float u1, float v1, float u2, float v2, uint32_t& quadCount) {
-    if (((mColorKey >> quadCount++) & 0x1) == 1) {
+    uint32_t oldQuadCount = quadCount;
+
+    // Degenerate quads are an artifact of our implementation and should not
+    // be taken into account when checking for transparent quads
+    if (x2 - x1 > 0.999f && y2 - y1 > 0.999f) {
+        quadCount++;
+    }
+
+    if (((mColorKey >> oldQuadCount) & 0x1) == 1) {
         return;
     }
 
diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp
index 91b1c32..b86bbc5 100644
--- a/libs/hwui/SkiaColorFilter.cpp
+++ b/libs/hwui/SkiaColorFilter.cpp
@@ -36,6 +36,11 @@
 
 SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector):
         SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) {
+    // Skia uses the range [0..255] for the addition vector, but we need
+    // the [0..1] range to apply the vector in GLSL
+    for (int i = 0; i < 4; i++) {
+        mVector[i] /= 255.0f;
+    }
 }
 
 SkiaColorMatrixFilter::~SkiaColorMatrixFilter() {
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 5948e04..d0e041a 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -21,6 +21,7 @@
 	Keyboard.cpp \
 	KeyLayoutMap.cpp \
 	KeyCharacterMap.cpp \
+	VirtualKeyMap.cpp
 
 # For the host
 # =====================================================
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index b312cda..8f4bac6 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -33,6 +33,8 @@
 #include <assert.h>
 
 #include <ui/KeyLayoutMap.h>
+#include <ui/KeyCharacterMap.h>
+#include <ui/VirtualKeyMap.h>
 
 #include <string.h>
 #include <stdint.h>
@@ -56,10 +58,6 @@
 /* this macro computes the number of bytes needed to represent a bit array of the specified size */
 #define sizeof_bit_array(bits)  ((bits + 7) / 8)
 
-#define ID_MASK  0x0000ffff
-#define SEQ_MASK 0x7fff0000
-#define SEQ_SHIFT 16
-
 #ifndef ABS_MT_TOUCH_MAJOR
 #define ABS_MT_TOUCH_MAJOR      0x30    /* Major axis of touching ellipse */
 #endif
@@ -72,6 +70,9 @@
 #define ABS_MT_POSITION_Y       0x36    /* Center Y ellipse position */
 #endif
 
+// Fd at index 0 is always reserved for inotify
+#define FIRST_ACTUAL_DEVICE_INDEX 1
+
 #define INDENT "  "
 #define INDENT2 "    "
 #define INDENT3 "      "
@@ -79,7 +80,7 @@
 namespace android {
 
 static const char *WAKE_LOCK_ID = "KeyEvents";
-static const char *device_path = "/dev/input";
+static const char *DEVICE_PATH = "/dev/input";
 
 /* return the larger integer */
 static inline int max(int v1, int v2)
@@ -91,63 +92,69 @@
     return value ? "true" : "false";
 }
 
-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(NULL), configuration(NULL), fd(-1), next(NULL) {
+// --- EventHub::Device ---
+
+EventHub::Device::Device(int fd, int32_t id, const String8& path,
+        const InputDeviceIdentifier& identifier) :
+        next(NULL),
+        fd(fd), id(id), path(path), identifier(identifier),
+        classes(0), keyBitmask(NULL), configuration(NULL), virtualKeyMap(NULL) {
 }
 
-EventHub::device_t::~device_t() {
-    delete [] keyBitmask;
-    delete layoutMap;
+EventHub::Device::~Device() {
+    close();
+    delete[] keyBitmask;
     delete configuration;
+    delete virtualKeyMap;
 }
 
-EventHub::EventHub(void)
-    : 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)
-    , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
-{
+void EventHub::Device::close() {
+    if (fd >= 0) {
+        ::close(fd);
+        fd = -1;
+    }
+}
+
+
+// --- EventHub ---
+
+EventHub::EventHub(void) :
+        mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1),
+        mOpeningDevices(0), mClosingDevices(0),
+        mOpened(false), mNeedToSendFinishedDeviceScan(false),
+        mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 #ifdef EV_SW
     memset(mSwitches, 0, sizeof(mSwitches));
 #endif
 }
 
-/*
- * Clean up.
- */
-EventHub::~EventHub(void)
-{
+EventHub::~EventHub(void) {
     release_wake_lock(WAKE_LOCK_ID);
     // we should free stuff here...
 }
 
-status_t EventHub::errorCheck() const
-{
+status_t EventHub::errorCheck() const {
     return mError;
 }
 
-String8 EventHub::getDeviceName(int32_t deviceId) const
-{
+String8 EventHub::getDeviceName(int32_t deviceId) const {
     AutoMutex _l(mLock);
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device == NULL) return String8();
-    return device->name;
+    return device->identifier.name;
 }
 
-uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
-{
+uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
     AutoMutex _l(mLock);
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device == NULL) return 0;
     return device->classes;
 }
 
 void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
     AutoMutex _l(mLock);
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device && device->configuration) {
         *outConfiguration = *device->configuration;
     } else {
@@ -160,14 +167,14 @@
     outAxisInfo->clear();
 
     AutoMutex _l(mLock);
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device == NULL) return -1;
 
     struct input_absinfo info;
 
     if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
         LOGW("Error reading absolute controller %d for device %s fd %d\n",
-             axis, device->name.string(), device->fd);
+             axis, device->identifier.name.string(), device->fd);
         return -errno;
     }
 
@@ -185,7 +192,7 @@
     if (scanCode >= 0 && scanCode <= KEY_MAX) {
         AutoMutex _l(mLock);
 
-        device_t* device = getDeviceLocked(deviceId);
+        Device* device = getDeviceLocked(deviceId);
         if (device != NULL) {
             return getScanCodeStateLocked(device, scanCode);
         }
@@ -193,7 +200,7 @@
     return AKEY_STATE_UNKNOWN;
 }
 
-int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
+int32_t EventHub::getScanCodeStateLocked(Device* device, int32_t scanCode) const {
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
     if (ioctl(device->fd,
@@ -206,20 +213,20 @@
 int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
     AutoMutex _l(mLock);
 
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device != NULL) {
         return getKeyCodeStateLocked(device, keyCode);
     }
     return AKEY_STATE_UNKNOWN;
 }
 
-int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const {
-    if (!device->layoutMap) {
+int32_t EventHub::getKeyCodeStateLocked(Device* device, int32_t keyCode) const {
+    if (!device->keyMap.haveKeyLayout()) {
         return AKEY_STATE_UNKNOWN;
     }
 
     Vector<int32_t> scanCodes;
-    device->layoutMap->findScanCodes(keyCode, &scanCodes);
+    device->keyMap.keyLayoutMap->findScanCodes(keyCode, &scanCodes);
 
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
@@ -247,7 +254,7 @@
     if (sw >= 0 && sw <= SW_MAX) {
         AutoMutex _l(mLock);
 
-        device_t* device = getDeviceLocked(deviceId);
+        Device* device = getDeviceLocked(deviceId);
         if (device != NULL) {
             return getSwitchStateLocked(device, sw);
         }
@@ -256,7 +263,7 @@
     return AKEY_STATE_UNKNOWN;
 }
 
-int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
+int32_t EventHub::getSwitchStateLocked(Device* device, int32_t sw) const {
     uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
     memset(sw_bitmask, 0, sizeof(sw_bitmask));
     if (ioctl(device->fd,
@@ -270,16 +277,16 @@
         const int32_t* keyCodes, uint8_t* outFlags) const {
     AutoMutex _l(mLock);
 
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device != NULL) {
         return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
     }
     return false;
 }
 
-bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+bool EventHub::markSupportedKeyCodesLocked(Device* device, size_t numCodes,
         const int32_t* keyCodes, uint8_t* outFlags) const {
-    if (device->layoutMap == NULL || device->keyBitmask == NULL) {
+    if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) {
         return false;
     }
 
@@ -287,7 +294,7 @@
     for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
         scanCodes.clear();
 
-        status_t err = device->layoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes);
+        status_t err = device->keyMap.keyLayoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes);
         if (! err) {
             // check the possible scan codes identified by the layout map against the
             // map of codes actually emitted by the driver
@@ -306,20 +313,20 @@
         int32_t* outKeycode, uint32_t* outFlags) const
 {
     AutoMutex _l(mLock);
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     
-    if (device != NULL && device->layoutMap != NULL) {
-        status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+    if (device && device->keyMap.haveKeyLayout()) {
+        status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags);
         if (err == NO_ERROR) {
             return NO_ERROR;
         }
     }
     
-    if (mHaveFirstKeyboard) {
-        device = getDeviceLocked(mFirstKeyboardId);
+    if (mBuiltInKeyboardId != -1) {
+        device = getDeviceLocked(mBuiltInKeyboardId);
         
-        if (device != NULL && device->layoutMap != NULL) {
-            status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+        if (device && device->keyMap.haveKeyLayout()) {
+            status_t err = device->keyMap.keyLayoutMap->map(scancode, outKeycode, outFlags);
             if (err == NO_ERROR) {
                 return NO_ERROR;
             }
@@ -341,7 +348,7 @@
 
 bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
     AutoMutex _l(mLock);
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device) {
         uint8_t bitmask[sizeof_bit_array(LED_MAX + 1)];
         memset(bitmask, 0, sizeof(bitmask));
@@ -356,7 +363,7 @@
 
 void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
     AutoMutex _l(mLock);
-    device_t* device = getDeviceLocked(deviceId);
+    Device* device = getDeviceLocked(deviceId);
     if (device) {
         struct input_event ev;
         ev.time.tv_sec = 0;
@@ -372,21 +379,33 @@
     }
 }
 
-EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
-{
-    if (deviceId == 0) deviceId = mFirstKeyboardId;
-    int32_t id = deviceId & ID_MASK;
-    if (id >= mNumDevicesById || id < 0) return NULL;
-    device_t* dev = mDevicesById[id].device;
-    if (dev == NULL) return NULL;
-    if (dev->id == deviceId) {
-        return dev;
+void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
+        Vector<VirtualKeyDefinition>& outVirtualKeys) const {
+    outVirtualKeys.clear();
+
+    AutoMutex _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+    if (device && device->virtualKeyMap) {
+        outVirtualKeys.appendVector(device->virtualKeyMap->getVirtualKeys());
+    }
+}
+
+EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
+    if (deviceId == 0) {
+        deviceId = mBuiltInKeyboardId;
+    }
+
+    size_t numDevices = mDevices.size();
+    for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < numDevices; i++) {
+        Device* device = mDevices[i];
+        if (device->id == deviceId) {
+            return device;
+        }
     }
     return NULL;
 }
 
-bool EventHub::getEvent(RawEvent* outEvent)
-{
+bool EventHub::getEvent(RawEvent* outEvent) {
     outEvent->deviceId = 0;
     outEvent->type = 0;
     outEvent->scanCode = 0;
@@ -407,11 +426,11 @@
     for (;;) {
         // Report any devices that had last been added/removed.
         if (mClosingDevices != NULL) {
-            device_t* device = mClosingDevices;
-            LOGV("Reporting device closed: id=0x%x, name=%s\n",
+            Device* device = mClosingDevices;
+            LOGV("Reporting device closed: id=%d, name=%s\n",
                  device->id, device->path.string());
             mClosingDevices = device->next;
-            if (device->id == mFirstKeyboardId) {
+            if (device->id == mBuiltInKeyboardId) {
                 outEvent->deviceId = 0;
             } else {
                 outEvent->deviceId = device->id;
@@ -424,11 +443,11 @@
         }
 
         if (mOpeningDevices != NULL) {
-            device_t* device = mOpeningDevices;
-            LOGV("Reporting device opened: id=0x%x, name=%s\n",
+            Device* device = mOpeningDevices;
+            LOGV("Reporting device opened: id=%d, name=%s\n",
                  device->id, device->path.string());
             mOpeningDevices = device->next;
-            if (device->id == mFirstKeyboardId) {
+            if (device->id == mBuiltInKeyboardId) {
                 outEvent->deviceId = 0;
             } else {
                 outEvent->deviceId = device->id;
@@ -451,11 +470,11 @@
             // Consume buffered input events, if any.
             if (mInputBufferIndex < mInputBufferCount) {
                 const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
-                const device_t* device = mDevices[mInputDeviceIndex];
+                const Device* device = mDevices[mInputFdIndex];
 
                 LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
                      (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
-                if (device->id == mFirstKeyboardId) {
+                if (device->id == mBuiltInKeyboardId) {
                     outEvent->deviceId = 0;
                 } else {
                     outEvent->deviceId = device->id;
@@ -465,8 +484,8 @@
                 outEvent->flags = 0;
                 if (iev.type == EV_KEY) {
                     outEvent->keyCode = AKEYCODE_UNKNOWN;
-                    if (device->layoutMap) {
-                        status_t err = device->layoutMap->map(iev.code,
+                    if (device->keyMap.haveKeyLayout()) {
+                        status_t err = device->keyMap.keyLayoutMap->map(iev.code,
                                 &outEvent->keyCode, &outEvent->flags);
                         LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
                                 iev.code, outEvent->keyCode, outEvent->flags, err);
@@ -486,13 +505,13 @@
             // Finish reading all events from devices identified in previous poll().
             // This code assumes that mInputDeviceIndex is initially 0 and that the
             // revents member of pollfd is initialized to 0 when the device is first added.
-            // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
-            mInputDeviceIndex += 1;
-            if (mInputDeviceIndex >= mFDCount) {
+            // Since mFds[0] is used for inotify, we process regular events starting at index 1.
+            mInputFdIndex += 1;
+            if (mInputFdIndex >= mFds.size()) {
                 break;
             }
 
-            const struct pollfd& pfd = mFDs[mInputDeviceIndex];
+            const struct pollfd& pfd = mFds[mInputFdIndex];
             if (pfd.revents & POLLIN) {
                 int32_t readSize = read(pfd.fd, mInputBufferData,
                         sizeof(struct input_event) * INPUT_BUFFER_SIZE);
@@ -503,7 +522,7 @@
                 } else if ((readSize % sizeof(struct input_event)) != 0) {
                     LOGE("could not get event (wrong size: %d)", readSize);
                 } else {
-                    mInputBufferCount = readSize / sizeof(struct input_event);
+                    mInputBufferCount = size_t(readSize) / sizeof(struct input_event);
                     mInputBufferIndex = 0;
                 }
             }
@@ -512,14 +531,14 @@
 #if HAVE_INOTIFY
         // readNotify() will modify mFDs and mFDCount, so this must be done after
         // processing all other events.
-        if(mFDs[0].revents & POLLIN) {
-            readNotify(mFDs[0].fd);
-            mFDs[0].revents = 0;
+        if(mFds[0].revents & POLLIN) {
+            readNotify(mFds[0].fd);
+            mFds.editItemAt(0).revents = 0;
             continue; // report added or removed devices immediately
         }
 #endif
 
-        mInputDeviceIndex = 0;
+        mInputFdIndex = 0;
 
         // Poll for events.  Mind the wake lock dance!
         // We hold a wake lock at all times except during poll().  This works due to some
@@ -531,7 +550,7 @@
         // pending or currently being processed.
         release_wake_lock(WAKE_LOCK_ID);
 
-        int pollResult = poll(mFDs, mFDCount, -1);
+        int pollResult = poll(mFds.editArray(), mFds.size(), -1);
 
         acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
@@ -547,36 +566,37 @@
 /*
  * Open the platform-specific input device.
  */
-bool EventHub::openPlatformInput(void)
-{
+bool EventHub::openPlatformInput(void) {
     /*
      * Open platform-specific input device(s).
      */
-    int res;
+    int res, fd;
 
-    mFDCount = 1;
-    mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
-    mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
-    mFDs[0].events = POLLIN;
-    mFDs[0].revents = 0;
-    mDevices[0] = NULL;
 #ifdef HAVE_INOTIFY
-    mFDs[0].fd = inotify_init();
-    res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE);
+    fd = inotify_init();
+    res = inotify_add_watch(fd, DEVICE_PATH, IN_DELETE | IN_CREATE);
     if(res < 0) {
-        LOGE("could not add watch for %s, %s\n", device_path, strerror(errno));
+        LOGE("could not add watch for %s, %s\n", DEVICE_PATH, strerror(errno));
     }
 #else
     /*
      * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd.
      * We allocate space for it and set it to something invalid.
      */
-    mFDs[0].fd = -1;
+    fd = -1;
 #endif
 
-    res = scanDir(device_path);
+    // Reserve fd index 0 for inotify.
+    struct pollfd pollfd;
+    pollfd.fd = fd;
+    pollfd.events = POLLIN;
+    pollfd.revents = 0;
+    mFds.push(pollfd);
+    mDevices.push(NULL);
+
+    res = scanDir(DEVICE_PATH);
     if(res < 0) {
-        LOGE("scan dir failed for %s\n", device_path);
+        LOGE("scan dir failed for %s\n", DEVICE_PATH);
     }
 
     return true;
@@ -604,129 +624,102 @@
         AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
 };
 
-int EventHub::openDevice(const char *deviceName) {
-    int version;
-    int fd;
-    struct pollfd *new_mFDs;
-    device_t **new_devices;
-    char **new_device_names;
-    char name[80];
-    char location[80];
-    char idstr[80];
-    struct input_id id;
+int EventHub::openDevice(const char *devicePath) {
+    char buffer[80];
 
-    LOGV("Opening device: %s", deviceName);
+    LOGV("Opening device: %s", devicePath);
 
     AutoMutex _l(mLock);
 
-    fd = open(deviceName, O_RDWR);
+    int fd = open(devicePath, O_RDWR);
     if(fd < 0) {
-        LOGE("could not open %s, %s\n", deviceName, strerror(errno));
+        LOGE("could not open %s, %s\n", devicePath, strerror(errno));
         return -1;
     }
 
-    if(ioctl(fd, EVIOCGVERSION, &version)) {
-        LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno));
-        return -1;
-    }
-    if(ioctl(fd, EVIOCGID, &id)) {
-        LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno));
-        return -1;
-    }
-    name[sizeof(name) - 1] = '\0';
-    location[sizeof(location) - 1] = '\0';
-    idstr[sizeof(idstr) - 1] = '\0';
-    if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
-        //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno));
-        name[0] = '\0';
+    InputDeviceIdentifier identifier;
+
+    // Get device name.
+    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
+        //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
+    } else {
+        buffer[sizeof(buffer) - 1] = '\0';
+        identifier.name.setTo(buffer);
     }
 
-    // check to see if the device is on our excluded list
+    // Check to see if the device is on our excluded list
     List<String8>::iterator iter = mExcludedDevices.begin();
     List<String8>::iterator end = mExcludedDevices.end();
     for ( ; iter != end; iter++) {
         const char* test = *iter;
-        if (strcmp(name, test) == 0) {
-            LOGI("ignoring event id %s driver %s\n", deviceName, test);
+        if (identifier.name == test) {
+            LOGI("ignoring event id %s driver %s\n", devicePath, test);
             close(fd);
             return -1;
         }
     }
 
-    if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
-        //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno));
-        location[0] = '\0';
-    }
-    if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
-        //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno));
-        idstr[0] = '\0';
+    // Get device driver version.
+    int driverVersion;
+    if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
+        LOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
+        close(fd);
+        return -1;
     }
 
+    // Get device identifier.
+    struct input_id inputId;
+    if(ioctl(fd, EVIOCGID, &inputId)) {
+        LOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
+        close(fd);
+        return -1;
+    }
+    identifier.bus = inputId.bustype;
+    identifier.product = inputId.product;
+    identifier.vendor = inputId.vendor;
+    identifier.version = inputId.version;
+
+    // Get device physical location.
+    if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
+        //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
+    } else {
+        buffer[sizeof(buffer) - 1] = '\0';
+        identifier.location.setTo(buffer);
+    }
+
+    // Get device unique id.
+    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
+        //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
+    } else {
+        buffer[sizeof(buffer) - 1] = '\0';
+        identifier.uniqueId.setTo(buffer);
+    }
+
+    // Make file descriptor non-blocking for use with poll().
     if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
         LOGE("Error %d making device file descriptor non-blocking.", errno);
         close(fd);
         return -1;
     }
 
-    int devid = 0;
-    while (devid < mNumDevicesById) {
-        if (mDevicesById[devid].device == NULL) {
-            break;
-        }
-        devid++;
-    }
-    if (devid >= mNumDevicesById) {
-        device_ent* new_devids = (device_ent*)realloc(mDevicesById,
-                sizeof(mDevicesById[0]) * (devid + 1));
-        if (new_devids == NULL) {
-            LOGE("out of memory");
-            return -1;
-        }
-        mDevicesById = new_devids;
-        mNumDevicesById = devid+1;
-        mDevicesById[devid].device = NULL;
-        mDevicesById[devid].seq = 0;
-    }
-
-    mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
-    if (mDevicesById[devid].seq == 0) {
-        mDevicesById[devid].seq = 1<<SEQ_SHIFT;
-    }
-
-    new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));
-    new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));
-    if (new_mFDs == NULL || new_devices == NULL) {
-        LOGE("out of memory");
-        return -1;
-    }
-    mFDs = new_mFDs;
-    mDevices = new_devices;
+    // Allocate device.  (The device object takes ownership of the fd at this point.)
+    int32_t deviceId = mNextDeviceId++;
+    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
 
 #if 0
-    LOGI("add device %d: %s\n", mFDCount, deviceName);
-    LOGI("  bus:      %04x\n"
-         "  vendor    %04x\n"
-         "  product   %04x\n"
-         "  version   %04x\n",
-        id.bustype, id.vendor, id.product, id.version);
-    LOGI("  name:     \"%s\"\n", name);
-    LOGI("  location: \"%s\"\n"
-         "  id:       \"%s\"\n", location, idstr);
-    LOGI("  version:  %d.%d.%d\n",
-        version >> 16, (version >> 8) & 0xff, version & 0xff);
+    LOGI("add device %d: %s\n", deviceId, devicePath);
+    LOGI("  bus:       %04x\n"
+         "  vendor     %04x\n"
+         "  product    %04x\n"
+         "  version    %04x\n",
+        identifier.bus, identifier.vendor, identifier.product, identifier.version);
+    LOGI("  name:      \"%s\"\n", identifier.name.string());
+    LOGI("  location:  \"%s\"\n", identifier.location.string());
+    LOGI("  unique id: \"%s\"\n", identifier.uniqueId.string());
+    LOGI("  driver:    v%d.%d.%d\n",
+        driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
 #endif
 
-    device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
-    if (device == NULL) {
-        LOGE("out of memory");
-        return -1;
-    }
-
-    device->fd = fd;
-    mFDs[mFDCount].fd = fd;
-    mFDs[mFDCount].events = POLLIN;
-    mFDs[mFDCount].revents = 0;
-
     // Load the configuration file for the device.
     loadConfiguration(device);
 
@@ -798,7 +791,7 @@
     bool hasSwitches = false;
     if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
         for (int i=0; i<EV_SW; i++) {
-            //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
+            //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
             if (test_bit(i, sw_bitmask)) {
                 hasSwitches = true;
                 if (mSwitches[i] == 0) {
@@ -812,37 +805,29 @@
     }
 #endif
 
-    if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
-        // a more descriptive name
-        device->name = name;
-
-        // Configure the keymap for the device.
-        configureKeyMap(device);
-
-        // Tell the world about the devname (the descriptive name)
-        if (!mHaveFirstKeyboard && !device->keyMapInfo.isDefaultKeyMap && 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;
-            setKeyboardProperties(device, true);
-        } else {
-            // ensure mFirstKeyboardId is set to -something-.
-            if (mFirstKeyboardId == -1) {
-                mFirstKeyboardId = device->id;
-                setKeyboardProperties(device, true);
-            }
+    if ((device->classes & INPUT_DEVICE_CLASS_TOUCHSCREEN)) {
+        // Load the virtual keys for the touch screen, if any.
+        // We do this now so that we can make sure to load the keymap if necessary.
+        status_t status = loadVirtualKeyMap(device);
+        if (!status) {
+            device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
         }
+    }
+
+    if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
+        // Load the keymap for the device.
+        status_t status = loadKeyMap(device);
+
+        // Set system properties for the keyboard.
         setKeyboardProperties(device, false);
 
-        // Load the keylayout.
-        if (!device->keyMapInfo.keyLayoutFile.isEmpty()) {
-            status_t status = KeyLayoutMap::load(device->keyMapInfo.keyLayoutFile,
-                    &device->layoutMap);
-            if (status) {
-                LOGE("Error %d loading key layout file '%s'.", status,
-                        device->keyMapInfo.keyLayoutFile.string());
-            }
+        // Register the keyboard as a built-in keyboard if it is eligible.
+        if (!status
+                && mBuiltInKeyboardId == -1
+                && isEligibleBuiltInKeyboard(device->identifier,
+                        device->configuration, &device->keyMap)) {
+            mBuiltInKeyboardId = device->id;
+            setKeyboardProperties(device, true);
         }
 
         // 'Q' key support = cheap test of whether this is an alpha-capable kbd
@@ -866,76 +851,87 @@
                 break;
             }
         }
-
-        LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n",
-                device->id, name,
-                device->keyMapInfo.keyLayoutFile.string(),
-                device->keyMapInfo.keyCharacterMapFile.string());
     }
 
     // If the device isn't recognized as something we handle, don't monitor it.
     if (device->classes == 0) {
-        LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
-        close(fd);
+        LOGV("Dropping device: id=%d, path='%s', name='%s'",
+                deviceId, devicePath, device->identifier.name.string());
         delete device;
         return -1;
     }
 
-    LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x "
-            "configuration='%s'\n",
-         deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes,
-         device->configurationFile.string());
+    LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
+            "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
+         deviceId, fd, devicePath, device->identifier.name.string(),
+         device->classes,
+         device->configurationFile.string(),
+         device->keyMap.keyLayoutFile.string(),
+         device->keyMap.keyCharacterMapFile.string(),
+         toString(mBuiltInKeyboardId == deviceId));
 
-    LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n",
-         deviceName, device, mFDCount, devid, device->classes);
+    struct pollfd pollfd;
+    pollfd.fd = fd;
+    pollfd.events = POLLIN;
+    pollfd.revents = 0;
+    mFds.push(pollfd);
+    mDevices.push(device);
 
-    mDevicesById[devid].device = device;
     device->next = mOpeningDevices;
     mOpeningDevices = device;
-    mDevices[mFDCount] = device;
-
-    mFDCount++;
     return 0;
 }
 
-void EventHub::loadConfiguration(device_t* device) {
-    device->configurationFile = getInputDeviceConfigurationFilePath(device->name,
-            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+void EventHub::loadConfiguration(Device* device) {
+    device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
+            device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
     if (device->configurationFile.isEmpty()) {
-        LOGI("No input device configuration file found for device '%s'.",
-                device->name.string());
+        LOGD("No input device configuration file found for device '%s'.",
+                device->identifier.name.string());
     } else {
         status_t status = PropertyMap::load(device->configurationFile,
                 &device->configuration);
         if (status) {
-            LOGE("Error loading input device configuration file for device '%s'.",
-                    device->name.string());
+            LOGE("Error loading input device configuration file for device '%s'.  "
+                    "Using default configuration.",
+                    device->identifier.name.string());
         }
     }
 }
 
-void EventHub::configureKeyMap(device_t* device) {
-    android::resolveKeyMap(device->name, device->configuration, device->keyMapInfo);
+status_t EventHub::loadVirtualKeyMap(Device* device) {
+    // The virtual key map is supplied by the kernel as a system board property file.
+    String8 path;
+    path.append("/sys/board_properties/virtualkeys.");
+    path.append(device->identifier.name);
+    if (access(path.string(), R_OK)) {
+        return NAME_NOT_FOUND;
+    }
+    return VirtualKeyMap::load(path, &device->virtualKeyMap);
 }
 
-void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) {
-    int32_t id = firstKeyboard ? 0 : device->id;
-    android::setKeyboardProperties(id, device->name, device->keyMapInfo);
+status_t EventHub::loadKeyMap(Device* device) {
+    return device->keyMap.load(device->identifier, device->configuration);
 }
 
-void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) {
-    int32_t id = firstKeyboard ? 0 : device->id;
+void EventHub::setKeyboardProperties(Device* device, bool builtInKeyboard) {
+    int32_t id = builtInKeyboard ? 0 : device->id;
+    android::setKeyboardProperties(id, device->identifier,
+            device->keyMap.keyLayoutFile, device->keyMap.keyCharacterMapFile);
+}
+
+void EventHub::clearKeyboardProperties(Device* device, bool builtInKeyboard) {
+    int32_t id = builtInKeyboard ? 0 : device->id;
     android::clearKeyboardProperties(id);
 }
 
-bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
-{
-    if (device->keyBitmask == NULL || device->layoutMap == NULL) {
+bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
+    if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) {
         return false;
     }
     
     Vector<int32_t> scanCodes;
-    device->layoutMap->findScanCodes(keycode, &scanCodes);
+    device->keyMap.keyLayoutMap->findScanCodes(keycode, &scanCodes);
     const size_t N = scanCodes.size();
     for (size_t i=0; i<N && i<=KEY_MAX; i++) {
         int32_t sc = scanCodes.itemAt(i);
@@ -947,29 +943,15 @@
     return false;
 }
 
-int EventHub::closeDevice(const char *deviceName) {
+int EventHub::closeDevice(const char *devicePath) {
     AutoMutex _l(mLock);
 
-    int i;
-    for(i = 1; i < mFDCount; i++) {
-        if(strcmp(mDevices[i]->path.string(), deviceName) == 0) {
-            //LOGD("remove device %d: %s\n", i, deviceName);
-            device_t* device = mDevices[i];
-            
-            LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
-                 device->path.string(), device->name.string(), device->id,
-                 mNumDevicesById, mFDCount, mFDs[i].fd, device->classes);
-         
-            // Clear this device's entry.
-            int index = (device->id&ID_MASK);
-            mDevicesById[index].device = NULL;
-            
-            // Close the file descriptor and compact the fd array.
-            close(mFDs[i].fd);
-            int count = mFDCount - i - 1;
-            memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count);
-            memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count);
-            mFDCount--;
+    for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) {
+        Device* device = mDevices[i];
+        if (device->path == devicePath) {
+            LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
+                 device->path.string(), device->identifier.name.string(), device->id,
+                 device->fd, device->classes);
 
 #ifdef EV_SW
             for (int j=0; j<EV_SW; j++) {
@@ -978,21 +960,25 @@
                 }
             }
 #endif
-            
-            device->next = mClosingDevices;
-            mClosingDevices = device;
 
-            if (device->id == mFirstKeyboardId) {
+            if (device->id == mBuiltInKeyboardId) {
                 LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
-                        device->path.string(), mFirstKeyboardId);
-                mFirstKeyboardId = -1;
+                        device->path.string(), mBuiltInKeyboardId);
+                mBuiltInKeyboardId = -1;
                 clearKeyboardProperties(device, true);
             }
             clearKeyboardProperties(device, false);
+
+            mFds.removeAt(i);
+            mDevices.removeAt(i);
+            device->close();
+
+            device->next = mClosingDevices;
+            mClosingDevices = device;
             return 0;
         }
     }
-    LOGE("remove device: %s not found\n", deviceName);
+    LOGE("remove device: %s not found\n", devicePath);
     return -1;
 }
 
@@ -1016,7 +1002,7 @@
     }
     //printf("got %d bytes of event information\n", res);
 
-    strcpy(devname, device_path);
+    strcpy(devname, DEVICE_PATH);
     filename = devname + strlen(devname);
     *filename++ = '/';
 
@@ -1040,7 +1026,6 @@
     return 0;
 }
 
-
 int EventHub::scanDir(const char *dirname)
 {
     char devname[PATH_MAX];
@@ -1071,28 +1056,32 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard));
-        dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId);
+        dump.appendFormat(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId);
 
         dump.append(INDENT "Devices:\n");
 
-        for (int i = 0; i < mNumDevicesById; i++) {
-            const device_t* device = mDevicesById[i].device;
+        for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) {
+            const Device* device = mDevices[i];
             if (device) {
-                if (mFirstKeyboardId == device->id) {
-                    dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n",
-                            device->id, device->name.string());
+                if (mBuiltInKeyboardId == device->id) {
+                    dump.appendFormat(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
+                            device->id, device->identifier.name.string());
                 } else {
-                    dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string());
+                    dump.appendFormat(INDENT2 "%d: %s\n", device->id,
+                            device->identifier.name.string());
                 }
                 dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
                 dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
-                dump.appendFormat(INDENT3 "IsDefaultKeyMap: %s\n",
-                        toString(device->keyMapInfo.isDefaultKeyMap));
+                dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string());
+                dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
+                dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
+                        "product=0x%04x, version=0x%04x\n",
+                        device->identifier.bus, device->identifier.vendor,
+                        device->identifier.product, device->identifier.version);
                 dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n",
-                        device->keyMapInfo.keyLayoutFile.string());
+                        device->keyMap.keyLayoutFile.string());
                 dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n",
-                        device->keyMapInfo.keyCharacterMapFile.string());
+                        device->keyMap.keyCharacterMapFile.string());
                 dump.appendFormat(INDENT3 "ConfigurationFile: %s\n",
                         device->configurationFile.string());
             }
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 9e697db..b8d59e6 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -11,6 +11,7 @@
 
 #include <stdlib.h>
 #include <unistd.h>
+#include <ctype.h>
 
 #include <ui/Input.h>
 
@@ -28,12 +29,16 @@
         ".kcm",
 };
 
+static bool isValidNameChar(char ch) {
+    return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
+}
+
 static void appendInputDeviceConfigurationFileRelativePath(String8& path,
         const String8& name, InputDeviceConfigurationFileType type) {
     path.append(CONFIGURATION_FILE_DIR[type]);
     for (size_t i = 0; i < name.length(); i++) {
         char ch = name[i];
-        if (ch == ' ') {
+        if (!isValidNameChar(ch)) {
             ch = '_';
         }
         path.append(&ch, 1);
@@ -41,7 +46,37 @@
     path.append(CONFIGURATION_FILE_EXTENSION[type]);
 }
 
-extern String8 getInputDeviceConfigurationFilePath(
+String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+        const InputDeviceIdentifier& deviceIdentifier,
+        InputDeviceConfigurationFileType type) {
+    if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
+        if (deviceIdentifier.version != 0) {
+            // Try vendor product version.
+            String8 versionPath(getInputDeviceConfigurationFilePathByName(
+                    String8::format("Vendor_%04x_Product_%04x_Version_%04x",
+                            deviceIdentifier.vendor, deviceIdentifier.product,
+                            deviceIdentifier.version),
+                    type));
+            if (!versionPath.isEmpty()) {
+                return versionPath;
+            }
+        }
+
+        // Try vendor product.
+        String8 productPath(getInputDeviceConfigurationFilePathByName(
+                String8::format("Vendor_%04x_Product_%04x",
+                        deviceIdentifier.vendor, deviceIdentifier.product),
+                type));
+        if (!productPath.isEmpty()) {
+            return productPath;
+        }
+    }
+
+    // Try device name.
+    return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
+}
+
+String8 getInputDeviceConfigurationFilePathByName(
         const String8& name, InputDeviceConfigurationFileType type) {
     // Search system repository.
     String8 path;
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 0708223..f1223f1 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -315,7 +315,7 @@
                         // Throttle it!
 #if DEBUG_THROTTLING
                         LOGD("Throttling - Delaying motion event for "
-                                "device 0x%x, source 0x%08x by up to %0.3fms.",
+                                "device %d, source 0x%08x by up to %0.3fms.",
                                 deviceId, source, (nextTime - currentTime) * 0.000001);
 #endif
                         if (nextTime < *nextWakeupTime) {
@@ -704,7 +704,7 @@
 
 void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+    LOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
             "repeatCount=%d, downTime=%lld",
             prefix,
@@ -767,7 +767,7 @@
 
 void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+    LOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, "
             "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
             prefix,
@@ -2072,7 +2072,7 @@
         uint32_t policyFlags, int32_t action, int32_t flags,
         int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
+    LOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
             "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
             eventTime, deviceId, source, policyFlags, action, flags,
             keyCode, scanCode, metaState, downTime);
@@ -2120,7 +2120,7 @@
         uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
         float xPrecision, float yPrecision, nsecs_t downTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+    LOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, "
             "xPrecision=%f, yPrecision=%f, downTime=%lld",
             eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index aa690e5..9cc96ad 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -25,6 +25,7 @@
 #include <cutils/log.h>
 #include <ui/InputReader.h>
 #include <ui/Keyboard.h>
+#include <ui/VirtualKeyMap.h>
 
 #include <stddef.h>
 #include <stdlib.h>
@@ -121,7 +122,7 @@
     mEventHub->getEvent(& rawEvent);
 
 #if DEBUG_RAW_EVENTS
-    LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
+    LOGD("Input event: device=%d type=0x%x scancode=%d keycode=%d value=%d",
             rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
             rawEvent.value);
 #endif
@@ -157,9 +158,9 @@
     device->configure();
 
     if (device->isIgnored()) {
-        LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string());
+        LOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string());
     } else {
-        LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(),
+        LOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, name.string(),
                 device->getSources());
     }
 
@@ -201,10 +202,10 @@
     }
 
     if (device->isIgnored()) {
-        LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
+        LOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
                 device->getId(), device->getName().string());
     } else {
-        LOGI("Device removed: id=0x%x, name=%s, sources=%08x",
+        LOGI("Device removed: id=%d, name='%s', sources=0x%08x",
                 device->getId(), device->getName().string(), device->getSources());
     }
 
@@ -535,7 +536,7 @@
     InputDeviceInfo deviceInfo;
     getDeviceInfo(& deviceInfo);
 
-    dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(),
+    dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(),
             deviceInfo.getName().string());
     dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -1439,7 +1440,7 @@
 
     bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
     if (sizeChanged) {
-        LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d",
+        LOGI("Device reconfigured: id=%d, name='%s', display size is now %dx%d",
                 getDeviceId(), getDeviceName().string(), width, height);
 
         mLocked.surfaceWidth = width;
@@ -1651,9 +1652,8 @@
 void TouchInputMapper::configureVirtualKeysLocked() {
     assert(mRawAxes.x.valid && mRawAxes.y.valid);
 
-    // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock.
     Vector<VirtualKeyDefinition> virtualKeyDefinitions;
-    getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
+    getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
 
     mLocked.virtualKeys.clear();
 
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
index a4cc22d..6faa600 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/ui/Keyboard.cpp
@@ -22,97 +22,167 @@
 
 #include <ui/Keyboard.h>
 #include <ui/KeycodeLabels.h>
+#include <ui/KeyLayoutMap.h>
+#include <ui/KeyCharacterMap.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <cutils/properties.h>
 
 namespace android {
 
-static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
-    bool foundOne = false;
-    if (keyMapInfo.keyLayoutFile.isEmpty()) {
-        keyMapInfo.keyLayoutFile.setTo(getInputDeviceConfigurationFilePath(keyMapName,
-                INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
-        if (!keyMapInfo.keyLayoutFile.isEmpty()) {
-            foundOne = true;
-        }
-    }
+// --- KeyMap ---
 
-    if (keyMapInfo.keyCharacterMapFile.isEmpty()) {
-        keyMapInfo.keyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(keyMapName,
-                INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
-        if (!keyMapInfo.keyCharacterMapFile.isEmpty()) {
-            foundOne = true;
-        }
-    }
-
-    if (foundOne && defaultKeyMap) {
-        keyMapInfo.isDefaultKeyMap = true;
-    }
-    return keyMapInfo.isComplete();
+KeyMap::KeyMap() :
+        keyLayoutMap(NULL), keyCharacterMap(NULL) {
 }
 
-status_t resolveKeyMap(const String8& deviceName,
-        const PropertyMap* deviceConfiguration, KeyMapInfo& outKeyMapInfo) {
+KeyMap::~KeyMap() {
+    delete keyLayoutMap;
+    delete keyCharacterMap;
+}
+
+status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
+        const PropertyMap* deviceConfiguration) {
     // Use the configured key layout if available.
     if (deviceConfiguration) {
         String8 keyLayoutName;
         if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
                 keyLayoutName)) {
-            outKeyMapInfo.keyLayoutFile.setTo(getInputDeviceConfigurationFilePath(
-                    keyLayoutName, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
-            if (outKeyMapInfo.keyLayoutFile.isEmpty()) {
-                LOGW("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
+            status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
+            if (status == NAME_NOT_FOUND) {
+                LOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
                         "it was not found.",
-                        deviceName.string(), keyLayoutName.string());
+                        deviceIdenfifier.name.string(), keyLayoutName.string());
             }
         }
 
         String8 keyCharacterMapName;
         if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
                 keyCharacterMapName)) {
-            outKeyMapInfo.keyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(
-                    keyCharacterMapName, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
-            if (outKeyMapInfo.keyCharacterMapFile.isEmpty()) {
-                LOGW("Configuration for keyboard device '%s' requested keyboard character "
+            status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
+            if (status == NAME_NOT_FOUND) {
+                LOGE("Configuration for keyboard device '%s' requested keyboard character "
                         "map '%s' but it was not found.",
-                        deviceName.string(), keyCharacterMapName.string());
+                        deviceIdenfifier.name.string(), keyLayoutName.string());
             }
         }
 
-        if (outKeyMapInfo.isComplete()) {
+        if (isComplete()) {
             return OK;
         }
     }
 
-    // Try searching by device name.
-    if (probeKeyMap(outKeyMapInfo, deviceName, false)) {
+    // Try searching by device identifier.
+    if (probeKeyMap(deviceIdenfifier, String8::empty())) {
         return OK;
     }
 
     // Fall back on the Generic key map.
     // TODO Apply some additional heuristics here to figure out what kind of
-    //      generic key map to use (US English, etc.).
-    if (probeKeyMap(outKeyMapInfo, String8("Generic"), true)) {
+    //      generic key map to use (US English, etc.) for typical external keyboards.
+    if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
+        return OK;
+    }
+
+    // Try the Virtual key map as a last resort.
+    if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
         return OK;
     }
 
     // Give up!
-    LOGE("Could not determine key map for device '%s' and the Generic key map was not found!",
-            deviceName.string());
-    outKeyMapInfo.isDefaultKeyMap = true;
+    LOGE("Could not determine key map for device '%s' and no default key maps were found!",
+            deviceIdenfifier.name.string());
     return NAME_NOT_FOUND;
 }
 
-void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
-        const KeyMapInfo& keyMapInfo) {
+bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
+        const String8& keyMapName) {
+    if (!haveKeyLayout()) {
+        loadKeyLayout(deviceIdentifier, keyMapName);
+    }
+    if (!haveKeyCharacterMap()) {
+        loadKeyCharacterMap(deviceIdentifier, keyMapName);
+    }
+    return isComplete();
+}
+
+status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
+        const String8& name) {
+    String8 path(getPath(deviceIdentifier, name,
+            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+    if (path.isEmpty()) {
+        return NAME_NOT_FOUND;
+    }
+
+    KeyLayoutMap* map;
+    status_t status = KeyLayoutMap::load(path, &map);
+    if (status) {
+        return status;
+    }
+
+    keyLayoutFile.setTo(path);
+    keyLayoutMap = map;
+    return OK;
+}
+
+status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
+        const String8& name) {
+    String8 path(getPath(deviceIdentifier, name,
+            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
+    if (path.isEmpty()) {
+        return NAME_NOT_FOUND;
+    }
+
+    KeyCharacterMap* map;
+    status_t status = KeyCharacterMap::load(path, &map);
+    if (status) {
+        return status;
+    }
+
+    keyCharacterMapFile.setTo(path);
+    keyCharacterMap = map;
+    return OK;
+}
+
+String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
+        const String8& name, InputDeviceConfigurationFileType type) {
+    return name.isEmpty()
+            ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
+            : getInputDeviceConfigurationFilePathByName(name, type);
+}
+
+
+// --- Global functions ---
+
+bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
+        const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
+    if (!keyMap->haveKeyCharacterMap()
+            || keyMap->keyCharacterMap->getKeyboardType()
+                    == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
+        return false;
+    }
+
+    if (deviceConfiguration) {
+        bool builtIn = false;
+        if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
+                && builtIn) {
+            return true;
+        }
+    }
+
+    return strstr(deviceIdentifier.name.string(), "-keypad");
+}
+
+void setKeyboardProperties(int32_t deviceId,
+        const InputDeviceIdentifier& deviceIdentifier,
+        const String8& keyLayoutFile, const String8& keyCharacterMapFile) {
     char propName[PROPERTY_KEY_MAX];
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
-    property_set(propName, deviceName.string());
+    property_set(propName, deviceIdentifier.name.string());
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
-    property_set(propName, keyMapInfo.keyLayoutFile.string());
+    property_set(propName, keyLayoutFile.string());
     snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
-    property_set(propName, keyMapInfo.keyCharacterMapFile.string());
+    property_set(propName, keyCharacterMapFile.string());
 }
 
 void clearKeyboardProperties(int32_t deviceId) {
@@ -126,29 +196,24 @@
 }
 
 status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) {
-    if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) {
-        outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(String8("Virtual"),
-                INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
-        if (!outKeyCharacterMapFile.isEmpty()) {
+    if (deviceId != DEVICE_ID_VIRTUAL_KEYBOARD) {
+        char propName[PROPERTY_KEY_MAX];
+        char fn[PROPERTY_VALUE_MAX];
+        snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
+        if (property_get(propName, fn, "") > 0) {
+            outKeyCharacterMapFile.setTo(fn);
             return OK;
         }
     }
 
-    char propName[PROPERTY_KEY_MAX];
-    char fn[PROPERTY_VALUE_MAX];
-    snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
-    if (property_get(propName, fn, "") > 0) {
-        outKeyCharacterMapFile.setTo(fn);
-        return OK;
-    }
-
-    outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(String8("Generic"),
+    // Default to Virtual since the keyboard does not appear to be installed.
+    outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePathByName(String8("Virtual"),
             INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
     if (!outKeyCharacterMapFile.isEmpty()) {
         return OK;
     }
 
-    LOGE("Can't find any key character map files (also tried Virtual and Generic key maps)");
+    LOGE("Can't find any key character map files including the Virtual key map!");
     return NAME_NOT_FOUND;
 }
 
diff --git a/libs/ui/VirtualKeyMap.cpp b/libs/ui/VirtualKeyMap.cpp
new file mode 100644
index 0000000..e756cdd
--- /dev/null
+++ b/libs/ui/VirtualKeyMap.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VirtualKeyMap"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ui/VirtualKeyMap.h>
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+#include <utils/Timers.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:";
+
+
+// --- VirtualKeyMap ---
+
+VirtualKeyMap::VirtualKeyMap() {
+}
+
+VirtualKeyMap::~VirtualKeyMap() {
+}
+
+status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {
+    *outMap = NULL;
+
+    Tokenizer* tokenizer;
+    status_t status = Tokenizer::open(filename, &tokenizer);
+    if (status) {
+        LOGE("Error %d opening virtual key map file %s.", status, filename.string());
+    } else {
+        VirtualKeyMap* map = new VirtualKeyMap();
+        if (!map) {
+            LOGE("Error allocating virtual key map.");
+            status = NO_MEMORY;
+        } else {
+#if DEBUG_PARSER_PERFORMANCE
+            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+            Parser parser(map, tokenizer);
+            status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+            LOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
+                    tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+                    elapsedTime / 1000000.0);
+#endif
+            if (status) {
+                delete map;
+            } else {
+                *outMap = map;
+            }
+        }
+        delete tokenizer;
+    }
+    return status;
+}
+
+
+// --- VirtualKeyMap::Parser ---
+
+VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) :
+        mMap(map), mTokenizer(tokenizer) {
+}
+
+VirtualKeyMap::Parser::~Parser() {
+}
+
+status_t VirtualKeyMap::Parser::parse() {
+    while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+        LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+                mTokenizer->peekRemainderOfLine().string());
+#endif
+
+        mTokenizer->skipDelimiters(WHITESPACE);
+
+        if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+            // Multiple keys can appear on one line or they can be broken up across multiple lines.
+            do {
+                String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
+                if (token != "0x01") {
+                    LOGE("%s: Unknown virtual key type, expected 0x01.",
+                          mTokenizer->getLocation().string());
+                    return BAD_VALUE;
+                }
+
+                VirtualKeyDefinition defn;
+                bool success = parseNextIntField(&defn.scanCode)
+                        && parseNextIntField(&defn.centerX)
+                        && parseNextIntField(&defn.centerY)
+                        && parseNextIntField(&defn.width)
+                        && parseNextIntField(&defn.height);
+                if (!success) {
+                    LOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",
+                          mTokenizer->getLocation().string());
+                    return BAD_VALUE;
+                }
+
+#if DEBUG_PARSER
+                LOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, "
+                        "width=%d, height=%d",
+                        defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height);
+#endif
+                mMap->mVirtualKeys.push(defn);
+            } while (consumeFieldDelimiterAndSkipWhitespace());
+
+            if (!mTokenizer->isEol()) {
+                LOGE("%s: Expected end of line, got '%s'.",
+                        mTokenizer->getLocation().string(),
+                        mTokenizer->peekRemainderOfLine().string());
+                return BAD_VALUE;
+            }
+        }
+
+        mTokenizer->nextLine();
+    }
+
+    return NO_ERROR;
+}
+
+bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() {
+    mTokenizer->skipDelimiters(WHITESPACE);
+    if (mTokenizer->peekChar() == ':') {
+        mTokenizer->nextChar();
+        mTokenizer->skipDelimiters(WHITESPACE);
+        return true;
+    }
+    return false;
+}
+
+bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) {
+    if (!consumeFieldDelimiterAndSkipWhitespace()) {
+        return false;
+    }
+
+    String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
+    char* end;
+    *outValue = strtol(token.string(), &end, 0);
+    if (token.isEmpty() || *end != '\0') {
+        LOGE("Expected an integer, got '%s'.", token.string());
+        return false;
+    }
+    return true;
+}
+
+} // namespace android
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index 05bebc5..d6c2cbd 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -42,7 +42,6 @@
     KeyedVector<int32_t, DisplayInfo> mDisplayInfos;
     bool mFilterTouchEvents;
     bool mFilterJumpyTouchEvents;
-    KeyedVector<String8, Vector<VirtualKeyDefinition> > mVirtualKeyDefinitions;
     Vector<String8> mExcludedDeviceNames;
 
 protected:
@@ -75,15 +74,6 @@
         mFilterJumpyTouchEvents = enabled;
     }
 
-    void addVirtualKeyDefinition(const String8& deviceName,
-            const VirtualKeyDefinition& definition) {
-        if (mVirtualKeyDefinitions.indexOfKey(deviceName) < 0) {
-            mVirtualKeyDefinitions.add(deviceName, Vector<VirtualKeyDefinition>());
-        }
-
-        mVirtualKeyDefinitions.editValueFor(deviceName).push(definition);
-    }
-
     void addExcludedDeviceName(const String8& deviceName) {
         mExcludedDeviceNames.push(deviceName);
     }
@@ -116,14 +106,6 @@
         return mFilterJumpyTouchEvents;
     }
 
-    virtual void getVirtualKeyDefinitions(const String8& deviceName,
-            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
-        ssize_t index = mVirtualKeyDefinitions.indexOfKey(deviceName);
-        if (index >= 0) {
-            outVirtualKeyDefinitions.appendVector(mVirtualKeyDefinitions.valueAt(index));
-        }
-    }
-
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
         outExcludedDeviceNames.appendVector(mExcludedDeviceNames);
     }
@@ -355,6 +337,7 @@
         KeyedVector<int32_t, int32_t> switchStates;
         KeyedVector<int32_t, KeyInfo> keys;
         KeyedVector<int32_t, bool> leds;
+        Vector<VirtualKeyDefinition> virtualKeys;
 
         Device(const String8& name, uint32_t classes) :
                 name(name), classes(classes) {
@@ -448,6 +431,11 @@
         return mExcludedDevices;
     }
 
+    void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
+        Device* device = getDevice(deviceId);
+        device->virtualKeys.push(definition);
+    }
+
     void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
             int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) {
         RawEvent event;
@@ -603,6 +591,16 @@
         }
     }
 
+    virtual void getVirtualKeyDefinitions(int32_t deviceId,
+            Vector<VirtualKeyDefinition>& outVirtualKeys) const {
+        outVirtualKeys.clear();
+
+        Device* device = getDevice(deviceId);
+        if (device) {
+            outVirtualKeys.appendVector(device->virtualKeys);
+        }
+    }
+
     virtual void dump(String8& dump) {
     }
 };
@@ -2147,8 +2145,8 @@
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
-    mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[0]);
-    mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[1]);
+    mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]);
+    mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]);
     mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
     mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, AKEYCODE_MENU, POLICY_FLAG_WAKE);
 }
diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp
index e1ba9b2..f1f8bda 100644
--- a/libs/utils/FileMap.cpp
+++ b/libs/utils/FileMap.cpp
@@ -63,16 +63,18 @@
         free(mFileName);
     }
 #ifdef HAVE_POSIX_FILEMAP    
-    if (munmap(mBasePtr, mBaseLength) != 0) {
+    if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {
         LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength);
     }
 #endif
 #ifdef HAVE_WIN32_FILEMAP
-    if ( UnmapViewOfFile(mBasePtr) == 0) {
+    if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
         LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, 
               GetLastError() );
     }
-    CloseHandle(mFileMapping);
+    if (mFileMapping != INVALID_HANDLE_VALUE) {
+        CloseHandle(mFileMapping);
+    }
     CloseHandle(mFileHandle);
 #endif
 }
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index e531a2a..0bc5aff 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -195,6 +195,24 @@
     SharedBuffer::bufferFromData(mString)->release();
 }
 
+String8 String8::format(const char* fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+
+    String8 result(formatV(fmt, args));
+
+    va_end(args);
+    return result;
+}
+
+String8 String8::formatV(const char* fmt, va_list args)
+{
+    String8 result;
+    result.appendFormatV(fmt, args);
+    return result;
+}
+
 void String8::clear() {
     SharedBuffer::bufferFromData(mString)->release();
     mString = getEmptyString();
diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp
index 9251973..b3445b7 100644
--- a/libs/utils/Tokenizer.cpp
+++ b/libs/utils/Tokenizer.cpp
@@ -35,16 +35,16 @@
     return strchr(delimiters, ch) != NULL;
 }
 
-
-Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap,
-        const char* buffer, size_t length) :
-        mFilename(filename), mFileMap(fileMap), mBuffer(buffer), mLength(length),
-        mCurrent(buffer), mLineNumber(1) {
+Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) :
+        mFilename(filename), mFileMap(fileMap),
+        mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) {
 }
 
 Tokenizer::~Tokenizer() {
     if (mFileMap) {
         mFileMap->release();
+    } else {
+        delete[] mBuffer;
     }
 }
 
@@ -63,22 +63,33 @@
             LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
         } else {
             size_t length = size_t(stat.st_size);
-            FileMap* fileMap = new FileMap();
-            if (!fileMap->create(NULL, fd, 0, length, true)) {
-                result = NO_MEMORY;
-                LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno));
-            } else {
-                fileMap->advise(FileMap::SEQUENTIAL);
 
-                *outTokenizer = new Tokenizer(filename, fileMap,
-                        static_cast<const char*>(fileMap->getDataPtr()), length);
-                if (!*outTokenizer) {
-                    result = NO_MEMORY;
-                    LOGE("Error allocating tokenizer for file=%s.", filename.string());
+            FileMap* fileMap = new FileMap();
+            char* buffer;
+            if (fileMap->create(NULL, fd, 0, length, true)) {
+                fileMap->advise(FileMap::SEQUENTIAL);
+                buffer = static_cast<char*>(fileMap->getDataPtr());
+            } else {
+                fileMap->release();
+                fileMap = NULL;
+
+                // Fall back to reading into a buffer since we can't mmap files in sysfs.
+                // The length we obtained from stat is wrong too (it will always be 4096)
+                // so we must trust that read will read the entire file.
+                buffer = new char[length];
+                ssize_t nrd = read(fd, buffer, length);
+                if (nrd < 0) {
+                    result = -errno;
+                    LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno));
+                    delete[] buffer;
+                    buffer = NULL;
+                } else {
+                    length = size_t(nrd);
                 }
             }
-            if (result) {
-                fileMap->release();
+
+            if (!result) {
+                *outTokenizer = new Tokenizer(filename, fileMap, buffer, length);
             }
         }
         close(fd);
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 0e689e4..beac04c 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -194,6 +194,7 @@
         addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
  
         addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
+        addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
         addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST);
         addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST);
         addFileType("M3U8", FILE_TYPE_HTTPLIVE, "application/vnd.apple.mpegurl");
diff --git a/media/java/android/media/videoeditor/VideoEditor.java b/media/java/android/media/videoeditor/VideoEditor.java
index 64d3229..37bb661 100755
--- a/media/java/android/media/videoeditor/VideoEditor.java
+++ b/media/java/android/media/videoeditor/VideoEditor.java
@@ -98,6 +98,31 @@
     }

 

     /**

+     * This listener interface is used by the VideoEditor to emit export status

+     * notifications.

+     * {@link #generatePreview(MediaProcessingProgressListener listener)}

+     */

+    public interface MediaProcessingProgressListener {

+        // Values used for the action parameter

+        public static final int ACTION_ENCODE = 1;

+        public static final int ACTION_DECODE = 2;

+

+        /**

+         * This method notifies the listener of the progress status of

+         * processing a media object such as a Transition, AudioTrack or a

+         * media image item (when Ken Burns effect is applied).

+         * This method may be called maximum 100 times for one operation.

+         *

+         * @param object The object that is being processed such as a

+         *          Transition or AudioTrack

+         * @param action The type of processing being performed

+         * @param progress The progress in %. At the beginning of the operation,

+         *          this value is set to 0; at the end, the value is set to 100.

+         */

+        public void onProgress(Object item, int action, int progress);

+    }

+

+    /**

      * @return The path where the VideoEditor stores all files related to the

      * project

      */

@@ -496,8 +521,11 @@
      * This method must be called after the aspect ratio of the project changes

      * and before startPreview is called. Note that this method may block for

      * an extensive period of time.

+     *

+     * @param listener The listener interface which will be used to notify

+     *  the caller of the progress of each storyboard item being processed.

      */

-    public void generatePreview();

+    public void generatePreview(MediaProcessingProgressListener listener);

 

     /**

      * Start the preview of all the storyboard items applied on all MediaItems

diff --git a/media/java/android/media/videoeditor/VideoEditorFactory.java b/media/java/android/media/videoeditor/VideoEditorFactory.java
index 41eed16..85b2666 100755
--- a/media/java/android/media/videoeditor/VideoEditorFactory.java
+++ b/media/java/android/media/videoeditor/VideoEditorFactory.java
@@ -19,8 +19,8 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
+
+import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
 
 
 /**
@@ -30,34 +30,19 @@
  * {@hide}
  */
 public class VideoEditorFactory {
-    // VideoEditor implementation classes
-    public static final String TEST_CLASS_IMPLEMENTATION
-            = "android.media.videoeditor.VideoEditorTestImpl";
-    public static final String DEFAULT_CLASS_IMPLEMENTATION
-            = "android.media.videoeditor.VideoEditorImpl";
-
     /**
      * This is the factory method for creating a new VideoEditor instance.
      *
      * @param projectPath The path where all VideoEditor internal
      *            files are stored. When a project is deleted the application is
      *            responsible for deleting the path and its contents.
-     * @param className The implementation class name
      *
      * @return The VideoEditor instance
      *
      * @throws IOException if path does not exist or if the path can
      *             not be accessed in read/write mode
-     * @throws IllegalStateException if a previous VideoEditor instance has not
-     *             been released
-     * @throws ClassNotFoundException, NoSuchMethodException,
-     *             InvocationTargetException, IllegalAccessException,
-     *             InstantiationException if the implementation class cannot
-     *             be instantiated.
      */
-    public static VideoEditor create(String projectPath, String className) throws IOException,
-            ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
-            IllegalAccessException, InstantiationException {
+    public static VideoEditor create(String projectPath) throws IOException {
         // If the project path does not exist create it
         final File dir = new File(projectPath);
         if (!dir.exists()) {
@@ -72,14 +57,7 @@
             }
         }
 
-        final Class<?> cls = Class.forName(className);
-        final Class<?> partypes[] = new Class[1];
-        partypes[0] = String.class;
-        final Constructor<?> ct = cls.getConstructor(partypes);
-        final Object arglist[] = new Object[1];
-        arglist[0] = projectPath;
-
-        return (VideoEditor)ct.newInstance(arglist);
+        return new VideoEditorImpl(projectPath);
     }
 
     /**
@@ -90,32 +68,21 @@
      * @param projectPath The path where all VideoEditor internal files
      *            are stored. When a project is deleted the application is
      *            responsible for deleting the path and its contents.
-     * @param className The implementation class name
      * @param generatePreview if set to true the
-     *      {@link MediaEditor#generatePreview()} will be called internally to
-     *      generate any needed transitions.
+     *      {@link MediaEditor#generatePreview(MediaProcessingProgressListener listener)}
+     *      will be called internally to generate any needed transitions.
      *
      * @return The VideoEditor instance
      *
      * @throws IOException if path does not exist or if the path can
      *             not be accessed in read/write mode or if one of the resource
      *             media files cannot be retrieved
-     * @throws IllegalStateException if a previous VideoEditor instance has not
-     *             been released
      */
-    public static VideoEditor load(String projectPath, String className, boolean generatePreview)
-            throws IOException, ClassNotFoundException, NoSuchMethodException,
-            InvocationTargetException, IllegalAccessException, InstantiationException {
-        final Class<?> cls = Class.forName(className);
-        final Class<?> partypes[] = new Class[1];
-        partypes[0] = String.class;
-        final Constructor<?> ct = cls.getConstructor(partypes);
-        final Object arglist[] = new Object[1];
-        arglist[0] = projectPath;
-
-        final VideoEditor videoEditor = (VideoEditor)ct.newInstance(arglist);
+    public static VideoEditor load(String projectPath, boolean generatePreview)
+            throws IOException {
+        final VideoEditor videoEditor = new VideoEditorImpl(projectPath);
         if (generatePreview) {
-            videoEditor.generatePreview();
+            videoEditor.generatePreview(null);
         }
         return videoEditor;
     }
diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorImpl.java
similarity index 98%
rename from media/java/android/media/videoeditor/VideoEditorTestImpl.java
rename to media/java/android/media/videoeditor/VideoEditorImpl.java
index ca896c3..1a145e6 100644
--- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorImpl.java
@@ -39,7 +39,7 @@
 /**
  * The VideoEditor implementation {@hide}
  */
-public class VideoEditorTestImpl implements VideoEditor {
+public class VideoEditorImpl implements VideoEditor {
     // Logging
     private static final String TAG = "VideoEditorImpl";
 
@@ -165,7 +165,7 @@
                 if (mPositionMs >= mToMs) {
                     if (!mLoop) {
                         if (mListener != null) {
-                            mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, true);
+                            mListener.onProgress(VideoEditorImpl.this, mPositionMs, true);
                         }
                         if (Log.isLoggable(TAG, Log.DEBUG)) {
                             Log.d(TAG, "PreviewThread.run playback complete");
@@ -174,13 +174,13 @@
                     } else {
                         // Fire a notification for the end of the clip
                         if (mListener != null) {
-                            mListener.onProgress(VideoEditorTestImpl.this, mToMs, false);
+                            mListener.onProgress(VideoEditorImpl.this, mToMs, false);
                         }
 
                         // Rewind
                         mPositionMs = mFromMs;
                         if (mListener != null) {
-                            mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+                            mListener.onProgress(VideoEditorImpl.this, mPositionMs, false);
                         }
                         if (Log.isLoggable(TAG, Log.DEBUG)) {
                             Log.d(TAG, "PreviewThread.run playback complete");
@@ -190,7 +190,7 @@
                 } else {
                     if (frameCount == mCallbackAfterFrameCount) {
                         if (mListener != null) {
-                            mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+                            mListener.onProgress(VideoEditorImpl.this, mPositionMs, false);
                         }
                         frameCount = 0;
                     }
@@ -222,7 +222,7 @@
      *
      * @param projectPath
      */
-    public VideoEditorTestImpl(String projectPath) throws IOException {
+    public VideoEditorImpl(String projectPath) throws IOException {
         mProjectPath = projectPath;
         final File projectXml = new File(projectPath, PROJECT_FILENAME);
         if (projectXml.exists()) {
@@ -1055,7 +1055,7 @@
     /*
      * {@inheritDoc}
      */
-    public void generatePreview() {
+    public void generatePreview(MediaProcessingProgressListener listener) {
         // Generate all the needed transitions
         for (Transition transition : mTransitions) {
             if (!transition.isGenerated()) {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index ec3b5a2..cee92d2 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -366,6 +366,9 @@
         return BAD_VALUE;
     }
 
+    if (timeUs <= 15 * 1000000LL) {
+        LOGW("Target duration (%lld us) too short to be respected", timeUs);
+    }
     mMaxFileDurationUs = timeUs;
     return OK;
 }
@@ -376,6 +379,11 @@
         LOGE("Max file size is too small: %lld bytes", bytes);
         return BAD_VALUE;
     }
+
+    if (bytes <= 100 * 1024) {
+        LOGW("Target file size (%lld bytes) is too small to be respected", bytes);
+    }
+
     mMaxFileSizeBytes = bytes;
     return OK;
 }
@@ -1156,8 +1164,11 @@
     CHECK_EQ(client.connect(), OK);
 
     // Use software codec for time lapse
-    uint32_t encoder_flags = (mCaptureTimeLapse) ? OMXCodec::kPreferSoftwareCodecs : 0;
-    if (mIsMetaDataStoredInVideoBuffers) {
+    uint32_t encoder_flags = 0;
+    if (mCaptureTimeLapse) {
+        encoder_flags |= OMXCodec::kPreferSoftwareCodecs;
+    } else if (mIsMetaDataStoredInVideoBuffers) {
+        encoder_flags |= OMXCodec::kHardwareCodecsOnly;
         encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
     }
     sp<MediaSource> encoder = OMXCodec::Create(
@@ -1165,6 +1176,11 @@
             true /* createEncoder */, cameraSource,
             NULL, encoder_flags);
     if (encoder == NULL) {
+        LOGW("Failed to create the encoder");
+        // When the encoder fails to be created, we need
+        // release the camera source due to the camera's lock
+        // and unlock mechanism.
+        cameraSource->stop();
         return UNKNOWN_ERROR;
     }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index a804866..8ebbe6c 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -426,7 +426,8 @@
         // Hack to support http live.
 
         size_t len = strlen(uri);
-        if (!strcasecmp(&uri[len - 5], ".m3u8")) {
+        if (!strcasecmp(&uri[len - 5], ".m3u8")
+                || strstr(&uri[7], "m3u8") != NULL) {
             mUri = "httplive://";
             mUri.append(&uri[7]);
         }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 6760707..602aa9f 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -878,7 +878,10 @@
         nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
     }
 
-    return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
+    // Be conservative in the estimate: do not exceed 95% of
+    // the target file limit. For small target file size limit, though,
+    // this will not help.
+    return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);
 }
 
 bool MPEG4Writer::exceedsFileDurationLimit() {
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index e6a0976..e3292e6 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -178,7 +178,7 @@
       mInputFrameData(NULL),
       mGroup(NULL) {
 
-    LOGV("Construct software AVCEncoder");
+    LOGI("Construct software AVCEncoder");
 
     mHandle = new tagAVCHandle;
     memset(mHandle, 0, sizeof(tagAVCHandle));
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index c7a475b..15ed219 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -178,7 +178,7 @@
       mInputFrameData(NULL),
       mGroup(NULL) {
 
-    LOGV("Construct software M4vH263Encoder");
+    LOGI("Construct software M4vH263Encoder");
 
     mHandle = new tagvideoEncControls;
     memset(mHandle, 0, sizeof(tagvideoEncControls));
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index 4b4c3d2..4451bd5 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -359,14 +359,6 @@
             return false;
         }
 
-        if (keyURI.size() >= 2
-                && keyURI.c_str()[0] == '"'
-                && keyURI.c_str()[keyURI.size() - 1] == '"') {
-            // Remove surrounding quotes.
-            AString tmp(keyURI, 1, keyURI.size() - 2);
-            keyURI = tmp;
-        }
-
         ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
 
         sp<ABuffer> key;
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index b166cc3..d4a29c0 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -162,7 +162,7 @@
                 if (mIsVariantPlaylist) {
                     return ERROR_MALFORMED;
                 }
-                err = parseCipherInfo(line, &itemMeta);
+                err = parseCipherInfo(line, &itemMeta, mBaseURI);
             } else if (line.startsWith("#EXT-X-ENDLIST")) {
                 mIsComplete = true;
             } else if (line.startsWith("#EXTINF")) {
@@ -298,7 +298,7 @@
 
 // static
 status_t M3UParser::parseCipherInfo(
-        const AString &line, sp<AMessage> *meta) {
+        const AString &line, sp<AMessage> *meta, const AString &baseURI) {
     ssize_t colonPos = line.find(":");
 
     if (colonPos < 0) {
@@ -338,6 +338,24 @@
                 *meta = new AMessage;
             }
 
+            if (key == "uri") {
+                if (val.size() >= 2
+                        && val.c_str()[0] == '"'
+                        && val.c_str()[val.size() - 1] == '"') {
+                    // Remove surrounding quotes.
+                    AString tmp(val, 1, val.size() - 2);
+                    val = tmp;
+                }
+
+                AString absURI;
+                if (MakeURL(baseURI.c_str(), val.c_str(), &absURI)) {
+                    val = absURI;
+                } else {
+                    LOGE("failed to make absolute url for '%s'.",
+                         val.c_str());
+                }
+            }
+
             key.insert(AString("cipher-"), 0);
 
             (*meta)->setString(key.c_str(), val.c_str(), val.size());
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index 531d184..63895b4 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -67,7 +67,7 @@
             const AString &line, sp<AMessage> *meta);
 
     static status_t parseCipherInfo(
-            const AString &line, sp<AMessage> *meta);
+            const AString &line, sp<AMessage> *meta, const AString &baseURI);
 
     static status_t ParseInt32(const char *s, int32_t *x);
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index f06a1bb..a559b21d 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -238,10 +238,15 @@
 }
 
 sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
+    size_t index = (type == MPEG2ADTS_AUDIO) ? 0 : 0;
+
     for (size_t i = 0; i < mStreams.size(); ++i) {
         sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type);
         if (source != NULL) {
-            return source;
+            if (index == 0) {
+                return source;
+            }
+            --index;
         }
     }
 
@@ -508,7 +513,10 @@
     int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
 
     status_t err = mQueue.appendData(data, size, timeUs);
-    CHECK_EQ(err, (status_t)OK);
+
+    if (err != OK) {
+        return;
+    }
 
     sp<ABuffer> accessUnit;
     while ((accessUnit = mQueue.dequeueAccessUnit()) != NULL) {
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index f11b3c3..37bcb23 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -55,9 +55,34 @@
         switch (mMode) {
             case H264:
             {
+#if 0
                 if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
                     return ERROR_MALFORMED;
                 }
+#else
+                uint8_t *ptr = (uint8_t *)data;
+
+                ssize_t startOffset = -1;
+                for (size_t i = 0; i + 3 < size; ++i) {
+                    if (!memcmp("\x00\x00\x00\x01", &ptr[i], 4)) {
+                        startOffset = i;
+                        break;
+                    }
+                }
+
+                if (startOffset < 0) {
+                    return ERROR_MALFORMED;
+                }
+
+                if (startOffset > 0) {
+                    LOGI("found something resembling an H.264 syncword at "
+                         "offset %ld",
+                         startOffset);
+                }
+
+                data = &ptr[startOffset];
+                size -= startOffset;
+#endif
                 break;
             }
 
@@ -65,9 +90,31 @@
             {
                 uint8_t *ptr = (uint8_t *)data;
 
+#if 0
                 if (size < 2 || ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
                     return ERROR_MALFORMED;
                 }
+#else
+                ssize_t startOffset = -1;
+                for (size_t i = 0; i + 1 < size; ++i) {
+                    if (ptr[i] == 0xff && (ptr[i + 1] >> 4) == 0x0f) {
+                        startOffset = i;
+                        break;
+                    }
+                }
+
+                if (startOffset < 0) {
+                    return ERROR_MALFORMED;
+                }
+
+                if (startOffset > 0) {
+                    LOGI("found something resembling an AAC syncword at offset %ld",
+                         startOffset);
+                }
+
+                data = &ptr[startOffset];
+                size -= startOffset;
+#endif
                 break;
             }
 
diff --git a/packages/DefaultContainerService/res/values-id/strings.xml b/packages/DefaultContainerService/res/values-in/strings.xml
similarity index 100%
rename from packages/DefaultContainerService/res/values-id/strings.xml
rename to packages/DefaultContainerService/res/values-in/strings.xml
diff --git a/packages/DefaultContainerService/res/values-he/strings.xml b/packages/DefaultContainerService/res/values-iw/strings.xml
similarity index 100%
rename from packages/DefaultContainerService/res/values-he/strings.xml
rename to packages/DefaultContainerService/res/values-iw/strings.xml
diff --git a/packages/SettingsProvider/res/values-id/strings.xml b/packages/SettingsProvider/res/values-in/strings.xml
similarity index 100%
rename from packages/SettingsProvider/res/values-id/strings.xml
rename to packages/SettingsProvider/res/values-in/strings.xml
diff --git a/packages/SettingsProvider/res/values-he/strings.xml b/packages/SettingsProvider/res/values-iw/strings.xml
similarity index 100%
rename from packages/SettingsProvider/res/values-he/strings.xml
rename to packages/SettingsProvider/res/values-iw/strings.xml
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
index 615c8b6..25fffd6 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
index 0122025..b6e4ebc 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
index 0786916..189a089 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
index 35f9240..ebade92 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
index 3eb22df..6ef71c7 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
index 1ce9bd1..f6b0a17 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
index fef2cf9..91bc4ee 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
index 05593bc..aa64de4 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
index 32c2c79..246c6fe 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
index 142c413..34515c7 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png
new file mode 100644
index 0000000..b8adc97
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png
new file mode 100644
index 0000000..621e980
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_toggle_button.xml b/packages/SystemUI/res/drawable/status_bar_toggle_button.xml
new file mode 100644
index 0000000..e17c62f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_toggle_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true"
+         android:drawable="@*android:drawable/scrubber_primary_holo" />
+    <item
+         android:drawable="@*android:drawable/scrubber_track_holo_dark" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 758377b..666bfdc 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -158,10 +158,8 @@
                 />
             <com.android.systemui.statusbar.tablet.ShirtPocket
                 android:id="@+id/pocket"
-                android:layout_width="96dip"
+                android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:paddingLeft="18dip"
-                android:paddingRight="18dip"
                 android:animateLayoutChanges="true"
                 android:clickable="true"
                 android:descendantFocusability="blocksDescendants"
@@ -170,10 +168,12 @@
                 <ImageView 
                     android:id="@+id/pocket_icon"
                     android:src="@drawable/ic_sysbar_pocket"
-                    android:visibility="invisible"
-                    android:layout_width="match_parent"
+                    android:paddingLeft="18dip"
+                    android:paddingRight="18dip"
+                    android:layout_width="96dip"
                     android:layout_height="wrap_content"
                     android:gravity="center"
+                    android:visibility="gone"
                     />
             </com.android.systemui.statusbar.tablet.ShirtPocket>
             <com.android.systemui.statusbar.tablet.InputMethodButton
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
index 6dd97c3..5e867e5 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
@@ -17,11 +17,13 @@
 
 <com.android.systemui.statusbar.tablet.SettingsView
         xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:background="@drawable/status_bar_item_background"
         android:paddingLeft="16dp"
+        android:paddingRight="46dp"
         >
 
     <!-- Airplane mode -->
@@ -39,11 +41,12 @@
                 style="@style/StatusBarPanelSettingsContents"
                 android:text="@string/status_bar_settings_airplane"
                 />
-        <CheckBox
+        <Switch
                 android:id="@+id/airplane_checkbox"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"
+                android:layout_marginRight="5dp"
                 />
     </LinearLayout>
     <View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -67,7 +70,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_marginTop="16dp"
-            android:layout_marginRight="8dp"
+            android:layout_marginRight="2dp"
             android:src="@drawable/ic_notification_open"
             />
     </LinearLayout>
@@ -88,11 +91,12 @@
                 style="@style/StatusBarPanelSettingsContents"
                 android:text="@string/status_bar_settings_rotation_lock"
                 />
-        <CheckBox
+        <Switch
                 android:id="@+id/rotate_checkbox"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"
+                android:layout_marginRight="5dp"
                 />
     </LinearLayout>
     <View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -104,6 +108,14 @@
                 style="@style/StatusBarPanelSettingsIcon"
                 android:src="@drawable/ic_sysbar_brightness"
                 />
+        <com.android.systemui.statusbar.policy.ToggleSlider
+                android:id="@+id/brightness"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1"
+                android:layout_marginRight="2dp"
+                systemui:text="@string/status_bar_settings_auto_brightness_label"
+                />
     </LinearLayout>
     <View style="@style/StatusBarPanelSettingsPanelSeparator" />
 
@@ -114,6 +126,14 @@
                 style="@style/StatusBarPanelSettingsIcon"
                 android:src="@drawable/ic_sysbar_sound_on"
                 />
+        <com.android.systemui.statusbar.policy.ToggleSlider
+                android:id="@+id/volume"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1"
+                android:layout_marginRight="2dp"
+                systemui:text="@string/status_bar_settings_mute_label"
+                />
     </LinearLayout>
     <View style="@style/StatusBarPanelSettingsPanelSeparator" />
 
@@ -131,11 +151,12 @@
                 style="@style/StatusBarPanelSettingsContents"
                 android:text="@string/status_bar_settings_notifications"
                 />
-        <CheckBox
+        <Switch
                 android:id="@+id/do_not_disturb_checkbox"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"
+                android:layout_marginRight="5dp"
                 />
     </LinearLayout>
     <View style="@style/StatusBarPanelSettingsPanelSeparator" />
@@ -160,7 +181,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_marginTop="16dp"
-            android:layout_marginRight="8dp"
+            android:layout_marginRight="2dp"
             android:src="@drawable/ic_notification_open"
             />
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
new file mode 100644
index 0000000..cdf56c5
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+    >
+    <CheckBox
+        android:id="@+id/toggle"
+        android:layout_width="48dp"
+        android:layout_height="0dp"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentBottom="true"
+        android:button="@drawable/status_bar_toggle_button"
+        />
+    <SeekBar
+        android:id="@+id/slider"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/toggle"
+        android:layout_centerVertical="true"
+        android:layout_alignParentRight="true"
+        android:paddingLeft="20dp"
+        android:paddingRight="20dp"
+        />
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_alignLeft="@id/toggle"
+        android:layout_alignRight="@id/toggle"
+        android:layout_centerVertical="true"
+        android:gravity="center"
+        android:paddingTop="26dp"
+        android:textColor="#666666"
+        android:textSize="12sp"
+        />
+</merge>
diff --git a/packages/SystemUI/res/values-id/strings.xml b/packages/SystemUI/res/values-in/strings.xml
similarity index 100%
rename from packages/SystemUI/res/values-id/strings.xml
rename to packages/SystemUI/res/values-in/strings.xml
diff --git a/packages/SystemUI/res/values-he/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
similarity index 100%
rename from packages/SystemUI/res/values-he/strings.xml
rename to packages/SystemUI/res/values-iw/strings.xml
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml
index e3e5148..279a135 100644
--- a/packages/SystemUI/res/values-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-xlarge/strings.xml
@@ -26,44 +26,16 @@
     <!-- Text to display underneath the graphical signal strength meter when
          no connection is available. [CHAR LIMIT=20] -->
     <string name="status_bar_settings_signal_meter_disconnected">
-        no internet connection
-    </string>
-
-    <!-- Text to display underneath the graphical signal strength meter when
-         it is displaying information about a connected, named Wi-Fi network.
-         [CHAR LIMIT=20] -->
-    <string name="status_bar_settings_signal_meter_wifi_ssid_format">
-        <xliff:g id="ssid">%s</xliff:g>
+        No Internet connection
     </string>
 
     <!-- Text to display underneath the graphical signal strength meter when
          it is displaying Wi-Fi status and Wi-Fi is connected to a network
          whose SSID is not available.
          [CHAR LIMIT=20] -->
-    <string name="status_bar_settings_signal_meter_wifi_nossid">
-        Wi-Fi: connected
-    </string>
+    <string name="status_bar_settings_signal_meter_wifi_nossid">Wi-Fi connected</string>
 
-    <!-- Text to display underneath the graphical signal strength meter when
-         it is displaying Wi-Fi status and Wi-Fi is in the process of
-         connecting to a network.  [CHAR LIMIT=20] -->
-    <string name="status_bar_settings_signal_meter_wifi_connecting">
-        Wi-Fi: connecting…
-    </string>
- 
-    <!-- Text to display underneath the graphical signal strength meter when
-         it is displaying mobile data (3G) status and a network connection is
-         available.
-         [CHAR LIMIT=20] -->
-    <string name="status_bar_settings_signal_meter_data_connected">
-        Mobile data: connected
-    </string>
+    <!-- Separator for PLMN and SPN in network name. -->
+    <string name="status_bar_network_name_separator" translatable="false">" – "</string>
 
-    <!-- Text to display underneath the graphical signal strength meter when
-         it is displaying mobile data (3G) status and a network connection is
-         unavailable.
-         [CHAR LIMIT=20] -->
-    <string name="status_bar_settings_signal_meter_data_connecting">
-        Mobile data: connecting…
-    </string>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 23bcf20..87395c1 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -18,5 +18,8 @@
     <declare-styleable name="KeyButtonView">
         <attr name="keyCode" format="integer" />
     </declare-styleable>
+    <declare-styleable name="ToggleSlider">
+        <attr name="text" format="string" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ed31a34..644cca0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -80,6 +80,12 @@
     <!-- Label in system panel saying the device will use the orientation sensor to rotate [CHAR LIMIT=30] -->
     <string name="status_bar_settings_rotation_lock">Lock screen orientation</string>
 
+    <!-- Abbreviation / label for mute brightness mode button. Should be all caps. [CHAR LIMIT=6] -->
+    <string name="status_bar_settings_mute_label">MUTE</string>
+
+    <!-- Abbreviation / label for automatic brightness mode button. Should be all caps. [CHAR LIMIT=6] -->
+    <string name="status_bar_settings_auto_brightness_label">AUTO</string>
+
     <!-- Label in system panel saying the device will show notifications [CHAR LIMIT=30] -->
     <string name="status_bar_settings_notifications">Notifications</string>
 
@@ -88,6 +94,9 @@
         <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g>
     </string>
 
+    <!-- Separator for PLMN and SPN in network name. -->
+    <string name="status_bar_network_name_separator" translatable="false">"\n"</string>
+
     <!-- Recent Tasks dialog: title [CHAR LIMIT=30] -->
     <string name="recent_tasks_title">Recent</string>
     <!-- Recent Tasks dialog: message when there are no recent applications [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
new file mode 100644
index 0000000..c11d04e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.IPowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Slog;
+import android.view.IWindowManager;
+import android.widget.CompoundButton;
+
+public class BrightnessController implements ToggleSlider.Listener {
+    private static final String TAG = "StatusBar.BrightnessController";
+
+    // Backlight range is from 0 - 255. Need to make sure that user
+    // doesn't set the backlight to 0 and get stuck
+    private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
+    private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+
+    private Context mContext;
+    private ToggleSlider mControl;
+    private IPowerManager mPower;
+
+    public BrightnessController(Context context, ToggleSlider control) {
+        mContext = context;
+        mControl = control;
+
+        boolean automaticAvailable = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_automatic_brightness_available);
+        mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
+
+        if (automaticAvailable) {
+            int automatic;
+            try {
+                automatic = Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.SCREEN_BRIGHTNESS_MODE);
+            } catch (SettingNotFoundException snfe) {
+                automatic = 0;
+            }
+            control.setChecked(automatic != 0);
+        } else {
+            control.setChecked(false);
+            //control.hideToggle();
+        }
+        
+        int value;
+        try {
+            value = Settings.System.getInt(mContext.getContentResolver(), 
+                    Settings.System.SCREEN_BRIGHTNESS);
+        } catch (SettingNotFoundException ex) {
+            value = MAXIMUM_BACKLIGHT;
+        }
+
+        control.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT);
+        control.setValue(value - MINIMUM_BACKLIGHT);
+
+        control.setOnChangedListener(this);
+    }
+
+    public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
+        setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
+                : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+        if (!automatic) {
+            setBrightness(value + MINIMUM_BACKLIGHT);
+        }
+    }
+
+    private void setMode(int mode) {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE, mode);
+    }
+    
+    private void setBrightness(int brightness) {
+        try {
+            mPower.setBacklightBrightness(brightness);
+        } catch (RemoteException ex) {
+        }        
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index ec23a3d..1090463 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -32,6 +32,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.provider.Settings;
+import android.provider.Telephony;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -65,6 +66,9 @@
     ServiceState mServiceState;
     SignalStrength mSignalStrength;
     int[] mDataIconList = TelephonyIcons.DATA_G[0];
+    String mNetworkName;
+    String mNetworkNameDefault;
+    String mNetworkNameSeparator;
     int mPhoneSignalIconId;
     int mDataDirectionIconId;
     int mDataSignalIconId;
@@ -116,7 +120,10 @@
                         | PhoneStateListener.LISTEN_DATA_ACTIVITY);
         mHspaDataDistinguishable = mContext.getResources().getBoolean(
                 R.bool.config_hspa_data_distinguishable);
-        
+        mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator);
+        mNetworkNameDefault = mContext.getString(
+                com.android.internal.R.string.lockscreen_carrier_default);
+        mNetworkName = mNetworkNameDefault;
 
         // wifi
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -127,6 +134,9 @@
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
         context.registerReceiver(this, filter);
 
         // yuck
@@ -168,6 +178,12 @@
             updateSimState(intent);
             updateDataIcon();
             refreshViews();
+        } else if (action.equals(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION)) {
+            updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+                        intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+                        intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+                        intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+            refreshViews();
         } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
                  action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
             updateConnectivity(intent);
@@ -516,6 +532,31 @@
         mDataConnected = visible;
     }
 
+    void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+        if (false) {
+            Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+                    + " showPlmn=" + showPlmn + " plmn=" + plmn);
+        }
+        StringBuilder str = new StringBuilder();
+        boolean something = false;
+        if (showPlmn && plmn != null) {
+            str.append(plmn);
+            something = true;
+        }
+        if (showSpn && spn != null) {
+            if (something) {
+                str.append(mNetworkNameSeparator);
+            }
+            str.append(spn);
+            something = true;
+        }
+        if (something) {
+            mNetworkName = str.toString();
+        } else {
+            mNetworkName = mNetworkNameDefault;
+        }
+    }
+
     // ===== Wifi ===================================================================
 
     private void updateWifiState(Intent intent) {
@@ -618,14 +659,13 @@
             if (mWifiSsid == null) {
                 label = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid);
             } else {
-                label = context.getString(R.string.status_bar_settings_signal_meter_wifi_ssid_format,
-                                      mWifiSsid);
+                label = mWifiSsid;
             }
             combinedSignalIconId = mWifiIconId;
             dataTypeIconId = 0;
         } else {
             if (mDataConnected) {
-                label = context.getString(R.string.status_bar_settings_signal_meter_data_connected);
+                label = mNetworkName;
             } else {
                 label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
new file mode 100644
index 0000000..46207ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.CompoundButton;
+
+import com.android.systemui.R;
+
+public class ToggleSlider extends RelativeLayout 
+        implements CompoundButton.OnCheckedChangeListener, SeekBar.OnSeekBarChangeListener {
+    private static final String TAG = "StatusBar.ToggleSlider";
+
+    public interface Listener {
+        public void onChanged(ToggleSlider v, boolean tracking, boolean checked, int value);
+    }
+
+    private Listener mListener;
+    private boolean mTracking;
+
+    private CompoundButton mToggle;
+    private SeekBar mSlider;
+    private TextView mLabel;
+
+    public ToggleSlider(Context context) {
+        this(context, null);
+    }
+
+    public ToggleSlider(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ToggleSlider(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        View.inflate(context, R.layout.status_bar_toggle_slider, this);
+
+        final Resources res = context.getResources();
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToggleSlider,
+                defStyle, 0);
+
+        mToggle = (CompoundButton)findViewById(R.id.toggle);
+        mToggle.setOnCheckedChangeListener(this);
+        mToggle.setBackgroundDrawable(res.getDrawable(R.drawable.status_bar_toggle_button));
+
+        mSlider = (SeekBar)findViewById(R.id.slider);
+        mSlider.setOnSeekBarChangeListener(this);
+
+        mLabel = (TextView)findViewById(R.id.label);
+        mLabel.setText(a.getString(R.styleable.ToggleSlider_text));
+
+        a.recycle();
+    }
+
+    public void onCheckedChanged(CompoundButton toggle, boolean checked) {
+        Drawable thumb;
+        final Resources res = getContext().getResources();
+        if (checked) {
+            thumb = res.getDrawable(R.drawable.scrubber_control_disabled_holo);
+        } else {
+            thumb = res.getDrawable(R.drawable.scrubber_control_holo);
+        }
+        mSlider.setThumb(thumb);
+
+        if (mListener != null) {
+            mListener.onChanged(this, mTracking, checked, mSlider.getProgress());
+        }
+    }
+
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+        if (mListener != null) {
+            mListener.onChanged(this, mTracking, mToggle.isChecked(), progress);
+        }
+    }
+
+    public void onStartTrackingTouch(SeekBar seekBar) {
+        mTracking = true;
+        if (mListener != null) {
+            mListener.onChanged(this, mTracking, mToggle.isChecked(), mSlider.getProgress());
+        }
+        mToggle.setChecked(false);
+    }
+
+    public void onStopTrackingTouch(SeekBar seekBar) {
+        mTracking = false;
+        if (mListener != null) {
+            mListener.onChanged(this, mTracking, mToggle.isChecked(), mSlider.getProgress());
+        }
+    }
+
+    public void setOnChangedListener(Listener l) {
+        mListener = l;
+    }
+
+    public void setChecked(boolean checked) {
+        mToggle.setChecked(checked);
+    }
+
+    public boolean isChecked() {
+        return mToggle.isChecked();
+    }
+
+    public void setMax(int max) {
+        mSlider.setMax(max);
+    }
+
+    public void setValue(int value) {
+        mSlider.setProgress(value);
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
new file mode 100644
index 0000000..c9da01a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.media.AudioManager;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.IWindowManager;
+import android.widget.CompoundButton;
+
+public class VolumeController implements ToggleSlider.Listener {
+    private static final String TAG = "StatusBar.VolumeController";
+    private static final int STREAM = AudioManager.STREAM_NOTIFICATION;
+
+    private Context mContext;
+    private ToggleSlider mControl;
+    private AudioManager mAudioManager;
+
+    private boolean mMute;
+    private int mVolume;
+
+    public VolumeController(Context context, ToggleSlider control) {
+        mContext = context;
+        mControl = control;
+        mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
+
+        mMute = mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+        mVolume = mAudioManager.getStreamVolume(STREAM);
+        control.setMax(mAudioManager.getStreamMaxVolume(STREAM));
+        control.setValue(mVolume);
+        control.setChecked(mMute);
+
+        control.setOnChangedListener(this);
+    }
+
+    public void onChanged(ToggleSlider view, boolean tracking, boolean mute, int level) {
+        if (!tracking) {
+            if (mute) {
+                boolean vibeInSilent = (1 == Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.VIBRATE_IN_SILENT, 1));
+                mAudioManager.setRingerMode(
+                        vibeInSilent ? AudioManager.RINGER_MODE_VIBRATE
+                                     : AudioManager.RINGER_MODE_SILENT);
+            } else {
+                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+                mAudioManager.setStreamVolume(STREAM, level, AudioManager.FLAG_PLAY_SOUND);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
index d1f8dd0b..0491baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
@@ -31,13 +31,18 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.AirplaneModeController;
 import com.android.systemui.statusbar.policy.AutoRotateController;
+import com.android.systemui.statusbar.policy.BrightnessController;
 import com.android.systemui.statusbar.policy.DoNotDisturbController;
+import com.android.systemui.statusbar.policy.ToggleSlider;
+import com.android.systemui.statusbar.policy.VolumeController;
 
 public class SettingsView extends LinearLayout implements View.OnClickListener {
     static final String TAG = "SettingsView";
 
     AirplaneModeController mAirplane;
     AutoRotateController mRotate;
+    VolumeController mVolume;
+    BrightnessController mBrightness;
     DoNotDisturbController mDoNotDisturb;
 
     public SettingsView(Context context, AttributeSet attrs) {
@@ -59,6 +64,10 @@
         findViewById(R.id.network).setOnClickListener(this);
         mRotate = new AutoRotateController(context,
                 (CompoundButton)findViewById(R.id.rotate_checkbox));
+        mVolume = new VolumeController(context,
+                (ToggleSlider)findViewById(R.id.volume));
+        mBrightness = new BrightnessController(context,
+                (ToggleSlider)findViewById(R.id.brightness));
         mDoNotDisturb = new DoNotDisturbController(context,
                 (CompoundButton)findViewById(R.id.do_not_disturb_checkbox));
         findViewById(R.id.settings).setOnClickListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
index 4d0835f..698f5af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
@@ -81,7 +81,7 @@
         mIcon.setImageResource(mClipping == null
                 ? R.drawable.ic_sysbar_pocket_hidden
                 : R.drawable.ic_sysbar_pocket_holding);
-        mIcon.setVisibility(mClipping == null ? View.INVISIBLE : View.VISIBLE);
+        mIcon.setVisibility(mClipping == null ? View.GONE : View.VISIBLE);
     }
     
     private void showWindow() {
diff --git a/packages/VpnServices/res/values-id/strings.xml b/packages/VpnServices/res/values-in/strings.xml
similarity index 100%
rename from packages/VpnServices/res/values-id/strings.xml
rename to packages/VpnServices/res/values-in/strings.xml
diff --git a/packages/VpnServices/res/values-he/strings.xml b/packages/VpnServices/res/values-iw/strings.xml
similarity index 100%
rename from packages/VpnServices/res/values-he/strings.xml
rename to packages/VpnServices/res/values-iw/strings.xml
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 138dff7..b487d92 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -867,7 +867,7 @@
         if (panel.isOpen) {
             // The window manager will give us a valid window token
             new MenuDialogHelper(subMenu).show(null);
-        } else {
+        } else if (hasFeature(FEATURE_ACTION_BAR)) {
             mActionButtonPopup = new ActionButtonSubmenu(getContext(), subMenu);
             mActionButtonPopup.show();
             Callback cb = getCallback();
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 5c67da7..b5e3888 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -430,6 +430,8 @@
 
         mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
         mSettingsObserver.observe(mContext);
+
+        loadGlobalProxy();
     }
 
 
@@ -2089,7 +2091,7 @@
             ContentResolver res = mContext.getContentResolver();
             Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, host);
             Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, port);
-            Settings.Secure.putString(res,Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+            Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
                     exclList);
         }
 
@@ -2099,6 +2101,20 @@
         sendProxyBroadcast(proxyProperties);
     }
 
+    private void loadGlobalProxy() {
+        ContentResolver res = mContext.getContentResolver();
+        String host = Settings.Secure.getString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST);
+        int port = Settings.Secure.getInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, 0);
+        String exclList = Settings.Secure.getString(res,
+                Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        if (!TextUtils.isEmpty(host)) {
+            ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
+            synchronized (mGlobalProxyLock) {
+                mGlobalProxy = proxyProperties;
+            }
+        }
+    }
+
     public ProxyProperties getGlobalProxy() {
         synchronized (mGlobalProxyLock) {
             return mGlobalProxy;
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 9078811..8634eec 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -358,16 +358,6 @@
         }
     }
     
-    private static final class VirtualKeyDefinition {
-        public int scanCode;
-        
-        // configured position data, specified in display coords
-        public int centerX;
-        public int centerY;
-        public int width;
-        public int height;
-    }
-    
     /*
      * Callbacks from native.
      */
@@ -438,54 +428,6 @@
         }
         
         @SuppressWarnings("unused")
-        public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
-            ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
-            
-            try {
-                FileInputStream fis = new FileInputStream(
-                        "/sys/board_properties/virtualkeys." + deviceName);
-                InputStreamReader isr = new InputStreamReader(fis);
-                BufferedReader br = new BufferedReader(isr, 2048);
-                String str = br.readLine();
-                if (str != null) {
-                    String[] it = str.split(":");
-                    if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
-                    final int N = it.length-6;
-                    for (int i=0; i<=N; i+=6) {
-                        if (!"0x01".equals(it[i])) {
-                            Slog.w(TAG, "Unknown virtual key type at elem #"
-                                    + i + ": " + it[i] + " for device " + deviceName);
-                            continue;
-                        }
-                        try {
-                            VirtualKeyDefinition key = new VirtualKeyDefinition();
-                            key.scanCode = Integer.parseInt(it[i+1]);
-                            key.centerX = Integer.parseInt(it[i+2]);
-                            key.centerY = Integer.parseInt(it[i+3]);
-                            key.width = Integer.parseInt(it[i+4]);
-                            key.height = Integer.parseInt(it[i+5]);
-                            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
-                                    + key.scanCode + ": center=" + key.centerX + ","
-                                    + key.centerY + " size=" + key.width + "x"
-                                    + key.height);
-                            keys.add(key);
-                        } catch (NumberFormatException e) {
-                            Slog.w(TAG, "Bad number in virtual key definition at region "
-                                    + i + " in: " + str + " for device " + deviceName, e);
-                        }
-                    }
-                }
-                br.close();
-            } catch (FileNotFoundException e) {
-                Slog.i(TAG, "No virtual keys found for device " + deviceName + ".");
-            } catch (IOException e) {
-                Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e);
-            }
-            
-            return keys.toArray(new VirtualKeyDefinition[keys.size()]);
-        }
-        
-        @SuppressWarnings("unused")
         public String[] getExcludedDeviceNames() {
             ArrayList<String> names = new ArrayList<String>();
             
diff --git a/services/java/com/android/server/SamplingProfilerService.java b/services/java/com/android/server/SamplingProfilerService.java
index 26af7f7..61267d0 100644
--- a/services/java/com/android/server/SamplingProfilerService.java
+++ b/services/java/com/android/server/SamplingProfilerService.java
@@ -88,7 +88,7 @@
     private void registerSettingObserver(Context context) {
         ContentResolver contentResolver = context.getContentResolver();
         contentResolver.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_HZ),
+                Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_MS),
                 false, new SamplingProfilerSettingsObserver(contentResolver));
     }
 
@@ -107,12 +107,11 @@
         }
         @Override
         public void onChange(boolean selfChange) {
-            Integer samplingProfilerHz = Settings.Secure.getInt(
-                    mContentResolver, Settings.Secure.SAMPLING_PROFILER_HZ, 0);
+            Integer samplingProfilerMs = Settings.Secure.getInt(
+                    mContentResolver, Settings.Secure.SAMPLING_PROFILER_MS, 0);
             // setting this secure property will start or stop sampling profiler,
-            // as well as adjust the frequency of taking snapshots.
-            SystemProperties.set("persist.sys.profiler_hz", samplingProfilerHz.toString());
+            // as well as adjust the the time between taking snapshots.
+            SystemProperties.set("persist.sys.profiler_ms", samplingProfilerMs.toString());
         }
     }
 }
-
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index d523fa8..7f81a25 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -197,7 +197,7 @@
         WifiServiceHandler(android.os.Looper looper, Context context) {
             super(looper);
             mWshChannel = new AsyncChannel();
-            mWshChannel.connect(context, this, mWifiStateMachine.getHandler(), 0);
+            mWshChannel.connect(context, this, mWifiStateMachine.getHandler());
         }
 
         @Override
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index d7a6a0e..7504bb4 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -6005,6 +6005,7 @@
         int deviceId = ev.getDeviceId();
         int scancode = ev.getScanCode();
         int source = ev.getSource();
+        int flags = ev.getFlags();
         
         if (source == InputDevice.SOURCE_UNKNOWN) {
             source = InputDevice.SOURCE_KEYBOARD;
@@ -6014,7 +6015,7 @@
         if (downTime == 0) downTime = eventTime;
 
         KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
-                deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source);
+                deviceId, scancode, flags | KeyEvent.FLAG_FROM_SYSTEM, source);
 
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index aa84db5..1996dd0 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,7 +56,6 @@
     jmethodID checkInjectEventsPermission;
     jmethodID filterTouchEvents;
     jmethodID filterJumpyTouchEvents;
-    jmethodID getVirtualKeyDefinitions;
     jmethodID getExcludedDeviceNames;
     jmethodID getMaxEventsPerSecond;
 } gCallbacksClassInfo;
@@ -64,16 +63,6 @@
 static struct {
     jclass clazz;
 
-    jfieldID scanCode;
-    jfieldID centerX;
-    jfieldID centerY;
-    jfieldID width;
-    jfieldID height;
-} gVirtualKeyDefinitionClassInfo;
-
-static struct {
-    jclass clazz;
-
     jfieldID inputChannel;
     jfieldID name;
     jfieldID layoutParamsFlags;
@@ -176,8 +165,6 @@
             int32_t* width, int32_t* height, int32_t* orientation);
     virtual bool filterTouchEvents();
     virtual bool filterJumpyTouchEvents();
-    virtual void getVirtualKeyDefinitions(const String8& deviceName,
-            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions);
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
@@ -442,41 +429,6 @@
     return mFilterJumpyTouchEvents;
 }
 
-void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName,
-        Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
-    outVirtualKeyDefinitions.clear();
-
-    JNIEnv* env = jniEnv();
-
-    jstring deviceNameStr = env->NewStringUTF(deviceName.string());
-    if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
-        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
-                gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
-        if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
-            jsize length = env->GetArrayLength(result);
-            for (jsize i = 0; i < length; i++) {
-                jobject item = env->GetObjectArrayElement(result, i);
-
-                outVirtualKeyDefinitions.add();
-                outVirtualKeyDefinitions.editTop().scanCode =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
-                outVirtualKeyDefinitions.editTop().centerX =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
-                outVirtualKeyDefinitions.editTop().centerY =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
-                outVirtualKeyDefinitions.editTop().width =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
-                outVirtualKeyDefinitions.editTop().height =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
-
-                env->DeleteLocalRef(item);
-            }
-            env->DeleteLocalRef(result);
-        }
-        env->DeleteLocalRef(deviceNameStr);
-    }
-}
-
 void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
     outExcludedDeviceNames.clear();
 
@@ -1366,36 +1318,12 @@
     GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz,
             "filterJumpyTouchEvents", "()Z");
 
-    GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyDefinitions, gCallbacksClassInfo.clazz,
-            "getVirtualKeyDefinitions",
-            "(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;");
-
     GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
     GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
             "getMaxEventsPerSecond", "()I");
 
-    // VirtualKeyDefinition
-
-    FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
-            "com/android/server/InputManager$VirtualKeyDefinition");
-
-    GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.scanCode, gVirtualKeyDefinitionClassInfo.clazz,
-            "scanCode", "I");
-
-    GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerX, gVirtualKeyDefinitionClassInfo.clazz,
-            "centerX", "I");
-
-    GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerY, gVirtualKeyDefinitionClassInfo.clazz,
-            "centerY", "I");
-
-    GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.width, gVirtualKeyDefinitionClassInfo.clazz,
-            "width", "I");
-
-    GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
-            "height", "I");
-
     // InputWindow
 
     FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7be58c6..145618e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -231,7 +231,7 @@
         hwcl->handle = NULL;
         return;
     }
-    hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
+    hwcl->handle = buffer->handle;
     // TODO: set the crop value properly
     hwcl->sourceCrop.left   = 0;
     hwcl->sourceCrop.top    = 0;
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 27a4dca..c7da3d4 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -951,22 +951,6 @@
 
     void writeSmsToRuim(int status, String pdu, Message response);
 
-    /**
-     * @param apn
-     * @param user
-     * @param password
-     * @param response
-     */
-    @Deprecated
-    void setupDefaultPDP(String apn, String user, String password, Message response);
-
-    /**
-     * @param cid
-     * @param response
-     */
-    @Deprecated
-    void deactivateDefaultPDP(int cid, Message response);
-
     void setRadioPower(boolean on, Message response);
 
     void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message response);
@@ -1354,10 +1338,12 @@
      *
      * @param cid
      *            The connection ID
+     * @param reason
+     *            Data disconnect reason.
      * @param result
      *            Callback message is empty on completion
      */
-    public void deactivateDataCall(int cid, Message result);
+    public void deactivateDataCall(int cid, int reason, Message result);
 
     /**
      * Activate or deactivate cell broadcast SMS for CDMA.
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 2536745..58fb13b 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -26,6 +26,7 @@
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.EventLog;
 
 import java.net.InetAddress;
@@ -312,9 +313,19 @@
      *          and is either a DisconnectParams or ConnectionParams.
      */
     private void tearDownData(Object o) {
+        int discReason = RILConstants.DEACTIVATE_REASON_NONE;
+        if ((o != null) && (o instanceof DisconnectParams)) {
+            DisconnectParams dp = (DisconnectParams)o;
+            Message m = dp.onCompletedMsg;
+            if ((m != null) && (m.obj != null) && (m.obj instanceof String)) {
+                String reason = (String)m.obj;
+                if (TextUtils.equals(reason, Phone.REASON_RADIO_TURNED_OFF))
+                    discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+            }
+        }
         if (phone.mCM.getRadioState().isOn()) {
             if (DBG) log("tearDownData radio is on, call deactivateDataCall");
-            phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, o));
+            phone.mCM.deactivateDataCall(cid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, o));
         } else {
             if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
             AsyncResult ar = new AsyncResult(o, null, null);
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index a77e73e..916602f 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1265,33 +1265,6 @@
     }
 
     /**
-     * @deprecated
-     */
-    public void
-    setupDefaultPDP(String apn, String user, String password, Message result) {
-        int radioTechnology;
-        int authType;
-        String profile = ""; //profile number, NULL for GSM/UMTS
-
-        radioTechnology = RILConstants.SETUP_DATA_TECH_GSM;
-        //TODO(): Add to the APN database, AuthType is set to CHAP/PAP
-        authType = (user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP
-                : RILConstants.SETUP_DATA_AUTH_NONE;
-
-        setupDataCall(Integer.toString(radioTechnology), profile, apn, user,
-                password, Integer.toString(authType), result);
-
-    }
-
-    /**
-     * @deprecated
-     */
-    public void
-    deactivateDefaultPDP(int cid, Message result) {
-        deactivateDataCall(cid, result);
-    }
-
-    /**
      * The preferred new alternative to setupDefaultPDP that is
      * CDMA-compatible.
      *
@@ -1329,15 +1302,16 @@
     }
 
     public void
-    deactivateDataCall(int cid, Message result) {
+    deactivateDataCall(int cid, int reason, Message result) {
         RILRequest rr
                 = RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DATA_CALL, result);
 
-        rr.mp.writeInt(1);
+        rr.mp.writeInt(2);
         rr.mp.writeString(Integer.toString(cid));
+        rr.mp.writeString(Integer.toString(reason));
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " +
-                requestToString(rr.mRequest) + " " + cid);
+                requestToString(rr.mRequest) + " " + cid + " " + reason);
 
         send(rr);
     }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 888f721..305e15f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -96,6 +96,10 @@
     int SETUP_DATA_AUTH_CHAP      = 2;
     int SETUP_DATA_AUTH_PAP_CHAP  = 3;
 
+    /* Deactivate data call reasons */
+    int DEACTIVATE_REASON_NONE = 0;
+    int DEACTIVATE_REASON_RADIO_OFF = 1;
+
 /*
 cat include/telephony/ril.h | \
    egrep '^#define' | \
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index b60be6e..de15408 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -569,45 +569,24 @@
         msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
 
         synchronized (this) {
-            if (networkType == ServiceState.RADIO_TECHNOLOGY_1xRTT) {
-                /*
-                 * In 1x CDMA , during radio power off modem will disconnect the
-                 * data call and sends the power down registration message along
-                 * with the data call release message to the network
-                 */
-
-                msg.arg1 = 0; // tearDown is false since modem does it anyway for 1X
-                dcTracker.sendMessage(msg);
-
-                Log.w(LOG_TAG, "Turn off the radio right away");
-                hangupAndPowerOff();
-            } else {
-                if (!mPendingRadioPowerOffAfterDataOff) {
-                    DataConnectionTracker.State currentState = dcTracker.getState();
-                    if (currentState != DataConnectionTracker.State.CONNECTED
-                            && currentState != DataConnectionTracker.State.DISCONNECTING
-                            && currentState != DataConnectionTracker.State.INITING) {
-
-                        msg.arg1 = 0; // tearDown is false as it is not needed.
-                        dcTracker.sendMessage(msg);
-
-                        if (DBG)
-                            log("Data disconnected, turn off radio right away.");
-                        hangupAndPowerOff();
+            if (!mPendingRadioPowerOffAfterDataOff) {
+                DataConnectionTracker.State currentState = dcTracker.getState();
+                if (currentState != DataConnectionTracker.State.CONNECTED
+                    && currentState != DataConnectionTracker.State.DISCONNECTING
+                    && currentState != DataConnectionTracker.State.INITING) {
+                    msg.arg1 = 0; // tearDown is false as it is not needed.
+                    dcTracker.sendMessage(msg);
+                    if (DBG) log("Data disconnected, turn off radio right away.");
+                    hangupAndPowerOff();
+                } else {
+                    msg.arg1 = 1; // tearDown is true
+                    dcTracker.sendMessage(msg);
+                    if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
+                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+                        mPendingRadioPowerOffAfterDataOff = true;
                     } else {
-                        // clean data connection
-                        msg.arg1 = 1; // tearDown is true
-                        dcTracker.sendMessage(msg);
-
-                        if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
-                            if (DBG) {
-                                log("Wait upto 30s for data to disconnect, then turn off radio.");
-                            }
-                            mPendingRadioPowerOffAfterDataOff = true;
-                        } else {
-                            Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
-                            hangupAndPowerOff();
-                        }
+                        Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
+                        hangupAndPowerOff();
                     }
                 }
             }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
index ed578c8..2e729fe 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -184,19 +184,12 @@
     public void writeSmsToRuim(int status, String pdu, Message response) {
     }
 
-    public void setupDefaultPDP(String apn, String user, String password,
-            Message result) {
-    }
-
-    public void deactivateDefaultPDP(int cid, Message result) {
-    }
-
     public void setupDataCall(String radioTechnology, String profile,
             String apn, String user, String password, String authType,
             Message result) {
     }
 
-    public void deactivateDataCall(int cid, Message result) {
+    public void deactivateDataCall(int cid, int reason, Message result) {
     }
 
     public void setRadioPower(boolean on, Message result) {
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index fdcf78d..c2f88e5 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -954,20 +954,12 @@
         unimplemented(response);
     }
 
-    @Deprecated
-    public void setupDefaultPDP(String apn, String user, String password, Message result) {
-        unimplemented(result);
-    }
-
     public void setupDataCall(String radioTechnology, String profile, String apn, String user,
             String password, String authType, Message result) {
         unimplemented(result);
     }
 
-    public void deactivateDataCall(int cid, Message result) {unimplemented(result);}
-
-    @Deprecated
-    public void deactivateDefaultPDP(int cid, Message result) {unimplemented(result);}
+    public void deactivateDataCall(int cid, int reason, Message result) {unimplemented(result);}
 
     public void setPreferredNetworkType(int networkType , Message result) {
         mNetworkType = networkType;
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index b901e0d..bc6ad64 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -92,13 +92,6 @@
         return mGraphicsStack.peek();
     }
 
-    /**
-     * Disposes of the {@link Graphics2D} stack.
-     */
-    public void dispose() {
-
-    }
-
     // ---- native methods ----
 
     /*package*/ static boolean isOpaque(Canvas thisCanvas) {
@@ -985,6 +978,16 @@
     }
 
     /*package*/ static void finalizer(int nativeCanvas) {
+        // get the delegate from the native int so that it can be disposed.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.dispose();
+
+        // remove it from the manager.
         sManager.removeDelegate(nativeCanvas);
     }
 
@@ -997,6 +1000,15 @@
     private Canvas_Delegate() {
     }
 
+    /**
+     * Disposes of the {@link Graphics2D} stack.
+     */
+    private void dispose() {
+        while (mGraphicsStack.size() > 0) {
+            mGraphicsStack.pop().dispose();
+        }
+    }
+
     private void setBitmap(BufferedImage image) {
         mBufferedImage = image;
         mGraphicsStack.push(mBufferedImage.createGraphics());
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index ca87b4e..392717e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge;
 
+import com.android.layoutlib.api.Capabilities;
 import com.android.layoutlib.api.ILayoutLog;
 import com.android.layoutlib.api.IProjectCallback;
 import com.android.layoutlib.api.IResourceValue;
@@ -33,12 +34,14 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Typeface_Delegate;
+import android.os.Looper;
 import android.util.Finalizers;
 
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.locks.ReentrantLock;
@@ -151,11 +154,19 @@
         }
     };
 
+    private EnumSet<Capabilities> mCapabilities;
+
+
     @Override
     public int getApiLevel() {
         return LayoutBridge.API_CURRENT;
     }
 
+    @Override
+    public EnumSet<Capabilities> getCapabilities() {
+        return mCapabilities;
+    }
+
     /*
      * (non-Javadoc)
      * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map)
@@ -164,6 +175,15 @@
     public boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) {
         sEnumValueMap = enumValueMap;
 
+        // don't use EnumSet.allOf(), because the bridge doesn't come with it's specific version
+        // of layoutlib_api. It is provided by the client which could have a more recent version
+        // with newer, unsupported capabilities.
+        mCapabilities = EnumSet.of(
+                Capabilities.RENDER,
+                Capabilities.VIEW_MANIPULATION,
+                Capabilities.ANIMATE);
+
+
         Finalizers.init();
 
         BridgeAssetManager.initSystem();
@@ -291,20 +311,20 @@
     @Override
     public BridgeLayoutScene createScene(SceneParams params) {
         try {
-            SceneResult lastResult = SceneResult.SUCCESS;
+            SceneResult lastResult = SceneStatus.SUCCESS.getResult();
             LayoutSceneImpl scene = new LayoutSceneImpl(params);
             try {
-                scene.prepareThread();
+                prepareThread();
                 lastResult = scene.init(params.getTimeout());
-                if (lastResult == SceneResult.SUCCESS) {
+                if (lastResult.isSuccess()) {
                     lastResult = scene.inflate();
-                    if (lastResult == SceneResult.SUCCESS) {
+                    if (lastResult.isSuccess()) {
                         lastResult = scene.render();
                     }
                 }
             } finally {
                 scene.release();
-                scene.cleanupThread();
+                cleanupThread();
             }
 
             return new BridgeLayoutScene(scene, lastResult);
@@ -339,6 +359,31 @@
     }
 
     /**
+     * Prepares the current thread for rendering.
+     *
+     * Note that while this can be called several time, the first call to {@link #cleanupThread()}
+     * will do the clean-up, and make the thread unable to do further scene actions.
+     */
+    public static void prepareThread() {
+        // we need to make sure the Looper has been initialized for this thread.
+        // this is required for View that creates Handler objects.
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+    }
+
+    /**
+     * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
+     * <p>
+     * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
+     * call to this will prevent the thread from doing further scene actions
+     */
+    public static void cleanupThread() {
+        // clean up the looper
+        Looper.sThreadLocal.remove();
+    }
+
+    /**
      * Returns details of a framework resource from its integer value.
      * @param value the integer value
      * @return an array of 2 strings containing the resource name and type, or null if the id
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
index f807214..a491901 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
@@ -64,14 +64,14 @@
     @Override
     public SceneResult render(long timeout) {
         try {
-            mScene.prepareThread();
+            Bridge.prepareThread();
             mLastResult = mScene.acquire(timeout);
-            if (mLastResult == SceneResult.SUCCESS) {
+            if (mLastResult.isSuccess()) {
                 mLastResult = mScene.render();
             }
         } finally {
             mScene.release();
-            mScene.cleanupThread();
+            Bridge.cleanupThread();
         }
 
         return mLastResult;
@@ -81,40 +81,36 @@
     public SceneResult animate(Object targetObject, String animationName,
             boolean isFrameworkAnimation, IAnimationListener listener) {
         try {
-            mScene.prepareThread();
+            Bridge.prepareThread();
             mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
-            if (mLastResult == SceneResult.SUCCESS) {
+            if (mLastResult.isSuccess()) {
                 mLastResult = mScene.animate(targetObject, animationName, isFrameworkAnimation,
                         listener);
             }
         } finally {
             mScene.release();
-            mScene.cleanupThread();
+            Bridge.cleanupThread();
         }
 
         return mLastResult;
     }
 
     @Override
-    public SceneResult insertChild(Object parentView, IXmlPullParser childXml, Object beforeSibling,
+    public SceneResult insertChild(Object parentView, IXmlPullParser childXml, int index,
             IAnimationListener listener) {
         if (parentView instanceof ViewGroup == false) {
             throw new IllegalArgumentException("parentView is not a ViewGroup");
         }
-        if (beforeSibling != null && beforeSibling instanceof View == false) {
-            throw new IllegalArgumentException("beforeSibling is not a View");
-        }
 
         try {
-            mScene.prepareThread();
+            Bridge.prepareThread();
             mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
-            if (mLastResult == SceneResult.SUCCESS) {
-                mLastResult = mScene.insertChild((ViewGroup) parentView, childXml,
-                        (View) beforeSibling, listener);
+            if (mLastResult.isSuccess()) {
+                mLastResult = mScene.insertChild((ViewGroup) parentView, childXml, index, listener);
             }
         } finally {
             mScene.release();
-            mScene.cleanupThread();
+            Bridge.cleanupThread();
         }
 
         return mLastResult;
@@ -122,28 +118,25 @@
 
 
     @Override
-    public SceneResult moveChild(Object parentView, Object childView, Object beforeSibling,
-            IAnimationListener listener) {
+    public SceneResult moveChild(Object parentView, Object childView, int index,
+            Map<String, String> layoutParams, IAnimationListener listener) {
         if (parentView instanceof ViewGroup == false) {
             throw new IllegalArgumentException("parentView is not a ViewGroup");
         }
         if (childView instanceof View == false) {
             throw new IllegalArgumentException("childView is not a View");
         }
-        if (beforeSibling != null && beforeSibling instanceof View == false) {
-            throw new IllegalArgumentException("beforeSibling is not a View");
-        }
 
         try {
-            mScene.prepareThread();
+            Bridge.prepareThread();
             mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
-            if (mLastResult == SceneResult.SUCCESS) {
-                mLastResult = mScene.moveChild((ViewGroup) parentView, (View) childView,
-                        (View) beforeSibling, listener);
+            if (mLastResult.isSuccess()) {
+                mLastResult = mScene.moveChild((ViewGroup) parentView, (View) childView, index,
+                        listener);
             }
         } finally {
             mScene.release();
-            mScene.cleanupThread();
+            Bridge.cleanupThread();
         }
 
         return mLastResult;
@@ -156,14 +149,14 @@
         }
 
         try {
-            mScene.prepareThread();
+            Bridge.prepareThread();
             mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
-            if (mLastResult == SceneResult.SUCCESS) {
+            if (mLastResult.isSuccess()) {
                 mLastResult = mScene.removeChild((View) childView, listener);
             }
         } finally {
             mScene.release();
-            mScene.cleanupThread();
+            Bridge.cleanupThread();
         }
 
         return mLastResult;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
index d5766ab..2b9d52f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -16,8 +16,11 @@
 
 package com.android.layoutlib.bridge.impl;
 
+import com.android.layoutlib.api.LayoutScene;
 import com.android.layoutlib.api.SceneResult;
 import com.android.layoutlib.api.LayoutScene.IAnimationListener;
+import com.android.layoutlib.api.SceneResult.SceneStatus;
+import com.android.layoutlib.bridge.Bridge;
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
@@ -57,7 +60,7 @@
 
     @Override
     public void run() {
-        mScene.prepareThread();
+        Bridge.prepareThread();
         try {
             Handler_Delegate.setCallback(new IHandlerCallback() {
                 public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
@@ -75,6 +78,7 @@
             mAnimator.start();
 
             // loop the animation
+            LayoutScene scene = mScene.getScene();
             do {
                 // get the next message.
                 MessageBundle bundle = mQueue.poll();
@@ -88,14 +92,14 @@
                     try {
                         sleep(bundle.mUptimeMillis - currentTime);
                     } catch (InterruptedException e) {
-                        // TODO Auto-generated catch block
+                        // FIXME log/do something/sleep again?
                         e.printStackTrace();
                     }
                 }
 
                 // ready to do the work, acquire the scene.
                 SceneResult result = mScene.acquire(250);
-                if (result != SceneResult.SUCCESS) {
+                if (result.isSuccess() == false) {
                     mListener.done(result);
                     return;
                 }
@@ -104,18 +108,18 @@
                 // the next message, so mQueue will have another one.
                 try {
                     bundle.mTarget.handleMessage(bundle.mMessage);
-                    if (mScene.render() == SceneResult.SUCCESS) {
-                        mListener.onNewFrame(mScene.getScene());
+                    if (mScene.render().isSuccess()) {
+                        mListener.onNewFrame(scene);
                     }
                 } finally {
                     mScene.release();
                 }
             } while (mQueue.size() > 0);
 
-            mListener.done(SceneResult.SUCCESS);
+            mListener.done(SceneStatus.SUCCESS.getResult());
         } finally {
             Handler_Delegate.setCallback(null);
-            mScene.cleanupThread();
+            Bridge.cleanupThread();
         }
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index 05d207c..74e7fb2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -51,7 +51,6 @@
 import android.graphics.Canvas_Delegate;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.os.Looper;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.View;
@@ -104,6 +103,9 @@
     private int mScreenOffset;
     private IResourceValue mWindowBackground;
     private FrameLayout mViewRoot;
+    private Canvas mCanvas;
+    private int mMeasuredScreenWidth = -1;
+    private int mMeasuredScreenHeight = -1;
 
     // information being returned through the API
     private BufferedImage mImage;
@@ -198,32 +200,7 @@
         mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(),
                 mContext, false /* platformResourceFlag */);
 
-        return SceneResult.SUCCESS;
-    }
-
-    /**
-     * Prepares the current thread for rendering.
-     *
-     * Note that while this can be called several time, the first call to {@link #cleanupThread()}
-     * will do the clean-up, and make the thread unable to do further scene actions.
-     */
-    public void prepareThread() {
-        // we need to make sure the Looper has been initialized for this thread.
-        // this is required for View that creates Handler objects.
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-    }
-
-    /**
-     * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
-     * <p>
-     * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
-     * call to this will prevent the thread from doing further scene actions
-     */
-    public void cleanupThread() {
-        // clean up the looper
-        Looper.sThreadLocal.remove();
+        return SceneStatus.SUCCESS.getResult();
     }
 
     /**
@@ -365,7 +342,7 @@
                 mViewRoot.setBackgroundDrawable(d);
             }
 
-            return SceneResult.SUCCESS;
+            return SceneStatus.SUCCESS.getResult();
         } catch (PostInflateException e) {
             return new SceneResult(SceneStatus.ERROR_INFLATION, e.getMessage(), e);
         } catch (Throwable e) {
@@ -389,99 +366,110 @@
      *
      * @throws IllegalStateException if the current context is different than the one owned by
      *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see SceneParams#getRenderingMode()
      */
     public SceneResult render() {
         checkLock();
 
         try {
-            long current = System.currentTimeMillis();
             if (mViewRoot == null) {
                 return new SceneResult(SceneStatus.ERROR_NOT_INFLATED);
             }
             // measure the views
             int w_spec, h_spec;
 
-            int renderScreenWidth = mParams.getScreenWidth();
-            int renderScreenHeight = mParams.getScreenHeight();
-
             RenderingMode renderingMode = mParams.getRenderingMode();
 
-            if (renderingMode != RenderingMode.NORMAL) {
-                // measure the full size needed by the layout.
-                w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth,
-                        renderingMode.isHorizExpand() ?
-                                MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
-                                : MeasureSpec.EXACTLY);
-                h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
-                        renderingMode.isVertExpand() ?
-                                MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
-                                : MeasureSpec.EXACTLY);
-                mViewRoot.measure(w_spec, h_spec);
+            // only do the screen measure when needed.
+            boolean newRenderSize = false;
+            if (mMeasuredScreenWidth == -1) {
+                newRenderSize = true;
+                mMeasuredScreenWidth = mParams.getScreenWidth();
+                mMeasuredScreenHeight = mParams.getScreenHeight();
 
-                if (renderingMode.isHorizExpand()) {
-                    int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
-                    if (neededWidth > renderScreenWidth) {
-                        renderScreenWidth = neededWidth;
+                if (renderingMode != RenderingMode.NORMAL) {
+                    // measure the full size needed by the layout.
+                    w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth,
+                            renderingMode.isHorizExpand() ?
+                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                                    : MeasureSpec.EXACTLY);
+                    h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset,
+                            renderingMode.isVertExpand() ?
+                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                                    : MeasureSpec.EXACTLY);
+                    mViewRoot.measure(w_spec, h_spec);
+
+                    if (renderingMode.isHorizExpand()) {
+                        int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
+                        if (neededWidth > mMeasuredScreenWidth) {
+                            mMeasuredScreenWidth = neededWidth;
+                        }
                     }
-                }
 
-                if (renderingMode.isVertExpand()) {
-                    int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
-                    if (neededHeight > renderScreenHeight - mScreenOffset) {
-                        renderScreenHeight = neededHeight + mScreenOffset;
+                    if (renderingMode.isVertExpand()) {
+                        int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
+                        if (neededHeight > mMeasuredScreenHeight - mScreenOffset) {
+                            mMeasuredScreenHeight = neededHeight + mScreenOffset;
+                        }
                     }
                 }
             }
 
             // remeasure with the size we need
             // This must always be done before the call to layout
-            w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, MeasureSpec.EXACTLY);
-            h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
+            w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth, MeasureSpec.EXACTLY);
+            h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset,
                     MeasureSpec.EXACTLY);
             mViewRoot.measure(w_spec, h_spec);
 
             // now do the layout.
-            mViewRoot.layout(0, mScreenOffset, renderScreenWidth, renderScreenHeight);
+            mViewRoot.layout(0, mScreenOffset, mMeasuredScreenWidth, mMeasuredScreenHeight);
 
             // draw the views
             // create the BufferedImage into which the layout will be rendered.
-            if (mParams.getImageFactory() != null) {
-                mImage = mParams.getImageFactory().getImage(renderScreenWidth,
-                        renderScreenHeight - mScreenOffset);
-            } else {
-                mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset,
-                        BufferedImage.TYPE_INT_ARGB);
+            if (newRenderSize || mCanvas == null) {
+                if (mParams.getImageFactory() != null) {
+                    mImage = mParams.getImageFactory().getImage(mMeasuredScreenWidth,
+                            mMeasuredScreenHeight - mScreenOffset);
+                } else {
+                    mImage = new BufferedImage(mMeasuredScreenWidth,
+                            mMeasuredScreenHeight - mScreenOffset, BufferedImage.TYPE_INT_ARGB);
+                }
+
+                if (mParams.isCustomBackgroundEnabled()) {
+                    Graphics2D gc = mImage.createGraphics();
+                    gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
+                    gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight - mScreenOffset);
+                    gc.dispose();
+                }
+
+                // create an Android bitmap around the BufferedImage
+                Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
+                        true /*isMutable*/,
+                        Density.getEnum(mParams.getDensity()));
+
+                // create a Canvas around the Android bitmap
+                mCanvas = new Canvas(bitmap);
+                mCanvas.setDensity(mParams.getDensity());
             }
 
-            if (mParams.isCustomBackgroundEnabled()) {
-                Graphics2D gc = mImage.createGraphics();
-                gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
-                gc.fillRect(0, 0, renderScreenWidth, renderScreenHeight - mScreenOffset);
-                gc.dispose();
-            }
-
-            // create an Android bitmap around the BufferedImage
-            Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
-                    true /*isMutable*/,
-                    Density.getEnum(mParams.getDensity()));
-
-            // create a Canvas around the Android bitmap
-            Canvas canvas = new Canvas(bitmap);
-            canvas.setDensity(mParams.getDensity());
-
             // to set the logger, get the native delegate
-            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
+            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(mCanvas);
             canvasDelegate.setLogger(mParams.getLogger());
 
-            mViewRoot.draw(canvas);
-            canvasDelegate.dispose();
+            long preDrawTime = System.currentTimeMillis();
+
+            mViewRoot.draw(mCanvas);
+
+            long drawTime = System.currentTimeMillis();
 
             mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext);
 
-            System.out.println("rendering (ms): " + (System.currentTimeMillis() - current));
+            System.out.println(String.format("rendering (ms): %03d", drawTime - preDrawTime));
 
             // success!
-            return SceneResult.SUCCESS;
+            return SceneStatus.SUCCESS.getResult();
         } catch (Throwable e) {
             // get the real cause of the exception.
             Throwable t = e;
@@ -532,7 +520,7 @@
 
                     new AnimationThread(this, anim, listener).start();
 
-                    return SceneResult.SUCCESS;
+                    return SceneStatus.SUCCESS.getResult();
                 }
             } catch (Exception e) {
                 // get the real cause of the exception.
@@ -549,14 +537,9 @@
     }
 
     public SceneResult insertChild(ViewGroup parentView, IXmlPullParser childXml,
-            View beforeSibling, IAnimationListener listener) {
+            int index, IAnimationListener listener) {
         checkLock();
 
-        int index = parentView.indexOfChild(beforeSibling);
-        if (beforeSibling != null && index == -1) {
-            throw new IllegalArgumentException("beforeSibling not in parentView");
-        }
-
         // create a block parser for the XML
         BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(childXml, mContext,
                 false /* platformResourceFlag */);
@@ -567,28 +550,42 @@
         View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
 
         // add it to the parentView in the correct location
-        parentView.addView(child, index);
+        try {
+            parentView.addView(child, index);
+        } catch (UnsupportedOperationException e) {
+            // looks like this is a view class that doesn't support children manipulation!
+            return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+        }
 
-        return render();
+        invalidateRenderingSize();
+
+        SceneResult result = render();
+        if (result.isSuccess()) {
+            result.setData(child);
+        }
+
+        return result;
     }
 
-    public SceneResult moveChild(ViewGroup parentView, View childView, View beforeSibling,
+    public SceneResult moveChild(ViewGroup parentView, View childView, int index,
             IAnimationListener listener) {
         checkLock();
 
-        int index = parentView.indexOfChild(beforeSibling);
-        if (beforeSibling != null && index == -1) {
-            throw new IllegalArgumentException("beforeSibling not in parentView");
+        try {
+            ViewParent parent = childView.getParent();
+            if (parent instanceof ViewGroup) {
+                ViewGroup parentGroup = (ViewGroup) parent;
+                parentGroup.removeView(childView);
+            }
+
+            // add it to the parentView in the correct location
+            parentView.addView(childView, index);
+        } catch (UnsupportedOperationException e) {
+            // looks like this is a view class that doesn't support children manipulation!
+            return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
         }
 
-        ViewParent parent = childView.getParent();
-        if (parent instanceof ViewGroup) {
-            ViewGroup parentGroup = (ViewGroup) parent;
-            parentGroup.removeView(childView);
-        }
-
-        // add it to the parentView in the correct location
-        parentView.addView(childView, index);
+        invalidateRenderingSize();
 
         return render();
     }
@@ -596,12 +593,19 @@
     public SceneResult removeChild(View childView, IAnimationListener listener) {
         checkLock();
 
-        ViewParent parent = childView.getParent();
-        if (parent instanceof ViewGroup) {
-            ViewGroup parentGroup = (ViewGroup) parent;
-            parentGroup.removeView(childView);
+        try {
+            ViewParent parent = childView.getParent();
+            if (parent instanceof ViewGroup) {
+                ViewGroup parentGroup = (ViewGroup) parent;
+                parentGroup.removeView(childView);
+            }
+        } catch (UnsupportedOperationException e) {
+            // looks like this is a view class that doesn't support children manipulation!
+            return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
         }
 
+        invalidateRenderingSize();
+
         return render();
     }
 
@@ -971,6 +975,10 @@
         return result;
     }
 
+    private void invalidateRenderingSize() {
+        mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
+    }
+
     public BufferedImage getImage() {
         return mImage;
     }
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 6ec223b..097b109 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -16,6 +16,7 @@
 
 #include <ui/KeyCharacterMap.h>
 #include <ui/KeyLayoutMap.h>
+#include <ui/VirtualKeyMap.h>
 #include <utils/String8.h>
 
 #include <stdio.h>
@@ -30,6 +31,7 @@
     FILETYPE_UNKNOWN,
     FILETYPE_KEYLAYOUT,
     FILETYPE_KEYCHARACTERMAP,
+    FILETYPE_VIRTUALKEYDEFINITION,
 };
 
 
@@ -37,8 +39,10 @@
     fprintf(stderr, "Keymap Validation Tool\n\n");
     fprintf(stderr, "Usage:\n");
     fprintf(stderr,
-        " %s [FILENAME.kl] [FILENAME.kcm] [...]\n"
-        "   Validates the specified key layout and/or key character map files.\n\n", gProgName);
+        " %s [*.kl] [*.kcm] [virtualkeys.*] [...]\n"
+        "   Validates the specified key layouts, key character maps \n"
+        "   or virtual key definitions.\n\n",
+        gProgName);
 }
 
 static FileType getFileType(const char* filename) {
@@ -51,6 +55,11 @@
             return FILETYPE_KEYCHARACTERMAP;
         }
     }
+
+    if (strstr(filename, "virtualkeys.")) {
+        return FILETYPE_VIRTUALKEYDEFINITION;
+    }
+
     return FILETYPE_UNKNOWN;
 }
 
@@ -60,7 +69,7 @@
     FileType fileType = getFileType(filename);
     switch (fileType) {
     case FILETYPE_UNKNOWN:
-        fprintf(stderr, "File extension must be .kl or .kcm.\n\n");
+        fprintf(stderr, "Supported file types: *.kl, *.kcm, virtualkeys.*\n\n");
         return false;
 
     case FILETYPE_KEYLAYOUT: {
@@ -82,6 +91,16 @@
         }
         break;
     }
+
+    case FILETYPE_VIRTUALKEYDEFINITION: {
+        VirtualKeyMap* map;
+        status_t status = VirtualKeyMap::load(String8(filename), &map);
+        if (status) {
+            fprintf(stderr, "Error %d parsing virtual key definition file.\n\n", status);
+            return false;
+        }
+        break;
+    }
     }
 
     fputs("No errors.\n\n", stdout);
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index f275e39..51236fe9 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -594,12 +594,10 @@
      */
     public void holdCall(int timeout) throws SipException {
         synchronized (this) {
-        if (mHold) return;
+            if (mHold) return;
             mSipSession.changeCall(createHoldOffer().encode(), timeout);
             mHold = true;
-
-            AudioGroup audioGroup = getAudioGroup();
-            if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+            setAudioGroupMode();
         }
     }
 
@@ -643,8 +641,7 @@
             if (!mHold) return;
             mSipSession.changeCall(createContinueOffer().encode(), timeout);
             mHold = false;
-            AudioGroup audioGroup = getAudioGroup();
-            if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
+            setAudioGroupMode();
         }
     }
 
@@ -767,13 +764,8 @@
     /** Toggles mute. */
     public void toggleMute() {
         synchronized (this) {
-            AudioGroup audioGroup = getAudioGroup();
-            if (audioGroup != null) {
-                audioGroup.setMode(mMuted
-                        ? AudioGroup.MODE_NORMAL
-                        : AudioGroup.MODE_MUTED);
-                mMuted = !mMuted;
-            }
+            mMuted = !mMuted;
+            setAudioGroupMode();
         }
     }
 
@@ -792,14 +784,22 @@
      * Puts the device to speaker mode.
      * <p class="note"><strong>Note:</strong> Requires the
      *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
+     *
+     * @param speakerMode set true to enable speaker mode; false to disable
      */
     public void setSpeakerMode(boolean speakerMode) {
         synchronized (this) {
             ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
                     .setSpeakerphoneOn(speakerMode);
+            setAudioGroupMode();
         }
     }
 
+    private boolean isSpeakerOn() {
+        return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
+                .isSpeakerphoneOn();
+    }
+
     /**
      * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
      * event 0--9 maps to decimal
@@ -876,7 +876,11 @@
     /**
      * Sets the {@link AudioGroup} object which the {@link AudioStream} object
      * joins. If {@code audioGroup} is null, then the {@code AudioGroup} object
-     * will be dynamically created when needed.
+     * will be dynamically created when needed. Note that the mode of the
+     * {@code AudioGroup} is not changed according to the audio settings (i.e.,
+     * hold, mute, speaker phone) of this object. This is mainly used to merge
+     * multiple {@code SipAudioCall} objects to form a conference call. The
+     * settings of the first object (that merges others) override others'.
      *
      * @see #getAudioStream
      * @hide
@@ -992,16 +996,25 @@
         // AudioGroup logic:
         AudioGroup audioGroup = getAudioGroup();
         if (mHold) {
-            if (audioGroup != null) {
-                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-            }
             // don't create an AudioGroup here; doing so will fail if
             // there's another AudioGroup out there that's active
         } else {
             if (audioGroup == null) audioGroup = new AudioGroup();
             stream.join(audioGroup);
-            if (mMuted) {
+        }
+        setAudioGroupMode();
+    }
+
+    // set audio group mode based on current audio configuration
+    private void setAudioGroupMode() {
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) {
+            if (mHold) {
+                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+            } else if (mMuted) {
                 audioGroup.setMode(AudioGroup.MODE_MUTED);
+            } else if (isSpeakerOn()) {
+                audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION);
             } else {
                 audioGroup.setMode(AudioGroup.MODE_NORMAL);
             }