Merge "Fix a crash where the EOS was signalled but some output buffer were still pending."
diff --git a/api/current.txt b/api/current.txt
index dfc17ba..eb79f30 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10681,6 +10681,7 @@
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
     method public void setScreenOnWhilePlaying(boolean);
     method public void setTexture(android.graphics.SurfaceTexture);
+    method public void setSurface(android.view.Surface);
     method public void setVolume(float, float);
     method public void setWakeMode(android.content.Context, int);
     method public void start() throws java.lang.IllegalStateException;
@@ -12396,6 +12397,7 @@
   public final class NdefRecord implements android.os.Parcelable {
     ctor public NdefRecord(short, byte[], byte[], byte[]);
     ctor public NdefRecord(byte[]) throws android.nfc.FormatException;
+    method public static android.nfc.NdefRecord createApplicationRecord(java.lang.String);
     method public static android.nfc.NdefRecord createUri(android.net.Uri);
     method public static android.nfc.NdefRecord createUri(java.lang.String);
     method public int describeContents();
@@ -12430,9 +12432,9 @@
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
     method public static deprecated android.nfc.NfcAdapter getDefaultAdapter();
     method public boolean isEnabled();
-    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity...);
-    method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity...);
-    method public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity...);
+    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
+    method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
+    method public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
     field public static final java.lang.String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field public static final java.lang.String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
     field public static final java.lang.String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
@@ -12486,6 +12488,7 @@
     method public byte[] getHiLayerResponse();
     method public byte[] getHistoricalBytes();
     method public int getMaxTransceiveLength();
+    method public int getTimeout();
     method public void setTimeout(int);
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
@@ -12501,11 +12504,13 @@
     method public int getMaxTransceiveLength();
     method public int getSectorCount();
     method public int getSize();
+    method public int getTimeout();
     method public int getType();
     method public void increment(int, int) throws java.io.IOException;
     method public byte[] readBlock(int) throws java.io.IOException;
     method public void restore(int) throws java.io.IOException;
     method public int sectorToBlock(int);
+    method public void setTimeout(int);
     method public byte[] transceive(byte[]) throws java.io.IOException;
     method public void transfer(int) throws java.io.IOException;
     method public void writeBlock(int, byte[]) throws java.io.IOException;
@@ -12526,8 +12531,10 @@
   public final class MifareUltralight extends android.nfc.tech.BasicTagTechnology {
     method public static android.nfc.tech.MifareUltralight get(android.nfc.Tag);
     method public int getMaxTransceiveLength();
+    method public int getTimeout();
     method public int getType();
     method public byte[] readPages(int) throws java.io.IOException;
+    method public void setTimeout(int);
     method public byte[] transceive(byte[]) throws java.io.IOException;
     method public void writePage(int, byte[]) throws java.io.IOException;
     field public static final int PAGE_SIZE = 4; // 0x4
@@ -12564,6 +12571,8 @@
     method public byte[] getAtqa();
     method public int getMaxTransceiveLength();
     method public short getSak();
+    method public int getTimeout();
+    method public void setTimeout(int);
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
 
@@ -12580,6 +12589,8 @@
     method public byte[] getManufacturer();
     method public int getMaxTransceiveLength();
     method public byte[] getSystemCode();
+    method public int getTimeout();
+    method public void setTimeout(int);
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
 
@@ -16057,7 +16068,7 @@
 
   public final class ContactsContract {
     ctor public ContactsContract();
-    field public static final java.lang.String ALLOW_PROFILE = "allow_profile";
+    method public static boolean isProfileId(long);
     field public static final java.lang.String AUTHORITY = "com.android.contacts";
     field public static final android.net.Uri AUTHORITY_URI;
     field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
@@ -16584,6 +16595,16 @@
     field public static final android.net.Uri CONTENT_RAW_CONTACTS_URI;
     field public static final android.net.Uri CONTENT_URI;
     field public static final android.net.Uri CONTENT_VCARD_URI;
+    field public static final long MIN_ID = 9223372034707292160L; // 0x7fffffff80000000L
+  }
+
+  public static final class ContactsContract.ProfileSyncState implements android.provider.SyncStateContract.Columns {
+    method public static byte[] get(android.content.ContentProviderClient, android.accounts.Account) throws android.os.RemoteException;
+    method public static android.util.Pair<android.net.Uri, byte[]> getWithUri(android.content.ContentProviderClient, android.accounts.Account) throws android.os.RemoteException;
+    method public static android.content.ContentProviderOperation newSetOperation(android.accounts.Account, byte[]);
+    method public static void set(android.content.ContentProviderClient, android.accounts.Account, byte[]) throws android.os.RemoteException;
+    field public static final java.lang.String CONTENT_DIRECTORY = "syncstate";
+    field public static final android.net.Uri CONTENT_URI;
   }
 
   public static final class ContactsContract.QuickContact {
@@ -16682,6 +16703,7 @@
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/status-update";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/status-update";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final android.net.Uri PROFILE_CONTENT_URI;
   }
 
   public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
@@ -20677,6 +20699,14 @@
     field protected final int mVerticalAlignment;
   }
 
+  public class EasyEditSpan implements android.text.ParcelableSpan {
+    ctor public EasyEditSpan();
+    ctor public EasyEditSpan(android.os.Parcel);
+    method public int describeContents();
+    method public int getSpanTypeId();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public class ForegroundColorSpan extends android.text.style.CharacterStyle implements android.text.ParcelableSpan android.text.style.UpdateAppearance {
     ctor public ForegroundColorSpan(int);
     ctor public ForegroundColorSpan(android.os.Parcel);
@@ -22514,6 +22544,7 @@
   }
 
   public class Surface implements android.os.Parcelable {
+    ctor public Surface(android.graphics.SurfaceTexture);
     method public int describeContents();
     method public boolean isValid();
     method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException;
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 6ba3451..26571ff 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -152,8 +152,6 @@
      * RTD_ANDROID_APP records.
      * @hide
      */
-    // TODO unhide for ICS
-    // TODO recheck docs
     public static final byte[] RTD_ANDROID_APP = "android.com:pkg".getBytes();
 
     private static final byte FLAG_MB = (byte) 0x80;
@@ -352,21 +350,29 @@
     /**
      * Creates an Android application NDEF record.
      * <p>
+     * This record indicates to other Android devices the package
+     * that should be used to handle the rest of the NDEF message.
+     * You can embed this record anywhere into your NDEF message
+     * to ensure that the intended package receives the message.
+     * <p>
      * When an Android device dispatches an {@link NdefMessage}
      * containing one or more Android application records,
      * the applications contained in those records will be the
      * preferred target for the NDEF_DISCOVERED intent, in
      * the order in which they appear in the {@link NdefMessage}.
+     * This dispatch behavior was first added to Android in
+     * Ice Cream Sandwich.
      * <p>
      * If none of the applications are installed on the device,
      * a Market link will be opened to the first application.
      * <p>
      * Note that Android application records do not overrule
-     * applications that have called {@link NfcAdapter#enableForegroundDispatch}.
-     * @hide
+     * applications that have called
+     * {@link NfcAdapter#enableForegroundDispatch}.
+     *
+     * @param packageName Android package name
+     * @return Android application NDEF record
      */
-    // TODO unhide for ICS
-    // TODO recheck javadoc - should mention this works from ICS only
     public static NdefRecord createApplicationRecord(String packageName) {
         return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, new byte[] {},
                 packageName.getBytes(Charsets.US_ASCII));
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index d58b249..e392bca 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -507,16 +507,24 @@
      * <p>Pass a null NDEF message to disable foreground NDEF push in the
      * specified activities.
      *
+     * <p>One or more activities must be specified.
+     *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param message NDEF message to push over NFC, or null to disable
-     * @param activities one or more {@link Activity} to enable for NDEF push
+     * @param activity an activity to enable for NDEF push (at least one is required)
+     * @param activities zero or more additional activities to enable for NDEF Push
      */
-    public void setNdefPushMessage(NdefMessage message, Activity ... activities) {
-        if (activities.length == 0) {
-            throw new NullPointerException("Must specificy one or more activities");
+    public void setNdefPushMessage(NdefMessage message, Activity activity,
+            Activity ... activities) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
         }
+        mNfcActivityManager.setNdefPushMessage(activity, message);
         for (Activity a : activities) {
+            if (a == null) {
+                throw new NullPointerException("activities cannot contain null");
+            }
             mNfcActivityManager.setNdefPushMessage(a, message);
         }
     }
@@ -536,17 +544,24 @@
      * <p>Pass a null callback to disable the callback in the
      * specified activities.
      *
+     * <p>One or more activities must be specified.
+     *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param callback callback, or null to disable
-     * @param activities one or more {@link Activity} to enable for NDEF push
+     * @param activity an activity to enable for NDEF push (at least one is required)
+     * @param activities zero or more additional activities to enable for NDEF Push
      */
-    public void setNdefPushMessageCallback(CreateNdefMessageCallback callback,
+    public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             Activity ... activities) {
-        if (activities.length == 0) {
-            throw new NullPointerException("Must specificy one or more activities");
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
         }
+        mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
         for (Activity a : activities) {
+            if (a == null) {
+                throw new NullPointerException("activities cannot contain null");
+            }
             mNfcActivityManager.setNdefPushMessageCallback(a, callback);
         }
     }
@@ -558,17 +573,24 @@
      * can only occur when one of the specified activities is in resumed
      * (foreground) state.
      *
+     * <p>One or more activities must be specified.
+     *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param callback callback, or null to disable
-     * @param activities one or more {@link Activity} to enable the callback
+     * @param activity an activity to enable the callback (at least one is required)
+     * @param activities zero or more additional activities to enable to callback
      */
     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
-            Activity ... activities) {
-        if (activities.length == 0) {
-            throw new NullPointerException("Must specificy one or more activities");
+            Activity activity, Activity ... activities) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
         }
+        mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
         for (Activity a : activities) {
+            if (a == null) {
+                throw new NullPointerException("activities cannot contain null");
+            }
             mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
         }
     }
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 6054fe8..1859877 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -101,14 +101,12 @@
     }
 
     /**
-     * Gets the currently set timeout of {@link #transceive} in milliseconds.
+     * Get the current timeout for {@link #transceive} in milliseconds.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public int getTimeout() {
         try {
             return mTag.getTagService().getTimeout(TagTechnology.ISO_DEP);
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index ce923ae..9d1e6a1 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -584,9 +584,11 @@
     }
 
     /**
-     * Set the timeout of {@link #transceive} in milliseconds.
-     * <p>The timeout only applies to MifareUltralight {@link #transceive},
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
      * and is reset to a default value when {@link #close} is called.
+     *
      * <p>Setting a longer timeout may be useful when performing
      * transactions that require a long processing time on the tag
      * such as key generation.
@@ -594,9 +596,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public void setTimeout(int timeout) {
         try {
             int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
@@ -609,14 +609,12 @@
     }
 
     /**
-     * Gets the currently set timeout of {@link #transceive} in milliseconds.
+     * Get the current {@link #transceive} timeout in milliseconds.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public int getTimeout() {
         try {
             return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC);
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
index 890b735..dec2c65 100644
--- a/core/java/android/nfc/tech/MifareUltralight.java
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -224,9 +224,11 @@
     }
 
     /**
-     * Set the timeout of {@link #transceive} in milliseconds.
-     * <p>The timeout only applies to MifareUltralight {@link #transceive},
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
      * and is reset to a default value when {@link #close} is called.
+     *
      * <p>Setting a longer timeout may be useful when performing
      * transactions that require a long processing time on the tag
      * such as key generation.
@@ -234,9 +236,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public void setTimeout(int timeout) {
         try {
             int err = mTag.getTagService().setTimeout(
@@ -250,14 +250,12 @@
     }
 
     /**
-     * Gets the currently set timeout of {@link #transceive} in milliseconds.
+     * Get the current {@link #transceive} timeout in milliseconds.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public int getTimeout() {
         try {
             return mTag.getTagService().getTimeout(TagTechnology.MIFARE_ULTRALIGHT);
diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java
index bb8aec9..88730f9 100644
--- a/core/java/android/nfc/tech/NfcA.java
+++ b/core/java/android/nfc/tech/NfcA.java
@@ -129,9 +129,11 @@
     }
 
     /**
-     * Set the timeout of {@link #transceive} in milliseconds.
-     * <p>The timeout only applies to NfcA {@link #transceive}, and is
-     * reset to a default value when {@link #close} is called.
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
+     * and is reset to a default value when {@link #close} is called.
+     *
      * <p>Setting a longer timeout may be useful when performing
      * transactions that require a long processing time on the tag
      * such as key generation.
@@ -139,9 +141,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public void setTimeout(int timeout) {
         try {
             int err = mTag.getTagService().setTimeout(TagTechnology.NFC_A, timeout);
@@ -154,14 +154,12 @@
     }
 
     /**
-     * Gets the currently set timeout of {@link #transceive} in milliseconds.
+     * Get the current {@link #transceive} timeout in milliseconds.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public int getTimeout() {
         try {
             return mTag.getTagService().getTimeout(TagTechnology.NFC_A);
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index 0938fb4..b3e3ab6 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -128,9 +128,11 @@
     }
 
     /**
-     * Set the timeout of {@link #transceive} in milliseconds.
-     * <p>The timeout only applies to NfcF {@link #transceive}, and is
-     * reset to a default value when {@link #close} is called.
+     * Set the {@link #transceive} timeout in milliseconds.
+     *
+     * <p>The timeout only applies to {@link #transceive} on this object,
+     * and is reset to a default value when {@link #close} is called.
+     *
      * <p>Setting a longer timeout may be useful when performing
      * transactions that require a long processing time on the tag
      * such as key generation.
@@ -138,9 +140,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public void setTimeout(int timeout) {
         try {
             int err = mTag.getTagService().setTimeout(TagTechnology.NFC_F, timeout);
@@ -153,14 +153,12 @@
     }
 
     /**
-     * Gets the currently set timeout of {@link #transceive} in milliseconds.
+     * Get the current {@link #transceive} timeout in milliseconds.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
-     * @hide
      */
-    // TODO Unhide for ICS
     public int getTimeout() {
         try {
             return mTag.getTagService().getTimeout(TagTechnology.NFC_F);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 5321c6a..1f2b342 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -125,19 +125,6 @@
     public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
 
     /**
-     * An optional URI parameter for selection queries that instructs the
-     * provider to allow the user's personal profile contact entry (if any)
-     * to appear in a list of contact results.  It is only useful when issuing
-     * a query that may retrieve more than one contact.  If present, the user's
-     * profile will always be the first entry returned.  The default value is
-     * false.
-     *
-     * Specifying this parameter will result in a security error if the calling
-     * application does not have android.permission.READ_PROFILE permission.
-     */
-    public static final String ALLOW_PROFILE = "allow_profile";
-
-    /**
      * Query parameter that should be used by the client to access a specific
      * {@link Directory}. The parameter value should be the _ID of the corresponding
      * directory, e.g.
@@ -557,7 +544,7 @@
     }
 
     /**
-     * A table provided for sync adapters to use for storing private sync state data.
+     * A table provided for sync adapters to use for storing private sync state data for contacts.
      *
      * @see SyncStateContract
      */
@@ -608,6 +595,60 @@
         }
     }
 
+
+    /**
+     * A table provided for sync adapters to use for storing private sync state data for the
+     * user's personal profile.
+     *
+     * @see SyncStateContract
+     */
+    public static final class ProfileSyncState implements SyncStateContract.Columns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private ProfileSyncState() {}
+
+        public static final String CONTENT_DIRECTORY =
+                SyncStateContract.Constants.CONTENT_DIRECTORY;
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI =
+                Uri.withAppendedPath(Profile.CONTENT_URI, CONTENT_DIRECTORY);
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#get
+         */
+        public static byte[] get(ContentProviderClient provider, Account account)
+                throws RemoteException {
+            return SyncStateContract.Helpers.get(provider, CONTENT_URI, account);
+        }
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#get
+         */
+        public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Account account)
+                throws RemoteException {
+            return SyncStateContract.Helpers.getWithUri(provider, CONTENT_URI, account);
+        }
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#set
+         */
+        public static void set(ContentProviderClient provider, Account account, byte[] data)
+                throws RemoteException {
+            SyncStateContract.Helpers.set(provider, CONTENT_URI, account, data);
+        }
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#newSetOperation
+         */
+        public static ContentProviderOperation newSetOperation(Account account, byte[] data) {
+            return SyncStateContract.Helpers.newSetOperation(CONTENT_URI, account, data);
+        }
+    }
+
     /**
      * Generic columns for use by sync adapters. The specific functions of
      * these columns are private to the sync adapter. Other clients of the API
@@ -1875,8 +1916,8 @@
      * Constants for the user's profile data, which is represented as a single contact on
      * the device that represents the user.  The profile contact is not aggregated
      * together automatically in the same way that normal contacts are; instead, each
-     * account on the device may contribute a single raw contact representing the user's
-     * personal profile data from that source.
+     * account (including data set, if applicable) on the device may contribute a single
+     * raw contact representing the user's personal profile data from that source.
      * </p>
      * <p>
      * Access to the profile entry through these URIs (or incidental access to parts of
@@ -1950,6 +1991,31 @@
          */
         public static final Uri CONTENT_RAW_CONTACTS_URI = Uri.withAppendedPath(CONTENT_URI,
                 "raw_contacts");
+
+        /**
+         * The minimum ID for any entity that belongs to the profile.  This essentially
+         * defines an ID-space in which profile data is stored, and is used by the provider
+         * to determine whether a request via a non-profile-specific URI should be directed
+         * to the profile data rather than general contacts data, along with all the special
+         * permission checks that entails.
+         *
+         * Callers may use {@link #isProfileId} to check whether a specific ID falls into
+         * the set of data intended for the profile.
+         */
+        public static final long MIN_ID = Long.MAX_VALUE - (long) Integer.MAX_VALUE;
+    }
+
+    /**
+     * This method can be used to identify whether the given ID is associated with profile
+     * data.  It does not necessarily indicate that the ID is tied to valid data, merely
+     * that accessing data using this ID will result in profile access checks and will only
+     * return data from the profile.
+     *
+     * @param id The ID to check.
+     * @return Whether the ID is associated with profile data.
+     */
+    public static boolean isProfileId(long id) {
+        return id >= Profile.MIN_ID;
     }
 
     protected interface RawContactsColumns {
@@ -4542,6 +4608,12 @@
      * either.
      * </p>
      * <p>
+     * Inserting or updating a status update for the user's profile requires either using
+     * the {@link #DATA_ID} to identify the data row to attach the update to, or
+     * {@link StatusUpdates#PROFILE_CONTENT_URI} to ensure that the change is scoped to the
+     * profile.
+     * </p>
+     * <p>
      * You cannot use {@link ContentResolver#update} to change a status, but
      * {@link ContentResolver#insert} will replace the latests status if it already
      * exists.
@@ -4687,6 +4759,12 @@
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "status_updates");
 
         /**
+         * The content:// style URI for this table, specific to the user's profile.
+         */
+        public static final Uri PROFILE_CONTENT_URI =
+                Uri.withAppendedPath(Profile.CONTENT_URI, "status_updates");
+
+        /**
          * Gets the resource ID for the proper presence icon.
          *
          * @param status the status to get the icon for
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4c563ce..39f9367 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -139,6 +139,7 @@
         boolean mSurfaceCreated;
         boolean mIsCreating;
         boolean mDrawingAllowed;
+        boolean mOffsetsChanged;
         int mWidth;
         int mHeight;
         int mFormat;
@@ -604,12 +605,15 @@
                         if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
                         return;
                     }
-                    
+
+                    boolean didSurface = false;
+
                     try {
                         mSurfaceHolder.ungetCallbacks();
 
                         if (surfaceCreating) {
                             mIsCreating = true;
+                            didSurface = true;
                             if (DEBUG) Log.v(TAG, "onSurfaceCreated("
                                     + mSurfaceHolder + "): " + this);
                             onSurfaceCreated(mSurfaceHolder);
@@ -637,6 +641,7 @@
                                     + mSurfaceHolder + ", " + mFormat
                                     + ", " + mCurWidth + ", " + mCurHeight
                                     + "): " + this);
+                            didSurface = true;
                             onSurfaceChanged(mSurfaceHolder, mFormat,
                                     mCurWidth, mCurHeight);
                             SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -661,6 +666,26 @@
                             }
                         }
 
+                        if (didSurface && !mReportedVisible) {
+                            // This wallpaper is currently invisible, but its
+                            // surface has changed.  At this point let's tell it
+                            // again that it is invisible in case the report about
+                            // the surface caused it to start running.  We really
+                            // don't want wallpapers running when not visible.
+                            if (mIsCreating) {
+                                // Some wallpapers will ignore this call if they
+                                // had previously been told they were invisble,
+                                // so if we are creating a new surface then toggle
+                                // the state to get them to notice.
+                                if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
+                                        + this);
+                                onVisibilityChanged(true);
+                            }
+                            if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
+                                        + this);
+                            onVisibilityChanged(false);
+                        }
+
                     } finally {
                         mIsCreating = false;
                         mSurfaceCreated = true;
@@ -701,6 +726,7 @@
             onCreate(mSurfaceHolder);
             
             mInitializing = false;
+            mReportedVisible = false;
             updateSurface(false, false, false);
         }
         
@@ -711,7 +737,7 @@
                 mIWallpaperEngine.mReqWidth = desiredWidth;
                 mIWallpaperEngine.mReqHeight = desiredHeight;
                 onDesiredSizeChanged(desiredWidth, desiredHeight);
-                doOffsetsChanged();
+                doOffsetsChanged(true);
             }
         }
         
@@ -733,6 +759,7 @@
                         // If becoming visible, in preview mode the surface
                         // may have been destroyed so now we need to make
                         // sure it is re-created.
+                        doOffsetsChanged(false);
                         updateSurface(false, false, false);
                     }
                     onVisibilityChanged(visible);
@@ -740,11 +767,15 @@
             }
         }
         
-        void doOffsetsChanged() {
+        void doOffsetsChanged(boolean always) {
             if (mDestroyed) {
                 return;
             }
-            
+
+            if (!always && !mOffsetsChanged) {
+                return;
+            }
+
             float xOffset;
             float yOffset;
             float xOffsetStep;
@@ -759,15 +790,19 @@
                 mPendingSync = false;
                 mOffsetMessageEnqueued = false;
             }
-            
+
             if (mSurfaceCreated) {
-                if (DEBUG) Log.v(TAG, "Offsets change in " + this
-                        + ": " + xOffset + "," + yOffset);
-                final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
-                final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
-                final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
-                final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
-                onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
+                if (mReportedVisible) {
+                    if (DEBUG) Log.v(TAG, "Offsets change in " + this
+                            + ": " + xOffset + "," + yOffset);
+                    final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
+                    final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
+                    final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
+                    final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
+                    onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
+                } else {
+                    mOffsetsChanged = true;
+                }
             }
             
             if (sync) {
@@ -953,7 +988,7 @@
                     mEngine.doVisibilityChanged(message.arg1 != 0);
                     break;
                 case MSG_WALLPAPER_OFFSETS: {
-                    mEngine.doOffsetsChanged();
+                    mEngine.doOffsetsChanged(true);
                 } break;
                 case MSG_WALLPAPER_COMMAND: {
                     WallpaperCommand cmd = (WallpaperCommand)message.obj;
@@ -962,7 +997,7 @@
                 case MSG_WINDOW_RESIZED: {
                     final boolean reportDraw = message.arg1 != 0;
                     mEngine.updateSurface(true, false, reportDraw);
-                    mEngine.doOffsetsChanged();
+                    mEngine.doOffsetsChanged(true);
                 } break;
                 case MSG_TOUCH_EVENT: {
                     boolean skip = false;
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index 89b6f32..75dcd6e 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -390,10 +390,10 @@
             audioTrack.play();
         }
         int count = 0;
-        while (count < bufferCopy.mLength) {
+        while (count < bufferCopy.mBytes.length) {
             // Note that we don't take bufferCopy.mOffset into account because
             // it is guaranteed to be 0.
-            int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength);
+            int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mBytes.length);
             if (written <= 0) {
                 break;
             }
@@ -415,6 +415,9 @@
         }
 
         if (params.mBytesWritten < params.mAudioBufferSize) {
+            if (DBG) Log.d(TAG, "Stopping audio track to flush audio, state was : " +
+                    audioTrack.getPlayState());
+            params.mIsShortUtterance = true;
             audioTrack.stop();
         }
 
@@ -452,10 +455,40 @@
             return;
         }
 
+        if (params.mIsShortUtterance) {
+            // In this case we would have called AudioTrack#stop() to flush
+            // buffers to the mixer. This makes the playback head position
+            // unobservable and notification markers do not work reliably. We
+            // have no option but to wait until we think the track would finish
+            // playing and release it after.
+            //
+            // This isn't as bad as it looks because (a) We won't end up waiting
+            // for much longer than we should because even at 4khz mono, a short
+            // utterance weighs in at about 2 seconds, and (b) such short utterances
+            // are expected to be relatively infrequent and in a stream of utterances
+            // this shows up as a slightly longer pause.
+            blockUntilEstimatedCompletion(params);
+        } else {
+            blockUntilCompletion(params);
+        }
+    }
+
+    private static void blockUntilEstimatedCompletion(SynthesisMessageParams params) {
+        final int lengthInFrames = params.mBytesWritten / params.mBytesPerFrame;
+        final long estimatedTimeMs = (lengthInFrames * 1000 / params.mSampleRateInHz);
+
+        if (DBG) Log.d(TAG, "About to sleep for: " + estimatedTimeMs + "ms for a short utterance");
+
+        try {
+            Thread.sleep(estimatedTimeMs);
+        } catch (InterruptedException ie) {
+            // Do nothing.
+        }
+    }
+
+    private static void blockUntilCompletion(SynthesisMessageParams params) {
         final AudioTrack audioTrack = params.mAudioTrack;
-        final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat);
-        final int lengthInBytes = params.mBytesWritten;
-        final int lengthInFrames = lengthInBytes / bytesPerFrame;
+        final int lengthInFrames = params.mBytesWritten / params.mBytesPerFrame;
 
         int currentPosition = 0;
         while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
@@ -511,16 +544,6 @@
         return 0;
     }
 
-    static int getBytesPerFrame(int audioFormat) {
-        if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
-            return 1;
-        } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
-            return 2;
-        }
-
-        return -1;
-    }
-
     private static void setupVolume(AudioTrack audioTrack, float volume, float pan) {
         float vol = clip(volume, 0.0f, 1.0f);
         float panning = clip(pan, -1.0f, 1.0f);
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index 7dbf1ac..0cca06a 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -85,6 +85,7 @@
         // Note that mLogger.mError might be true too at this point.
         mLogger.onStopped();
 
+        SynthesisMessageParams token = null;
         synchronized (mStateLock) {
             if (mStopped) {
                 Log.w(TAG, "stop() called twice");
@@ -97,9 +98,19 @@
                 // In all other cases, mAudioTrackHandler.stop() will
                 // result in onComplete being called.
                 mLogger.onWriteData();
+            } else {
+                token = mToken;
             }
             mStopped = true;
         }
+
+        if (token != null) {
+            // This might result in the synthesis thread being woken up, at which
+            // point it will write an additional buffer to the token - but we
+            // won't worry about that because the audio playback queue will be cleared
+            // soon after (see SynthHandler#stop(String).
+            token.clearBuffers();
+        }
     }
 
     @Override
@@ -155,18 +166,22 @@
                     + length + " bytes)");
         }
 
+        SynthesisMessageParams token = null;
         synchronized (mStateLock) {
             if (mToken == null || mStopped) {
                 return TextToSpeech.ERROR;
             }
-
-            // Sigh, another copy.
-            final byte[] bufferCopy = new byte[length];
-            System.arraycopy(buffer, offset, bufferCopy, 0, length);
-            mToken.addBuffer(bufferCopy);
-            mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken);
+            token = mToken;
         }
 
+        // Sigh, another copy.
+        final byte[] bufferCopy = new byte[length];
+        System.arraycopy(buffer, offset, bufferCopy, 0, length);
+        // Might block on mToken.this, if there are too many buffers waiting to
+        // be consumed.
+        token.addBuffer(bufferCopy);
+        mAudioTrackHandler.enqueueSynthesisDataAvailable(token);
+
         mLogger.onEngineDataReceived();
 
         return TextToSpeech.SUCCESS;
@@ -176,6 +191,7 @@
     public int done() {
         if (DBG) Log.d(TAG, "done()");
 
+        SynthesisMessageParams token = null;
         synchronized (mStateLock) {
             if (mDone) {
                 Log.w(TAG, "Duplicate call to done()");
@@ -188,9 +204,12 @@
                 return TextToSpeech.ERROR;
             }
 
-            mAudioTrackHandler.enqueueSynthesisDone(mToken);
-            mLogger.onEngineComplete();
+            token = mToken;
         }
+
+        mAudioTrackHandler.enqueueSynthesisDone(token);
+        mLogger.onEngineComplete();
+
         return TextToSpeech.SUCCESS;
     }
 
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
index 7da5daa..779721e 100644
--- a/core/java/android/speech/tts/SynthesisMessageParams.java
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -15,6 +15,7 @@
  */
 package android.speech.tts;
 
+import android.media.AudioFormat;
 import android.media.AudioTrack;
 import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
 
@@ -24,6 +25,8 @@
  * Params required to play back a synthesis request.
  */
 final class SynthesisMessageParams extends MessageParams {
+    private static final long MAX_UNCONSUMED_AUDIO_MS = 500;
+
     final int mStreamType;
     final int mSampleRateInHz;
     final int mAudioFormat;
@@ -32,10 +35,22 @@
     final float mPan;
     final EventLogger mLogger;
 
+    final int mBytesPerFrame;
+
     volatile AudioTrack mAudioTrack;
-    // Not volatile, accessed only from the synthesis thread.
-    int mBytesWritten;
+    // Written by the synthesis thread, but read on the audio playback
+    // thread.
+    volatile int mBytesWritten;
+    // A "short utterance" is one that uses less bytes than the audio
+    // track buffer size (mAudioBufferSize). In this case, we need to call
+    // AudioTrack#stop() to send pending buffers to the mixer, and slightly
+    // different logic is required to wait for the track to finish.
+    //
+    // Not volatile, accessed only from the audio playback thread.
+    boolean mIsShortUtterance;
     int mAudioBufferSize;
+    // Always synchronized on "this".
+    int mUnconsumedBytes;
 
     private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
 
@@ -53,6 +68,8 @@
         mPan = pan;
         mLogger = logger;
 
+        mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount;
+
         // initially null.
         mAudioTrack = null;
         mBytesWritten = 0;
@@ -64,18 +81,36 @@
         return TYPE_SYNTHESIS;
     }
 
-    synchronized void addBuffer(byte[] buffer, int offset, int length) {
-        mDataBufferList.add(new ListEntry(buffer, offset, length));
+    synchronized void addBuffer(byte[] buffer) {
+        long unconsumedAudioMs = 0;
+
+        while ((unconsumedAudioMs = getUnconsumedAudioLengthMs()) > MAX_UNCONSUMED_AUDIO_MS) {
+            try {
+                wait();
+            } catch (InterruptedException ie) {
+                return;
+            }
+        }
+
+        mDataBufferList.add(new ListEntry(buffer));
+        mUnconsumedBytes += buffer.length;
     }
 
-    synchronized void addBuffer(byte[] buffer) {
-        mDataBufferList.add(new ListEntry(buffer, 0, buffer.length));
+    synchronized void clearBuffers() {
+        mDataBufferList.clear();
+        mUnconsumedBytes = 0;
+        notifyAll();
     }
 
     synchronized ListEntry getNextBuffer() {
-        return mDataBufferList.poll();
-    }
+        ListEntry entry = mDataBufferList.poll();
+        if (entry != null) {
+            mUnconsumedBytes -= entry.mBytes.length;
+            notifyAll();
+        }
 
+        return entry;
+    }
 
     void setAudioTrack(AudioTrack audioTrack) {
         mAudioTrack = audioTrack;
@@ -85,15 +120,29 @@
         return mAudioTrack;
     }
 
+    // Must be called synchronized on this.
+    private long getUnconsumedAudioLengthMs() {
+        final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame;
+        final long estimatedTimeMs = unconsumedFrames * 1000 / mSampleRateInHz;
+
+        return estimatedTimeMs;
+    }
+
+    private static int getBytesPerFrame(int audioFormat) {
+        if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
+            return 1;
+        } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
+            return 2;
+        }
+
+        return -1;
+    }
+
     static final class ListEntry {
         final byte[] mBytes;
-        final int mOffset;
-        final int mLength;
 
-        ListEntry(byte[] bytes, int offset, int length) {
+        ListEntry(byte[] bytes) {
             mBytes = bytes;
-            mOffset = offset;
-            mLength = length;
         }
     }
 }
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 421e995..768071f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1296,7 +1296,10 @@
                     float h1 = getHorizontal(st, false, line);
                     float h2 = getHorizontal(en, true, line);
 
-                    dest.addRect(h1, top, h2, bottom, Path.Direction.CW);
+                    float left = Math.min(h1, h2);
+                    float right = Math.max(h1, h2);
+
+                    dest.addRect(left, top, right, bottom, Path.Direction.CW);
                 }
             }
         }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 788711d..e8b2045 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -729,12 +729,22 @@
                     start - widthStart, end - start);
         }
 
-        // If ellipsize is in marquee mode, do not apply ellipsis on the first line
-        if (ellipsize != null && (ellipsize != TextUtils.TruncateAt.MARQUEE || j != 0)) {
+        if (ellipsize != null) {
+            // If there is only one line, then do any type of ellipsis except when it is MARQUEE
+            // if there are multiple lines, just allow END ellipsis on the last line
+            boolean firstLine = (j == 0);
+            boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
             boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
-            calculateEllipsis(start, end, widths, widthStart,
-                    ellipsisWidth, ellipsize, j,
-                    textWidth, paint, forceEllipsis);
+
+            boolean doEllipsis = (firstLine && !moreChars &&
+                                ellipsize != TextUtils.TruncateAt.MARQUEE) ||
+                        (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
+                                ellipsize == TextUtils.TruncateAt.END);
+            if (doEllipsis) {
+                calculateEllipsis(start, end, widths, widthStart,
+                        ellipsisWidth, ellipsize, j,
+                        textWidth, paint, forceEllipsis);
+            }
         }
 
         mLineCount++;
@@ -797,8 +807,8 @@
 
             ellipsisStart = i;
             ellipsisCount = len - i;
-            if (forceEllipsis && ellipsisCount == 0 && i > 0) {
-                ellipsisStart = i - 1;
+            if (forceEllipsis && ellipsisCount == 0 && len > 0) {
+                ellipsisStart = len - 1;
                 ellipsisCount = 1;
             }
         } else {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index fcc372e..68a6b3e 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -723,7 +723,7 @@
         float ret = 0;
 
         int contextLen = contextEnd - contextStart;
-        if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor !=0 || runIsRtl))) {
+        if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineCount != 0 || runIsRtl))) {
             int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
             if (mCharsValid) {
                 ret = wp.getTextRunAdvances(mChars, start, runLen,
@@ -753,21 +753,26 @@
                 wp.setColor(previousColor);
             }
 
-            if (wp.underlineColor != 0) {
+            if (wp.underlineCount != 0) {
                 // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
-                float middle = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
-                // kStdUnderline_Thickness = 1/18, defined in SkTextFormatParams.h
-                float halfHeight = wp.underlineThickness * (1.0f / 18.0f / 2.0f) * wp.getTextSize();
+                float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
 
                 int previousColor = wp.getColor();
                 Paint.Style previousStyle = wp.getStyle();
+                boolean previousAntiAlias = wp.isAntiAlias();
 
-                wp.setColor(wp.underlineColor);
                 wp.setStyle(Paint.Style.FILL);
-                c.drawRect(x, middle - halfHeight, x + ret, middle + halfHeight, wp);
+                wp.setAntiAlias(true);
+
+                for (int i = 0; i < wp.underlineCount; i++) {
+                    wp.setColor(wp.underlineColors[i]);
+                    c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThicknesses[i],
+                            wp);
+                }
 
                 wp.setStyle(previousStyle);
                 wp.setColor(previousColor);
+                wp.setAntiAlias(previousAntiAlias);
             }
 
             drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index de57dfa..625d869 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -23,6 +23,9 @@
  * data used during text measuring and drawing.
  */
 public class TextPaint extends Paint {
+
+    private static final int DEFAULT_UNDERLINE_SIZE = 3;
+
     // Special value 0 means no background paint
     public int bgColor;
     public int baselineShift;
@@ -33,12 +36,17 @@
      * Special value 0 means no custom underline
      * @hide
      */
-    public int underlineColor;
+    public int[] underlineColors;
     /**
      * Defined as a multiplier of the default underline thickness. Use 1.0f for default thickness.
      * @hide
      */
-    public float underlineThickness;
+    public float[] underlineThicknesses;
+    /**
+     * The number of underlines currently stored in the array. If 0, no underline is drawn.
+     * @hide
+     */
+    public int underlineCount;
 
     public TextPaint() {
         super();
@@ -64,24 +72,43 @@
         linkColor = tp.linkColor;
         drawableState = tp.drawableState;
         density = tp.density;
-        underlineColor = tp.underlineColor;
-        underlineThickness = tp.underlineThickness;
+        underlineColors = tp.underlineColors;
+        underlineThicknesses = tp.underlineThicknesses;
+        underlineCount = tp.underlineCount;
     }
 
     /**
      * Defines a custom underline for this Paint.
      * @param color underline solid color
-     * @param thickness underline thickness, defined as a multiplier of the default underline
-     * thickness.
+     * @param thickness underline thickness
      * @hide
      */
-    public void setUnderlineText(boolean isUnderlined, int color, float thickness) {
-        setUnderlineText(false);
-        if (isUnderlined) {
-            underlineColor = color;
-            underlineThickness = thickness;
+    public void setUnderlineText(int color, float thickness) {
+        if (color == 0) {
+            // No underline
+            return;
+        }
+
+        if (underlineCount == 0) {
+            underlineColors = new int[DEFAULT_UNDERLINE_SIZE];
+            underlineThicknesses = new float[DEFAULT_UNDERLINE_SIZE];
+            underlineColors[underlineCount] = color;
+            underlineThicknesses[underlineCount] = thickness;
+            underlineCount++;
         } else {
-            underlineColor = 0;
+            if (underlineCount == underlineColors.length) {
+                int[] newColors = new int[underlineColors.length + DEFAULT_UNDERLINE_SIZE];
+                float[] newThickness = new float[underlineThicknesses.length
+                        + DEFAULT_UNDERLINE_SIZE];
+                System.arraycopy(underlineColors, 0, newColors, 0, underlineColors.length);
+                System.arraycopy(
+                        underlineThicknesses, 0, newThickness, 0, underlineThicknesses.length);
+                underlineColors = newColors;
+                underlineThicknesses = newThickness;
+            }
+            underlineColors[underlineCount] = color;
+            underlineThicknesses[underlineCount] = thickness;
+            underlineCount++;
         }
     }
 }
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index b8b54f4..e914316 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -25,6 +25,7 @@
 import android.text.style.BulletSpan;
 import android.text.style.CharacterStyle;
 import android.text.style.ForegroundColorSpan;
+import android.text.style.EasyEditSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.MetricAffectingSpan;
 import android.text.style.QuoteSpan;
@@ -585,6 +586,8 @@
     public static final int SPELL_CHECK_SPAN = 20;
     /** @hide */
     public static final int SUGGESTION_RANGE_SPAN = 21;
+    /** @hide */
+    public static final int EASY_EDIT_SPAN = 22;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -748,6 +751,10 @@
                     readSpan(p, sp, new SuggestionRangeSpan());
                     break;
                     
+                case EASY_EDIT_SPAN:
+                    readSpan(p, sp, new EasyEditSpan(p));
+                    break;
+
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
new file mode 100644
index 0000000..e6e4d2c
--- /dev/null
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
+import android.widget.TextView;
+
+/**
+ * Provides an easy way to edit a portion of text.
+ * <p>
+ * The {@link TextView} uses this span to allow the user to delete a chuck of text in one click.
+ * the text. {@link TextView} removes this span as soon as the text is edited, or the cursor moves.
+ */
+public class EasyEditSpan implements ParcelableSpan {
+
+    public EasyEditSpan() {
+        // Empty
+    }
+
+    public EasyEditSpan(Parcel src) {
+        this();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // Empty
+    }
+
+    @Override
+    public int getSpanTypeId() {
+        return TextUtils.EASY_EDIT_SPAN;
+    }
+}
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index ea57f91..8625257 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -61,12 +61,6 @@
     public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
     public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
 
-    /**
-     * The default underline thickness as a percentage of the system's default underline thickness
-     * (i.e., 100 means the default thickness, and 200 is a double thickness).
-     */
-    private static final int DEFAULT_UNDERLINE_PERCENTAGE = 100;
-
     public static final int SUGGESTIONS_MAX_SIZE = 5;
 
     /*
@@ -82,10 +76,8 @@
     private final String mNotificationTargetClassName;
     private final int mHashCode;
 
-    private float mMisspelledUnderlineThickness;
-    private int mMisspelledUnderlineColor;
-    private float mEasyCorrectUnderlineThickness;
-    private int mEasyCorrectUnderlineColor;
+    private float mUnderlineThickness;
+    private int mUnderlineColor;
 
     /*
      * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo
@@ -140,31 +132,26 @@
     }
 
     private void initStyle(Context context) {
-        // Read the colors. We need to store the color and the underline thickness, as the span
-        // does not have access to the context when it is read from a parcel.
-        TypedArray typedArray;
+        int defStyle = 0;
+        if ((getFlags() & FLAG_MISSPELLED) != 0) {
+            defStyle = com.android.internal.R.attr.textAppearanceMisspelledSuggestion;
+        } else if ((getFlags() & FLAG_EASY_CORRECT) != 0) {
+            defStyle = com.android.internal.R.attr.textAppearanceEasyCorrectSuggestion;
+        } else {
+            // No style is applied.
+            mUnderlineThickness = 0;
+            mUnderlineColor = 0;
+            return;
+        }
 
-        typedArray = context.obtainStyledAttributes(null,
+        TypedArray typedArray = context.obtainStyledAttributes(null,
                 com.android.internal.R.styleable.SuggestionSpan,
-                com.android.internal.R.attr.textAppearanceEasyCorrectSuggestion, 0);
+                defStyle, 0);
 
-        mEasyCorrectUnderlineThickness = getThicknessPercentage(typedArray,
-                com.android.internal.R.styleable.SuggestionSpan_textUnderlineThicknessPercentage);
-        mEasyCorrectUnderlineColor = typedArray.getColor(
+        mUnderlineThickness = typedArray.getDimension(
+                com.android.internal.R.styleable.SuggestionSpan_textUnderlineThickness, 0);
+        mUnderlineColor = typedArray.getColor(
                 com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK);
-
-        typedArray = context.obtainStyledAttributes(null,
-                com.android.internal.R.styleable.SuggestionSpan,
-                com.android.internal.R.attr.textAppearanceMisspelledSuggestion, 0);
-        mMisspelledUnderlineThickness = getThicknessPercentage(typedArray,
-                com.android.internal.R.styleable.SuggestionSpan_textUnderlineThicknessPercentage);
-        mMisspelledUnderlineColor = typedArray.getColor(
-                com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK);
-    }
-
-    private static float getThicknessPercentage(TypedArray typedArray, int index) {
-        int value  = typedArray.getInteger(index, DEFAULT_UNDERLINE_PERCENTAGE);
-        return value / 100.0f;
     }
 
     public SuggestionSpan(Parcel src) {
@@ -173,10 +160,8 @@
         mLocaleString = src.readString();
         mNotificationTargetClassName = src.readString();
         mHashCode = src.readInt();
-        mEasyCorrectUnderlineColor = src.readInt();
-        mEasyCorrectUnderlineThickness = src.readFloat();
-        mMisspelledUnderlineColor = src.readInt();
-        mMisspelledUnderlineThickness = src.readFloat();
+        mUnderlineColor = src.readInt();
+        mUnderlineThickness = src.readFloat();
     }
 
     /**
@@ -226,10 +211,8 @@
         dest.writeString(mLocaleString);
         dest.writeString(mNotificationTargetClassName);
         dest.writeInt(mHashCode);
-        dest.writeInt(mEasyCorrectUnderlineColor);
-        dest.writeFloat(mEasyCorrectUnderlineThickness);
-        dest.writeInt(mMisspelledUnderlineColor);
-        dest.writeFloat(mMisspelledUnderlineThickness);
+        dest.writeInt(mUnderlineColor);
+        dest.writeFloat(mUnderlineThickness);
     }
 
     @Override
@@ -271,10 +254,6 @@
 
     @Override
     public void updateDrawState(TextPaint tp) {
-        if ((getFlags() & FLAG_MISSPELLED) != 0) {
-            tp.setUnderlineText(true, mMisspelledUnderlineColor, mMisspelledUnderlineThickness);
-        } else if ((getFlags() & FLAG_EASY_CORRECT) != 0) {
-            tp.setUnderlineText(true, mEasyCorrectUnderlineColor, mEasyCorrectUnderlineThickness);
-        }
+        tp.setUnderlineText(mUnderlineColor, mUnderlineThickness);
     }
 }
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ef3d3fa5..3880bc45 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -36,10 +36,14 @@
     public static final int ROTATION_270     = 3;
 
     /**
-     * Create Surface from a SurfaceTexture.
+     * Create Surface from a {@link SurfaceTexture}.
      *
-     * @param surfaceTexture The {@link SurfaceTexture} that is updated by this Surface.
-     * @hide
+     * Images drawn to the Surface will be made available to the {@link
+     * SurfaceTexture}, which can attach them an OpenGL ES texture via {@link
+     * SurfaceTexture#updateTexImage}.
+     *
+     * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
+     * Surface.
      */
     public Surface(SurfaceTexture surfaceTexture) {
         if (DEBUG_RELEASE) {
@@ -442,7 +446,9 @@
     /** @hide */
     public native   void setLayer(int zorder);
     /** @hide */
-    public native   void setPosition(int x, int y);
+    public void setPosition(int x, int y) { setPosition((float)x, (float)y); }
+    /** @hide */
+    public native   void setPosition(float x, float y);
     /** @hide */
     public native   void setSize(int w, int h);
     /** @hide */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a0cc287..5c045bb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -454,7 +454,10 @@
                 // manager, to make sure we do the relayout before receiving
                 // any other events from the system.
                 requestLayout();
-                mInputChannel = new InputChannel();
+                if ((mWindowAttributes.inputFeatures
+                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
+                    mInputChannel = new InputChannel();
+                }
                 try {
                     res = sWindowSession.add(mWindow, mWindowAttributes,
                             getHostVisibility(), mAttachInfo.mContentInsets,
@@ -524,12 +527,14 @@
                     mInputQueueCallback =
                         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                 }
-                if (mInputQueueCallback != null) {
-                    mInputQueue = new InputQueue(mInputChannel);
-                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
-                } else {
-                    InputQueue.registerInputChannel(mInputChannel, mInputHandler,
-                            Looper.myQueue());
+                if (mInputChannel != null) {
+                    if (mInputQueueCallback != null) {
+                        mInputQueue = new InputQueue(mInputChannel);
+                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
+                    } else {
+                        InputQueue.registerInputChannel(mInputChannel, mInputHandler,
+                                Looper.myQueue());
+                    }
                 }
 
                 view.assignParent(this);
@@ -2152,13 +2157,12 @@
 
         mSurface.release();
 
-        if (mInputChannel != null) {
-            if (mInputQueueCallback != null) {
-                mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
-                mInputQueueCallback = null;
-            } else {
-                InputQueue.unregisterInputChannel(mInputChannel);
-            }
+        if (mInputQueueCallback != null && mInputQueue != null) {
+            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
+            mInputQueueCallback = null;
+            mInputQueue = null;
+        } else if (mInputChannel != null) {
+            InputQueue.unregisterInputChannel(mInputChannel);
         }
         try {
             sWindowSession.remove(mWindow);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index fdd9b2c..52d25d9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1029,9 +1029,18 @@
         public static final int INPUT_FEATURE_DISABLE_POINTER_GESTURES = 0x00000001;
 
         /**
+         * Does not construct an input channel for this window.  The channel will therefore
+         * be incapable of receiving input.
+         *
+         * @hide
+         */
+        public static final int INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002;
+
+        /**
          * Control special features of the input subsystem.
          *
          * @see #INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES
+         * @see #INPUT_FEATURE_NO_INPUT_CHANNEL
          * @hide
          */
         public int inputFeatures;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index da5baf8..302be57 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1583,6 +1583,14 @@
     /**
      * Set additional input method subtypes. Only a process which shares the same uid with the IME
      * can add additional input method subtypes to the IME.
+     * Please note that a subtype's status is stored in the system.
+     * For example, enabled subtypes are remembered by the framework even after they are removed
+     * by using this method. If you re-add the same subtypes again,
+     * they will just get enabled. If you want to avoid such conflicts, for instance, you may
+     * want to create a "different" new subtype even with the same locale and mode,
+     * by changing its extra value. The different subtype won't get affected by the stored past
+     * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer
+     * to the current implementation.)
      * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
      * @param subtypes subtypes will be added as additional subtypes of the current input method.
      * @return true if the additional input method subtypes are successfully added.
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index c85b2d9..01587aa 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -66,7 +66,13 @@
 
     /**
      * Get a spell checker session for the specified spell checker
-     * @param locale the locale for the spell checker
+     * @param locale the locale for the spell checker. If {@param locale} is null and
+     * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
+     * returned. If {@param locale} is not null and referToSpellCheckerLanguageSettings is true,
+     * the locale specified in Settings will be returned only when it is same as {@param locale}.
+     * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@param locale} is
+     * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
+     * selected.
      * @param listener a spell checker session lister for getting results from a spell checker.
      * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
      * languages in settings will be returned.
@@ -81,6 +87,11 @@
             throw new IllegalArgumentException("Locale should not be null if you don't refer"
                     + " settings.");
         }
+
+        if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
+            return null;
+        }
+
         final SpellCheckerInfo sci;
         try {
             sci = sService.getCurrentSpellChecker(null);
@@ -108,7 +119,12 @@
             final String localeStr = locale.toString();
             for (int i = 0; i < sci.getSubtypeCount(); ++i) {
                 final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
-                if (subtype.getLocale().equals(localeStr)) {
+                final String tempSubtypeLocale = subtype.getLocale();
+                if (tempSubtypeLocale.equals(localeStr)) {
+                    subtypeInUse = subtype;
+                    break;
+                } else if (localeStr.length() >= 2 && tempSubtypeLocale.length() >= 2
+                        && localeStr.startsWith(tempSubtypeLocale)) {
                     subtypeInUse = subtype;
                 }
             }
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index ef1906c..1b9a25e 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -5,6 +5,7 @@
 import android.media.MediaPlayer;
 import android.webkit.HTML5VideoView;
 import android.webkit.HTML5VideoViewProxy;
+import android.view.Surface;
 import android.opengl.GLES20;
 
 /**
@@ -38,7 +39,10 @@
 
     @Override
     public void decideDisplayMode() {
-        mPlayer.setTexture(getSurfaceTexture(getVideoLayerId()));
+        SurfaceTexture surfaceTexture = getSurfaceTexture(getVideoLayerId());
+        Surface surface = new Surface(surfaceTexture);
+        mPlayer.setSurface(surface);
+        surface.release();
     }
 
     // Normally called immediately after setVideoURI. But for full screen,
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index ff13dcb..b89b8ec 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -112,6 +112,8 @@
 
     private void scheduleSpellCheck() {
         if (mLength == 0) return;
+        if (spellCheckerSession == null) return;
+
         if (mChecker != null) {
             mTextView.removeCallbacks(mChecker);
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e9662ae..4ce2d90 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -64,6 +64,7 @@
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.text.TextUtils.TruncateAt;
 import android.text.method.AllCapsTransformationMethod;
 import android.text.method.ArrowKeyMovementMethod;
 import android.text.method.DateKeyListener;
@@ -81,7 +82,9 @@
 import android.text.method.TransformationMethod;
 import android.text.method.TransformationMethod2;
 import android.text.method.WordIterator;
+import android.text.style.CharacterStyle;
 import android.text.style.ClickableSpan;
+import android.text.style.EasyEditSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.SpellCheckSpan;
 import android.text.style.SuggestionRangeSpan;
@@ -364,6 +367,35 @@
 
     private boolean mResolvedDrawables = false;
 
+    /**
+     * On some devices the fading edges add a performance penalty if used
+     * extensively in the same layout. This mode indicates how the marquee
+     * is currently being shown, if applicable. (mEllipsize will == MARQUEE)
+     */
+    private int mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
+
+    /**
+     * When mMarqueeFadeMode is not MARQUEE_FADE_NORMAL, this stores
+     * the layout that should be used when the mode switches.
+     */
+    private Layout mSavedMarqueeModeLayout;
+
+    /**
+     * Draw marquee text with fading edges as usual
+     */
+    private static final int MARQUEE_FADE_NORMAL = 0;
+
+    /**
+     * Draw marquee text as ellipsize end while inactive instead of with the fade.
+     * (Useful for devices where the fade can be expensive if overdone)
+     */
+    private static final int MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS = 1;
+
+    /**
+     * Draw marquee text with fading edges because it is currently active/animating.
+     */
+    private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2;
+
     /*
      * Kick-start the font cache for the zygote process (to pay the cost of
      * initializing freetype for our default font only once).
@@ -995,8 +1027,13 @@
                 setEllipsize(TextUtils.TruncateAt.END);
                 break;
             case 4:
-                setHorizontalFadingEdgeEnabled(
-                        ViewConfiguration.get(context).isFadingMarqueeEnabled());
+                if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
+                    setHorizontalFadingEdgeEnabled(true);
+                    mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
+                } else {
+                    setHorizontalFadingEdgeEnabled(false);
+                    mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
+                }
                 setEllipsize(TextUtils.TruncateAt.MARQUEE);
                 break;
         }
@@ -3067,8 +3104,13 @@
 
         if (text instanceof Spanned &&
             ((Spanned) text).getSpanStart(TextUtils.TruncateAt.MARQUEE) >= 0) {
-            setHorizontalFadingEdgeEnabled(
-                    ViewConfiguration.get(mContext).isFadingMarqueeEnabled());
+            if (ViewConfiguration.get(mContext).isFadingMarqueeEnabled()) {
+                setHorizontalFadingEdgeEnabled(true);
+                mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
+            } else {
+                setHorizontalFadingEdgeEnabled(false);
+                mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
+            }
             setEllipsize(TextUtils.TruncateAt.MARQUEE);
         }
 
@@ -4761,7 +4803,8 @@
 
         final int layoutDirection = getResolvedLayoutDirection();
         final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-        if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
+        if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
+                mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
             if (!mSingleLine && getLineCount() == 1 && canMarquee() &&
                     (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
                 canvas.translate(mLayout.getLineRight(0) - (mRight - mLeft -
@@ -5945,7 +5988,7 @@
             mSavedHintLayout = (BoringLayout) mHintLayout;
         }
 
-        mLayout = mHintLayout = null;
+        mSavedMarqueeModeLayout = mLayout = mHintLayout = null;
 
         // Since it depends on the value of mLayout
         prepareCursorControllers();
@@ -6065,73 +6108,25 @@
 
         Layout.Alignment alignment = getLayoutAlignment();
         boolean shouldEllipsize = mEllipsize != null && mInput == null;
+        final boolean switchEllipsize = mEllipsize == TruncateAt.MARQUEE &&
+                mMarqueeFadeMode != MARQUEE_FADE_NORMAL;
+        TruncateAt effectiveEllipsize = mEllipsize;
+        if (mEllipsize == TruncateAt.MARQUEE &&
+                mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
+            effectiveEllipsize = TruncateAt.END;
+        }
 
         if (mTextDir == null) {
             resolveTextDirection();
         }
-        if (mText instanceof Spannable) {
-            mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w,
-                    alignment, mTextDir, mSpacingMult,
-                    mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null,
-                    ellipsisWidth);
-        } else {
-            if (boring == UNKNOWN_BORING) {
-                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
-                if (boring != null) {
-                    mBoring = boring;
-                }
-            }
 
-            if (boring != null) {
-                if (boring.width <= w &&
-                    (mEllipsize == null || boring.width <= ellipsisWidth)) {
-                    if (mSavedLayout != null) {
-                        mLayout = mSavedLayout.
-                                replaceOrMake(mTransformed, mTextPaint,
-                                w, alignment, mSpacingMult, mSpacingAdd,
-                                boring, mIncludePad);
-                    } else {
-                        mLayout = BoringLayout.make(mTransformed, mTextPaint,
-                                w, alignment, mSpacingMult, mSpacingAdd,
-                                boring, mIncludePad);
-                    }
-
-                    mSavedLayout = (BoringLayout) mLayout;
-                } else if (shouldEllipsize && boring.width <= w) {
-                    if (mSavedLayout != null) {
-                        mLayout = mSavedLayout.
-                                replaceOrMake(mTransformed, mTextPaint,
-                                w, alignment, mSpacingMult, mSpacingAdd,
-                                boring, mIncludePad, mEllipsize,
-                                ellipsisWidth);
-                    } else {
-                        mLayout = BoringLayout.make(mTransformed, mTextPaint,
-                                w, alignment, mSpacingMult, mSpacingAdd,
-                                boring, mIncludePad, mEllipsize,
-                                ellipsisWidth);
-                    }
-                } else if (shouldEllipsize) {
-                    mLayout = new StaticLayout(mTransformed,
-                                0, mTransformed.length(),
-                                mTextPaint, w, alignment, mTextDir, mSpacingMult,
-                                mSpacingAdd, mIncludePad, mEllipsize,
-                                ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
-                } else {
-                    mLayout = new StaticLayout(mTransformed, mTextPaint,
-                            w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
-                            mIncludePad);
-                }
-            } else if (shouldEllipsize) {
-                mLayout = new StaticLayout(mTransformed,
-                            0, mTransformed.length(),
-                            mTextPaint, w, alignment, mTextDir, mSpacingMult,
-                            mSpacingAdd, mIncludePad, mEllipsize,
-                            ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
-            } else {
-                mLayout = new StaticLayout(mTransformed, mTextPaint,
-                        w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
-                        mIncludePad);
-            }
+        mLayout = makeSingleLayout(w, boring, ellipsisWidth, alignment, shouldEllipsize,
+                effectiveEllipsize, effectiveEllipsize == mEllipsize);
+        if (switchEllipsize) {
+            TruncateAt oppositeEllipsize = effectiveEllipsize == TruncateAt.MARQUEE ?
+                    TruncateAt.END : TruncateAt.MARQUEE;
+            mSavedMarqueeModeLayout = makeSingleLayout(w, boring, ellipsisWidth, alignment,
+                    shouldEllipsize, oppositeEllipsize, effectiveEllipsize != mEllipsize);
         }
 
         shouldEllipsize = mEllipsize != null;
@@ -6222,6 +6217,77 @@
         prepareCursorControllers();
     }
 
+    private Layout makeSingleLayout(int w, BoringLayout.Metrics boring, int ellipsisWidth,
+            Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
+            boolean useSaved) {
+        Layout result = null;
+        if (mText instanceof Spannable) {
+            result = new DynamicLayout(mText, mTransformed, mTextPaint, w,
+                    alignment, mTextDir, mSpacingMult,
+                    mSpacingAdd, mIncludePad, mInput == null ? effectiveEllipsize : null,
+                            ellipsisWidth);
+        } else {
+            if (boring == UNKNOWN_BORING) {
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
+                if (boring != null) {
+                    mBoring = boring;
+                }
+            }
+
+            if (boring != null) {
+                if (boring.width <= w &&
+                        (effectiveEllipsize == null || boring.width <= ellipsisWidth)) {
+                    if (useSaved && mSavedLayout != null) {
+                        result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
+                                w, alignment, mSpacingMult, mSpacingAdd,
+                                boring, mIncludePad);
+                    } else {
+                        result = BoringLayout.make(mTransformed, mTextPaint,
+                                w, alignment, mSpacingMult, mSpacingAdd,
+                                boring, mIncludePad);
+                    }
+
+                    if (useSaved) {
+                        mSavedLayout = (BoringLayout) result;
+                    }
+                } else if (shouldEllipsize && boring.width <= w) {
+                    if (useSaved && mSavedLayout != null) {
+                        result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
+                                w, alignment, mSpacingMult, mSpacingAdd,
+                                boring, mIncludePad, effectiveEllipsize,
+                                ellipsisWidth);
+                    } else {
+                        result = BoringLayout.make(mTransformed, mTextPaint,
+                                w, alignment, mSpacingMult, mSpacingAdd,
+                                boring, mIncludePad, effectiveEllipsize,
+                                ellipsisWidth);
+                    }
+                } else if (shouldEllipsize) {
+                    result = new StaticLayout(mTransformed,
+                            0, mTransformed.length(),
+                            mTextPaint, w, alignment, mTextDir, mSpacingMult,
+                            mSpacingAdd, mIncludePad, effectiveEllipsize,
+                            ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+                } else {
+                    result = new StaticLayout(mTransformed, mTextPaint,
+                            w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
+                            mIncludePad);
+                }
+            } else if (shouldEllipsize) {
+                result = new StaticLayout(mTransformed,
+                        0, mTransformed.length(),
+                        mTextPaint, w, alignment, mTextDir, mSpacingMult,
+                        mSpacingAdd, mIncludePad, effectiveEllipsize,
+                        ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+            } else {
+                result = new StaticLayout(mTransformed, mTextPaint,
+                        w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
+                        mIncludePad);
+            }
+        }
+        return result;
+    }
+
     private boolean compressText(float width) {
         if (isHardwareAccelerated()) return false;
         
@@ -7177,7 +7243,9 @@
 
     private boolean canMarquee() {
         int width = (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight());
-        return width > 0 && mLayout.getLineWidth(0) > width;
+        return width > 0 && (mLayout.getLineWidth(0) > width ||
+                (mMarqueeFadeMode != MARQUEE_FADE_NORMAL && mSavedMarqueeModeLayout != null &&
+                        mSavedMarqueeModeLayout.getLineWidth(0) > width));
     }
 
     private void startMarquee() {
@@ -7191,6 +7259,16 @@
         if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected()) &&
                 getLineCount() == 1 && canMarquee()) {
 
+            if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
+                mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE;
+                final Layout tmp = mLayout;
+                mLayout = mSavedMarqueeModeLayout;
+                mSavedMarqueeModeLayout = tmp;
+                setHorizontalFadingEdgeEnabled(true);
+                requestLayout();
+                invalidate();
+            }
+
             if (mMarquee == null) mMarquee = new Marquee(this);
             mMarquee.start(mMarqueeRepeatLimit);
         }
@@ -7200,6 +7278,16 @@
         if (mMarquee != null && !mMarquee.isStopped()) {
             mMarquee.stop();
         }
+
+        if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_FADE) {
+            mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
+            final Layout tmp = mSavedMarqueeModeLayout;
+            mSavedMarqueeModeLayout = mLayout;
+            mLayout = tmp;
+            setHorizontalFadingEdgeEnabled(false);
+            requestLayout();
+            invalidate();
+        }
     }
 
     private void startStopMarquee(boolean start) {
@@ -7705,10 +7793,148 @@
         }
     }
 
+    /**
+     * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
+     * pop-up should be displayed.
+     */
+    private class EditTextShortcutController {
+
+        private EditTextShortcutPopupWindow mPopupWindow;
+
+        private EasyEditSpan mEditTextShortcutSpan;
+
+        private void hide() {
+            if (mEditTextShortcutSpan != null) {
+                mPopupWindow.hide();
+                if (mText instanceof Spannable) {
+                    ((Spannable) mText).removeSpan(mEditTextShortcutSpan);
+                }
+                mEditTextShortcutSpan = null;
+            }
+        }
+
+        /**
+         * Monitors the changes in the text.
+         *
+         * <p>{@link ChangeWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used,
+         * as the notifications are not sent when a spannable (with spans) is inserted.
+         */
+        public void onTextChange(CharSequence buffer) {
+            if (mEditTextShortcutSpan != null) {
+                hide();
+            }
+
+            if (buffer instanceof Spanned) {
+                mEditTextShortcutSpan = getSpan((Spanned) buffer);
+                if (mEditTextShortcutSpan != null) {
+                    if (mPopupWindow == null) {
+                        mPopupWindow = new EditTextShortcutPopupWindow();
+                    }
+                    mPopupWindow.show(mEditTextShortcutSpan);
+                }
+            }
+        }
+
+        private EasyEditSpan getSpan(Spanned spanned) {
+            EasyEditSpan[] inputMethodSpans = spanned.getSpans(0, spanned.length(),
+                    EasyEditSpan.class);
+
+            if (inputMethodSpans.length == 0) {
+                return null;
+            } else {
+                return inputMethodSpans[0];
+            }
+        }
+    }
+
+    /**
+     * Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled
+     * by {@link EditTextShortcutController}.
+     */
+    private class EditTextShortcutPopupWindow extends PinnedPopupWindow
+            implements OnClickListener {
+        private static final int POPUP_TEXT_LAYOUT =
+                com.android.internal.R.layout.text_edit_action_popup_text;
+        private TextView mDeleteTextView;
+        private EasyEditSpan mEditTextShortcutSpan;
+
+        @Override
+        protected void createPopupWindow() {
+            mPopupWindow = new PopupWindow(TextView.this.mContext, null,
+                    com.android.internal.R.attr.textSelectHandleWindowStyle);
+            mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+            mPopupWindow.setClippingEnabled(true);
+        }
+
+        @Override
+        protected void initContentView() {
+            mContentView.setOrientation(LinearLayout.HORIZONTAL);
+            mContentView.setBackgroundResource(
+                    com.android.internal.R.drawable.text_edit_side_paste_window);
+
+            LayoutInflater inflater = (LayoutInflater)TextView.this.mContext.
+                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+            LayoutParams wrapContent = new LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+            mDeleteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
+            mDeleteTextView.setLayoutParams(wrapContent);
+            mDeleteTextView.setText(com.android.internal.R.string.delete);
+            mDeleteTextView.setOnClickListener(this);
+            mContentView.addView(mDeleteTextView);
+        }
+
+        public void show(EasyEditSpan inputMethodSpan) {
+            mEditTextShortcutSpan = inputMethodSpan;
+            super.show();
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (view == mDeleteTextView) {
+                deleteText();
+            }
+        }
+
+        private void deleteText() {
+            Editable editable = (Editable) mText;
+            int start = editable.getSpanStart(mEditTextShortcutSpan);
+            int end = editable.getSpanEnd(mEditTextShortcutSpan);
+            if (start >= 0 && end >= 0) {
+                editable.delete(start, end);
+            }
+        }
+
+        @Override
+        protected int getTextOffset() {
+            // Place the pop-up at the end of the span
+            Editable editable = (Editable) mText;
+            return editable.getSpanEnd(mEditTextShortcutSpan);
+        }
+
+        @Override
+        protected int getVerticalLocalPosition(int line) {
+            return mLayout.getLineBottom(line);
+        }
+
+        @Override
+        protected int clipVertically(int positionY) {
+            // As we display the pop-up below the span, no vertical clipping is required.
+            return positionY;
+        }
+    }
+
     private class ChangeWatcher implements TextWatcher, SpanWatcher {
 
         private CharSequence mBeforeText;
 
+        private EditTextShortcutController mEditTextShortcutController;
+
+        private ChangeWatcher() {
+            mEditTextShortcutController = new EditTextShortcutController();
+        }
+
         public void beforeTextChanged(CharSequence buffer, int start,
                                       int before, int after) {
             if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start
@@ -7729,6 +7955,8 @@
                     + " before=" + before + " after=" + after + ": " + buffer);
             TextView.this.handleTextChanged(buffer, start, before, after);
 
+            mEditTextShortcutController.onTextChange(buffer);
+
             if (AccessibilityManager.getInstance(mContext).isEnabled() &&
                     (isFocused() || isSelected() && isShown())) {
                 sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
@@ -7763,6 +7991,10 @@
                     + " what=" + what + ": " + buf);
             TextView.this.spanChange(buf, what, s, -1, e, -1);
         }
+
+        private void hideControllers() {
+            mEditTextShortcutController.hide();
+        }
     }
 
     /**
@@ -8261,7 +8493,8 @@
     @Override
     protected float getLeftFadingEdgeStrength() {
         if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f;
-        if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
+        if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
+                mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
             if (mMarquee != null && !mMarquee.isStopped()) {
                 final Marquee marquee = mMarquee;
                 if (marquee.shouldDrawLeftFade()) {
@@ -8290,7 +8523,8 @@
     @Override
     protected float getRightFadingEdgeStrength() {
         if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f;
-        if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
+        if (mEllipsize == TextUtils.TruncateAt.MARQUEE &&
+                mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
             if (mMarquee != null && !mMarquee.isStopped()) {
                 final Marquee marquee = mMarquee;
                 return (marquee.mMaxFadeScroll - marquee.mScroll) / getHorizontalFadingEdgeLength();
@@ -8928,6 +9162,7 @@
                 stopSelectionActionMode();
             } else {
                 selectCurrentWord();
+                getSelectionController().show();
             }
             handled = true;
         }
@@ -9183,6 +9418,34 @@
         }
     }
 
+    private static class SuggestionRangeSpan extends CharacterStyle {
+
+        private final int mTextColor;
+        private final int mBackgroundColor;
+
+        public SuggestionRangeSpan(Context context) {
+            TypedArray typedArray = context.obtainStyledAttributes(null,
+                    com.android.internal.R.styleable.SuggestionRangeSpan,
+                    com.android.internal.R.attr.textAppearanceSuggestionRange, 0);
+
+            mTextColor = typedArray.getColor(
+                    com.android.internal.R.styleable.SuggestionRangeSpan_textColor, 0);
+            mBackgroundColor = typedArray.getColor(
+                    com.android.internal.R.styleable.SuggestionRangeSpan_colorBackground, 0);
+        }
+
+        @Override
+        public void updateDrawState(TextPaint tp) {
+            if (mTextColor != 0) {
+                tp.setColor(mTextColor);
+            }
+
+            if (mBackgroundColor != 0) {
+                tp.bgColor = mBackgroundColor;
+            }
+        }
+    }
+
     private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnClickListener {
         private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
         private static final int NO_SUGGESTIONS = -1;
@@ -9367,7 +9630,15 @@
 
             if (totalNbSuggestions == 0) return false;
 
-            if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+            if (mSuggestionRangeSpan == null) {
+                mSuggestionRangeSpan = new SuggestionRangeSpan(getContext());
+            }
+            
+            ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
+                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+            if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = 
+                    new SuggestionRangeSpan(getContext());
             ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
@@ -10694,6 +10965,10 @@
     private void hideControllers() {
         hideInsertionPointCursorController();
         stopSelectionActionMode();
+
+        if (mChangeWatcher != null) {
+            mChangeWatcher.hideControllers();
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index ff26d50..63b0274 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -266,6 +266,17 @@
         if (mActionMenuPresenter != null) {
             mActionMenuPresenter.onConfigurationChanged(newConfig);
         }
+
+        mTitleView = null;
+        mSubtitleView = null;
+        mTitleUpView = null;
+        if (mTitleLayout != null && mTitleLayout.getParent() == this) {
+            removeView(mTitleLayout);
+        }
+        mTitleLayout = null;
+        if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+            initTitle();
+        }
     }
 
     @Override
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 4cf4afa..494a2b3 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -119,6 +119,11 @@
 
 } gBinderProxyOffsets;
 
+static struct class_offsets_t
+{
+    jmethodID mGetName;
+} gClassOffsets;
+
 // ----------------------------------------------------------------------------
 
 static struct parcel_offsets_t
@@ -452,22 +457,18 @@
             // Okay, something is wrong -- we have a hard reference to a live death
             // recipient on the VM side, but the list is being torn down.
             JNIEnv* env = javavm_to_jnienv(mVM);
-            ScopedLocalRef<jclass> classRef(env, env->GetObjectClass(mObject));
-            jmethodID getnameMethod = env->GetMethodID(classRef.get(),
-                    "getName", "()Ljava/lang/String;");
-            if (getnameMethod) {
-                ScopedLocalRef<jstring> nameRef(env,
-                        (jstring) env->CallObjectMethod(classRef.get(), getnameMethod));
-                ScopedUtfChars nameUtf(env, nameRef.get());
-                if (nameUtf.c_str() != NULL) {
-                    LOGW("BinderProxy is being destroyed but the application did not call "
-                            "unlinkToDeath to unlink all of its death recipients beforehand.  "
-                            "Releasing leaked death recipient: %s", nameUtf.c_str());
-                } else {
-                    LOGW("BinderProxy being destroyed; unable to get DR object name");
-                    env->ExceptionClear();
-                }
-            } else LOGW("BinderProxy being destroyed; unable to find DR class getName");
+            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
+            ScopedLocalRef<jstring> nameRef(env,
+                    (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
+            ScopedUtfChars nameUtf(env, nameRef.get());
+            if (nameUtf.c_str() != NULL) {
+                LOGW("BinderProxy is being destroyed but the application did not call "
+                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
+                        "Releasing leaked death recipient: %s", nameUtf.c_str());
+            } else {
+                LOGW("BinderProxy being destroyed; unable to get DR object name");
+                env->ExceptionClear();
+            }
         }
     }
 
@@ -1230,6 +1231,11 @@
         = env->GetFieldID(clazz, "mOrgue", "I");
     assert(gBinderProxyOffsets.mOrgue);
 
+    clazz = env->FindClass("java/lang/Class");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find java.lang.Class");
+    gClassOffsets.mGetName = env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");
+    assert(gClassOffsets.mGetName);
+
     return AndroidRuntime::registerNativeMethods(
         env, kBinderProxyPathName,
         gBinderProxyMethods, NELEM(gBinderProxyMethods));
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 0c2801c..5fcf8fa 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -111,7 +111,9 @@
         NativeInputChannel* nativeInputChannel) {
     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
             gInputChannelClassInfo.ctor);
-    android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
+    if (inputChannelObj) {
+        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
+    }
     return inputChannelObj;
 }
 
@@ -126,18 +128,29 @@
     status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
 
     if (result) {
-        LOGE("Could not open input channel pair.  status=%d", result);
-        jniThrowRuntimeException(env, "Could not open input channel pair.");
+        String8 message;
+        message.appendFormat("Could not open input channel pair.  status=%d", result);
+        jniThrowRuntimeException(env, message.string());
         return NULL;
     }
 
-    // TODO more robust error checking
+    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+
     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
             new NativeInputChannel(serverChannel));
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+
     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
             new NativeInputChannel(clientChannel));
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
 
-    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
     env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
     env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
     return channelPair;
@@ -161,7 +174,7 @@
 
 static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
         jobject otherObj) {
-    if (android_view_InputChannel_getInputChannel(env, otherObj) != NULL) {
+    if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "Other object already has a native input channel.");
         return;
@@ -175,7 +188,7 @@
 
 static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
         jobject parcelObj) {
-    if (android_view_InputChannel_getInputChannel(env, obj) != NULL) {
+    if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "This object already has a native input channel.");
         return;
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 80c4871..300c04a 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -455,8 +455,9 @@
             env, inputChannelObj, inputHandlerObj, messageQueueObj);
 
     if (status) {
-        jniThrowRuntimeException(env, "Failed to register input channel.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to register input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
@@ -465,8 +466,9 @@
     status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);
 
     if (status) {
-        jniThrowRuntimeException(env, "Failed to unregister input channel.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to unregister input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
@@ -479,8 +481,9 @@
     // was no longer registered (DEAD_OBJECT) since it is a common race that can occur
     // during application shutdown.  The input dispatcher recovers gracefully anyways.
     if (status != OK && status != DEAD_OBJECT) {
-        jniThrowRuntimeException(env, "Failed to finish input event.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to finish input event.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4c1ca31..49441eb 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -589,7 +589,7 @@
 }
 
 static void Surface_setPosition(
-        JNIEnv* env, jobject clazz, jint x, jint y)
+        JNIEnv* env, jobject clazz, jfloat x, jfloat y)
 {
     const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
     if (surface == 0) return;
@@ -832,7 +832,7 @@
     {"screenshot",          "(II)Landroid/graphics/Bitmap;", (void*)Surface_screenshotAll },
     {"screenshot",          "(IIII)Landroid/graphics/Bitmap;", (void*)Surface_screenshot },
     {"setLayer",            "(I)V", (void*)Surface_setLayer },
-    {"setPosition",         "(II)V",(void*)Surface_setPosition },
+    {"setPosition",         "(FF)V",(void*)Surface_setPosition },
     {"setSize",             "(II)V",(void*)Surface_setSize },
     {"hide",                "()V",  (void*)Surface_hide },
     {"show",                "()V",  (void*)Surface_show },
diff --git a/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png
index 81829bb..769463b 100644
--- a/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png
index 0436801..88f11dc 100644
--- a/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png
index 6574c8c..7305047 100644
--- a/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png
index 1565ac2..712a551 100644
--- a/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png
index 77d4c4b..bf3b943 100644
--- a/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png
index 0fc8632..cbbaec5 100644
--- a/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png b/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png
index 74540c1..af917e5 100644
--- a/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png b/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png
index 69bcd7a..2d59f35 100644
--- a/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png
index 9f8829f..0520e5a 100644
--- a/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png
index d63e85e..42528b1 100644
--- a/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png
index 9cff5d8..e3e3f93 100644
--- a/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png
index 12a6454..1e39572 100644
--- a/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png
index 3355d30..a16db85 100644
--- a/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png
index a5e8570..0eff695 100644
--- a/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png b/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png
index c49412a..219b170 100644
--- a/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png
+++ b/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png
index fabc252..0d165bb 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png
index f59ef4e..73c7e25 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png
index c87c5a6..1459eee 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png
index 6e4cae2..04de530 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png
index e6f83cc..bab70fa 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png
index 5c97e3f..b46adfa 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png
index 269def4..1d836f6 100644
--- a/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png
index 49bcfb4..5818666 100644
--- a/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png
index b7ddc14..564fb34 100644
--- a/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png
index af80ad5..ae21b76 100644
--- a/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
index 1e69eac..5f2f604 100644
--- a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
index 2f6accc..7f3459c 100644
--- a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png
index b3d51ed..b229367 100644
--- a/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png
index abae537..c65f443 100644
--- a/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png
index 4c98afb..0706c8a 100644
--- a/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png
index de8010a..d814d02 100644
--- a/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png
index ecb2a0e..b139c8e 100644
--- a/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png
index 56d27a8..743d00b 100644
--- a/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png b/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png
index 98b4956..17c1fb9 100644
--- a/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png b/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png
index dcd3703..ddfc8e3 100644
--- a/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png
index aa0a3a0..007a4b2 100644
--- a/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png
index cf17967..a823841 100644
--- a/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png
index ab0003b..ad6e1a4 100644
--- a/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png
index 5f1eb1e..0ad6c88 100644
--- a/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png
index 89822b6..19b50ab 100644
--- a/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png
index bd9921f..ad980b1 100644
--- a/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png b/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png
index 8d93926..60e6c52 100644
--- a/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png
+++ b/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png
index d00d235..9e936b3 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png
index d7b20493..0360104 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png
index 59c1580..dd947d2 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png
index 143564a..51cfca2 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png
index e5b9cfd..fd6e6c7 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png
index ec43f85..5db212c 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png
index 0e4b28e..d8f1c8b 100644
--- a/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png
index e449090..31e4989 100644
--- a/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png
index d0ebc1a..7c2cbe5 100644
--- a/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png
index c2dd608..30cbdc1 100644
--- a/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
index 135ca6e..1d2592a 100644
--- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
index ccbf143..4060afe 100644
--- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png
index 506cd68ff..5753346 100644
--- a/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png
index c185112..7e6c047 100644
--- a/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png
index 8ed7f9b..8155fe8 100644
--- a/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png
index c4694cd..6cee9a1 100644
--- a/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png
index 57f5cfa..fa4d76a 100644
--- a/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png
index d16e50cc..6622cba 100644
--- a/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png
index 45cc807..c427297 100644
--- a/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png b/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png
index a9e0d4d..d0df29d 100644
--- a/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png
index 5ea235d..a0d9c1b 100644
--- a/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png
index 0220c8d..16b9bef 100644
--- a/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png
index be13077..d36f99f 100644
--- a/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png
index ab02e7a..5ad475d 100644
--- a/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png
index 0b5a24e..6ade5ee 100644
--- a/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png
index 6d21429..719b923 100644
--- a/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png
index c34c46f..6da264d 100644
--- a/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png
+++ b/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png
index a338924..01efef4 100644
--- a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png
index 6f2d3e5..c287605 100644
--- a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png
index faa36b0..9a496e8 100644
--- a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png
index c57f3ff..e2a38b4 100644
--- a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png
index 9d6402f..911722f 100644
--- a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png
index bfd966c2..da169bf 100644
--- a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png
index 2bd8cee..0bd0980 100644
--- a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png
index d254e77..43ed26d 100644
--- a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
index 919eac0..6b31579 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
index 1357105..df0121b 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png
index 81306ca..1ce25e2 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png
index 1f46e7a..dcd6514 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
index f3850d5..3a7c1e1 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
@@ -42,11 +42,8 @@
     <com.android.internal.widget.DigitalClock android:id="@+id/time"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true"
         android:layout_marginTop="8dip"
         android:layout_marginBottom="8dip"
-        android:layout_marginLeft="-10dip"
         >
 
         <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
index f4c99f1..c02341e 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
@@ -45,8 +45,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="8dip"
-        android:layout_marginBottom="8dip"
-        android_layout_marginLeft="-10dip">
+        android:layout_marginBottom="8dip">
 
         <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
         top of the other. Hence the redundant layout... -->
diff --git a/core/res/res/layout/action_bar_title_item.xml b/core/res/res/layout/action_bar_title_item.xml
index 0828402..4c74f6a 100644
--- a/core/res/res/layout/action_bar_title_item.xml
+++ b/core/res/res/layout/action_bar_title_item.xml
@@ -16,7 +16,7 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="wrap_content"
-              android:layout_height="match_parent"
+              android:layout_height="wrap_content"
               android:orientation="horizontal"
               android:paddingRight="16dip"
               android:background="?android:attr/actionBarItemBackground"
@@ -41,6 +41,8 @@
         <TextView android:id="@+id/action_bar_subtitle"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
+                  android:layout_marginTop="@dimen/action_bar_subtitle_top_margin"
+                  android:layout_marginBottom="@dimen/action_bar_subtitle_bottom_margin"
                   android:singleLine="true"
                   android:ellipsize="end"
                   android:visibility="gone" />
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 02bb3c8..6f96852 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -35,6 +35,10 @@
     <dimen name="action_bar_title_text_size">16dp</dimen>
     <!-- Text size for action bar subtitles -->
     <dimen name="action_bar_subtitle_text_size">12dp</dimen>
+    <!-- Top margin for action bar subtitles -->
+    <dimen name="action_bar_subtitle_top_margin">-2dp</dimen>
+    <!-- Bottom margin for action bar subtitles -->
+    <dimen name="action_bar_subtitle_bottom_margin">4dip</dimen>
 
     <!-- Size of clock font in LockScreen on Unsecure unlock screen. -->
     <dimen name="keyguard_lockscreen_clock_font_size">70sp</dimen>
diff --git a/core/res/res/values-sw600dp/colors.xml b/core/res/res/values-sw600dp/colors.xml
index 6b5a55a..edd2712 100644
--- a/core/res/res/values-sw600dp/colors.xml
+++ b/core/res/res/values-sw600dp/colors.xml
@@ -21,7 +21,7 @@
     <!-- keyguard clock -->
     <color name="lockscreen_clock_background">#b3ffffff</color>
     <color name="lockscreen_clock_foreground">#7affffff</color>
-    <color name="lockscreen_clock_am_pm">#ff9a9a9a</color>
+    <color name="lockscreen_clock_am_pm">#ffffffff</color>
     <color name="lockscreen_owner_info">#ff9a9a9a</color>
 
 </resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 792066e..2daaaa2 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -37,6 +37,10 @@
     <dimen name="action_bar_title_text_size">18dp</dimen>
     <!-- Text size for action bar subtitles -->
     <dimen name="action_bar_subtitle_text_size">14dp</dimen>
+    <!-- Top margin for action bar subtitles -->
+    <dimen name="action_bar_subtitle_top_margin">-3dp</dimen>
+    <!-- Bottom margin for action bar subtitles -->
+    <dimen name="action_bar_subtitle_bottom_margin">9dip</dimen>
 
     <!-- Size of clock font in LockScreen. -->
     <dimen name="keyguard_pattern_unlock_clock_font_size">98sp</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8c0d826..fed5651 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -158,11 +158,14 @@
         <!-- The underline color and thickness for misspelled suggestion -->
         <attr name="textAppearanceMisspelledSuggestion" format="reference" />
 
+        <!-- The text color and the background for suggestion range span. This span identifies the
+        portion of text the suggestions refer to). -->
+        <attr name="textAppearanceSuggestionRange" format="reference" />
+
         <!--  The underline color -->
         <attr name="textUnderlineColor" format="reference|color" />
-        <!--  The underline thickness, expressed as a percentage of the default underline thickness
-              (i.e., 100 means default thickness, and 200 means double thickness). -->
-        <attr name="textUnderlineThicknessPercentage" format="reference|integer" />
+        <!--  The underline thickness -->
+        <attr name="textUnderlineThickness" format="reference|dimension" />
 
         <!-- EditText text foreground color. -->
         <attr name="editTextColor" format="reference|color" />
@@ -3149,7 +3152,11 @@
     </declare-styleable>
     <declare-styleable name="SuggestionSpan">
         <attr name="textUnderlineColor" />
-        <attr name="textUnderlineThicknessPercentage" />
+        <attr name="textUnderlineThickness" />
+    </declare-styleable>
+    <declare-styleable name="SuggestionRangeSpan">
+        <attr name="textColor" />
+        <attr name="colorBackground" />
     </declare-styleable>
     <!-- An <code>input-extras</code> is a container for extra data to supply to
          an input method.  Contains
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 62a2187..2753eab 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -146,6 +146,10 @@
     <dimen name="action_bar_title_text_size">18dp</dimen>
     <!-- Text size for action bar subtitles -->
     <dimen name="action_bar_subtitle_text_size">14dp</dimen>
+    <!-- Top margin for action bar subtitles -->
+    <dimen name="action_bar_subtitle_top_margin">-3dp</dimen>
+    <!-- Bottom margin for action bar subtitles -->
+    <dimen name="action_bar_subtitle_bottom_margin">5dip</dimen>
 
     <!-- Size of clock font in LockScreen on Unsecure unlock screen. -->
     <dimen name="keyguard_lockscreen_clock_font_size">80sp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index de10825..a9e2971 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1763,7 +1763,7 @@
                                           --> <skip />
 
     <!-- On the keyguard screen, it shows the carrier the phone is connected to.  This is displayed if the phone is not connected to a carrier.-->
-    <string name="lockscreen_carrier_default">(No service)</string>
+    <string name="lockscreen_carrier_default">No service.</string>
 
     <!-- Shown in the lock screen to tell the user that the screen is locked. -->
     <string name="lockscreen_screen_locked">Screen locked.</string>
@@ -2472,6 +2472,9 @@
     <!-- Item on EditText context menu. This action is used to replace the current word by other suggested words, suggested by the IME or the spell checker -->
     <string name="replace">Replace\u2026</string>
 
+    <!-- Item on EditText pop-up window. This action is used to delete the text that the user recently added. [CHAR LIMIT=15] -->
+    <string name="delete">Delete</string>
+
     <!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
     <string name="copyUrl">Copy URL</string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 052a040..1a2ad05 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -244,7 +244,7 @@
     </style>
 
     <style name="TextAppearance.Suggestion">
-        <item name="android:textUnderlineThicknessPercentage">200</item>
+        <item name="android:textUnderlineThickness">2dip</item>
     </style>
 
     <style name="TextAppearance.EasyCorrectSuggestion" parent="TextAppearance.Suggestion">
@@ -255,6 +255,11 @@
         <item name="android:textUnderlineColor">@color/holo_red_light</item>
     </style>
 
+    <style name="TextAppearance.SuggestionRange">
+        <item name="android:textColor">@color/white</item>
+        <item name="android:colorBackground">@color/holo_blue_dark</item>
+    </style>
+
     <!-- Widget Styles -->
 
     <style name="Widget">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f434ce8..899c9d5 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -91,6 +91,7 @@
 
         <item name="textAppearanceEasyCorrectSuggestion">@android:style/TextAppearance.EasyCorrectSuggestion</item>
         <item name="textAppearanceMisspelledSuggestion">@android:style/TextAppearance.MisspelledSuggestion</item>
+        <item name="textAppearanceSuggestionRange">@android:style/TextAppearance.SuggestionRange</item>
 
         <item name="textAppearanceButton">@android:style/TextAppearance.Widget.Button</item>
         
diff --git a/data/fonts/DroidSansArmenian.ttf b/data/fonts/DroidSansArmenian.ttf
new file mode 100644
index 0000000..62f67e0
--- /dev/null
+++ b/data/fonts/DroidSansArmenian.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
index 03ceae5..ba9d76f 100644
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackFull.ttf b/data/fonts/DroidSansFallbackFull.ttf
new file mode 100644
index 0000000..03ceae5
--- /dev/null
+++ b/data/fonts/DroidSansFallbackFull.ttf
Binary files differ
diff --git a/data/fonts/DroidSansGeorgian.ttf b/data/fonts/DroidSansGeorgian.ttf
new file mode 100644
index 0000000..743ae66
--- /dev/null
+++ b/data/fonts/DroidSansGeorgian.ttf
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index d8c1fa2..cacbdaa 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -15,10 +15,10 @@
 # Warning: this is actually a product definition, to be inherited from
 
 PRODUCT_COPY_FILES := \
-	frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \
-	frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
-	frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
-	frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
+    frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \
+    frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
+    frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
+    frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
     frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \
     frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \
     frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index 97df84f..f87e1f6 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -1,16 +1,16 @@
 ndk=true
 
-ndk.win_download=android-ndk-r6-windows.zip
-ndk.win_bytes=67642809
-ndk.win_checksum=9c7d5ccc02151a3e5e950c70dc05ac6d
+ndk.win_download=android-ndk-r6b-windows.zip
+ndk.win_bytes=67670219
+ndk.win_checksum=f496b48fffb6d341303de170a081b812
 
-ndk.mac_download=android-ndk-r6-darwin-x86.tar.bz2
-ndk.mac_bytes=52682746
-ndk.mac_checksum=a154905e49a6246abd823b75b6eda738
+ndk.mac_download=android-ndk-r6b-darwin-x86.tar.bz2
+ndk.mac_bytes=52798843
+ndk.mac_checksum=65f2589ac1b08aabe3183f9ed1a8ce8e
 
-ndk.linux_download=android-ndk-r6-linux-x86.tar.bz2
-ndk.linux_bytes=46425290
-ndk.linux_checksum=ff0a43085fe206696d5cdcef3f4f4637
+ndk.linux_download=android-ndk-r6b-linux-x86.tar.bz2
+ndk.linux_bytes=46532436
+ndk.linux_checksum=309f35e49b64313cfb20ac428df4cec2
 
 page.title=Android NDK
 @jd:body
@@ -58,10 +58,42 @@
 }
 </style>
 
-
 <div class="toggleable open">
   <a href="#" onclick="return toggleDiv(this)"><img src=
   "{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px">
+  Android NDK, Revision 6b</a> <em>(August 2011)</em>
+
+   <div class="toggleme">
+      <p>This release of the NDK does not include any new features compared to r6. The r6b release
+      addresses the following issues in the r6 release:</p>
+      <dl>
+        <dt>Important bug fixes</dt>
+        <dd>
+          <ul>
+            <li>Fixed the build when <code>APP_ABI="armeabi x86"</code> is used for
+            multi-architecture builds.</li>
+            <li>Fixed the location of prebuilt STLport binaries in the NDK release package.
+            A bug in the packaging script placed them in the wrong location.</li>
+            <li>Fixed <code>atexit()</code> usage in shared libraries with the x86standalone
+            toolchain.</li>
+            <li>Fixed <code>make-standalone-toolchain.sh --arch=x86</code>. It used to fail
+            to copy the proper GNU libstdc++ binaries to the right location.</li>
+            <li>Fixed the standalone toolchain linker warnings about missing the definition and
+            size for the <code>__dso_handle</code> symbol (ARM only).</li>
+            <li>Fixed the inclusion order of <code>$(SYSROOT)/usr/include</code> for x86 builds.
+            See the <a href="http://code.google.com/p/android/issues/detail?id=18540">bug</a> for
+            more information.</li>
+            <li>Fixed the definitions of <code>ptrdiff_t</code> and <code>size_t</code> in
+            x86-specific systems when they are used with the x86 standalone toolchain.</li>
+          </ul>
+        </dd>
+      </dl>
+  </div>
+</div>
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)"><img src=
+  "{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px">
   Android NDK, Revision 6</a> <em>(July 2011)</em>
 
    <div class="toggleme">
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 1b1fc8d..a00ca12 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -183,7 +183,7 @@
       <span style="display:none" class="zh-TW"></span>
     </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r6</a> 
+      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r6b</a> 
         <span class="new">new!</span>
         </li>
       <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b4d94f3..896f81e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1349,7 +1349,7 @@
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
-        if ((index | count) < 0 || index + count > text.length) {
+        if (index < 0 || text.length - index < Math.abs(count)) {
             throw new ArrayIndexOutOfBoundsException();
         }
 
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 94a8488..7c4e147 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -55,7 +55,7 @@
 
     private void init() {
         final AnimatedRotateState state = mState;
-        mIncrement = 360.0f / (float) state.mFramesCount;
+        mIncrement = 360.0f / state.mFramesCount;
         final Drawable drawable = state.mDrawable;
         if (drawable != null) {
             drawable.setFilterBitmap(true);
@@ -65,6 +65,7 @@
         }
     }
 
+    @Override
     public void draw(Canvas canvas) {
         int saveCount = canvas.save();
 
@@ -146,14 +147,17 @@
                 | mState.mDrawable.getChangingConfigurations();
     }
     
+    @Override
     public void setAlpha(int alpha) {
         mState.mDrawable.setAlpha(alpha);
     }
 
+    @Override
     public void setColorFilter(ColorFilter cf) {
         mState.mDrawable.setColorFilter(cf);
     }
 
+    @Override
     public int getOpacity() {
         return mState.mDrawable.getOpacity();
     }
@@ -228,10 +232,10 @@
         tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
         final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION;
         final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
-        
-        final int framesCount = a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12);
-        final int frameDuration = a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150);
-        
+
+        setFramesCount(a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12));
+        setFramesDuration(a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150));
+
         final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0);
         Drawable drawable = null;
         if (res > 0) {
@@ -265,8 +269,6 @@
         rotateState.mPivotX = pivotX;
         rotateState.mPivotYRel = pivotYRel;
         rotateState.mPivotY = pivotY;
-        rotateState.mFramesCount = framesCount;
-        rotateState.mFrameDuration = frameDuration;
 
         init();
 
@@ -275,6 +277,15 @@
         }
     }
 
+    public void setFramesCount(int framesCount) {
+        mState.mFramesCount = framesCount;
+        mIncrement = 360.0f / mState.mFramesCount;
+    }
+
+    public void setFramesDuration(int framesDuration) {
+        mState.mFrameDuration = framesDuration;
+    }
+
     @Override
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index e0d7898..6a15f6e 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -185,6 +185,10 @@
     static status_t unregisterEffect(int id);
     static status_t setEffectEnabled(int id, bool enabled);
 
+    // clear stream to output mapping cache (gStreamOutputMap)
+    // and output configuration cache (gOutputs)
+    static void clearAudioConfigCache();
+
     static const sp<IAudioPolicyService>& get_audio_policy_service();
 
     // ----------------------------------------------------------------------------
@@ -236,7 +240,8 @@
 
     // mapping between stream types and outputs
     static DefaultKeyedVector<int, audio_io_handle_t> gStreamOutputMap;
-    // list of output descritor containing cached parameters (sampling rate, framecount, channel count...)
+    // list of output descriptors containing cached parameters
+    // (sampling rate, framecount, channel count...)
     static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
 };
 
diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h
index 74d54d1..f7f0ed7 100644
--- a/include/media/stagefright/SurfaceMediaSource.h
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -347,6 +347,13 @@
     // encoder
     int mNumFramesEncoded;
 
+    // mFirstFrameTimestamp is the timestamp of the first received frame.
+    // It is used to offset the output timestamps so recording starts at time 0.
+    int64_t mFirstFrameTimestamp;
+    // mStartTimeNs is the start time passed into the source at start, used to
+    // offset timestamps.
+    int64_t mStartTimeNs;
+
     // mFrameAvailableCondition condition used to indicate whether there
     // is a frame available for dequeuing
     Condition mFrameAvailableCondition;
diff --git a/include/private/surfaceflinger/LayerState.h b/include/private/surfaceflinger/LayerState.h
index d2fed41..3eb5c99 100644
--- a/include/private/surfaceflinger/LayerState.h
+++ b/include/private/surfaceflinger/LayerState.h
@@ -54,8 +54,8 @@
             };
             SurfaceID       surface;
             uint32_t        what;
-            int32_t         x;
-            int32_t         y;
+            float           x;
+            float           y;
             uint32_t        z;
             uint32_t        w;
             uint32_t        h;
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 7fbbfb24..ace0735 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -145,7 +145,7 @@
     status_t    setAlpha(SurfaceID id, float alpha=1.0f);
     status_t    setFreezeTint(SurfaceID id, uint32_t tint);
     status_t    setMatrix(SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy);
-    status_t    setPosition(SurfaceID id, int32_t x, int32_t y);
+    status_t    setPosition(SurfaceID id, float x, float y);
     status_t    setSize(SurfaceID id, uint32_t w, uint32_t h);
     status_t    destroySurface(SurfaceID sid);
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 3b0ffea..00a4bf6 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -102,7 +102,7 @@
 public:
 
     status_t setPosition(const sp<SurfaceComposerClient>& client, SurfaceID id,
-            int32_t x, int32_t y);
+            float x, float y);
     status_t setSize(const sp<SurfaceComposerClient>& client, SurfaceID id,
             uint32_t w, uint32_t h);
     status_t setLayer(const sp<SurfaceComposerClient>& client, SurfaceID id,
@@ -161,7 +161,7 @@
 }
 
 status_t Composer::setPosition(const sp<SurfaceComposerClient>& client,
-        SurfaceID id, int32_t x, int32_t y) {
+        SurfaceID id, float x, float y) {
     Mutex::Autolock _l(mLock);
     layer_state_t* s = getLayerStateLocked(client, id);
     if (!s)
@@ -372,7 +372,7 @@
     return getComposer().setFreezeTint(this, id, tint);
 }
 
-status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) {
+status_t SurfaceComposerClient::setPosition(SurfaceID id, float x, float y) {
     return getComposer().setPosition(this, id, x, y);
 }
 
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 0308af3..55ac133 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -1,4 +1,4 @@
-# Build the unit tests.
+# Build the unit tests,
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -22,17 +22,15 @@
 	libui \
 	libutils \
 
-LOCAL_STATIC_LIBRARIES := \
-	libgtest \
-	libgtest_main \
-
 LOCAL_C_INCLUDES := \
     bionic \
     bionic/libstdc++/include \
     external/gtest/include \
     external/stlport/stlport \
 
-include $(BUILD_EXECUTABLE)
+# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+# to integrate with auto-test framework.
+include $(BUILD_NATIVE_TEST)
 
 # Include subdirectory makefiles
 # ============================================================
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 9acf99b..dd05e61 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -394,14 +394,14 @@
 
 bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
     // If the glyph is too tall, don't cache it
-    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+    if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
         if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
             // Default cache not large enough for large glyphs - resize cache to
             // max size and try again
             flushAllAndInvalidate();
             initTextTexture(true);
         }
-        if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+        if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
             LOGE("Font size to large to fit in cache. width, height = %i, %i",
                     (int) glyph.fWidth, (int) glyph.fHeight);
             return false;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index dbe4115..ddd9d1f 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1558,21 +1558,29 @@
     private boolean checkForRingerModeChange(int oldIndex, int direction) {
         boolean adjustVolumeIndex = true;
         int newRingerMode = mRingerMode;
+        int uiIndex = (oldIndex + 5) / 10;
 
         if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
-            // audible mode, at the bottom of the scale
-            if ((direction == AudioManager.ADJUST_LOWER &&
-                 mPrevVolDirection != AudioManager.ADJUST_LOWER) &&
-                ((oldIndex + 5) / 10 == 0)) {
-                // "silent mode", but which one?
-                newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
-                    ? AudioManager.RINGER_MODE_VIBRATE
-                    : AudioManager.RINGER_MODE_SILENT;
+            if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) {
+                // enter silent mode if current index is the last audible one and not repeating a
+                // volume key down
+                if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
+                    // "silent mode", but which one?
+                    newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
+                        ? AudioManager.RINGER_MODE_VIBRATE
+                        : AudioManager.RINGER_MODE_SILENT;
+                }
+                if (uiIndex == 0) {
+                    adjustVolumeIndex = false;
+                }
             }
         } else {
             if (direction == AudioManager.ADJUST_RAISE) {
                 // exiting silent mode
                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
+                if (uiIndex != 0) {
+                    adjustVolumeIndex = false;
+                }
             } else {
                 // prevent last audible index to reach 0
                 adjustVolumeIndex = false;
@@ -1581,13 +1589,6 @@
 
         if (newRingerMode != mRingerMode) {
             setRingerMode(newRingerMode);
-
-            /*
-             * If we are changing ringer modes, do not increment/decrement the
-             * volume index. Instead, the handler for the message above will
-             * take care of changing the index.
-             */
-            adjustVolumeIndex = false;
         }
 
         mPrevVolDirection = direction;
@@ -2637,7 +2638,7 @@
                 notifyTopOfAudioFocusStack();
                 // there's a new top of the stack, let the remote control know
                 synchronized(mRCStack) {
-                    checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                 }
             }
         } else {
@@ -2680,7 +2681,7 @@
             notifyTopOfAudioFocusStack();
             // there's a new top of the stack, let the remote control know
             synchronized(mRCStack) {
-                checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
             }
         }
     }
@@ -2784,7 +2785,7 @@
 
             // there's a new top of the stack, let the remote control know
             synchronized(mRCStack) {
-                checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
             }
         }//synchronized(mAudioFocusLock)
 
@@ -3182,7 +3183,7 @@
      * Helper function:
      * Called synchronized on mRCStack
      */
-    private void clearRemoteControlDisplay() {
+    private void clearRemoteControlDisplay_syncRcs() {
         synchronized(mCurrentRcLock) {
             mCurrentRcClient = null;
         }
@@ -3193,16 +3194,16 @@
     /**
      * Helper function:
      * Called synchronized on mRCStack
-     * mRCStack.empty() is false
+     * mRCStack.isEmpty() is false
      */
-    private void updateRemoteControlDisplay(int infoChangedFlags) {
+    private void updateRemoteControlDisplay_syncRcs(int infoChangedFlags) {
         RemoteControlStackEntry rcse = mRCStack.peek();
         int infoFlagsAboutToBeUsed = infoChangedFlags;
         // this is where we enforce opt-in for information display on the remote controls
         //   with the new AudioManager.registerRemoteControlClient() API
         if (rcse.mRcClient == null) {
             //Log.w(TAG, "Can't update remote control display with null remote control client");
-            clearRemoteControlDisplay();
+            clearRemoteControlDisplay_syncRcs();
             return;
         }
         synchronized(mCurrentRcLock) {
@@ -3225,11 +3226,11 @@
      *     that has changed, if applicable (checking for the update conditions might trigger a
      *     clear, rather than an update event).
      */
-    private void checkUpdateRemoteControlDisplay(int infoChangedFlags) {
+    private void checkUpdateRemoteControlDisplay_syncRcs(int infoChangedFlags) {
         // determine whether the remote control display should be refreshed
         // if either stack is empty, there is a mismatch, so clear the RC display
         if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
-            clearRemoteControlDisplay();
+            clearRemoteControlDisplay_syncRcs();
             return;
         }
         // if the top of the two stacks belong to different packages, there is a mismatch, clear
@@ -3237,17 +3238,18 @@
                 && (mFocusStack.peek().mPackageName != null)
                 && !(mRCStack.peek().mCallingPackageName.compareTo(
                         mFocusStack.peek().mPackageName) == 0)) {
-            clearRemoteControlDisplay();
+            clearRemoteControlDisplay_syncRcs();
             return;
         }
         // if the audio focus didn't originate from the same Uid as the one in which the remote
         //   control information will be retrieved, clear
         if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
-            clearRemoteControlDisplay();
+            clearRemoteControlDisplay_syncRcs();
             return;
         }
         // refresh conditions were verified: update the remote controls
-        updateRemoteControlDisplay(infoChangedFlags);
+        // ok to call, mRCStack is not empty
+        updateRemoteControlDisplay_syncRcs(infoChangedFlags);
     }
 
     /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
@@ -3258,7 +3260,7 @@
             synchronized(mRCStack) {
                 pushMediaButtonReceiver(eventReceiver);
                 // new RC client, assume every type of information shall be queried
-                checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
             }
         }
     }
@@ -3273,7 +3275,7 @@
                 removeMediaButtonReceiver(eventReceiver);
                 if (topOfStackWillChange) {
                     // current RC client will change, assume every type of info needs to be queried
-                    checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                 }
             }
         }
@@ -3329,7 +3331,7 @@
                 // if the eventReceiver is at the top of the stack
                 // then check for potential refresh of the remote controls
                 if (isCurrentRcController(eventReceiver)) {
-                    checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                 }
             }
         }
@@ -3426,7 +3428,9 @@
     }
 
     /**
-     * Register an IRemoteControlDisplay and notify all IRemoteControlClient of the new display.
+     * Register an IRemoteControlDisplay.
+     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
+     * at the top of the stack to update the new display with its information.
      * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
      * @param rcd the IRemoteControlDisplay to register. No effect if null.
      */
@@ -3458,19 +3462,9 @@
                 }
             }
 
-            // we have a new display, tell the current client that it needs to send info
-            // (following lock order: mRCStack then mCurrentRcLock)
-            synchronized(mCurrentRcLock) {
-                if (mCurrentRcClient != null) {
-                    // tell the current client that it needs to send info
-                    try {
-                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
-                                RC_INFO_ALL, mArtworkExpectedWidth, mArtworkExpectedHeight);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Current valid remote client is dead: "+e);
-                        mCurrentRcClient = null;
-                    }
-                }
+            if (!mRCStack.isEmpty()) {
+                // we have a new display, of which all the clients are now aware: have it be updated
+                updateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
             }
         }
     }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 66bd56a..1ee9a1f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -381,7 +381,7 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
- * <tr><td>setTexture </p></td>
+ * <tr><td>setSurface </p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
@@ -608,7 +608,7 @@
      * portion of the media.
      *
      * Either a surface holder or surface must be set if a display or video sink
-     * is needed.  Not calling this method or {@link #setTexture(SurfaceTexture)}
+     * is needed.  Not calling this method or {@link #setSurface(Surface)}
      * when playing back a video will result in only the audio track being played.
      * A null surface holder or surface will result in only the audio track being
      * played.
@@ -629,14 +629,21 @@
 
     /**
      * Sets the {@link Surface} to be used as the sink for the video portion of
-     * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but does not
-     * support {@link #setScreenOnWhilePlaying(boolean)} or {@link #updateSurfaceScreenOn()}.
-     * Setting a Surface will un-set any Surface or SurfaceHolder that was previously set.
+     * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but
+     * does not support {@link #setScreenOnWhilePlaying(boolean)}.  Setting a
+     * Surface will un-set any Surface or SurfaceHolder that was previously set.
      * A null surface will result in only the audio track being played.
      *
-     * @param surface The {@link Surface} to be used for the video portion of the media.
+     * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
+     * returned from {@link SurfaceTexture#getTimestamp()} will have an
+     * unspecified zero point.  These timestamps cannot be directly compared
+     * between different media sources, different instances of the same media
+     * source, or multiple runs of the same program.  The timestamp is normally
+     * monotonically increasing and is unaffected by time-of-day adjustments,
+     * but it is reset when the position is set.
      *
-     * @hide Pending review by API council.
+     * @param surface The {@link Surface} to be used for the video portion of
+     * the media.
      */
     public void setSurface(Surface surface) {
         if (mScreenOnWhilePlaying && surface != null) {
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index d59bc2b..d7b85e4 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -22,6 +22,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.RectF;
+import android.media.MediaMetadataRetriever;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -29,6 +30,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.IllegalArgumentException;
 import java.util.HashMap;
 
 /**
@@ -236,6 +238,23 @@
         mEventHandler = new EventHandler(this, looper);
     }
 
+    private static final int[] METADATA_KEYS_TYPE_STRING = {
+        MediaMetadataRetriever.METADATA_KEY_ALBUM,
+        MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
+        MediaMetadataRetriever.METADATA_KEY_TITLE,
+        MediaMetadataRetriever.METADATA_KEY_ARTIST,
+        MediaMetadataRetriever.METADATA_KEY_AUTHOR,
+        MediaMetadataRetriever.METADATA_KEY_COMPILATION,
+        MediaMetadataRetriever.METADATA_KEY_COMPOSER,
+        MediaMetadataRetriever.METADATA_KEY_DATE,
+        MediaMetadataRetriever.METADATA_KEY_GENRE,
+        MediaMetadataRetriever.METADATA_KEY_TITLE,
+        MediaMetadataRetriever.METADATA_KEY_WRITER };
+    private static final int[] METADATA_KEYS_TYPE_LONG = {
+        MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+        MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
+        MediaMetadataRetriever.METADATA_KEY_DURATION };
+
     /**
      * Class used to modify metadata in a {@link RemoteControlClient} object.
      */
@@ -256,6 +275,11 @@
         }
 
         /**
+         * The metadata key for the content artwork / album art.
+         */
+        public final static int METADATA_KEY_ARTWORK = 100;
+
+        /**
          * Adds textual information to be displayed.
          * Note that none of the information added after {@link #apply()} has been called,
          * will be displayed.
@@ -265,49 +289,73 @@
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
-         * @param value the text for the given key, or null to signify there is no valid
+         *      .
+         * @param value the text for the given key, or {@code null} to signify there is no valid
          *      information for the field.
          * @return      FIXME description
          */
-        public synchronized MetadataEditor putString(int key, String value) {
+        public synchronized MetadataEditor putString(int key, String value)
+                throws IllegalArgumentException {
             if (mApplied) {
                 Log.e(TAG, "Can't edit a previously applied MetadataEditor");
                 return this;
             }
+            if (!validTypeForKey(key, METADATA_KEYS_TYPE_STRING)) {
+                throw(new IllegalArgumentException("Invalid type 'String' for key "+ key));
+            }
             mEditorMetadata.putString(String.valueOf(key), value);
             mMetadataChanged = true;
             return this;
         }
 
         /**
-         * The metadata key for the content artwork / album art.
+         * FIXME javadoc
+         * @param key the identifier of a the metadata field to set. Valid values are
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value
+         *      expressed in milliseconds),
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+         * @param value FIXME javadoc
+         * @return FIXME javadoc
+         * @throws IllegalArgumentException
          */
-        public final int METADATA_KEY_ARTWORK = 100;
+        public synchronized MetadataEditor putLong(int key, long value)
+                throws IllegalArgumentException {
+            if (mApplied) {
+                Log.e(TAG, "Can't edit a previously applied MetadataEditor");
+                return this;
+            }
+            if (!validTypeForKey(key, METADATA_KEYS_TYPE_LONG)) {
+                throw(new IllegalArgumentException("Invalid type 'long' for key "+ key));
+            }
+            mEditorMetadata.putLong(String.valueOf(key), value);
+            mMetadataChanged = true;
+            return this;
+        }
 
         /**
          * Sets the album / artwork picture to be displayed on the remote control.
          * @param key FIXME description
          * @param bitmap the bitmap for the artwork, or null if there isn't any.
          * @return FIXME description
+         * @throws IllegalArgumentException
          * @see android.graphics.Bitmap
          */
-        public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) {
+        public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap)
+                throws IllegalArgumentException {
             if (mApplied) {
                 Log.e(TAG, "Can't edit a previously applied MetadataEditor");
                 return this;
             }
             if (key != METADATA_KEY_ARTWORK) {
-                return this;
+                throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key));
             }
             if ((mArtworkExpectedWidth > 0) && (mArtworkExpectedHeight > 0)) {
                 mEditorArtwork = scaleBitmapIfTooBig(bitmap,
@@ -740,6 +788,24 @@
             }
         }
         return bitmap;
+    }
 
+    /**
+     *  Fast routine to go through an array of allowed keys and return whether the key is part
+     *  of that array
+     * @param key the key value
+     * @param validKeys the array of valid keys for a given type
+     * @return true if the key is part of the array, false otherwise
+     */
+    private static boolean validTypeForKey(int key, int[] validKeys) {
+        try {
+            for (int i = 0 ; ; i++) {
+                if (key == validKeys[i]) {
+                    return true;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return false;
+        }
     }
 }
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index bb91fa9..853a5f6 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -727,6 +727,14 @@
 }
 
 
+void AudioSystem::clearAudioConfigCache()
+{
+    Mutex::Autolock _l(gLock);
+    LOGV("clearAudioConfigCache()");
+    gStreamOutputMap.clear();
+    gOutputs.clear();
+}
+
 // ---------------------------------------------------------------------------
 
 void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who) {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index cecedb5..3b6c64d 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1164,6 +1164,10 @@
         cblk->cv.broadcast();
         cblk->lock.unlock();
 
+        // refresh the audio configuration cache in this process to make sure we get new
+        // output parameters in getOutput_l() and createTrack_l()
+        AudioSystem::clearAudioConfigCache();
+
         // if the new IAudioTrack is created, createTrack_l() will modify the
         // following member variables: mAudioTrack, mCblkMemory and mCblk.
         // It will also delete the strong references on previous IAudioTrack and IMemory
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index a6a3a18..f41e9d2 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -42,7 +42,7 @@
 
 void NuPlayer::StreamingSource::start() {
     mStreamListener = new NuPlayerStreamListener(mSource, 0);
-    mTSParser = new ATSParser;
+    mTSParser = new ATSParser(ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
 
     mStreamListener->start();
 }
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index a5a6b64..52b1200 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -33,8 +33,6 @@
 
 namespace android {
 
-#define ADTS_HEADER_LENGTH 7
-
 class AACSource : public MediaSource {
 public:
     AACSource(const sp<DataSource> &source,
@@ -88,7 +86,16 @@
     return 0;
 }
 
-static size_t getFrameSize(const sp<DataSource> &source, off64_t offset) {
+// Returns the frame length in bytes as described in an ADTS header starting at the given offset,
+//     or 0 if the size can't be read due to an error in the header or a read failure.
+// The returned value is the AAC frame size with the ADTS header length (regardless of
+//     the presence of the CRC).
+// If headerSize is non-NULL, it will be used to return the size of the header of this ADTS frame.
+static size_t getAdtsFrameLength(const sp<DataSource> &source, off64_t offset, size_t* headerSize) {
+
+    const size_t kAdtsHeaderLengthNoCrc = 7;
+    const size_t kAdtsHeaderLengthWithCrc = 9;
+
     size_t frameSize = 0;
 
     uint8_t syncword[2];
@@ -111,7 +118,15 @@
     }
 
     frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5;
-    frameSize += ADTS_HEADER_LENGTH + protectionAbsent ? 0 : 2;
+
+    // protectionAbsent is 0 if there is CRC
+    size_t headSize = protectionAbsent ? kAdtsHeaderLengthNoCrc : kAdtsHeaderLengthWithCrc;
+    if (headSize > frameSize) {
+        return 0;
+    }
+    if (headerSize != NULL) {
+        *headerSize = headSize;
+    }
 
     return frameSize;
 }
@@ -148,7 +163,7 @@
 
     if (mDataSource->getSize(&streamSize) == OK) {
          while (offset < streamSize) {
-            if ((frameSize = getFrameSize(source, offset)) == 0) {
+            if ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) {
                 return;
             }
 
@@ -268,8 +283,8 @@
         }
     }
 
-    size_t frameSize, frameSizeWithoutHeader;
-    if ((frameSize = getFrameSize(mDataSource, mOffset)) == 0) {
+    size_t frameSize, frameSizeWithoutHeader, headerSize;
+    if ((frameSize = getAdtsFrameLength(mDataSource, mOffset, &headerSize)) == 0) {
         return ERROR_END_OF_STREAM;
     }
 
@@ -279,8 +294,8 @@
         return err;
     }
 
-    frameSizeWithoutHeader = frameSize - ADTS_HEADER_LENGTH;
-    if (mDataSource->readAt(mOffset + ADTS_HEADER_LENGTH, buffer->data(),
+    frameSizeWithoutHeader = frameSize - headerSize;
+    if (mDataSource->readAt(mOffset + headerSize, buffer->data(),
                 frameSizeWithoutHeader) != (ssize_t)frameSizeWithoutHeader) {
         buffer->release();
         buffer = NULL;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 142dda0..f98b0de 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -513,7 +513,8 @@
     // If we did this later, audio would continue playing while we
     // shutdown the video-related resources and the player appear to
     // not be as responsive to a reset request.
-    if (mAudioPlayer == NULL && mAudioSource != NULL) {
+    if ((mAudioPlayer == NULL || !(mFlags & AUDIOPLAYER_STARTED))
+            && mAudioSource != NULL) {
         // If we had an audio player, it would have effectively
         // taken possession of the audio source and stopped it when
         // _it_ is stopped. Otherwise this is still our responsibility.
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 91b81c2..50dd804 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -46,9 +46,10 @@
                 mSynchronousMode(true),
                 mConnectedApi(NO_CONNECTED_API),
                 mFrameRate(30),
+                mStopped(false),
                 mNumFramesReceived(0),
                 mNumFramesEncoded(0),
-                mStopped(false) {
+                mFirstFrameTimestamp(0) {
     LOGV("SurfaceMediaSource::SurfaceMediaSource");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -471,10 +472,25 @@
         return -EINVAL;
     }
 
+    if (mNumFramesReceived == 0) {
+        mFirstFrameTimestamp = timestamp;
+        // Initial delay
+        if (mStartTimeNs > 0) {
+            if (timestamp < mStartTimeNs) {
+                // This frame predates start of record, discard
+                mSlots[bufIndex].mBufferState = BufferSlot::FREE;
+                mDequeueCondition.signal();
+                return OK;
+            }
+            mStartTimeNs = timestamp - mStartTimeNs;
+        }
+    }
+    timestamp = mStartTimeNs + (timestamp - mFirstFrameTimestamp);
+
+    mNumFramesReceived++;
     if (mSynchronousMode) {
         // in synchronous mode we queue all buffers in a FIFO
         mQueue.push_back(bufIndex);
-        mNumFramesReceived++;
         LOGV("Client queued buf# %d @slot: %d, Q size = %d, handle = %p, timestamp = %lld",
             mNumFramesReceived, bufIndex, mQueue.size(),
             mSlots[bufIndex].mGraphicBuffer->handle, timestamp);
@@ -684,6 +700,13 @@
 status_t SurfaceMediaSource::start(MetaData *params)
 {
     LOGV("started!");
+
+    mStartTimeNs = 0;
+    int64_t startTimeUs;
+    if (params && params->findInt64(kKeyTime, &startTimeUs)) {
+        mStartTimeNs = startTimeUs * 1000;
+    }
+
     return OK;
 }
 
@@ -753,6 +776,7 @@
     mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer;
     int64_t prevTimeStamp = mCurrentTimestamp;
     mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp;
+
     mNumFramesEncoded++;
     // Pass the data to the MediaBuffer. Pass in only the metadata
     passMetadataBufferLocked(buffer);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 5bbc2b4..74a3b32 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -325,14 +325,16 @@
 }
 
 int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
-    if (!mFirstPTSValid) {
-        mFirstPTSValid = true;
-        mFirstPTS = PTS;
-        PTS = 0;
-    } else if (PTS < mFirstPTS) {
-        PTS = 0;
-    } else {
-        PTS -= mFirstPTS;
+    if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) {
+        if (!mFirstPTSValid) {
+            mFirstPTSValid = true;
+            mFirstPTS = PTS;
+            PTS = 0;
+        } else if (PTS < mFirstPTS) {
+            PTS = 0;
+        } else {
+            PTS -= mFirstPTS;
+        }
     }
 
     return (PTS * 100) / 9;
@@ -734,7 +736,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-ATSParser::ATSParser() {
+ATSParser::ATSParser(uint32_t flags)
+    : mFlags(flags) {
 }
 
 ATSParser::~ATSParser() {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 1e6451d..d12d998 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -38,7 +38,16 @@
         DISCONTINUITY_FORMATCHANGE
     };
 
-    ATSParser();
+    enum Flags {
+        // The 90kHz clock (PTS/DTS) is absolute, i.e. PTS=0 corresponds to
+        // a media time of 0.
+        // If this flag is _not_ specified, the first PTS encountered in a
+        // program of this stream will be assumed to correspond to media time 0
+        // instead.
+        TS_TIMESTAMPS_ARE_ABSOLUTE = 1
+    };
+
+    ATSParser(uint32_t flags = 0);
 
     void feedTSPacket(const void *data, size_t size);
 
@@ -73,6 +82,7 @@
     struct Program;
     struct Stream;
 
+    uint32_t mFlags;
     Vector<sp<Program> > mPrograms;
 
     void parseProgramAssociationTable(ABitReader *br);
diff --git a/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java b/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java
index 809ee82..f76cf37 100644
--- a/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java
+++ b/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java
@@ -49,6 +49,7 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.View;
 import android.widget.MediaController;
@@ -569,7 +570,9 @@
             mSurface = new SurfaceTexture(mTextureID);
             mSurface.setOnFrameAvailableListener(this);
 
-            mMediaPlayer.setTexture(mSurface);
+            Surface surface = new Surface(mSurface);
+            mMediaPlayer.setSurface(surface);
+            surface.release();
 
             try {
                 mMediaPlayer.prepare();
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index ed13ace..f3d0bee 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -71,6 +71,7 @@
         </LinearLayout>
 
         <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
             android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 6e6567b..e3ea0de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1060,6 +1060,12 @@
         */
     }
 
+    public void showClock(boolean show) {
+        View clock = mStatusBarView.findViewById(R.id.clock);
+        if (clock != null) {
+            clock.setVisibility(show ? View.VISIBLE : View.GONE);
+        }
+    }
 
     /**
      * State is one or more of the DISABLE constants from StatusBarManager.
@@ -1074,6 +1080,10 @@
                 old, state, diff));
         }
 
+        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
+            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
+            showClock(show);
+        }
         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
                 Slog.d(TAG, "DISABLE_EXPAND: yes");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
index 1e417ac..5911378 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
@@ -428,7 +428,7 @@
 
     private CharSequence getIMIName(InputMethodInfo imi) {
         if (imi == null) return null;
-        return mPackageManager.getApplicationLabel(imi.getServiceInfo().applicationInfo);
+        return imi.loadLabel(mPackageManager);
     }
 
     private CharSequence getSubtypeName(InputMethodInfo imi, InputMethodSubtype subtype) {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 5661116..162381d 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -145,10 +145,10 @@
     private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
 
     /**
-     * Allow the user to operate the status bar when the keyguard is engaged (without a pattern or
-     * password).
+     * Allow the user to expand the status bar when the keyguard is engaged
+     * (without a pattern or password).
      */
-    private static final boolean ENABLE_STATUS_BAR_IN_KEYGUARD = true;
+    private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true;
 
     private Context mContext;
     private AlarmManager mAlarmManager;
@@ -1184,19 +1184,25 @@
                 }
             }
 
-            // if the keyguard is shown, allow the status bar to open only if the keyguard is
-            // insecure and (is covered by another window OR this feature is enabled in general)
-            boolean enable = !mShowing
-                || ((ENABLE_STATUS_BAR_IN_KEYGUARD || mHidden) && !isSecure());
-            if (DEBUG) {
-                Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden
-                    + " isSecure=" + isSecure() + " --> enable=" + enable);
+            int flags = StatusBarManager.DISABLE_NONE;
+            if (mShowing && !mHidden) {
+                // showing lockscreen exclusively; disable various extra
+                // statusbar components.
+                flags |= StatusBarManager.DISABLE_NAVIGATION;
+                flags |= StatusBarManager.DISABLE_CLOCK;
             }
-            mStatusBarManager.disable(enable ?
-                         StatusBarManager.DISABLE_NONE :
-                         ( StatusBarManager.DISABLE_EXPAND
-                         | StatusBarManager.DISABLE_NAVIGATION
-                         | StatusBarManager.DISABLE_CLOCK));
+            if (mShowing && (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND)) {
+                // showing secure lockscreen; disable expanding.
+                flags |= StatusBarManager.DISABLE_EXPAND;
+            }
+
+            if (DEBUG) {
+                Log.d(TAG,
+                        "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden
+                                + " isSecure=" + isSecure() + " --> flags=" + flags);
+            }
+
+            mStatusBarManager.disable(flags);
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index dfd1b00..c988846 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -900,6 +900,7 @@
             lp.setTitle("PointerLocation");
             WindowManager wm = (WindowManager)
                     mContext.getSystemService(Context.WINDOW_SERVICE);
+            lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
             wm.addView(addView, lp);
             
             if (mPointerLocationInputChannel == null) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index d617af8..94efa74 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1362,6 +1362,7 @@
     for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
         mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
         mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
+        mStreamTypes[stream].valid = true;
     }
 }
 
@@ -1530,6 +1531,14 @@
             chain->setStrategy(AudioSystem::getStrategyForStream((audio_stream_type_t)track->type()));
             chain->incTrackCnt();
         }
+
+        // invalidate track immediately if the stream type was moved to another thread since
+        // createTrack() was called by the client process.
+        if (!mStreamTypes[streamType].valid) {
+            LOGW("createTrack_l() on thread %p: invalidating track on stream %d",
+                 this, streamType);
+            android_atomic_or(CBLK_INVALID_ON, &track->mCblk->flags);
+        }
     }
     lStatus = NO_ERROR;
 
@@ -2219,6 +2228,14 @@
     }
 }
 
+void AudioFlinger::PlaybackThread::setStreamValid(int streamType, bool valid)
+{
+    LOGV ("PlaybackThread::setStreamValid() thread %p, streamType %d, valid %d",
+            this,  streamType, valid);
+    Mutex::Autolock _l(mLock);
+
+    mStreamTypes[streamType].valid = valid;
+}
 
 // getTrackName_l() must be called with ThreadBase::mLock held
 int AudioFlinger::MixerThread::getTrackName_l()
@@ -5074,11 +5091,14 @@
     LOGV("setStreamOutput() stream %d to output %d", stream, output);
     audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream);
 
+    dstThread->setStreamValid(stream, true);
+
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
         PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
         if (thread != dstThread &&
             thread->type() != ThreadBase::DIRECT) {
             MixerThread *srcThread = (MixerThread *)thread;
+            srcThread->setStreamValid(stream, false);
             srcThread->invalidateTracks(stream);
         }
     }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 1ceb0ec..2e05593 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -751,14 +751,18 @@
                     virtual uint32_t hasAudioSession(int sessionId);
                     virtual uint32_t getStrategyForSession_l(int sessionId);
 
+                            void setStreamValid(int streamType, bool valid);
+
         struct  stream_type_t {
             stream_type_t()
                 :   volume(1.0f),
-                    mute(false)
+                    mute(false),
+                    valid(true)
             {
             }
             float       volume;
             bool        mute;
+            bool        valid;
         };
 
     protected:
diff --git a/services/input/Android.mk b/services/input/Android.mk
index afbe546..86c6c8a 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_SRC_FILES:= \
     EventHub.cpp \
+    InputApplication.cpp \
     InputDispatcher.cpp \
     InputListener.cpp \
     InputManager.cpp \
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 1d7cc19..06dea36 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -1094,9 +1094,8 @@
 bool EventHub::isExternalDeviceLocked(Device* device) {
     if (device->configuration) {
         bool value;
-        if (device->configuration->tryGetProperty(String8("device.internal"), value)
-                && value) {
-            return false;
+        if (device->configuration->tryGetProperty(String8("device.internal"), value)) {
+            return !value;
         }
     }
     return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH;
diff --git a/services/input/InputApplication.cpp b/services/input/InputApplication.cpp
new file mode 100644
index 0000000..a99e637
--- /dev/null
+++ b/services/input/InputApplication.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputApplication"
+
+#include "InputApplication.h"
+
+#include <cutils/log.h>
+
+namespace android {
+
+// --- InputApplicationHandle ---
+
+InputApplicationHandle::InputApplicationHandle() :
+    mInfo(NULL) {
+}
+
+InputApplicationHandle::~InputApplicationHandle() {
+    delete mInfo;
+}
+
+void InputApplicationHandle::releaseInfo() {
+    if (mInfo) {
+        delete mInfo;
+        mInfo = NULL;
+    }
+}
+
+} // namespace android
diff --git a/services/input/InputApplication.h b/services/input/InputApplication.h
index 8902f7a..67ae94b 100644
--- a/services/input/InputApplication.h
+++ b/services/input/InputApplication.h
@@ -27,14 +27,32 @@
 
 /*
  * Describes the properties of an application that can receive input.
+ */
+struct InputApplicationInfo {
+    String8 name;
+    nsecs_t dispatchingTimeout;
+};
+
+
+/*
+ * Handle for an application that can receive input.
  *
  * Used by the native input dispatcher as a handle for the window manager objects
  * that describe an application.
  */
 class InputApplicationHandle : public RefBase {
 public:
-    String8 name;
-    nsecs_t dispatchingTimeout;
+    inline const InputApplicationInfo* getInfo() const {
+        return mInfo;
+    }
+
+    inline String8 getName() const {
+        return mInfo ? mInfo->name : String8("<invalid>");
+    }
+
+    inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
+        return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+    }
 
     /**
      * Requests that the state of this object be updated to reflect
@@ -45,11 +63,19 @@
      *
      * Returns true on success, or false if the handle is no longer valid.
      */
-    virtual bool update() = 0;
+    virtual bool updateInfo() = 0;
+
+    /**
+     * Releases the storage used by the associated information when it is
+     * no longer needed.
+     */
+    void releaseInfo();
 
 protected:
-    InputApplicationHandle() { }
-    virtual ~InputApplicationHandle() { }
+    InputApplicationHandle();
+    virtual ~InputApplicationHandle();
+
+    InputApplicationInfo* mInfo;
 };
 
 } // namespace android
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index cf167ae..1eb5f0e 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -306,7 +306,12 @@
                     }
                 }
             }
+
+            // Nothing to do if there is no pending event.
             if (! mPendingEvent) {
+                if (mActiveConnections.isEmpty()) {
+                    dispatchIdleLocked();
+                }
                 return;
             }
         } else {
@@ -462,6 +467,16 @@
     }
 }
 
+void InputDispatcher::dispatchIdleLocked() {
+#if DEBUG_FOCUS
+    LOGD("Dispatcher idle.  There are no pending events or active connections.");
+#endif
+
+    // Reset targets when idle, to release input channels and other resources
+    // they are holding onto.
+    resetTargetsLocked();
+}
+
 bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
     bool needWake = mInboundQueue.isEmpty();
     mInboundQueue.enqueueAtTail(entry);
@@ -525,20 +540,21 @@
     size_t numWindows = mWindowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-        int32_t flags = windowHandle->layoutParamsFlags;
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        int32_t flags = windowInfo->layoutParamsFlags;
 
-        if (windowHandle->visible) {
-            if (!(flags & InputWindowHandle::FLAG_NOT_TOUCHABLE)) {
-                bool isTouchModal = (flags & (InputWindowHandle::FLAG_NOT_FOCUSABLE
-                        | InputWindowHandle::FLAG_NOT_TOUCH_MODAL)) == 0;
-                if (isTouchModal || windowHandle->touchableRegionContainsPoint(x, y)) {
+        if (windowInfo->visible) {
+            if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
+                bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
+                        | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+                if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                     // Found window.
                     return windowHandle;
                 }
             }
         }
 
-        if (flags & InputWindowHandle::FLAG_SYSTEM_ERROR) {
+        if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) {
             // Error window is on top but not visible, so touch is dropped.
             return NULL;
         }
@@ -1051,9 +1067,15 @@
             LOGD("Waiting for application to become ready for input: %s",
                     getApplicationWindowLabelLocked(applicationHandle, windowHandle).string());
 #endif
-            nsecs_t timeout = windowHandle != NULL ? windowHandle->dispatchingTimeout :
-                applicationHandle != NULL ?
-                        applicationHandle->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+            nsecs_t timeout;
+            if (windowHandle != NULL) {
+                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+            } else if (applicationHandle != NULL) {
+                timeout = applicationHandle->getDispatchingTimeout(
+                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+            } else {
+                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+            }
 
             mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
             mInputTargetWaitStartTime = currentTime;
@@ -1168,7 +1190,7 @@
     }
 
     // If the currently focused window is paused then keep waiting.
-    if (mFocusedWindowHandle->paused) {
+    if (mFocusedWindowHandle->getInfo()->paused) {
 #if DEBUG_FOCUS
         LOGD("Waiting because focused window is paused.");
 #endif
@@ -1302,21 +1324,22 @@
         size_t numWindows = mWindowHandles.size();
         for (size_t i = 0; i < numWindows; i++) {
             sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-            int32_t flags = windowHandle->layoutParamsFlags;
+            const InputWindowInfo* windowInfo = windowHandle->getInfo();
+            int32_t flags = windowInfo->layoutParamsFlags;
 
-            if (flags & InputWindowHandle::FLAG_SYSTEM_ERROR) {
+            if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) {
                 if (topErrorWindowHandle == NULL) {
                     topErrorWindowHandle = windowHandle;
                 }
             }
 
-            if (windowHandle->visible) {
-                if (! (flags & InputWindowHandle::FLAG_NOT_TOUCHABLE)) {
-                    isTouchModal = (flags & (InputWindowHandle::FLAG_NOT_FOCUSABLE
-                            | InputWindowHandle::FLAG_NOT_TOUCH_MODAL)) == 0;
-                    if (isTouchModal || windowHandle->touchableRegionContainsPoint(x, y)) {
+            if (windowInfo->visible) {
+                if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
+                    isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
+                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                         if (! screenWasOff
-                                || (flags & InputWindowHandle::FLAG_TOUCHABLE_WHEN_WAKING)) {
+                                || (flags & InputWindowInfo::FLAG_TOUCHABLE_WHEN_WAKING)) {
                             newTouchedWindowHandle = windowHandle;
                         }
                         break; // found touched window, exit window loop
@@ -1324,7 +1347,7 @@
                 }
 
                 if (maskedAction == AMOTION_EVENT_ACTION_DOWN
-                        && (flags & InputWindowHandle::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                        && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
                     int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
                     if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
                         outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
@@ -1350,7 +1373,8 @@
         }
 
         // Figure out whether splitting will be allowed for this window.
-        if (newTouchedWindowHandle != NULL && newTouchedWindowHandle->supportsSplitTouch()) {
+        if (newTouchedWindowHandle != NULL
+                && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
             // New window supports splitting.
             isSplit = true;
         } else if (isSplit) {
@@ -1396,7 +1420,7 @@
             // within the touched window.
             if (!isTouchModal) {
                 while (sample->next) {
-                    if (!newHoverWindowHandle->touchableRegionContainsPoint(
+                    if (!newHoverWindowHandle->getInfo()->touchableRegionContainsPoint(
                             sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
                             sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y))) {
                         *outSplitBatchAfterSample = sample;
@@ -1444,15 +1468,15 @@
                     && newTouchedWindowHandle != NULL) {
 #if DEBUG_FOCUS
                 LOGD("Touch is slipping out of window %s into window %s.",
-                        oldTouchedWindowHandle->name.string(),
-                        newTouchedWindowHandle->name.string());
+                        oldTouchedWindowHandle->getName().string(),
+                        newTouchedWindowHandle->getName().string());
 #endif
                 // Make a slippery exit from the old window.
                 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
                         InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
 
                 // Make a slippery entrance into the new window.
-                if (newTouchedWindowHandle->supportsSplitTouch()) {
+                if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
                     isSplit = true;
                 }
 
@@ -1484,7 +1508,8 @@
         // Let the previous window know that the hover sequence is over.
         if (mLastHoverWindowHandle != NULL) {
 #if DEBUG_HOVER
-            LOGD("Sending hover exit event to window %s.", mLastHoverWindowHandle->name.string());
+            LOGD("Sending hover exit event to window %s.",
+                    mLastHoverWindowHandle->getName().string());
 #endif
             mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
                     InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
@@ -1493,7 +1518,8 @@
         // Let the new window know that the hover sequence is starting.
         if (newHoverWindowHandle != NULL) {
 #if DEBUG_HOVER
-            LOGD("Sending hover enter event to window %s.", newHoverWindowHandle->name.string());
+            LOGD("Sending hover enter event to window %s.",
+                    newHoverWindowHandle->getName().string());
 #endif
             mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
                     InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
@@ -1533,12 +1559,12 @@
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
                 mTempTouchState.getFirstForegroundWindowHandle();
-        const int32_t foregroundWindowUid = foregroundWindowHandle->ownerUid;
+        const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
         for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
             const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
             if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                 sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
-                if (inputWindowHandle->ownerUid != foregroundWindowUid) {
+                if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
                     mTempTouchState.addOrUpdateWindow(inputWindowHandle,
                             InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
                 }
@@ -1551,7 +1577,7 @@
         const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
         if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
             // If the touched window is paused then keep waiting.
-            if (touchedWindow.windowHandle->paused) {
+            if (touchedWindow.windowHandle->getInfo()->paused) {
 #if DEBUG_FOCUS
                 LOGD("Waiting because touched window is paused.");
 #endif
@@ -1581,10 +1607,11 @@
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
                 mTempTouchState.getFirstForegroundWindowHandle();
-        if (foregroundWindowHandle->hasWallpaper) {
+        if (foregroundWindowHandle->getInfo()->hasWallpaper) {
             for (size_t i = 0; i < mWindowHandles.size(); i++) {
                 sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-                if (windowHandle->layoutParamsType == InputWindowHandle::TYPE_WALLPAPER) {
+                if (windowHandle->getInfo()->layoutParamsType
+                        == InputWindowInfo::TYPE_WALLPAPER) {
                     mTempTouchState.addOrUpdateWindow(windowHandle,
                             InputTarget::FLAG_WINDOW_IS_OBSCURED
                                     | InputTarget::FLAG_DISPATCH_AS_IS,
@@ -1708,12 +1735,13 @@
         int32_t targetFlags, BitSet32 pointerIds) {
     mCurrentInputTargets.push();
 
+    const InputWindowInfo* windowInfo = windowHandle->getInfo();
     InputTarget& target = mCurrentInputTargets.editTop();
-    target.inputChannel = windowHandle->inputChannel;
+    target.inputChannel = windowInfo->inputChannel;
     target.flags = targetFlags;
-    target.xOffset = - windowHandle->frameLeft;
-    target.yOffset = - windowHandle->frameTop;
-    target.scaleFactor = windowHandle->scaleFactor;
+    target.xOffset = - windowInfo->frameLeft;
+    target.yOffset = - windowInfo->frameTop;
+    target.scaleFactor = windowInfo->scaleFactor;
     target.pointerIds = pointerIds;
 }
 
@@ -1734,14 +1762,15 @@
 bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
         const InjectionState* injectionState) {
     if (injectionState
-            && (windowHandle == NULL || windowHandle->ownerUid != injectionState->injectorUid)
+            && (windowHandle == NULL
+                    || windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
             && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
         if (windowHandle != NULL) {
             LOGW("Permission denied: injecting event from pid %d uid %d to window %s "
                     "owned by uid %d",
                     injectionState->injectorPid, injectionState->injectorUid,
-                    windowHandle->name.string(),
-                    windowHandle->ownerUid);
+                    windowHandle->getName().string(),
+                    windowHandle->getInfo()->ownerUid);
         } else {
             LOGW("Permission denied: injecting event from pid %d uid %d",
                     injectionState->injectorPid, injectionState->injectorUid);
@@ -1759,8 +1788,10 @@
         if (otherHandle == windowHandle) {
             break;
         }
-        if (otherHandle->visible && ! otherHandle->isTrustedOverlay()
-                && otherHandle->frameContainsPoint(x, y)) {
+
+        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        if (otherInfo->visible && ! otherInfo->isTrustedOverlay()
+                && otherInfo->frameContainsPoint(x, y)) {
             return true;
         }
     }
@@ -1769,7 +1800,7 @@
 
 bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(
         const sp<InputWindowHandle>& windowHandle) {
-    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->inputChannel);
+    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
     if (connectionIndex >= 0) {
         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
         return connection->outboundQueue.isEmpty();
@@ -1783,15 +1814,15 @@
         const sp<InputWindowHandle>& windowHandle) {
     if (applicationHandle != NULL) {
         if (windowHandle != NULL) {
-            String8 label(applicationHandle->name);
+            String8 label(applicationHandle->getName());
             label.append(" - ");
-            label.append(windowHandle->name);
+            label.append(windowHandle->getName());
             return label;
         } else {
-            return applicationHandle->name;
+            return applicationHandle->getName();
         }
     } else if (windowHandle != NULL) {
-        return windowHandle->name;
+        return windowHandle->getName();
     } else {
         return String8("<unknown application or window>");
     }
@@ -2127,7 +2158,7 @@
         if (status) {
             LOGE("channel '%s' ~ Could not publish key event, "
                     "status=%d", connection->getInputChannelName(), status);
-            abortBrokenDispatchCycleLocked(currentTime, connection);
+            abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             return;
         }
         break;
@@ -2190,7 +2221,7 @@
         if (status) {
             LOGE("channel '%s' ~ Could not publish motion event, "
                     "status=%d", connection->getInputChannelName(), status);
-            abortBrokenDispatchCycleLocked(currentTime, connection);
+            abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             return;
         }
 
@@ -2223,7 +2254,7 @@
                     LOGE("channel '%s' ~ Could not append motion sample "
                             "for a reason other than out of memory, status=%d",
                             connection->getInputChannelName(), status);
-                    abortBrokenDispatchCycleLocked(currentTime, connection);
+                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                     return;
                 }
             }
@@ -2245,7 +2276,7 @@
     if (status) {
         LOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
                 connection->getInputChannelName(), status);
-        abortBrokenDispatchCycleLocked(currentTime, connection);
+        abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
         return;
     }
 
@@ -2280,7 +2311,7 @@
     if (status) {
         LOGE("channel '%s' ~ Could not reset publisher, status=%d",
                 connection->getInputChannelName(), status);
-        abortBrokenDispatchCycleLocked(currentTime, connection);
+        abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
         return;
     }
 
@@ -2324,10 +2355,10 @@
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection) {
+        const sp<Connection>& connection, bool notify) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ abortBrokenDispatchCycle",
-            connection->getInputChannelName());
+    LOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
+            connection->getInputChannelName(), toString(notify));
 #endif
 
     // Clear the outbound queue.
@@ -2338,8 +2369,10 @@
     if (connection->status == Connection::STATUS_NORMAL) {
         connection->status = Connection::STATUS_BROKEN;
 
-        // Notify other system components.
-        onDispatchCycleBrokenLocked(currentTime, connection);
+        if (notify) {
+            // Notify other system components.
+            onDispatchCycleBrokenLocked(currentTime, connection);
+        }
     }
 }
 
@@ -2368,36 +2401,41 @@
             return 0; // remove the callback
         }
 
-        nsecs_t currentTime = now();
-
+        bool notify;
         sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
-        if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
-            LOGE("channel '%s' ~ Consumer closed input channel or an error occurred.  "
-                    "events=0x%x", connection->getInputChannelName(), events);
-            d->abortBrokenDispatchCycleLocked(currentTime, connection);
-            d->runCommandsLockedInterruptible();
-            return 0; // remove the callback
-        }
+        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
+            if (!(events & ALOOPER_EVENT_INPUT)) {
+                LOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
+                        "events=0x%x", connection->getInputChannelName(), events);
+                return 1;
+            }
 
-        if (! (events & ALOOPER_EVENT_INPUT)) {
-            LOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
-                    "events=0x%x", connection->getInputChannelName(), events);
-            return 1;
-        }
+            bool handled = false;
+            status_t status = connection->inputPublisher.receiveFinishedSignal(&handled);
+            if (!status) {
+                nsecs_t currentTime = now();
+                d->finishDispatchCycleLocked(currentTime, connection, handled);
+                d->runCommandsLockedInterruptible();
+                return 1;
+            }
 
-        bool handled = false;
-        status_t status = connection->inputPublisher.receiveFinishedSignal(&handled);
-        if (status) {
             LOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                     connection->getInputChannelName(), status);
-            d->abortBrokenDispatchCycleLocked(currentTime, connection);
-            d->runCommandsLockedInterruptible();
-            return 0; // remove the callback
+            notify = true;
+        } else {
+            // Monitor channels are never explicitly unregistered.
+            // We do it automatically when the remote endpoint is closed so don't warn
+            // about them.
+            notify = !connection->monitor;
+            if (notify) {
+                LOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
+                        "events=0x%x", connection->getInputChannelName(), events);
+            }
         }
 
-        d->finishDispatchCycleLocked(currentTime, connection, handled);
-        d->runCommandsLockedInterruptible();
-        return 1;
+        // Unregister the channel.
+        d->unregisterInputChannelLocked(connection->inputChannel, notify);
+        return 0; // remove the callback
     } // release lock
 }
 
@@ -2450,9 +2488,10 @@
             InputTarget target;
             sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel);
             if (windowHandle != NULL) {
-                target.xOffset = -windowHandle->frameLeft;
-                target.yOffset = -windowHandle->frameTop;
-                target.scaleFactor = windowHandle->scaleFactor;
+                const InputWindowInfo* windowInfo = windowHandle->getInfo();
+                target.xOffset = -windowInfo->frameLeft;
+                target.yOffset = -windowInfo->frameTop;
+                target.scaleFactor = windowInfo->scaleFactor;
             } else {
                 target.xOffset = 0;
                 target.yOffset = 0;
@@ -2854,9 +2893,9 @@
 #if DEBUG_BATCHING
                             LOGD("Not streaming hover move because the last hovered window "
                                     "is '%s' but the currently hovered window is '%s'.",
-                                    mLastHoverWindowHandle->name.string(),
+                                    mLastHoverWindowHandle->getName().string(),
                                     hoverWindowHandle != NULL
-                                            ? hoverWindowHandle->name.string() : "<null>");
+                                            ? hoverWindowHandle->getName().string() : "<null>");
 #endif
                             goto NoBatchingOrStreaming;
                         }
@@ -3183,7 +3222,7 @@
     size_t numWindows = mWindowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-        if (windowHandle->inputChannel == inputChannel) {
+        if (windowHandle->getInputChannel() == inputChannel) {
             return windowHandle;
         }
     }
@@ -3208,17 +3247,18 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
+        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
         mWindowHandles = inputWindowHandles;
 
         sp<InputWindowHandle> newFocusedWindowHandle;
         bool foundHoveredWindow = false;
         for (size_t i = 0; i < mWindowHandles.size(); i++) {
             const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-            if (!windowHandle->update() || windowHandle->inputChannel == NULL) {
+            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
                 mWindowHandles.removeAt(i--);
                 continue;
             }
-            if (windowHandle->hasFocus) {
+            if (windowHandle->getInfo()->hasFocus) {
                 newFocusedWindowHandle = windowHandle;
             }
             if (windowHandle == mLastHoverWindowHandle) {
@@ -3234,19 +3274,20 @@
             if (mFocusedWindowHandle != NULL) {
 #if DEBUG_FOCUS
                 LOGD("Focus left window: %s",
-                        mFocusedWindowHandle->name.string());
+                        mFocusedWindowHandle->getName().string());
 #endif
-                if (mFocusedWindowHandle->inputChannel != NULL) {
+                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
+                if (focusedInputChannel != NULL) {
                     CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                             "focus left window");
                     synthesizeCancelationEventsForInputChannelLocked(
-                            mFocusedWindowHandle->inputChannel, options);
+                            focusedInputChannel, options);
                 }
             }
             if (newFocusedWindowHandle != NULL) {
 #if DEBUG_FOCUS
                 LOGD("Focus entered window: %s",
-                        newFocusedWindowHandle->name.string());
+                        newFocusedWindowHandle->getName().string());
 #endif
             }
             mFocusedWindowHandle = newFocusedWindowHandle;
@@ -3256,17 +3297,34 @@
             TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
             if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
 #if DEBUG_FOCUS
-                LOGD("Touched window was removed: %s", touchedWindow.windowHandle->name.string());
+                LOGD("Touched window was removed: %s",
+                        touchedWindow.windowHandle->getName().string());
 #endif
-                if (touchedWindow.windowHandle->inputChannel != NULL) {
+                sp<InputChannel> touchedInputChannel =
+                        touchedWindow.windowHandle->getInputChannel();
+                if (touchedInputChannel != NULL) {
                     CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                             "touched window was removed");
                     synthesizeCancelationEventsForInputChannelLocked(
-                            touchedWindow.windowHandle->inputChannel, options);
+                            touchedInputChannel, options);
                 }
                 mTouchState.windows.removeAt(i--);
             }
         }
+
+        // Release information for windows that are no longer present.
+        // This ensures that unused input channels are released promptly.
+        // Otherwise, they might stick around until the window handle is destroyed
+        // which might not happen until the next GC.
+        for (size_t i = 0; i < oldWindowHandles.size(); i++) {
+            const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
+            if (!hasWindowHandleLocked(oldWindowHandle)) {
+#if DEBUG_FOCUS
+                LOGD("Window went away: %s", oldWindowHandle->getName().string());
+#endif
+                oldWindowHandle->releaseInfo();
+            }
+        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3281,15 +3339,17 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        if (inputApplicationHandle != NULL && inputApplicationHandle->update()) {
+        if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) {
             if (mFocusedApplicationHandle != inputApplicationHandle) {
                 if (mFocusedApplicationHandle != NULL) {
                     resetTargetsLocked();
+                    mFocusedApplicationHandle->releaseInfo();
                 }
                 mFocusedApplicationHandle = inputApplicationHandle;
             }
         } else if (mFocusedApplicationHandle != NULL) {
             resetTargetsLocked();
+            mFocusedApplicationHandle->releaseInfo();
             mFocusedApplicationHandle.clear();
         }
 
@@ -3469,13 +3529,14 @@
 
     if (mFocusedApplicationHandle != NULL) {
         dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
-                mFocusedApplicationHandle->name.string(),
-                mFocusedApplicationHandle->dispatchingTimeout / 1000000.0);
+                mFocusedApplicationHandle->getName().string(),
+                mFocusedApplicationHandle->getDispatchingTimeout(
+                        DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
     } else {
         dump.append(INDENT "FocusedApplication: <null>\n");
     }
     dump.appendFormat(INDENT "FocusedWindow: name='%s'\n",
-            mFocusedWindowHandle != NULL ? mFocusedWindowHandle->name.string() : "<null>");
+            mFocusedWindowHandle != NULL ? mFocusedWindowHandle->getName().string() : "<null>");
 
     dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down));
     dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split));
@@ -3486,7 +3547,8 @@
         for (size_t i = 0; i < mTouchState.windows.size(); i++) {
             const TouchedWindow& touchedWindow = mTouchState.windows[i];
             dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
-                    i, touchedWindow.windowHandle->name.string(), touchedWindow.pointerIds.value,
+                    i, touchedWindow.windowHandle->getName().string(),
+                    touchedWindow.pointerIds.value,
                     touchedWindow.targetFlags);
         }
     } else {
@@ -3497,26 +3559,28 @@
         dump.append(INDENT "Windows:\n");
         for (size_t i = 0; i < mWindowHandles.size(); i++) {
             const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
+            const InputWindowInfo* windowInfo = windowHandle->getInfo();
+
             dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
                     "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
                     "frame=[%d,%d][%d,%d], scale=%f, "
                     "touchableRegion=",
-                    i, windowHandle->name.string(),
-                    toString(windowHandle->paused),
-                    toString(windowHandle->hasFocus),
-                    toString(windowHandle->hasWallpaper),
-                    toString(windowHandle->visible),
-                    toString(windowHandle->canReceiveKeys),
-                    windowHandle->layoutParamsFlags, windowHandle->layoutParamsType,
-                    windowHandle->layer,
-                    windowHandle->frameLeft, windowHandle->frameTop,
-                    windowHandle->frameRight, windowHandle->frameBottom,
-                    windowHandle->scaleFactor);
-            dumpRegion(dump, windowHandle->touchableRegion);
-            dump.appendFormat(", inputFeatures=0x%08x", windowHandle->inputFeatures);
+                    i, windowInfo->name.string(),
+                    toString(windowInfo->paused),
+                    toString(windowInfo->hasFocus),
+                    toString(windowInfo->hasWallpaper),
+                    toString(windowInfo->visible),
+                    toString(windowInfo->canReceiveKeys),
+                    windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
+                    windowInfo->layer,
+                    windowInfo->frameLeft, windowInfo->frameTop,
+                    windowInfo->frameRight, windowInfo->frameBottom,
+                    windowInfo->scaleFactor);
+            dumpRegion(dump, windowInfo->touchableRegion);
+            dump.appendFormat(", inputFeatures=0x%08x", windowInfo->inputFeatures);
             dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
-                    windowHandle->ownerPid, windowHandle->ownerUid,
-                    windowHandle->dispatchingTimeout / 1000000.0);
+                    windowInfo->ownerPid, windowInfo->ownerUid,
+                    windowInfo->dispatchingTimeout / 1000000.0);
         }
     } else {
         dump.append(INDENT "Windows: <none>\n");
@@ -3572,7 +3636,7 @@
             return BAD_VALUE;
         }
 
-        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle);
+        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
         status_t status = connection->initialize();
         if (status) {
             LOGE("Failed to initialize input publisher for input channel '%s', status=%d",
@@ -3602,31 +3666,10 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-        if (connectionIndex < 0) {
-            LOGW("Attempted to unregister already unregistered input channel '%s'",
-                    inputChannel->getName().string());
-            return BAD_VALUE;
+        status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
+        if (status) {
+            return status;
         }
-
-        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-        mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
-
-        connection->status = Connection::STATUS_ZOMBIE;
-
-        for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-            if (mMonitoringChannels[i] == inputChannel) {
-                mMonitoringChannels.removeAt(i);
-                break;
-            }
-        }
-
-        mLooper->removeFd(inputChannel->getReceivePipeFd());
-
-        nsecs_t currentTime = now();
-        abortBrokenDispatchCycleLocked(currentTime, connection);
-
-        runCommandsLockedInterruptible();
     } // release lock
 
     // Wake the poll loop because removing the connection may have changed the current
@@ -3635,6 +3678,42 @@
     return OK;
 }
 
+status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
+        bool notify) {
+    ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
+    if (connectionIndex < 0) {
+        LOGW("Attempted to unregister already unregistered input channel '%s'",
+                inputChannel->getName().string());
+        return BAD_VALUE;
+    }
+
+    sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+    mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
+
+    if (connection->monitor) {
+        removeMonitorChannelLocked(inputChannel);
+    }
+
+    mLooper->removeFd(inputChannel->getReceivePipeFd());
+
+    nsecs_t currentTime = now();
+    abortBrokenDispatchCycleLocked(currentTime, connection, notify);
+
+    runCommandsLockedInterruptible();
+
+    connection->status = Connection::STATUS_ZOMBIE;
+    return OK;
+}
+
+void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+         if (mMonitoringChannels[i] == inputChannel) {
+             mMonitoringChannels.removeAt(i);
+             break;
+         }
+    }
+}
+
 ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
     ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
     if (connectionIndex >= 0) {
@@ -3736,7 +3815,7 @@
 
     resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
             commandEntry->inputWindowHandle != NULL
-                    ? commandEntry->inputWindowHandle->inputChannel : NULL);
+                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -4495,8 +4574,9 @@
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
-        const sp<InputWindowHandle>& inputWindowHandle) :
+        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
         status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
+        monitor(monitor),
         inputPublisher(inputChannel),
         lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
 }
@@ -4627,8 +4707,9 @@
     for (size_t i = 0; i < windows.size(); i++) {
         const TouchedWindow& window = windows.itemAt(i);
         if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
-            if (haveSlipperyForegroundWindow || !(window.windowHandle->layoutParamsFlags
-                    & InputWindowHandle::FLAG_SLIPPERY)) {
+            if (haveSlipperyForegroundWindow
+                    || !(window.windowHandle->getInfo()->layoutParamsFlags
+                            & InputWindowInfo::FLAG_SLIPPERY)) {
                 return false;
             }
             haveSlipperyForegroundWindow = true;
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 3c83691..e78f7bd 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -814,6 +814,7 @@
         Status status;
         sp<InputChannel> inputChannel; // never null
         sp<InputWindowHandle> inputWindowHandle; // may be null
+        bool monitor;
         InputPublisher inputPublisher;
         InputState inputState;
         Queue<DispatchEntry> outboundQueue;
@@ -822,7 +823,7 @@
         nsecs_t lastDispatchTime; // the time when the last event was dispatched
 
         explicit Connection(const sp<InputChannel>& inputChannel,
-                const sp<InputWindowHandle>& inputWindowHandle);
+                const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
 
         inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
 
@@ -868,6 +869,7 @@
     Vector<EventEntry*> mTempCancelationEvents;
 
     void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
+    void dispatchIdleLocked();
 
     // Batches a new sample onto a motion entry.
     // Assumes that the we have already checked that we can append samples.
@@ -1073,7 +1075,8 @@
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool handled);
     void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
-    void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+            bool notify);
     void drainOutboundQueueLocked(Connection* connection);
     static int handleReceiveCallback(int receiveFd, int events, void* data);
 
@@ -1094,6 +1097,10 @@
     void dumpDispatchStateLocked(String8& dump);
     void logDispatchStateLocked();
 
+    // Registration.
+    void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel);
+    status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify);
+
     // Add or remove a connection to the mActiveConnections vector.
     void activateConnectionLocked(Connection* connection);
     void deactivateConnectionLocked(Connection* connection);
diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp
index 0ce8867..fe61918 100644
--- a/services/input/InputWindow.cpp
+++ b/services/input/InputWindow.cpp
@@ -22,25 +22,43 @@
 
 namespace android {
 
-// --- InputWindowHandle ---
+// --- InputWindowInfo ---
 
-bool InputWindowHandle::touchableRegionContainsPoint(int32_t x, int32_t y) const {
+bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
     return touchableRegion.contains(x, y);
 }
 
-bool InputWindowHandle::frameContainsPoint(int32_t x, int32_t y) const {
+bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
     return x >= frameLeft && x <= frameRight
             && y >= frameTop && y <= frameBottom;
 }
 
-bool InputWindowHandle::isTrustedOverlay() const {
+bool InputWindowInfo::isTrustedOverlay() const {
     return layoutParamsType == TYPE_INPUT_METHOD
             || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
             || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
 }
 
-bool InputWindowHandle::supportsSplitTouch() const {
+bool InputWindowInfo::supportsSplitTouch() const {
     return layoutParamsFlags & FLAG_SPLIT_TOUCH;
 }
 
+
+// --- InputWindowHandle ---
+
+InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
+    inputApplicationHandle(inputApplicationHandle), mInfo(NULL) {
+}
+
+InputWindowHandle::~InputWindowHandle() {
+    delete mInfo;
+}
+
+void InputWindowHandle::releaseInfo() {
+    if (mInfo) {
+        delete mInfo;
+        mInfo = NULL;
+    }
+}
+
 } // namespace android
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 272081c..8861bee 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -30,15 +30,9 @@
 namespace android {
 
 /*
- * A handle to a window that can receive input.
- *
- * Used by the native input dispatcher to indirectly refer to the window manager objects
- * that describe a window.
+ * Describes the properties of a window that can receive input.
  */
-class InputWindowHandle : public RefBase {
-public:
-    const sp<InputApplicationHandle> inputApplicationHandle;
-
+struct InputWindowInfo {
     // Window flags from WindowManager.LayoutParams
     enum {
         FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001,
@@ -116,7 +110,6 @@
         INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
     };
 
-    sp<InputWindowHandle> inputWindowHandle;
     sp<InputChannel> inputChannel;
     String8 name;
     int32_t layoutParamsFlags;
@@ -149,6 +142,34 @@
     bool isTrustedOverlay() const;
 
     bool supportsSplitTouch() const;
+};
+
+
+/*
+ * Handle for a window that can receive input.
+ *
+ * Used by the native input dispatcher to indirectly refer to the window manager objects
+ * that describe a window.
+ */
+class InputWindowHandle : public RefBase {
+public:
+    const sp<InputApplicationHandle> inputApplicationHandle;
+
+    inline const InputWindowInfo* getInfo() const {
+        return mInfo;
+    }
+
+    inline sp<InputChannel> getInputChannel() const {
+        return mInfo ? mInfo->inputChannel : NULL;
+    }
+
+    inline String8 getName() const {
+        return mInfo ? mInfo->name : String8("<invalid>");
+    }
+
+    inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
+        return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+    }
 
     /**
      * Requests that the state of this object be updated to reflect
@@ -159,12 +180,19 @@
      *
      * Returns true on success, or false if the handle is no longer valid.
      */
-    virtual bool update() = 0;
+    virtual bool updateInfo() = 0;
+
+    /**
+     * Releases the storage used by the associated information when it is
+     * no longer needed.
+     */
+    void releaseInfo();
 
 protected:
-    InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
-            inputApplicationHandle(inputApplicationHandle) { }
-    virtual ~InputWindowHandle() { }
+    InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
+    virtual ~InputWindowHandle();
+
+    InputWindowInfo* mInfo;
 };
 
 } // namespace android
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index b3309e5..997318a 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -197,14 +197,14 @@
         = new SparseArray<HashSet<ApplicationInfo>>();
     // set of backup services that have pending changes
     class BackupRequest {
-        public ApplicationInfo appInfo;
+        public String packageName;
 
-        BackupRequest(ApplicationInfo app) {
-            appInfo = app;
+        BackupRequest(String pkgName) {
+            packageName = pkgName;
         }
 
         public String toString() {
-            return "BackupRequest{app=" + appInfo + "}";
+            return "BackupRequest{pkg=" + packageName + "}";
         }
     }
     // Backups that we haven't started yet.  Keys are package names.
@@ -877,6 +877,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addDataScheme("package");
         mContext.registerReceiver(mBroadcastReceiver, filter);
         // Register for events related to sdcard installation.
@@ -1174,7 +1175,8 @@
             Bundle extras = intent.getExtras();
             String pkgList[] = null;
             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                    Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+                    Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                 Uri uri = intent.getData();
                 if (uri == null) {
                     return;
@@ -1183,8 +1185,14 @@
                 if (pkgName != null) {
                     pkgList = new String[] { pkgName };
                 }
-                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
-                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+                if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                    // use the existing "add with replacement" logic
+                    if (MORE_DEBUG) Slog.d(TAG, "PACKAGE_REPLACED, updating package " + pkgName);
+                    added = replacing = true;
+                } else {
+                    added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+                    replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+                }
             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                 added = true;
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -1192,6 +1200,7 @@
                 added = false;
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
+
             if (pkgList == null || pkgList.length == 0) {
                 return;
             }
@@ -1665,9 +1674,7 @@
                 if (status == BackupConstants.TRANSPORT_OK) {
                     PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
                             mPackageManager, allAgentPackages());
-                    BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
-                    pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
-                    status = processOneBackup(pmRequest,
+                    status = processOneBackup(PACKAGE_MANAGER_SENTINEL,
                             IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
                 }
 
@@ -1716,7 +1723,7 @@
                 if (status != BackupConstants.TRANSPORT_OK) {
                     Slog.w(TAG, "Backup pass unsuccessful, restaging");
                     for (BackupRequest req : mQueue) {
-                        dataChangedImpl(req.appInfo.packageName);
+                        dataChangedImpl(req.packageName);
                     }
 
                     // We also want to reset the backup schedule based on whatever
@@ -1750,9 +1757,11 @@
                 // Verify that the requested app exists; it might be something that
                 // requested a backup but was then uninstalled.  The request was
                 // journalled and rather than tamper with the journal it's safer
-                // to sanity-check here.
+                // to sanity-check here.  This also gives us the classname of the
+                // package's backup agent.
+                PackageInfo pkg;
                 try {
-                    mPackageManager.getPackageInfo(request.appInfo.packageName, 0);
+                    pkg = mPackageManager.getPackageInfo(request.packageName, 0);
                 } catch (NameNotFoundException e) {
                     Slog.d(TAG, "Package does not exist; skipping");
                     continue;
@@ -1760,11 +1769,11 @@
 
                 IBackupAgent agent = null;
                 try {
-                    mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
-                    agent = bindToAgentSynchronous(request.appInfo,
+                    mWakelock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
+                    agent = bindToAgentSynchronous(pkg.applicationInfo,
                             IApplicationThread.BACKUP_MODE_INCREMENTAL);
                     if (agent != null) {
-                        int result = processOneBackup(request, agent, transport);
+                        int result = processOneBackup(request.packageName, agent, transport);
                         if (result != BackupConstants.TRANSPORT_OK) return result;
                     }
                 } catch (SecurityException ex) {
@@ -1772,7 +1781,7 @@
                     Slog.d(TAG, "error in bind/backup", ex);
                 } finally {
                     try {  // unbind even on timeout, just in case
-                        mActivityManager.unbindBackupAgent(request.appInfo);
+                        mActivityManager.unbindBackupAgent(pkg.applicationInfo);
                     } catch (RemoteException e) {}
                 }
             }
@@ -1782,9 +1791,8 @@
             return BackupConstants.TRANSPORT_OK;
         }
 
-        private int processOneBackup(BackupRequest request, IBackupAgent agent,
+        private int processOneBackup(String packageName, IBackupAgent agent,
                 IBackupTransport transport) {
-            final String packageName = request.appInfo.packageName;
             if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
 
             File savedStateName = new File(mStateDir, packageName);
@@ -4207,7 +4215,7 @@
                 if (app.packageName.equals(packageName)) {
                     // Add the caller to the set of pending backups.  If there is
                     // one already there, then overwrite it, but no harm done.
-                    BackupRequest req = new BackupRequest(app);
+                    BackupRequest req = new BackupRequest(packageName);
                     if (mPendingBackups.put(app.packageName, req) == null) {
                         // Journal this request in case of crash.  The put()
                         // operation returned null when this package was not already
@@ -4218,7 +4226,7 @@
                             int numKeys = mPendingBackups.size();
                             Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
                             for (BackupRequest b : mPendingBackups.values()) {
-                                Slog.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
+                                Slog.d(TAG, "    + " + b);
                             }
                         }
                     }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 38bcebc..bb831f5 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1682,7 +1682,12 @@
                 for (int i = 0; i < packageNum; ++i) {
                     if (packageInfos[i].equals(imi.getPackageName())) {
                         mFileManager.addInputMethodSubtypes(imi, subtypes);
-                        buildInputMethodListLocked(mMethodList, mMethodMap);
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            buildInputMethodListLocked(mMethodList, mMethodMap);
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
                         return true;
                     }
                 }
@@ -1707,7 +1712,7 @@
                 return;
             }
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 setInputMethodLocked(id, subtypeId);
             } finally {
@@ -3065,17 +3070,11 @@
         public void addInputMethodSubtypes(
                 InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
             synchronized (mMethodMap) {
-                final HashSet<InputMethodSubtype> existingSubtypes =
-                        new HashSet<InputMethodSubtype>();
-                for (int i = 0; i < imi.getSubtypeCount(); ++i) {
-                    existingSubtypes.add(imi.getSubtypeAt(i));
-                }
-
                 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
                 final int N = additionalSubtypes.length;
                 for (int i = 0; i < N; ++i) {
                     final InputMethodSubtype subtype = additionalSubtypes[i];
-                    if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) {
+                    if (!subtypes.contains(subtype)) {
                         subtypes.add(subtype);
                     }
                 }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 7a3a344..6ee20bb 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -101,6 +101,7 @@
     private Vibrator mVibrator = new Vibrator();
 
     // for enabling and disabling notification pulse behavior
+    private boolean mScreenOn = true;
     private boolean mInCall = false;
     private boolean mNotificationPulseEnabled;
 
@@ -344,9 +345,19 @@
                         cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
                     }
                 }
+            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
+                // Keep track of screen on/off state, but do not turn off the notification light
+                // until user passes through the lock screen or views the notification.
+                mScreenOn = true;
+            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+                mScreenOn = false;
             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
-                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK));
+                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
+                        TelephonyManager.EXTRA_STATE_OFFHOOK));
                 updateNotificationPulse();
+            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
+                // turn off LED when user passes through lock screen
+                mNotificationLight.turnOff();
             }
         }
     };
@@ -417,6 +428,7 @@
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+        filter.addAction(Intent.ACTION_USER_PRESENT);
         mContext.registerReceiver(mIntentReceiver, filter);
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -1057,8 +1069,8 @@
             }
         }
 
-        // Don't flash while we are in a call
-        if (mLedNotification == null || mInCall) {
+        // Don't flash while we are in a call or screen is on
+        if (mLedNotification == null || mInCall || mScreenOn) {
             mNotificationLight.turnOff();
         } else {
             int ledARGB = mLedNotification.notification.ledARGB;
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index f6c369e..c792b33 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -201,7 +201,7 @@
                     Settings.Secure.getString(mContext.getContentResolver(),
                             Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
             if (DBG) {
-                Slog.w(TAG, "getCurrentSpellChecker: " + subtypeHashCodeStr);
+                Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCodeStr);
             }
             final SpellCheckerInfo sci = getCurrentSpellChecker(null);
             if (sci == null || sci.getSubtypeCount() == 0) {
@@ -509,7 +509,8 @@
                                 listener.mScLocale, listener.mScListener, listener.mBundle);
                         listener.mTsListener.onServiceConnected(session);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Exception in getting the spell checker session: " + e);
+                        Slog.e(TAG, "Exception in getting the spell checker session."
+                                + "Reconnect to the spellchecker. ", e);
                         removeAll();
                         return;
                     }
@@ -579,8 +580,12 @@
                 Slog.d(TAG, "cleanLocked");
             }
             if (mListeners.isEmpty()) {
-                if (mSpellCheckerBindGroups.containsKey(this)) {
-                    mSpellCheckerBindGroups.remove(this);
+                final String sciId = mInternalConnection.mSciId;
+                if (mSpellCheckerBindGroups.containsKey(sciId)) {
+                    if (DBG) {
+                        Slog.d(TAG, "Remove bind group.");
+                    }
+                    mSpellCheckerBindGroups.remove(sciId);
                 }
                 // Unbind service when there is no active clients.
                 mContext.unbindService(mInternalConnection);
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index b37d1c2..e622503 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -139,6 +139,9 @@
             mServerChannel.dispose();
             mClientChannel = null;
             mServerChannel = null;
+
+            mDragWindowHandle = null;
+            mDragApplicationHandle = null;
         }
     }
 
diff --git a/services/java/com/android/server/wm/InputApplicationHandle.java b/services/java/com/android/server/wm/InputApplicationHandle.java
index d78b1d9..1812f11 100644
--- a/services/java/com/android/server/wm/InputApplicationHandle.java
+++ b/services/java/com/android/server/wm/InputApplicationHandle.java
@@ -46,7 +46,10 @@
 
     @Override
     protected void finalize() throws Throwable {
-        nativeDispose();
-        super.finalize();
+        try {
+            nativeDispose();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 12ef238..573a7d42 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -17,10 +17,10 @@
 package com.android.server.wm;
 
 import android.graphics.Rect;
-import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
+import android.view.InputChannel;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
@@ -160,13 +160,21 @@
             if (WindowManagerService.DEBUG_DRAG) {
                 Log.d(WindowManagerService.TAG, "Inserting drag window");
             }
-            addInputWindowHandleLw(mService.mDragState.mDragWindowHandle);
+            final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
+            if (dragWindowHandle != null) {
+                addInputWindowHandleLw(dragWindowHandle);
+            } else {
+                Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no "
+                        + "drag window handle.");
+            }
         }
 
         final int N = windows.size();
         for (int i = N - 1; i >= 0; i--) {
             final WindowState child = windows.get(i);
-            if (child.mInputChannel == null || child.mRemoved) {
+            final InputChannel inputChannel = child.mInputChannel;
+            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
+            if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
                 // Skip this window because it cannot possibly receive input.
                 continue;
             }
@@ -186,8 +194,6 @@
             }
 
             // Add a window to our list of input windows.
-            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
-            inputWindowHandle.inputChannel = child.mInputChannel;
             inputWindowHandle.name = child.toString();
             inputWindowHandle.layoutParamsFlags = flags;
             inputWindowHandle.layoutParamsType = type;
diff --git a/services/java/com/android/server/wm/InputWindowHandle.java b/services/java/com/android/server/wm/InputWindowHandle.java
index abf68d9..264877c 100644
--- a/services/java/com/android/server/wm/InputWindowHandle.java
+++ b/services/java/com/android/server/wm/InputWindowHandle.java
@@ -98,7 +98,10 @@
 
     @Override
     protected void finalize() throws Throwable {
-        nativeDispose();
-        super.finalize();
+        try {
+            nativeDispose();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/services/java/com/android/server/wm/ViewServer.java b/services/java/com/android/server/wm/ViewServer.java
index 70cb26a..a763e2c 100644
--- a/services/java/com/android/server/wm/ViewServer.java
+++ b/services/java/com/android/server/wm/ViewServer.java
@@ -319,7 +319,7 @@
                     }
                 }
             } catch (Exception e) {
-                Slog.w(LOG_TAG, "Connection error: ", e);
+                // Ignore
             } finally {
                 if (out != null) {
                     try {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 192d32b..4adf304 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2051,10 +2051,11 @@
                 return res;
             }
             
-            if (outInputChannel != null) {
+            if (outInputChannel != null && (attrs.inputFeatures
+                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                 String name = win.makeInputChannelName();
                 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
-                win.mInputChannel = inputChannels[0];
+                win.setInputChannel(inputChannels[0]);
                 inputChannels[1].transferTo(outInputChannel);
                 
                 mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
@@ -2533,6 +2534,7 @@
 
             boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                     && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+            wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
 
             win.mRelayoutCalled = true;
             final int oldVisibility = win.mViewVisibility;
@@ -7925,11 +7927,26 @@
                 }
 
                 if (windowAnimationBackgroundColor != 0) {
+                    // If this window that wants black is the current wallpaper
+                    // target, then the black goes *below* the wallpaper so we
+                    // don't cause the wallpaper to suddenly disappear.
+                    WindowState target = windowAnimationBackground;
+                    if (mWallpaperTarget == windowAnimationBackground
+                            || mLowerWallpaperTarget == windowAnimationBackground
+                            || mUpperWallpaperTarget == windowAnimationBackground) {
+                        for (i=0; i<mWindows.size(); i++) {
+                            WindowState w = mWindows.get(i);
+                            if (w.mIsWallpaper) {
+                                target = w;
+                                break;
+                            }
+                        }
+                    }
                     if (mWindowAnimationBackgroundSurface == null) {
                         mWindowAnimationBackgroundSurface = new DimSurface(mFxSession);
                     }
                     mWindowAnimationBackgroundSurface.show(dw, dh,
-                            windowAnimationBackground.mAnimLayer - LAYER_OFFSET_DIM,
+                            target.mAnimLayer - LAYER_OFFSET_DIM,
                             windowAnimationBackgroundColor);
                 } else if (mWindowAnimationBackgroundSurface != null) {
                     mWindowAnimationBackgroundSurface.hide();
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index cdd0047..a384b03 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -1236,7 +1236,8 @@
      * Input Manager uses when discarding windows from input consideration.
      */
     boolean isPotentialDragTarget() {
-        return isVisibleNow() && (mInputChannel != null) && !mRemoved;
+        return isVisibleNow() && !mRemoved
+                && mInputChannel != null && mInputWindowHandle != null;
     }
 
     /**
@@ -1372,7 +1373,16 @@
             // we are doing this as part of processing a death note.)
         }
     }
-    
+
+    void setInputChannel(InputChannel inputChannel) {
+        if (mInputChannel != null) {
+            throw new IllegalStateException("Window already has an input channel.");
+        }
+
+        mInputChannel = inputChannel;
+        mInputWindowHandle.inputChannel = inputChannel;
+    }
+
     void disposeInputChannel() {
         if (mInputChannel != null) {
             mService.mInputManager.unregisterInputChannel(mInputChannel);
@@ -1380,6 +1390,8 @@
             mInputChannel.dispose();
             mInputChannel = null;
         }
+
+        mInputWindowHandle.inputChannel = null;
     }
 
     private class DeathRecipient implements IBinder.DeathRecipient {
diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp
index 7de67d9..c76ab53 100644
--- a/services/jni/com_android_server_InputApplicationHandle.cpp
+++ b/services/jni/com_android_server_InputApplicationHandle.cpp
@@ -49,25 +49,30 @@
     return env->NewLocalRef(mObjWeak);
 }
 
-bool NativeInputApplicationHandle::update() {
+bool NativeInputApplicationHandle::updateInfo() {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jobject obj = env->NewLocalRef(mObjWeak);
     if (!obj) {
+        releaseInfo();
         return false;
     }
 
+    if (!mInfo) {
+        mInfo = new InputApplicationInfo();
+    }
+
     jstring nameObj = jstring(env->GetObjectField(obj,
             gInputApplicationHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        name.setTo(nameStr);
+        mInfo->name.setTo(nameStr);
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        name.setTo("<null>");
+        mInfo->name.setTo("<null>");
     }
 
-    dispatchingTimeout = env->GetLongField(obj,
+    mInfo->dispatchingTimeout = env->GetLongField(obj,
             gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
 
     env->DeleteLocalRef(obj);
diff --git a/services/jni/com_android_server_InputApplicationHandle.h b/services/jni/com_android_server_InputApplicationHandle.h
index 04cd9d6..89d48c6 100644
--- a/services/jni/com_android_server_InputApplicationHandle.h
+++ b/services/jni/com_android_server_InputApplicationHandle.h
@@ -31,7 +31,7 @@
 
     jobject getInputApplicationHandleObjLocalRef(JNIEnv* env);
 
-    virtual bool update();
+    virtual bool updateInfo();
 
 private:
     jweak mObjWeak;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0a723e8..f976301 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -618,8 +618,9 @@
     size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
-        if (windowHandle->hasFocus && (windowHandle->inputFeatures
-                & InputWindowHandle::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures
+                & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
             newPointerGesturesEnabled = false;
         }
     }
@@ -1086,8 +1087,9 @@
     status_t status = gNativeInputManager->registerInputChannel(
             env, inputChannel, inputWindowHandle, monitor);
     if (status) {
-        jniThrowRuntimeException(env, "Failed to register input channel.  "
-                "Check logs for details.");
+        String8 message;
+        message.appendFormat("Failed to register input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
         return;
     }
 
@@ -1113,9 +1115,10 @@
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
 
     status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);
-    if (status) {
-        jniThrowRuntimeException(env, "Failed to unregister input channel.  "
-                "Check logs for details.");
+    if (status && status != BAD_VALUE) { // ignore already unregistered channel
+        String8 message;
+        message.appendFormat("Failed to unregister input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
     }
 }
 
diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp
index 09be881..0607eee 100644
--- a/services/jni/com_android_server_InputWindowHandle.cpp
+++ b/services/jni/com_android_server_InputWindowHandle.cpp
@@ -74,77 +74,82 @@
     return env->NewLocalRef(mObjWeak);
 }
 
-bool NativeInputWindowHandle::update() {
+bool NativeInputWindowHandle::updateInfo() {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jobject obj = env->NewLocalRef(mObjWeak);
     if (!obj) {
+        releaseInfo();
         return false;
     }
 
+    if (!mInfo) {
+        mInfo = new InputWindowInfo();
+    }
+
     jobject inputChannelObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.inputChannel);
     if (inputChannelObj) {
-        inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
+        mInfo->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
         env->DeleteLocalRef(inputChannelObj);
     } else {
-        inputChannel = NULL;
+        mInfo->inputChannel.clear();
     }
 
     jstring nameObj = jstring(env->GetObjectField(obj,
             gInputWindowHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        name.setTo(nameStr);
+        mInfo->name.setTo(nameStr);
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        name.setTo("<null>");
+        mInfo->name.setTo("<null>");
     }
 
-    layoutParamsFlags = env->GetIntField(obj,
+    mInfo->layoutParamsFlags = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layoutParamsFlags);
-    layoutParamsType = env->GetIntField(obj,
+    mInfo->layoutParamsType = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layoutParamsType);
-    dispatchingTimeout = env->GetLongField(obj,
+    mInfo->dispatchingTimeout = env->GetLongField(obj,
             gInputWindowHandleClassInfo.dispatchingTimeoutNanos);
-    frameLeft = env->GetIntField(obj,
+    mInfo->frameLeft = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameLeft);
-    frameTop = env->GetIntField(obj,
+    mInfo->frameTop = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameTop);
-    frameRight = env->GetIntField(obj,
+    mInfo->frameRight = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameRight);
-    frameBottom = env->GetIntField(obj,
+    mInfo->frameBottom = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameBottom);
-    scaleFactor = env->GetFloatField(obj,
+    mInfo->scaleFactor = env->GetFloatField(obj,
             gInputWindowHandleClassInfo.scaleFactor);
 
     jobject regionObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.touchableRegion);
     if (regionObj) {
         SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-        touchableRegion.set(*region);
+        mInfo->touchableRegion.set(*region);
         env->DeleteLocalRef(regionObj);
     } else {
-        touchableRegion.setEmpty();
+        mInfo->touchableRegion.setEmpty();
     }
 
-    visible = env->GetBooleanField(obj,
+    mInfo->visible = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.visible);
-    canReceiveKeys = env->GetBooleanField(obj,
+    mInfo->canReceiveKeys = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.canReceiveKeys);
-    hasFocus = env->GetBooleanField(obj,
+    mInfo->hasFocus = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.hasFocus);
-    hasWallpaper = env->GetBooleanField(obj,
+    mInfo->hasWallpaper = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.hasWallpaper);
-    paused = env->GetBooleanField(obj,
+    mInfo->paused = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.paused);
-    layer = env->GetIntField(obj,
+    mInfo->layer = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layer);
-    ownerPid = env->GetIntField(obj,
+    mInfo->ownerPid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerPid);
-    ownerUid = env->GetIntField(obj,
+    mInfo->ownerUid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerUid);
-    inputFeatures = env->GetIntField(obj,
+    mInfo->inputFeatures = env->GetIntField(obj,
             gInputWindowHandleClassInfo.inputFeatures);
 
     env->DeleteLocalRef(obj);
diff --git a/services/jni/com_android_server_InputWindowHandle.h b/services/jni/com_android_server_InputWindowHandle.h
index 913c3b1..2cfa17d3 100644
--- a/services/jni/com_android_server_InputWindowHandle.h
+++ b/services/jni/com_android_server_InputWindowHandle.h
@@ -32,7 +32,7 @@
 
     jobject getInputWindowHandleObjLocalRef(JNIEnv* env);
 
-    virtual bool update();
+    virtual bool updateInfo();
 
 private:
     jweak mObjWeak;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f8925b8..edbc7b0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -212,6 +212,24 @@
     } else {
         hwcl->transform = finalTransform;
     }
+
+    if (isCropped()) {
+        hwcl->sourceCrop.left   = mCurrentCrop.left;
+        hwcl->sourceCrop.top    = mCurrentCrop.top;
+        hwcl->sourceCrop.right  = mCurrentCrop.right;
+        hwcl->sourceCrop.bottom = mCurrentCrop.bottom;
+    } else {
+        const sp<GraphicBuffer>& buffer(mActiveBuffer);
+        hwcl->sourceCrop.left   = 0;
+        hwcl->sourceCrop.top    = 0;
+        if (buffer != NULL) {
+            hwcl->sourceCrop.right  = buffer->width;
+            hwcl->sourceCrop.bottom = buffer->height;
+        } else {
+            hwcl->sourceCrop.right  = mTransformedBounds.width();
+            hwcl->sourceCrop.bottom = mTransformedBounds.height();
+        }
+    }
 }
 
 void Layer::setPerFrameData(hwc_layer_t* hwcl) {
@@ -225,23 +243,6 @@
     } else {
         hwcl->handle = buffer->handle;
     }
-
-    if (isCropped()) {
-        hwcl->sourceCrop.left   = mCurrentCrop.left;
-        hwcl->sourceCrop.top    = mCurrentCrop.top;
-        hwcl->sourceCrop.right  = mCurrentCrop.right;
-        hwcl->sourceCrop.bottom = mCurrentCrop.bottom;
-    } else {
-        hwcl->sourceCrop.left   = 0;
-        hwcl->sourceCrop.top    = 0;
-        if (buffer != NULL) {
-            hwcl->sourceCrop.right  = buffer->width;
-            hwcl->sourceCrop.bottom = buffer->height;
-        } else {
-            hwcl->sourceCrop.right  = mTransformedBounds.width();
-            hwcl->sourceCrop.bottom = mTransformedBounds.height();
-        }
-    }
 }
 
 void Layer::onDraw(const Region& clip) const
@@ -416,8 +417,7 @@
             return;
         }
 
-        mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
-        mSurfaceTexture->getTransformMatrix(mTextureMatrix);
+        sp<GraphicBuffer> newFrontBuffer(mSurfaceTexture->getCurrentBuffer());
 
         const Rect crop(mSurfaceTexture->getCurrentCrop());
         const uint32_t transform(mSurfaceTexture->getCurrentTransform());
@@ -432,7 +432,23 @@
             mFlinger->invalidateHwcGeometry();
         }
 
-        mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
+        GLfloat textureMatrix[16];
+        mSurfaceTexture->getTransformMatrix(textureMatrix);
+        if (memcmp(textureMatrix, mTextureMatrix, sizeof(textureMatrix))) {
+            memcpy(mTextureMatrix, textureMatrix, sizeof(textureMatrix));
+            mFlinger->invalidateHwcGeometry();
+        }
+
+        uint32_t bufWidth  = newFrontBuffer->getWidth();
+        uint32_t bufHeight = newFrontBuffer->getHeight();
+        if (mActiveBuffer != NULL) {
+            if (bufWidth != uint32_t(mActiveBuffer->width) ||
+                bufHeight != uint32_t(mActiveBuffer->height)) {
+                mFlinger->invalidateHwcGeometry();
+            }
+        }
+
+        mCurrentOpacity = getOpacityForFormat(newFrontBuffer->format);
         if (oldOpacity != isOpaque()) {
             recomputeVisibleRegions = true;
         }
@@ -446,15 +462,14 @@
         // FIXME: mPostedDirtyRegion = dirty & bounds
         mPostedDirtyRegion.set(front.w, front.h);
 
+        // update active buffer
+        mActiveBuffer = newFrontBuffer;
 
         if ((front.w != front.requested_w) ||
             (front.h != front.requested_h))
         {
             // check that we received a buffer of the right size
             // (Take the buffer's orientation into account)
-            sp<GraphicBuffer> newFrontBuffer(mActiveBuffer);
-            uint32_t bufWidth  = newFrontBuffer->getWidth();
-            uint32_t bufHeight = newFrontBuffer->getHeight();
             if (mCurrentTransform & Transform::ROT_90) {
                 swap(bufWidth, bufHeight);
             }
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 4cc245a..603fb60 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -45,7 +45,6 @@
       mFlinger(flinger), mFiltering(false),
       mNeedsFiltering(false),
       mOrientation(0),
-      mLeft(0), mTop(0),
       mTransactionFlags(0),
       mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
       mInvalidate(0)
@@ -119,7 +118,7 @@
     return android_atomic_or(flags, &mTransactionFlags);
 }
 
-bool LayerBase::setPosition(int32_t x, int32_t y) {
+bool LayerBase::setPosition(float x, float y) {
     if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y)
         return false;
     mCurrentState.sequence++;
@@ -259,8 +258,6 @@
     mOrientation = tr.getOrientation();
     mTransform = tr;
     mTransformedBounds = tr.makeBounds(w, h);
-    mLeft = tr.tx();
-    mTop  = tr.ty();
 }
 
 void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
@@ -335,15 +332,16 @@
             reinterpret_cast<hwc_rect_t const *>(
                     visibleRegionScreen.getArray(
                             &hwcl->visibleRegionScreen.numRects));
+
+    hwcl->sourceCrop.left   = 0;
+    hwcl->sourceCrop.top    = 0;
+    hwcl->sourceCrop.right  = mTransformedBounds.width();
+    hwcl->sourceCrop.bottom = mTransformedBounds.height();
 }
 
 void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
     hwcl->compositionType = HWC_FRAMEBUFFER;
     hwcl->handle = NULL;
-    hwcl->sourceCrop.left   = 0;
-    hwcl->sourceCrop.top    = 0;
-    hwcl->sourceCrop.right  = mTransformedBounds.width();
-    hwcl->sourceCrop.bottom = mTransformedBounds.height();
 }
 
 void LayerBase::setFiltering(bool filtering)
@@ -476,10 +474,10 @@
     snprintf(buffer, SIZE,
             "+ %s %p\n"
             "      "
-            "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), "
+            "z=%9d, pos=(%g,%g), size=(%4d,%4d), "
             "isOpaque=%1d, needsDithering=%1d, invalidate=%1d, "
             "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
-            getTypeId(), this, s.z, tx(), ty(), s.w, s.h,
+            getTypeId(), this, s.z, s.transform.tx(), s.transform.ty(), s.w, s.h,
             isOpaque(), needsDithering(), contentDirty,
             s.alpha, s.flags,
             s.transform[0][0], s.transform[0][1],
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 2cd3a94..d20f06a 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -84,7 +84,7 @@
             String8 getName() const;
 
             // modify current state
-            bool setPosition(int32_t x, int32_t y);
+            bool setPosition(float x, float y);
             bool setLayer(uint32_t z);
             bool setSize(uint32_t w, uint32_t h);
             bool setAlpha(uint8_t alpha);
@@ -217,8 +217,6 @@
     inline  State&          currentState()          { return mCurrentState; }
 
     int32_t  getOrientation() const { return mOrientation; }
-    int  tx() const             { return mLeft; }
-    int  ty() const             { return mTop; }
     
 protected:
     const GraphicPlane& graphicPlane(int dpy) const;
@@ -250,8 +248,6 @@
                 Transform       mTransform;
                 GLfloat         mVertices[4][2];
                 Rect            mTransformedBounds;
-                int             mLeft;
-                int             mTop;
             
                 // these are protected by an external lock
                 State           mCurrentState;
diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
index 05b7527..ba345ce 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/services/surfaceflinger/Transform.cpp
@@ -91,12 +91,12 @@
     return type() > TRANSLATE;
 }
 
-int Transform::tx() const {
-    return floorf(mMatrix[2][0] + 0.5f);
+float Transform::tx() const {
+    return mMatrix[2][0];
 }
 
-int Transform::ty() const {
-    return floorf(mMatrix[2][1] + 0.5f);
+float Transform::ty() const {
+    return mMatrix[2][1];
 }
 
 void Transform::reset() {
@@ -239,7 +239,9 @@
             out.set(transform(reg.bounds()));
         }
     } else {
-        out = reg.translate(tx(), ty());
+        int xpos = floorf(tx() + 0.5f);
+        int ypos = floorf(ty() + 0.5f);
+        out = reg.translate(xpos, ypos);
     }
     return out;
 }
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 8fa5b86..ec74243 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -64,8 +64,8 @@
             uint32_t    getOrientation() const;
 
             float const* operator [] (int i) const;  // returns column i
-            int     tx() const;
-            int     ty() const;
+            float   tx() const;
+            float   ty() const;
 
             // modify the transform
             void        reset();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1954172..c59dd3c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -60,12 +60,13 @@
 
     /** @hide */
     public TelephonyManager(Context context) {
+        context = context.getApplicationContext();
         if (sContext == null) {
-            sContext = context.getApplicationContext();
+            sContext = context;
 
             sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                     "telephony.registry"));
-        } else {
+        } else if (sContext != context) {
             Log.e(TAG, "Hidden constructor called more than once per process!");
             Log.e(TAG, "Original: " + sContext.getPackageName() + ", new: " +
                     context.getPackageName());
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index bce9991..955849d 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -470,19 +470,6 @@
         } else if (isIccCardAdded) {
             mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
         }
-
-
-
-        /*
-         * TODO: We need to try to remove this, maybe if the RIL sends up a RIL_UNSOL_SIM_REFRESH?
-         */
-        if (oldState != State.READY && newState == State.READY &&
-                mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
-            if (mPhone.mIccRecords instanceof SIMRecords) {
-                ((SIMRecords)mPhone.mIccRecords).onSimReady();
-            }
-        }
-
     }
 
     private void onIccSwap(boolean isAdded) {
@@ -720,17 +707,14 @@
             currentRadioState == RadioState.SIM_NOT_READY     ||
             currentRadioState == RadioState.RUIM_NOT_READY    ||
             currentRadioState == RadioState.NV_NOT_READY      ||
-            (currentRadioState == RadioState.NV_READY &&
-                    (mPhone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE))) {
+            currentRadioState == RadioState.NV_READY) {
             return IccCard.State.NOT_READY;
         }
 
         if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT  ||
             currentRadioState == RadioState.SIM_READY             ||
             currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
-            currentRadioState == RadioState.RUIM_READY ||
-            (currentRadioState == RadioState.NV_READY &&
-                    (mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE))) {
+            currentRadioState == RadioState.RUIM_READY) {
 
             State csimState =
                 getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex());
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index e17d98d..6743da0 100755
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -36,6 +36,7 @@
 
 import android.content.res.Resources;
 
+import java.util.TimeZone;
 
 
 /**
@@ -231,7 +232,7 @@
     public static class TimeStamp extends Time {
 
         public TimeStamp() {
-            super(Time.TIMEZONE_UTC);
+            super(TimeZone.getDefault().getID());   // 3GPP2 timestamps use the local timezone
         }
 
         public static TimeStamp fromByteArray(byte[] data) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 28034cc..d84715e 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -181,11 +181,7 @@
         // recordsToLoad is set to 0 because no requests are made yet
         recordsToLoad = 0;
 
-        // SIMRecord is used by CDMA+LTE mode, and SIM_READY event
-        // will be subscribed by CdmaLteServiceStateTracker.
-        if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) {
-            p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null);
-        }
+        p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null);
         p.mCM.registerForOffOrNotAvailable(
                         this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
@@ -199,9 +195,7 @@
     @Override
     public void dispose() {
         //Unregister for all events
-        if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) {
-            phone.mCM.unregisterForSIMReady(this);
-        }
+        phone.mCM.unregisterForSIMReady(this);
         phone.mCM.unregisterForOffOrNotAvailable( this);
         phone.mCM.unregisterForIccRefresh(this);
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
index 643f709..1fa278a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimCard.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
@@ -49,7 +49,6 @@
         updateStateProperty();
 
         if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
-            mPhone.mCM.registerForNVReady(mHandler, EVENT_ICC_READY, null);
             mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
         }
     }
@@ -63,7 +62,6 @@
         mPhone.mCM.unregisterForSIMReady(mHandler);
 
         if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
-            mPhone.mCM.unregisterForNVReady(mHandler);
             mPhone.mCM.unregisterForIccStatusChanged(mHandler);
         }
     }
diff --git a/tests/BandwidthTests/Android.mk b/tests/BandwidthTests/Android.mk
new file mode 100644
index 0000000..7bc5f857
--- /dev/null
+++ b/tests/BandwidthTests/Android.mk
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PACKAGE_NAME := BandwidthEnforcementTest
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
diff --git a/tests/BandwidthTests/AndroidManifest.xml b/tests/BandwidthTests/AndroidManifest.xml
new file mode 100644
index 0000000..19f38ca
--- /dev/null
+++ b/tests/BandwidthTests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.bandwidthenforcement">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application>
+        <activity android:name=".BandwidthEnforcementTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <!-- adb shell am startservice -n com.android.tests.bandwidthenforcement/.BandwidthEnforcementTestService -->
+        <service android:name=".BandwidthEnforcementTestService" android:exported="true" />
+    </application>
+</manifest>
diff --git a/tests/BandwidthTests/res/layout/main.xml b/tests/BandwidthTests/res/layout/main.xml
new file mode 100644
index 0000000..3392b21
--- /dev/null
+++ b/tests/BandwidthTests/res/layout/main.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/app_name" />
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/app_desc" />
+</LinearLayout>
diff --git a/tests/BandwidthTests/res/values/strings.xml b/tests/BandwidthTests/res/values/strings.xml
new file mode 100644
index 0000000..a4a78c2
--- /dev/null
+++ b/tests/BandwidthTests/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">BandwidthEnforcementTest</string>
+    <string name="app_desc">Tries several tricks to get Internet access.</string>
+    <string name="start">Start</string>
+    <string name="stop">Stop</string>
+</resources>
diff --git a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestActivity.java b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestActivity.java
new file mode 100644
index 0000000..f0e43ac
--- /dev/null
+++ b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.bandwidthenforcement;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class BandwidthEnforcementTestActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
new file mode 100644
index 0000000..a2427f5
--- /dev/null
+++ b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.bandwidthenforcement;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.net.SntpClient;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.Random;
+
+import libcore.io.Streams;
+
+/*
+ * Test Service that tries to connect to the web via different methods and outputs the results to
+ * the log and a output file.
+ */
+public class BandwidthEnforcementTestService extends IntentService {
+    private static final String TAG = "BandwidthEnforcementTestService";
+    private static final String OUTPUT_FILE = "BandwidthEnforcementTestServiceOutputFile";
+
+    public BandwidthEnforcementTestService() {
+        super(TAG);
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        Log.d(TAG, "Trying to establish a connection.");
+        // Read output file path from intent.
+        String outputFile = intent.getStringExtra(OUTPUT_FILE);
+        dumpResult("testUrlConnection", testUrlConnection(), outputFile);
+        dumpResult("testUrlConnectionv6", testUrlConnectionv6(), outputFile);
+        dumpResult("testSntp", testSntp(), outputFile);
+        dumpResult("testDns", testDns(), outputFile);
+    }
+
+    public static void dumpResult(String tag, boolean result, String outputFile) {
+        Log.d(TAG, "Test output file: " + outputFile);
+        try {
+            if (outputFile != null){
+                File extStorage = Environment.getExternalStorageDirectory();
+                File outFile = new File(extStorage, outputFile);
+                FileWriter writer = new FileWriter(outFile, true);
+                BufferedWriter out = new BufferedWriter(writer);
+                if (result) {
+                    out.append(tag + ":fail\n");
+                } else {
+                    out.append(tag + ":pass\n");
+                }
+                out.close();
+            }
+            if (result) {
+                Log.e(TAG, tag + " FAILURE ====================");
+                Log.e(TAG, tag + " FAILURE was able to use data");
+                Log.e(TAG, tag + " FAILURE ====================");
+            } else {
+                Log.d(TAG, tag + " success; unable to use data");
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Could not write file " + e.getMessage());
+        }
+    }
+
+    /**
+     * Tests a normal http url connection.
+     * @return true if it was able to connect, false otherwise.
+     */
+    public static boolean testUrlConnection() {
+        try {
+            final HttpURLConnection conn = (HttpURLConnection) new URL("http://www.google.com/")
+            .openConnection();
+            try {
+                conn.connect();
+                final String content = Streams.readFully(
+                        new InputStreamReader(conn.getInputStream()));
+                if (content.contains("Google")) {
+                    return true;
+                }
+            } finally {
+                conn.disconnect();
+            }
+        } catch (IOException e) {
+            Log.d(TAG, "error: " + e);
+        }
+        return false;
+    }
+
+    /**
+     * Tests a ipv6 http url connection.
+     * @return true if it was able to connect, false otherwise.
+     */
+    public static boolean testUrlConnectionv6() {
+        try {
+            final HttpURLConnection conn = (HttpURLConnection) new URL("http://ipv6.google.com/")
+            .openConnection();
+            try {
+                conn.connect();
+                final String content = Streams.readFully(
+                        new InputStreamReader(conn.getInputStream()));
+                if (content.contains("Google")) {
+                    return true;
+                }
+            } finally {
+                conn.disconnect();
+            }
+        } catch (IOException e) {
+            Log.d(TAG, "error: " + e);
+        }
+        return false;
+    }
+
+    /**
+     * Tests to connect via sntp.
+     * @return true if it was able to connect, false otherwise.
+     */
+    public static boolean testSntp() {
+        final SntpClient client = new SntpClient();
+        if (client.requestTime("0.pool.ntp.org", 10000)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Tests dns query.
+     * @return true if it was able to connect, false otherwise.
+     */
+    public static boolean testDns() {
+        try {
+            final DatagramSocket socket = new DatagramSocket();
+            try {
+                socket.setSoTimeout(10000);
+
+                final byte[] query = buildDnsQuery("www", "android", "com");
+                final DatagramPacket queryPacket = new DatagramPacket(
+                        query, query.length, InetAddress.parseNumericAddress("8.8.8.8"), 53);
+                socket.send(queryPacket);
+
+                final byte[] reply = new byte[query.length];
+                final DatagramPacket replyPacket = new DatagramPacket(reply, reply.length);
+                socket.receive(replyPacket);
+                return true;
+
+            } finally {
+                socket.close();
+            }
+        } catch (IOException e) {
+            Log.d(TAG, "error: " + e);
+        }
+        return false;
+    }
+
+    /**
+     * Helper method to build a dns query
+     * @param query the dns strings of the server
+     * @return the byte array of the dns query to send
+     * @throws IOException
+     */
+    private static byte[] buildDnsQuery(String... query) throws IOException {
+        final Random random = new Random();
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        final byte[] id = new byte[2];
+        random.nextBytes(id);
+
+        out.write(id);
+        out.write(new byte[] { 0x01, 0x00 });
+        out.write(new byte[] { 0x00, 0x01 });
+        out.write(new byte[] { 0x00, 0x00 });
+        out.write(new byte[] { 0x00, 0x00 });
+        out.write(new byte[] { 0x00, 0x00 });
+
+        for (String phrase : query) {
+            final byte[] bytes = phrase.getBytes("US-ASCII");
+            out.write(bytes.length);
+            out.write(bytes);
+        }
+        out.write(0x00);
+
+        out.write(new byte[] { 0x00, 0x01 });
+        out.write(new byte[] { 0x00, 0x01 });
+
+        return out.toByteArray();
+    }
+}
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 3b3cbf3..49effa8 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -72,6 +72,7 @@
 import javax.sip.address.Address;
 import javax.sip.address.SipURI;
 import javax.sip.header.CSeqHeader;
+import javax.sip.header.ContactHeader;
 import javax.sip.header.ExpiresHeader;
 import javax.sip.header.FromHeader;
 import javax.sip.header.HeaderAddress;
@@ -873,16 +874,21 @@
         }
 
         private int getExpiryTime(Response response) {
-            int expires = EXPIRY_TIME;
-            ExpiresHeader expiresHeader = (ExpiresHeader)
-                    response.getHeader(ExpiresHeader.NAME);
-            if (expiresHeader != null) expires = expiresHeader.getExpires();
-            expiresHeader = (ExpiresHeader)
-                    response.getHeader(MinExpiresHeader.NAME);
-            if (expiresHeader != null) {
-                expires = Math.max(expires, expiresHeader.getExpires());
+            int time = -1;
+            ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME);
+            if (contact != null) {
+                time = contact.getExpires();
             }
-            return expires;
+            ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME);
+            if (expires != null && (time < 0 || time > expires.getExpires())) {
+                time = expires.getExpires();
+            }
+            expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME);
+            if (expires != null && time < expires.getExpires()) {
+                time = expires.getExpires();
+            }
+            Log.v(TAG, "Expiry time = " + time);
+            return (time > 0) ? time : EXPIRY_TIME;
         }
 
         private boolean registeringToReady(EventObject evt)
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 2cd54d9..450f816 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -284,8 +284,11 @@
                 break;
         }
 
+        //TODO: Add persist behavior once the supplicant interaction is fixed for both
+        // group and client scenarios
         /* Persist unless there is an explicit request to not do so*/
-        if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
+        //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
+
         if (joinExistingGroup) args.add("join");
 
         int groupOwnerIntent = config.groupOwnerIntent;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 4fc5e08..d116e5b 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1911,6 +1911,12 @@
                     transitionTo(mDriverUnloadingState);
                     break;
                 case CMD_START_SUPPLICANT:
+                    try {
+                        mNwService.wifiFirmwareReload(mInterfaceName, "STA");
+                    } catch (Exception e) {
+                        Log.e(TAG, "Failed to reload STA firmware " + e);
+                        // continue
+                    }
                     //A runtime crash can leave the interface up and
                     //this affects connectivity when supplicant starts up.
                     //Ensure interface is down before a supplicant start.