Merge "Make the SyncManager timeout syncs if it takes too long to bind to them."
diff --git a/api/current.txt b/api/current.txt
index e379f8b..21ee1f1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -370,6 +370,7 @@
     field public static final int dialogTitle = 16843250; // 0x10101f2
     field public static final int digits = 16843110; // 0x1010166
     field public static final int direction = 16843217; // 0x10101d1
+    field public static final int directionDescriptions = 16843695; // 0x10103af
     field public static final int directionPriority = 16843218; // 0x10101d2
     field public static final int disableDependentsState = 16843249; // 0x10101f1
     field public static final int disabledAlpha = 16842803; // 0x1010033
@@ -938,6 +939,7 @@
     field public static final int tag = 16842961; // 0x10100d1
     field public static final int targetActivity = 16843266; // 0x1010202
     field public static final int targetClass = 16842799; // 0x101002f
+    field public static final int targetDescriptions = 16843694; // 0x10103ae
     field public static final int targetDrawables = 16843654; // 0x1010386
     field public static final int targetPackage = 16842785; // 0x1010021
     field public static final int targetSdkVersion = 16843376; // 0x1010270
@@ -3277,6 +3279,7 @@
     method public void setArguments(android.os.Bundle);
     method public void setHasOptionsMenu(boolean);
     method public void setInitialSavedState(android.app.Fragment.SavedState);
+    method public void setMenuVisibility(boolean);
     method public void setRetainInstance(boolean);
     method public void setTargetFragment(android.app.Fragment, int);
     method public void startActivity(android.content.Intent);
@@ -3342,6 +3345,7 @@
     method public abstract java.lang.CharSequence getBreadCrumbTitle();
     method public abstract int getBreadCrumbTitleRes();
     method public abstract int getId();
+    method public abstract java.lang.String getName();
   }
 
   public static abstract interface FragmentManager.OnBackStackChangedListener {
@@ -4040,6 +4044,7 @@
 
   public abstract class BackupAgent extends android.content.ContextWrapper {
     ctor public BackupAgent();
+    method public final void fullBackupFile(java.io.File, android.app.backup.FullBackupDataOutput);
     method public abstract void onBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor) throws java.io.IOException;
     method public void onCreate();
     method public void onDestroy();
@@ -8643,6 +8648,7 @@
     method public void setEmpty();
     method public boolean setIntersect(android.graphics.RectF, android.graphics.RectF);
     method public void sort();
+    method public java.lang.String toShortString();
     method public void union(float, float, float, float);
     method public void union(android.graphics.RectF);
     method public void union(float, float);
@@ -10675,6 +10681,7 @@
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
     method public void setScreenOnWhilePlaying(boolean);
+    method public void setSurface(android.view.Surface);
     method public void setTexture(android.graphics.SurfaceTexture);
     method public void setVolume(float, float);
     method public void setWakeMode(android.content.Context, int);
@@ -12391,6 +12398,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();
@@ -12425,9 +12433,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";
@@ -12481,6 +12489,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;
   }
@@ -12496,11 +12505,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;
@@ -12521,8 +12532,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
@@ -12559,6 +12572,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;
   }
 
@@ -12575,6 +12590,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;
   }
 
@@ -15593,7 +15610,7 @@
   public static final class CalendarContract.Calendars implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.SyncColumns {
     field public static final java.lang.String CALENDAR_LOCATION = "calendar_location";
     field public static final android.net.Uri CONTENT_URI;
-    field public static final java.lang.String DEFAULT_SORT_ORDER = "displayName";
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "calendar_displayName";
     field public static final java.lang.String NAME = "name";
   }
 
@@ -16052,7 +16069,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";
@@ -16579,6 +16596,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 {
@@ -16677,6 +16704,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 {
@@ -16714,6 +16742,7 @@
     field public static final java.lang.String ACCOUNT_TYPE = "account_type";
     field public static final java.lang.String COMMENTS = "comments";
     field public static final java.lang.String CONTACT_ID = "contact_id";
+    field public static final java.lang.String CONTACT_LOOKUP_KEY = "contact_lookup";
     field public static final java.lang.String DATA_SET = "data_set";
     field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
     field public static final java.lang.String RAW_CONTACT_SOURCE_ID = "raw_contact_source_id";
@@ -20671,6 +20700,13 @@
     field protected final int mVerticalAlignment;
   }
 
+  public class EasyEditSpan implements android.text.ParcelableSpan {
+    ctor public EasyEditSpan();
+    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);
@@ -22508,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;
@@ -23008,7 +23045,6 @@
     method public boolean willNotDraw();
     field public static android.util.Property ALPHA;
     field protected static int DEFAULT_TEXT_DIRECTION;
-    field protected static float DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
     field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -25749,6 +25785,17 @@
     ctor public DigitalClock(android.content.Context, android.util.AttributeSet);
   }
 
+  public class EdgeEffect {
+    ctor public EdgeEffect(android.content.Context);
+    method public boolean draw(android.graphics.Canvas);
+    method public void finish();
+    method public boolean isFinished();
+    method public void onAbsorb(int);
+    method public void onPull(float);
+    method public void onRelease();
+    method public void setSize(int, int);
+  }
+
   public class EditText extends android.widget.TextView {
     ctor public EditText(android.content.Context);
     ctor public EditText(android.content.Context, android.util.AttributeSet);
@@ -26334,6 +26381,7 @@
     method public void fling(int, int, int, int, int, int, int, int);
     method public void fling(int, int, int, int, int, int, int, int, int, int);
     method public final void forceFinished(boolean);
+    method public float getCurrVelocity();
     method public final int getCurrX();
     method public final int getCurrY();
     method public final int getFinalX();
@@ -26676,6 +26724,7 @@
     method public void extendDuration(int);
     method public void fling(int, int, int, int, int, int, int, int);
     method public final void forceFinished(boolean);
+    method public float getCurrVelocity();
     method public final int getCurrX();
     method public final int getCurrY();
     method public final int getDuration();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1271ddd..41e3fdf 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4206,7 +4206,6 @@
             fragment.mContainerId = containerId;
             fragment.mTag = tag;
             fragment.mInLayout = true;
-            fragment.mImmediateActivity = this;
             fragment.mFragmentManager = mFragments;
             fragment.onInflate(this, attrs, fragment.mSavedFragmentState);
             mFragments.addFragment(fragment, true);
@@ -4222,7 +4221,6 @@
             // This fragment was retained from a previous instance; get it
             // going now.
             fragment.mInLayout = true;
-            fragment.mImmediateActivity = this;
             // If this fragment is newly instantiated (either right now, or
             // from last saved state), then give it the attributes to
             // initialize itself.
@@ -4473,9 +4471,12 @@
                     ManagedCursor mc = mManagedCursors.get(i);
                     if (mc.mReleased || mc.mUpdated) {
                         if (!mc.mCursor.requery()) {
-                            throw new IllegalStateException(
-                                    "trying to requery an already closed cursor  "
-                                    + mc.mCursor);
+                            if (getApplicationInfo().targetSdkVersion
+                                    >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                                throw new IllegalStateException(
+                                        "trying to requery an already closed cursor  "
+                                        + mc.mCursor);
+                            }
                         }
                         mc.mReleased = false;
                         mc.mUpdated = false;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e3075d7..8275cbd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1291,7 +1291,8 @@
         public final boolean queueIdle() {
             ActivityClientRecord a = mNewActivities;
             boolean stopProfiling = false;
-            if (mBoundApplication.profileFd != null && mBoundApplication.autoStopProfiler) {
+            if (mBoundApplication != null && mBoundApplication.profileFd != null
+                    && mBoundApplication.autoStopProfiler) {
                 stopProfiling = true;
             }
             if (a != null) {
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 93330a7..e9e8e16 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -344,10 +344,6 @@
     }
 
     private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
-        if (fragment.mImmediateActivity != null) {
-            throw new IllegalStateException("Fragment already added: " + fragment);
-        }
-        fragment.mImmediateActivity = mManager.mActivity;
         fragment.mFragmentManager = mManager;
 
         if (tag != null) {
@@ -388,11 +384,6 @@
     }
 
     public FragmentTransaction remove(Fragment fragment) {
-        if (fragment.mImmediateActivity == null) {
-            throw new IllegalStateException("Fragment not added: " + fragment);
-        }
-        fragment.mImmediateActivity = null;
-
         Op op = new Op();
         op.cmd = OP_REMOVE;
         op.fragment = fragment;
@@ -402,10 +393,6 @@
     }
 
     public FragmentTransaction hide(Fragment fragment) {
-        if (fragment.mImmediateActivity == null) {
-            throw new IllegalStateException("Fragment not added: " + fragment);
-        }
-
         Op op = new Op();
         op.cmd = OP_HIDE;
         op.fragment = fragment;
@@ -415,10 +402,6 @@
     }
 
     public FragmentTransaction show(Fragment fragment) {
-        if (fragment.mImmediateActivity == null) {
-            throw new IllegalStateException("Fragment not added: " + fragment);
-        }
-
         Op op = new Op();
         op.cmd = OP_SHOW;
         op.fragment = fragment;
@@ -428,10 +411,6 @@
     }
 
     public FragmentTransaction detach(Fragment fragment) {
-        //if (fragment.mImmediateActivity == null) {
-        //    throw new IllegalStateException("Fragment not added: " + fragment);
-        //}
-
         Op op = new Op();
         op.cmd = OP_DETACH;
         op.fragment = fragment;
@@ -441,10 +420,6 @@
     }
 
     public FragmentTransaction attach(Fragment fragment) {
-        //if (fragment.mImmediateActivity == null) {
-        //    throw new IllegalStateException("Fragment not added: " + fragment);
-        //}
-
         Op op = new Op();
         op.cmd = OP_ATTACH;
         op.fragment = fragment;
@@ -663,7 +638,6 @@
                 case OP_ADD: {
                     Fragment f = op.fragment;
                     f.mNextAnim = op.popExitAnim;
-                    f.mImmediateActivity = null;
                     mManager.removeFragment(f,
                             FragmentManagerImpl.reverseTransit(mTransition),
                             mTransitionStyle);
@@ -671,7 +645,6 @@
                 case OP_REPLACE: {
                     Fragment f = op.fragment;
                     f.mNextAnim = op.popExitAnim;
-                    f.mImmediateActivity = null;
                     mManager.removeFragment(f,
                             FragmentManagerImpl.reverseTransit(mTransition),
                             mTransitionStyle);
@@ -679,7 +652,6 @@
                         for (int i=0; i<op.removed.size(); i++) {
                             Fragment old = op.removed.get(i);
                             old.mNextAnim = op.popEnterAnim;
-                            f.mImmediateActivity = mManager.mActivity;
                             mManager.addFragment(old, false);
                         }
                     }
@@ -687,7 +659,6 @@
                 case OP_REMOVE: {
                     Fragment f = op.fragment;
                     f.mNextAnim = op.popEnterAnim;
-                    f.mImmediateActivity = mManager.mActivity;
                     mManager.addFragment(f, false);
                 } break;
                 case OP_HIDE: {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index a8621f8..3a08e6d 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -402,10 +402,6 @@
     // from all transactions.
     FragmentManager mFragmentManager;
 
-    // Set as soon as a fragment is added to a transaction (or removed),
-    // to be able to do validation.
-    Activity mImmediateActivity;
-    
     // Activity this fragment is attached to.
     Activity mActivity;
     
@@ -438,7 +434,10 @@
     
     // If set this fragment has menu items to contribute.
     boolean mHasMenu;
-    
+
+    // Set to true to allow the fragment's menu to be shown.
+    boolean mMenuVisible = true;
+
     // Used to verify that subclasses call through to super class.
     boolean mCalled;
     
@@ -888,7 +887,25 @@
             }
         }
     }
-    
+
+    /**
+     * Set a hint for whether this fragment's menu should be visible.  This
+     * is useful if you know that a fragment has been placed in your view
+     * hierarchy so that the user can not currently seen it, so any menu items
+     * it has should also not be shown.
+     *
+     * @param menuVisible The default is true, meaning the fragment's menu will
+     * be shown as usual.  If false, the user will not see the menu.
+     */
+    public void setMenuVisibility(boolean menuVisible) {
+        if (mMenuVisible != menuVisible) {
+            mMenuVisible = menuVisible;
+            if (mHasMenu && isAdded() && !isHidden()) {
+                mFragmentManager.invalidateOptionsMenu();
+            }
+        }
+    }
+
     /**
      * Return the LoaderManager for this fragment, creating it if needed.
      */
@@ -1233,7 +1250,7 @@
         mRestored = false;
         mBackStackNesting = 0;
         mFragmentManager = null;
-        mActivity = mImmediateActivity = null;
+        mActivity = null;
         mFragmentId = 0;
         mContainerId = 0;
         mTag = null;
@@ -1421,17 +1438,14 @@
                 writer.print(" mInLayout="); writer.println(mInLayout);
         writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
                 writer.print(" mDetached="); writer.print(mDetached);
-                writer.print(" mRetainInstance="); writer.print(mRetainInstance);
-                writer.print(" mRetaining="); writer.print(mRetaining);
+                writer.print(" mMenuVisible="); writer.print(mMenuVisible);
                 writer.print(" mHasMenu="); writer.println(mHasMenu);
+        writer.print(prefix); writer.print("mRetainInstance="); writer.print(mRetainInstance);
+                writer.print(" mRetaining="); writer.println(mRetaining);
         if (mFragmentManager != null) {
             writer.print(prefix); writer.print("mFragmentManager=");
                     writer.println(mFragmentManager);
         }
-        if (mImmediateActivity != null) {
-            writer.print(prefix); writer.print("mImmediateActivity=");
-                    writer.println(mImmediateActivity);
-        }
         if (mActivity != null) {
             writer.print(prefix); writer.print("mActivity=");
                     writer.println(mActivity);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 70e6866..7a6759f 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -67,6 +67,13 @@
         public int getId();
 
         /**
+         * Get the name that was supplied to
+         * {@link FragmentTransaction#addToBackStack(String)
+         * FragmentTransaction.addToBackStack(String)} when creating this entry.
+         */
+        public String getName();
+
+        /**
          * Return the full bread crumb title resource identifier for the entry,
          * or 0 if it does not have one.
          */
@@ -949,7 +956,6 @@
                             if (!f.mRetaining) {
                                 makeInactive(f);
                             } else {
-                                f.mImmediateActivity = null;
                                 f.mActivity = null;
                                 f.mFragmentManager = null;
                             }
@@ -1037,7 +1043,7 @@
             mAdded.add(fragment);
             fragment.mAdded = true;
             fragment.mRemoving = false;
-            if (fragment.mHasMenu) {
+            if (fragment.mHasMenu && fragment.mMenuVisible) {
                 mNeedMenuInvalidate = true;
             }
             if (moveToStateNow) {
@@ -1051,7 +1057,7 @@
         final boolean inactive = !fragment.isInBackStack();
         if (!fragment.mDetached || inactive) {
             mAdded.remove(fragment);
-            if (fragment.mHasMenu) {
+            if (fragment.mHasMenu && fragment.mMenuVisible) {
                 mNeedMenuInvalidate = true;
             }
             fragment.mAdded = false;
@@ -1086,7 +1092,7 @@
                     fragment.mView.setVisibility(View.GONE);
                 }
             }
-            if (fragment.mAdded && fragment.mHasMenu) {
+            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                 mNeedMenuInvalidate = true;
             }
             fragment.onHiddenChanged(true);
@@ -1106,7 +1112,7 @@
                 }
                 fragment.mView.setVisibility(View.VISIBLE);
             }
-            if (fragment.mAdded && fragment.mHasMenu) {
+            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                 mNeedMenuInvalidate = true;
             }
             fragment.onHiddenChanged(false);
@@ -1120,7 +1126,7 @@
             if (fragment.mAdded) {
                 // We are not already in back stack, so need to remove the fragment.
                 mAdded.remove(fragment);
-                if (fragment.mHasMenu) {
+                if (fragment.mHasMenu && fragment.mMenuVisible) {
                     mNeedMenuInvalidate = true;
                 }
                 fragment.mAdded = false;
@@ -1136,7 +1142,7 @@
             if (!fragment.mAdded) {
                 mAdded.add(fragment);
                 fragment.mAdded = true;
-                if (fragment.mHasMenu) {
+                if (fragment.mHasMenu && fragment.mMenuVisible) {
                     mNeedMenuInvalidate = true;
                 }
                 moveToState(fragment, mCurState, transition, transitionStyle);
@@ -1640,7 +1646,6 @@
                             "No instantiated fragment for index #" + fms.mAdded[i]);
                 }
                 f.mAdded = true;
-                f.mImmediateActivity = mActivity;
                 if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f);
                 mAdded.add(f);
             }
@@ -1748,7 +1753,7 @@
         if (mActive != null) {
             for (int i=0; i<mAdded.size(); i++) {
                 Fragment f = mAdded.get(i);
-                if (f != null && !f.mHidden && f.mHasMenu) {
+                if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
                     show = true;
                     f.onCreateOptionsMenu(menu, inflater);
                     if (newMenus == null) {
@@ -1778,7 +1783,7 @@
         if (mActive != null) {
             for (int i=0; i<mAdded.size(); i++) {
                 Fragment f = mAdded.get(i);
-                if (f != null && !f.mHidden && f.mHasMenu) {
+                if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
                     show = true;
                     f.onPrepareOptionsMenu(menu);
                 }
@@ -1791,7 +1796,7 @@
         if (mActive != null) {
             for (int i=0; i<mAdded.size(); i++) {
                 Fragment f = mAdded.get(i);
-                if (f != null && !f.mHidden && f.mHasMenu) {
+                if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
                     if (f.onOptionsItemSelected(item)) {
                         return true;
                     }
@@ -1819,7 +1824,7 @@
         if (mActive != null) {
             for (int i=0; i<mAdded.size(); i++) {
                 Fragment f = mAdded.get(i);
-                if (f != null && !f.mHidden && f.mHasMenu) {
+                if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
                     f.onOptionsMenuClosed(menu);
                 }
             }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 58ea0c3..9542874 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -255,10 +255,7 @@
      * will be delivered to the backup destination along with the metadata necessary
      * to place it with the proper location and permissions on the device where the
      * data is restored.
-     * @hide
      *
-     * @param context The BackupAgent that is calling this method.  It is an error to
-     *     call it from something other than a running BackupAgent instance.
      * @param file The file to be backed up.  The file must exist and be readable by
      *     the caller.
      * @param output The destination to which the backed-up file data will be sent.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2236928..254c98f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -22,6 +22,7 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -348,6 +349,8 @@
 
     private final IBluetooth mService;
 
+    private Handler mServiceRecordHandler;
+
     /**
      * Get a handle to the default local Bluetooth adapter.
      * <p>Currently Android only supports one Bluetooth adapter, but the API
@@ -376,6 +379,7 @@
             throw new IllegalArgumentException("service is null");
         }
         mService = service;
+        mServiceRecordHandler = null;
     }
 
     /**
@@ -1011,7 +1015,21 @@
             } catch (IOException e) {}
             throw new IOException("Not able to register SDP record for " + name);
         }
-        socket.setCloseHandler(mHandler, handle);
+
+        if (mServiceRecordHandler == null) {
+            mServiceRecordHandler = new Handler(Looper.getMainLooper()) {
+                    public void handleMessage(Message msg) {
+                        /* handle socket closing */
+                        int handle = msg.what;
+                        try {
+                            if (DBG) Log.d(TAG, "Removing service record " +
+                                           Integer.toHexString(handle));
+                            mService.removeServiceRecord(handle);
+                        } catch (RemoteException e) {Log.e(TAG, "", e);}
+                    }
+                };
+        }
+        socket.setCloseHandler(mServiceRecordHandler, handle);
         return socket;
     }
 
@@ -1243,17 +1261,6 @@
         return Collections.unmodifiableSet(devices);
     }
 
-    private Handler mHandler = new Handler() {
-        public void handleMessage(Message msg) {
-            /* handle socket closing */
-            int handle = msg.what;
-            try {
-                if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle));
-                mService.removeServiceRecord(handle);
-            } catch (RemoteException e) {Log.e(TAG, "", e);}
-        }
-    };
-
     /**
      * Validate a Bluetooth address, such as "00:43:A8:23:10:F0"
      * <p>Alphabetic characters must be uppercase to be valid.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8d6cee1..c7698bf 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5627,6 +5627,10 @@
             if (scheme != null) {
                 if (scheme.equalsIgnoreCase("tel")) {
                     b.append("tel:xxx-xxx-xxxx");
+                } else if (scheme.equalsIgnoreCase("sip")) {
+                    b.append("sip:xxxxxxxxxx");
+                } else if (scheme.equalsIgnoreCase("sms")) {
+                    b.append("sms:xxx-xxx-xxxx");
                 } else if (scheme.equalsIgnoreCase("smsto")) {
                     b.append("smsto:xxx-xxx-xxxx");
                 } else {
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index b6487bd..5fe42db 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -36,6 +36,8 @@
     DataSetObservable mDataSetObservable = new DataSetObservable();
     ContentObservable mContentObservable = new ContentObservable();
 
+    Bundle mExtras = Bundle.EMPTY;
+
     /* -------------------------------------------------------- */
     /* These need to be implemented by subclasses */
     abstract public int getCount();
@@ -71,11 +73,11 @@
     public int getColumnCount() {
         return getColumnNames().length;
     }
-    
+
     public void deactivate() {
         deactivateInternal();
     }
-    
+
     /**
      * @hide
      */
@@ -99,7 +101,7 @@
     public boolean isClosed() {
         return mClosed;
     }
-    
+
     public void close() {
         mClosed = true;
         mContentObservable.unregisterAll();
@@ -120,7 +122,7 @@
         return true;
     }
 
-    
+
     public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
         // Default implementation, uses getString
         String result = getString(columnIndex);
@@ -136,7 +138,7 @@
             buffer.sizeCopied = 0;
         }
     }
-    
+
     /* -------------------------------------------------------- */
     /* Implementation */
     public AbstractCursor() {
@@ -181,7 +183,7 @@
 
         return result;
     }
-    
+
     /**
      * Copy data from cursor to CursorWindow
      * @param position start position of data
@@ -199,7 +201,7 @@
             window.setStartPosition(position);
             int columnNum = getColumnCount();
             window.setNumColumns(columnNum);
-            while (moveToNext() && window.allocRow()) {            
+            while (moveToNext() && window.allocRow()) {
                 for (int i = 0; i < columnNum; i++) {
                     String field = getString(i);
                     if (field != null) {
@@ -215,7 +217,7 @@
                     }
                 }
             }
-            
+
             mPos = oldpos;
         } catch (IllegalStateException e){
             // simply ignore it
@@ -314,7 +316,7 @@
             mContentObservable.unregisterObserver(observer);
         }
     }
-    
+
     /**
      * This is hidden until the data set change model has been re-evaluated.
      * @hide
@@ -322,14 +324,14 @@
     protected void notifyDataSetChange() {
         mDataSetObservable.notifyChanged();
     }
-    
+
     /**
      * This is hidden until the data set change model has been re-evaluated.
      * @hide
      */
     protected DataSetObservable getDataSetObservable() {
         return mDataSetObservable;
-        
+
     }
 
     public void registerDataSetObserver(DataSetObserver observer) {
@@ -383,8 +385,19 @@
         return false;
     }
 
+    /**
+     * Sets a {@link Bundle} that will be returned by {@link #getExtras()}.  <code>null</code> will
+     * be converted into {@link Bundle#EMPTY}.
+     *
+     * @param extras {@link Bundle} to set.
+     * @hide
+     */
+    public void setExtras(Bundle extras) {
+        mExtras = (extras == null) ? Bundle.EMPTY : extras;
+    }
+
     public Bundle getExtras() {
-        return Bundle.EMPTY;
+        return mExtras;
     }
 
     public Bundle respond(Bundle extras) {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index bc45945..63f2244 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1468,6 +1468,7 @@
         private static final String KEY_MAX_NUM_DETECTED_FACES_HW = "max-num-detected-faces-hw";
         private static final String KEY_MAX_NUM_DETECTED_FACES_SW = "max-num-detected-faces-sw";
         private static final String KEY_RECORDING_HINT = "recording-hint";
+        private static final String KEY_VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported";
 
         // Parameter key suffix for supported values.
         private static final String SUPPORTED_VALUES_SUFFIX = "-values";
@@ -3210,6 +3211,35 @@
             set(KEY_RECORDING_HINT, hint ? TRUE : FALSE);
         }
 
+        /**
+         * Returns true if video snapshot is supported. That is, applications
+         * can call {@link #takePicture(Camera.ShutterCallback,
+         * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}
+         * during recording. Applications do not need to call {@link
+         * #startPreview()} after taking a picture. The preview will be still
+         * active. Other than that, taking a picture during recording is
+         * identical to taking a picture normally. All settings and methods
+         * related to takePicture work identically. Ex: {@link
+         * #getPictureSize()}, {@link #getSupportedPictureSizes()}, {@link
+         * #setJpegQuality(int)}, {@link #setRotation(int)}, and etc. The
+         * picture will have an EXIF header. {@link #FLASH_MODE_AUTO} and {@link
+         * #FLASH_MODE_ON} also still work, but the video will record the flash.
+         *
+         * Applications can set shutter callback as null to avoid the shutter
+         * sound. It is also recommended to set raw picture and post view
+         * callbacks to null to avoid the interrupt of preview display.
+         *
+         * Field-of-view of the recorded video may be different from that of the
+         * captured pictures.
+         *
+         * @return true if video snapshot is supported.
+         * @hide
+         */
+        public boolean isVideoSnapshotSupported() {
+            String str = get(KEY_VIDEO_SNAPSHOT_SUPPORTED);
+            return TRUE.equals(str);
+        }
+
         // Splits a comma delimited string to an ArrayList of String.
         // Return null if the passing string is null or the size is 0.
         private ArrayList<String> split(String str) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9c96883..530122c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -23,6 +23,7 @@
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.RemoteException;
+import android.provider.Settings;
 
 import java.net.InetAddress;
 
@@ -71,6 +72,15 @@
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
+     * Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any
+     * applicable {@link Settings.Secure#CONNECTIVITY_CHANGE_DELAY}.
+     *
+     * @hide
+     */
+    public static final String CONNECTIVITY_ACTION_IMMEDIATE =
+            "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE";
+
+    /**
      * The lookup key for a {@link NetworkInfo} object. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      *
@@ -804,4 +814,22 @@
         } catch (RemoteException e) {
         }
     }
+
+    /**
+     * Returns true if the hardware supports the given network type
+     * else it returns false.  This doesn't indicate we have coverage
+     * or are authorized onto a network, just whether or not the
+     * hardware supports it.  For example a gsm phone without a sim
+     * should still return true for mobile data, but a wifi only tablet
+     * would return false.
+     * @param networkType The nework type we'd like to check
+     * @return true if supported, else false
+     * @hide
+     */
+    public boolean isNetworkSupported(int networkType) {
+        try {
+            return mService.isNetworkSupported(networkType);
+        } catch (RemoteException e) {}
+        return false;
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index c9553c0..eef658e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -43,6 +43,8 @@
     NetworkInfo getNetworkInfo(int networkType);
     NetworkInfo[] getAllNetworkInfo();
 
+    boolean isNetworkSupported(int networkType);
+
     LinkProperties getActiveLinkProperties();
     LinkProperties getLinkProperties(int networkType);
 
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/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 6fe5124..9ba1fdb 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -599,7 +599,7 @@
         /**
          * The default sort order for this table
          */
-        public static final String DEFAULT_SORT_ORDER = "displayName";
+        public static final String DEFAULT_SORT_ORDER = CALENDAR_DISPLAY_NAME;
 
         /**
          * The name of the calendar. Column name.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index e9221fe..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 {
@@ -3083,12 +3149,25 @@
         /**
          * A reference to the {@link android.provider.ContactsContract.Contacts#_ID}
          * that this stream item belongs to.
+         *
+         * <p>Type: INTEGER</p>
+         * <p>read-only</p>
          */
         public static final String CONTACT_ID = "contact_id";
 
         /**
+         * A reference to the {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY}
+         * that this stream item belongs to.
+         *
+         * <p>Type: TEXT</p>
+         * <p>read-only</p>
+         */
+        public static final String CONTACT_LOOKUP_KEY = "contact_lookup";
+
+        /**
          * A reference to the {@link RawContacts#_ID}
          * that this stream item belongs to.
+         * <p>Type: INTEGER</p>
          */
         public static final String RAW_CONTACT_ID = "raw_contact_id";
 
@@ -3104,7 +3183,7 @@
          * The account type to which the raw_contact of this item is associated. See
          * {@link RawContacts#ACCOUNT_TYPE}
          *
-         * <p>TYPE: text</p>
+         * <p>Type: TEXT</p>
          * <p>read-only</p>
          */
         public static final String ACCOUNT_TYPE = "account_type";
@@ -3113,7 +3192,7 @@
          * The account name to which the raw_contact of this item is associated. See
          * {@link RawContacts#ACCOUNT_NAME}
          *
-         * <p>TYPE: text</p>
+         * <p>Type: TEXT</p>
          * <p>read-only</p>
          */
         public static final String ACCOUNT_NAME = "account_name";
@@ -4529,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.
@@ -4674,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/provider/Settings.java b/core/java/android/provider/Settings.java
index 0c791e1..efdb79e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1738,6 +1738,14 @@
         public static final String POINTER_LOCATION = "pointer_location";
 
         /**
+         * Show touch positions on screen?
+         * 0 = no
+         * 1 = yes
+         * @hide
+         */
+        public static final String SHOW_TOUCHES = "show_touches";
+
+        /**
          * Log raw orientation data from {@link WindowOrientationListener} for use with the
          * orientationplot.py tool.
          * 0 = no
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index e942969..55a0624 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -117,6 +117,7 @@
 
     private static final int MESSAGE_UUID_INTENT = 1;
     private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2;
+    private static final int MESSAGE_REMOVE_SERVICE_RECORD = 3;
 
     private static final int RFCOMM_RECORD_REAPER = 10;
     private static final int STATE_CHANGE_REAPER = 11;
@@ -537,6 +538,10 @@
                 }
                 if (attempt > 0) mBondState.clearPinAttempts(address);
                 break;
+            case MESSAGE_REMOVE_SERVICE_RECORD:
+                Pair<Integer, Integer> pair = (Pair<Integer, Integer>) msg.obj;
+                checkAndRemoveRecord(pair.first, pair.second);
+                break;
             }
         }
     };
@@ -1542,7 +1547,9 @@
     public void removeServiceRecord(int handle) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                                 "Need BLUETOOTH permission");
-        checkAndRemoveRecord(handle, Binder.getCallingPid());
+        Message message = mHandler.obtainMessage(MESSAGE_REMOVE_SERVICE_RECORD);
+        message.obj = new Pair<Integer, Integer>(handle, Binder.getCallingPid());
+        mHandler.sendMessage(message);
     }
 
     private synchronized void checkAndRemoveRecord(int handle, int pid) {
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..355d467 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -24,6 +24,7 @@
 import android.text.style.BackgroundColorSpan;
 import android.text.style.BulletSpan;
 import android.text.style.CharacterStyle;
+import android.text.style.EasyEditSpan;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.MetricAffectingSpan;
@@ -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
@@ -745,9 +748,13 @@
                     break;
 
                 case SUGGESTION_RANGE_SPAN:
-                    readSpan(p, sp, new SuggestionRangeSpan());
+                    readSpan(p, sp, new SuggestionRangeSpan(p));
                     break;
                     
+                case EASY_EDIT_SPAN:
+                    readSpan(p, sp, new EasyEditSpan());
+                    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..2feb719
--- /dev/null
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -0,0 +1,50 @@
+/*
+ * 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
+    }
+
+    @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/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index fc91697..a637b1c 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -16,7 +16,6 @@
 
 package android.text.style;
 
-import android.graphics.Color;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
@@ -29,12 +28,20 @@
  * @hide
  */
 public class SuggestionRangeSpan extends CharacterStyle implements ParcelableSpan {
+    private final int mBackgroundColor;
+
     @Override
     public void updateDrawState(TextPaint tp) {
-        tp.setColor(Color.GREEN);            
+        tp.bgColor = mBackgroundColor;
     }
 
-    public SuggestionRangeSpan() { /* Nothing to do*/ }
+    public SuggestionRangeSpan(int color) {
+        mBackgroundColor = color;
+    }
+
+    public SuggestionRangeSpan(Parcel src) {
+        mBackgroundColor = src.readInt();
+    }
 
     @Override
     public int describeContents() {
@@ -42,7 +49,9 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) { /* Nothing to do*/ }
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mBackgroundColor);
+    }
 
     @Override
     public int getSpanTypeId() {
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/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 2bf16d8..1fcde3d 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -344,9 +344,9 @@
         static final int EGL_SURFACE_TYPE = 0x3033;
         static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;        
 
-        private static final int SURFACE_STATE_ERROR = 0;
-        private static final int SURFACE_STATE_SUCCESS = 1;
-        private static final int SURFACE_STATE_UPDATED = 2;
+        static final int SURFACE_STATE_ERROR = 0;
+        static final int SURFACE_STATE_SUCCESS = 1;
+        static final int SURFACE_STATE_UPDATED = 2;
         
         static EGL10 sEgl;
         static EGLDisplay sEglDisplay;
@@ -913,8 +913,7 @@
 
         @Override
         void destroyLayers(View view) {
-            if (view != null && isEnabled()) {
-                checkCurrent();
+            if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) {
                 destroyHardwareLayer(view);
                 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
             }
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/TextureView.java b/core/java/android/view/TextureView.java
index 9d959fb..c735d6b 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -286,6 +286,11 @@
     }
 
     @Override
+    boolean destroyLayer() {
+        return false;
+    }
+
+    @Override
     HardwareLayer getHardwareLayer() {
         if (mLayer == null) {
             if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7713bd8..fa1d249 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2595,11 +2595,6 @@
     protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
 
     /**
-     * Default threshold for "char count" heuristic.
-     */
-    protected static float DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD = 0.6f;
-
-    /**
      * The text direction that has been defined by {@link #setTextDirection(int)}.
      *
      * {@hide}
@@ -6052,23 +6047,29 @@
      * @see #onHoverChanged
      */
     public boolean onHoverEvent(MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_HOVER_ENTER:
-                if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
-                    mSendingHoverAccessibilityEvents = true;
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
-                }
-                break;
-            case MotionEvent.ACTION_HOVER_EXIT:
-                if (mSendingHoverAccessibilityEvents) {
-                    mSendingHoverAccessibilityEvents = false;
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
-                }
-                break;
+        // The root view may receive hover (or touch) events that are outside the bounds of
+        // the window.  This code ensures that we only send accessibility events for
+        // hovers that are actually within the bounds of the root view.
+        final int action = event.getAction();
+        if (!mSendingHoverAccessibilityEvents) {
+            if ((action == MotionEvent.ACTION_HOVER_ENTER
+                    || action == MotionEvent.ACTION_HOVER_MOVE)
+                    && !hasHoveredChild()
+                    && pointInView(event.getX(), event.getY())) {
+                mSendingHoverAccessibilityEvents = true;
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+            }
+        } else {
+            if (action == MotionEvent.ACTION_HOVER_EXIT
+                    || (action == MotionEvent.ACTION_HOVER_MOVE
+                            && !pointInView(event.getX(), event.getY()))) {
+                mSendingHoverAccessibilityEvents = false;
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+            }
         }
 
         if (isHoverable()) {
-            switch (event.getAction()) {
+            switch (action) {
                 case MotionEvent.ACTION_HOVER_ENTER:
                     setHovered(true);
                     break;
@@ -6497,6 +6498,8 @@
             if (mParent instanceof ViewGroup) {
                 ((ViewGroup) mParent).onChildVisibilityChanged(this, (flags & VISIBILITY_MASK));
                 ((View) mParent).invalidate(true);
+            } else if (mParent != null) {
+                mParent.invalidateChild(this, null);
             }
             dispatchVisibilityChanged(this, (flags & VISIBILITY_MASK));
         }
@@ -9359,7 +9362,6 @@
 
         if (mAttachInfo != null) {
             mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_MSG, this);
-            mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_RECT_MSG, this);
         }
 
         mCurrentAnimation = null;
@@ -13907,6 +13909,7 @@
                         }
 
                         public void onReleased(InvalidateInfo element) {
+                            element.target = null;
                         }
                     }, POOL_LIMIT)
             );
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a0cc287..7db1b32 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);
@@ -2136,6 +2141,10 @@
 
     void dispatchDetachedFromWindow() {
         if (mView != null && mView.mAttachInfo != null) {
+            if (mAttachInfo.mHardwareRenderer != null &&
+                    mAttachInfo.mHardwareRenderer.isEnabled()) {
+                mAttachInfo.mHardwareRenderer.validate();
+            }
             mView.dispatchDetachedFromWindow();
         }
 
@@ -2152,13 +2161,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);
@@ -3564,6 +3572,11 @@
         checkThread();
         if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
         synchronized (this) {
+            if (mAdded) {
+                mAdded = false;
+                dispatchDetachedFromWindow();
+            }
+
             if (mAdded && !mFirst) {
                 destroyHardwareRenderer();
 
@@ -3584,10 +3597,6 @@
 
                 mSurface.release();
             }
-            if (mAdded) {
-                mAdded = false;
-                dispatchDetachedFromWindow();
-            }
         }
     }
 
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/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1b4edf1..dc1bbd7 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -20,6 +20,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.IBinder;
 import android.os.LocalPowerManager;
 import android.view.animation.Animation;
@@ -165,7 +166,7 @@
          * 
          * @return Rect The rectangle holding the shown window frame.
          */
-        public Rect getShownFrameLw();
+        public RectF getShownFrameLw();
 
         /**
          * Retrieve the frame of the display that this window was last
@@ -871,6 +872,12 @@
     public void systemReady();
 
     /**
+     * Called when the system is done booting to the point where the
+     * user can start interacting with it.
+     */
+    public void systemBooted();
+
+    /**
      * Show boot time message to the user.
      */
     public void showBootMessage(final CharSequence msg, final boolean always);
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 5d4fbbe..76b47ca 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -332,7 +332,7 @@
         // we perform an orientation change under ideal conditions.  It will take
         // proportionally longer than this to effect an orientation change when
         // the proposed orientation confidence is low.
-        private static final float ORIENTATION_SETTLE_TIME_MS = 100;
+        private static final float ORIENTATION_SETTLE_TIME_MS = 250;
 
         // The confidence that we have abount effecting each orientation change.
         // When one of these values exceeds 1.0, we have determined our new orientation!
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/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index 4264e9d..ef1641d 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -28,6 +28,7 @@
 
     static {
         System.loadLibrary("webcore");
+        System.loadLibrary("chromium_net");
     }
     private static final String LOGTAG = "webkit";
     private JniUtil() {} // Utility class, do not instantiate.
diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java
index ff5b30b..e906f7f 100644
--- a/core/java/android/webkit/OverScrollGlow.java
+++ b/core/java/android/webkit/OverScrollGlow.java
@@ -22,7 +22,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.view.View;
-import android.widget.EdgeGlow;
+import android.widget.EdgeEffect;
 
 /**
  * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges.
@@ -31,10 +31,10 @@
 public class OverScrollGlow {
     private WebView mHostView;
 
-    private EdgeGlow mEdgeGlowTop;
-    private EdgeGlow mEdgeGlowBottom;
-    private EdgeGlow mEdgeGlowLeft;
-    private EdgeGlow mEdgeGlowRight;
+    private EdgeEffect mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowBottom;
+    private EdgeEffect mEdgeGlowLeft;
+    private EdgeEffect mEdgeGlowRight;
 
     private int mOverScrollDeltaX;
     private int mOverScrollDeltaY;
@@ -42,13 +42,10 @@
     public OverScrollGlow(WebView host) {
         mHostView = host;
         Context context = host.getContext();
-        final Resources res = context.getResources();
-        final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-        final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-        mEdgeGlowTop = new EdgeGlow(context, edge, glow);
-        mEdgeGlowBottom = new EdgeGlow(context, edge, glow);
-        mEdgeGlowLeft = new EdgeGlow(context, edge, glow);
-        mEdgeGlowRight = new EdgeGlow(context, edge, glow);
+        mEdgeGlowTop = new EdgeEffect(context);
+        mEdgeGlowBottom = new EdgeEffect(context);
+        mEdgeGlowLeft = new EdgeEffect(context);
+        mEdgeGlowRight = new EdgeEffect(context);
     }
 
     /**
diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java
index 0fc76fa..5f91ed3 100644
--- a/core/java/android/webkit/ViewStateSerializer.java
+++ b/core/java/android/webkit/ViewStateSerializer.java
@@ -36,11 +36,15 @@
 
     static boolean serializeViewState(OutputStream stream, WebView web)
             throws IOException {
+        int baseLayer = web.getBaseLayer();
+        if (baseLayer == 0) {
+            return false;
+        }
         DataOutputStream dos = new DataOutputStream(stream);
         dos.writeInt(VERSION);
         dos.writeInt(web.getContentWidth());
         dos.writeInt(web.getContentHeight());
-        return nativeSerializeViewState(web.getBaseLayer(), dos,
+        return nativeSerializeViewState(baseLayer, dos,
                 new byte[WORKING_STREAM_STORAGE]);
     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2aa481c..2e7f923 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -34,7 +34,6 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.CornerPathEffect;
 import android.graphics.DrawFilter;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
@@ -58,8 +57,6 @@
 import android.os.StrictMode;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
-import android.text.Selection;
-import android.text.Spannable;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Log;
@@ -606,9 +603,15 @@
     // know to handle Shift and arrows natively first
     private boolean mAccessibilityScriptInjected;
 
+    static final boolean USE_JAVA_TEXT_SELECTION = true;
+    private Region mTextSelectionRegion = new Region();
+    private Paint mTextSelectionPaint;
+    private Drawable mSelectHandleLeft;
+    private Drawable mSelectHandleRight;
+
     static final boolean USE_WEBKIT_RINGS = true;
     // the color used to highlight the touch rectangles
-    private static final int mHightlightColor = 0x6633b5e5;
+    private static final int HIGHLIGHT_COLOR = 0x6633b5e5;
     // the round corner for the highlight path
     private static final float TOUCH_HIGHLIGHT_ARC = 5.0f;
     // the region indicating where the user touched on the screen
@@ -4004,12 +4007,9 @@
         // state.
         // If mNativeClass is 0, we should not reach here, so we do not
         // need to check it again.
-        if (mDrawCursorRing && drawRings) {
-            // Only update if we are actually going to use the result
-            nativeRecordButtons(hasFocus() && hasWindowFocus(),
-                    mTouchMode == TOUCH_SHORTPRESS_START_MODE
-                    || mTrackballDown || mGotCenterDown, false);
-        }
+        nativeRecordButtons(hasFocus() && hasWindowFocus(),
+                (mTouchMode == TOUCH_SHORTPRESS_START_MODE && !USE_WEBKIT_RINGS)
+                || mTrackballDown || mGotCenterDown, false);
         drawCoreAndCursorRing(canvas, mBackgroundColor,
                 mDrawCursorRing && drawRings);
     }
@@ -4050,8 +4050,10 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
+        // if mNativeClass is 0, the WebView is either destroyed or not
+        // initialized. In either case, just draw the background color and return
         if (mNativeClass == 0) {
+            canvas.drawColor(mBackgroundColor);
             return;
         }
 
@@ -4105,7 +4107,7 @@
             } else {
                 if (mTouchHightlightPaint == null) {
                     mTouchHightlightPaint = new Paint();
-                    mTouchHightlightPaint.setColor(mHightlightColor);
+                    mTouchHightlightPaint.setColor(HIGHLIGHT_COLOR);
                 }
                 RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
                 Rect r = new Rect();
@@ -4306,6 +4308,9 @@
     }
 
     int getBaseLayer() {
+        if (mNativeClass == 0) {
+            return 0;
+        }
         return nativeGetBaseLayer();
     }
 
@@ -4400,7 +4405,7 @@
         int extras = DRAW_EXTRAS_NONE;
         if (mFindIsUp) {
             extras = DRAW_EXTRAS_FIND;
-        } else if (mSelectingText) {
+        } else if (mSelectingText && !USE_JAVA_TEXT_SELECTION) {
             extras = DRAW_EXTRAS_SELECTION;
             nativeSetSelectionPointer(mDrawSelectionPointer,
                     mZoomManager.getInvScale(),
@@ -4427,6 +4432,10 @@
                 nativeUseHardwareAccelSkia(mHardwareAccelSkia);
             }
 
+            if (mSelectingText && USE_JAVA_TEXT_SELECTION) {
+                drawTextSelectionHandles(canvas);
+            }
+
         } else {
             DrawFilter df = null;
             if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
@@ -4461,6 +4470,56 @@
         }
     }
 
+    private void drawTextSelectionHandles(Canvas canvas) {
+        if (mTextSelectionPaint == null) {
+            mTextSelectionPaint = new Paint();
+            mTextSelectionPaint.setColor(HIGHLIGHT_COLOR);
+        }
+        mTextSelectionRegion.setEmpty();
+        nativeGetTextSelectionRegion(mTextSelectionRegion);
+        Rect r = new Rect();
+        RegionIterator iter = new RegionIterator(mTextSelectionRegion);
+        int start_x = -1;
+        int start_y = -1;
+        int end_x = -1;
+        int end_y = -1;
+        while (iter.next(r)) {
+            r = new Rect(
+                    contentToViewDimension(r.left),
+                    contentToViewDimension(r.top),
+                    contentToViewDimension(r.right),
+                    contentToViewDimension(r.bottom));
+            // Regions are in order. First one is where selection starts,
+            // last one is where it ends
+            if (start_x < 0 || start_y < 0) {
+                start_x = r.left;
+                start_y = r.bottom;
+            }
+            end_x = r.right;
+            end_y = r.bottom;
+            canvas.drawRect(r, mTextSelectionPaint);
+        }
+        if (mSelectHandleLeft == null) {
+            mSelectHandleLeft = mContext.getResources().getDrawable(
+                    com.android.internal.R.drawable.text_select_handle_left);
+        }
+        // Magic formula copied from TextView
+        start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4;
+        mSelectHandleLeft.setBounds(start_x, start_y,
+                start_x + mSelectHandleLeft.getIntrinsicWidth(),
+                start_y + mSelectHandleLeft.getIntrinsicHeight());
+        if (mSelectHandleRight == null) {
+            mSelectHandleRight = mContext.getResources().getDrawable(
+                    com.android.internal.R.drawable.text_select_handle_right);
+        }
+        end_x -= mSelectHandleRight.getIntrinsicWidth() / 4;
+        mSelectHandleRight.setBounds(end_x, end_y,
+                end_x + mSelectHandleRight.getIntrinsicWidth(),
+                end_y + mSelectHandleRight.getIntrinsicHeight());
+        mSelectHandleLeft.draw(canvas);
+        mSelectHandleRight.draw(canvas);
+    }
+
     // draw history
     private boolean mDrawHistory = false;
     private Picture mHistoryPicture = null;
@@ -9124,7 +9183,9 @@
 
     /** @hide send content invalidate */
     protected void contentInvalidateAll() {
-        mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
+        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
+            mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
+        }
     }
 
     /** @hide call pageSwapCallback upon next page swap */
@@ -9327,4 +9388,5 @@
     private native int      nativeGetBackgroundColor();
     native boolean  nativeSetProperty(String key, String value);
     native String   nativeGetProperty(String key);
+    private native void     nativeGetTextSelectionRegion(Region region);
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 3ca3eaa..73a30c4 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -57,13 +57,14 @@
     private static final String LOGTAG = "webcore";
 
     static {
-        // Load libwebcore during static initialization. This happens in the
-        // zygote process so it will be shared read-only across all app
-        // processes.
+        // Load libwebcore and libchromium_net during static initialization.
+        // This happens in the zygote process so they will be shared read-only
+        // across all app processes.
         try {
             System.loadLibrary("webcore");
+            System.loadLibrary("chromium_net");
         } catch (UnsatisfiedLinkError e) {
-            Log.e(LOGTAG, "Unable to load webcore library");
+            Log.e(LOGTAG, "Unable to load native support libraries.");
         }
     }
 
@@ -1040,6 +1041,7 @@
         // Flag for blocking messages. This is used during DESTROY to avoid
         // posting more messages to the EventHub or to WebView's event handler.
         private boolean mBlockMessages;
+        private boolean mDestroying;
 
         private int mTid;
         private int mSavedPriority;
@@ -1071,12 +1073,20 @@
                                 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
                                 + " obj=" + msg.obj);
                     }
-                    if (mWebView == null
+                    if (mWebView == null || mNativeClass == 0) {
+                        if (DebugFlags.WEB_VIEW_CORE) {
+                            Log.w(LOGTAG, "Rejecting message " + msg.what
+                                    + " because we are destroyed");
+                        }
+                        return;
+                    }
+                    if (mDestroying == true
                             && msg.what != EventHub.RESUME_TIMERS
-                            && msg.what != EventHub.PAUSE_TIMERS) {
+                            && msg.what != EventHub.PAUSE_TIMERS
+                            && msg.what != EventHub.DESTROY) {
                         if (DebugFlags.WEB_VIEW_CORE) {
                             Log.v(LOGTAG, "Rejecting message " + msg.what
-                                    + " because we are destroyed");
+                                    + " because we are being destroyed");
                         }
                         return;
                     }
@@ -1780,7 +1790,8 @@
             // or RESUME_TIMERS messages, which we must still handle as they
             // are per process. DESTROY will instead trigger a white list in
             // mEventHub, skipping any remaining messages in the queue
-            mEventHub.sendMessageAtFrontOfQueue(
+            mEventHub.mDestroying = true;
+            mEventHub.sendMessage(
                     Message.obtain(null, EventHub.DESTROY));
             mEventHub.blockMessages();
         }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 80cbbfb..05d4f05 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -594,12 +594,12 @@
     /**
      * Tracks the state of the top edge glow.
      */
-    private EdgeGlow mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowTop;
 
     /**
      * Tracks the state of the bottom edge glow.
      */
-    private EdgeGlow mEdgeGlowBottom;
+    private EdgeEffect mEdgeGlowBottom;
 
     /**
      * An estimate of how many pixels are between the top of the list and
@@ -788,11 +788,8 @@
         if (mode != OVER_SCROLL_NEVER) {
             if (mEdgeGlowTop == null) {
                 Context context = getContext();
-                final Resources res = context.getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowTop = new EdgeGlow(context, edge, glow);
-                mEdgeGlowBottom = new EdgeGlow(context, edge, glow);
+                mEdgeGlowTop = new EdgeEffect(context);
+                mEdgeGlowBottom = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowTop = null;
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeEffect.java
similarity index 77%
rename from core/java/android/widget/EdgeGlow.java
rename to core/java/android/widget/EdgeEffect.java
index 75cef38..fd2abc2 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -16,7 +16,10 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.view.animation.AnimationUtils;
@@ -24,19 +27,33 @@
 import android.view.animation.Interpolator;
 
 /**
- * This class performs the glow effect used at the edges of scrollable widgets.
- * @hide
+ * This class performs the graphical effect used at the edges of scrollable widgets
+ * when the user scrolls beyond the content bounds in 2D space.
+ *
+ * <p>EdgeEffect is stateful. Custom widgets using EdgeEffect should create an
+ * instance for each edge that should show the effect, feed it input data using
+ * the methods {@link #onAbsorb(int)}, {@link #onPull(float)}, and {@link #onRelease()},
+ * and draw the effect using {@link #draw(Canvas)} in the widget's overridden
+ * {@link android.view.View#draw(Canvas)} method. If {@link #isFinished()} returns
+ * false after drawing, the edge effect's animation is not yet complete and the widget
+ * should schedule another drawing pass to continue the animation.</p>
+ *
+ * <p>When drawing, widgets should draw their main content and child views first,
+ * usually by invoking <code>super.draw(canvas)</code> from an overridden <code>draw</code>
+ * method. (This will invoke onDraw and dispatch drawing to child views as needed.)
+ * The edge effect may then be drawn on top of the view's content using the
+ * {@link #draw(Canvas)} method.</p>
  */
-public class EdgeGlow {
-    private static final String TAG = "EdgeGlow";
+public class EdgeEffect {
+    private static final String TAG = "EdgeEffect";
 
     // Time it will take the effect to fully recede in ms
     private static final int RECEDE_TIME = 1000;
 
-    // Time it will take before a pulled glow begins receding
+    // Time it will take before a pulled glow begins receding in ms
     private static final int PULL_TIME = 167;
 
-    // Time it will take for a pulled glow to decay to partial strength before release
+    // Time it will take in ms for a pulled glow to decay to partial strength before release
     private static final int PULL_DECAY_TIME = 1000;
 
     private static final float MAX_ALPHA = 0.8f;
@@ -103,31 +120,58 @@
 
     private float mPullDistance;
 
-    public EdgeGlow(Context context, Drawable edge, Drawable glow) {
-        mEdge = edge;
-        mGlow = glow;
+    /**
+     * Construct a new EdgeEffect with a theme appropriate for the provided context.
+     * @param context Context used to provide theming and resource information for the EdgeEffect
+     */
+    public EdgeEffect(Context context) {
+        final Resources res = context.getResources();
+        mEdge = res.getDrawable(R.drawable.overscroll_edge);
+        mGlow = res.getDrawable(R.drawable.overscroll_glow);
 
         mMinWidth = (int) (context.getResources().getDisplayMetrics().density * MIN_WIDTH + 0.5f);
         mInterpolator = new DecelerateInterpolator();
     }
 
+    /**
+     * Set the size of this edge effect in pixels.
+     *
+     * @param width Effect width in pixels
+     * @param height Effect height in pixels
+     */
     public void setSize(int width, int height) {
         mWidth = width;
         mHeight = height;
     }
 
+    /**
+     * Reports if this EdgeEffect's animation is finished. If this method returns false
+     * after a call to {@link #draw(Canvas)} the host widget should schedule another
+     * drawing pass to continue the animation.
+     *
+     * @return true if animation is finished, false if drawing should continue on the next frame.
+     */
     public boolean isFinished() {
         return mState == STATE_IDLE;
     }
 
+    /**
+     * Immediately finish the current animation.
+     * After this call {@link #isFinished()} will return true.
+     */
     public void finish() {
         mState = STATE_IDLE;
     }
 
     /**
-     * Call when the object is pulled by the user.
+     * A view should call this when content is pulled away from an edge by the user.
+     * This will update the state of the current visual effect and its associated animation.
+     * The host view should always {@link android.view.View#invalidate()} after this
+     * and draw the results accordingly.
      *
-     * @param deltaDistance Change in distance since the last call
+     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+     *                      1.f (full length of the view) or negative values to express change
+     *                      back toward the edge reached to initiate the effect.
      */
     public void onPull(float deltaDistance) {
         final long now = AnimationUtils.currentAnimationTimeMillis();
@@ -173,6 +217,9 @@
 
     /**
      * Call when the object is released after being pulled.
+     * This will begin the "decay" phase of the effect. After calling this method
+     * the host view should {@link android.view.View#invalidate()} and thereby
+     * draw the results accordingly.
      */
     public void onRelease() {
         mPullDistance = 0;
@@ -198,6 +245,11 @@
 
     /**
      * Call when the effect absorbs an impact at the given velocity.
+     * Used when a fling reaches the scroll boundary.
+     *
+     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
+     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
+     * to use here.</p>
      *
      * @param velocity Velocity at impact in pixels per second.
      */
@@ -238,7 +290,7 @@
     /**
      * Draw into the provided canvas. Assumes that the canvas has been rotated
      * accordingly and the size has been set. The effect will be drawn the full
-     * width of X=0 to X=width, emitting from Y=0 and extending to some factor <
+     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
      * 1.f of height.
      *
      * @param canvas Canvas to draw into
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d638732..a7a05be 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -75,8 +75,8 @@
 
     private final Rect mTempRect = new Rect();
     private OverScroller mScroller;
-    private EdgeGlow mEdgeGlowLeft;
-    private EdgeGlow mEdgeGlowRight;
+    private EdgeEffect mEdgeGlowLeft;
+    private EdgeEffect mEdgeGlowRight;
 
     /**
      * Position of the last motion event.
@@ -1477,11 +1477,8 @@
         if (mode != OVER_SCROLL_NEVER) {
             if (mEdgeGlowLeft == null) {
                 Context context = getContext();
-                final Resources res = context.getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowLeft = new EdgeGlow(context, edge, glow);
-                mEdgeGlowRight = new EdgeGlow(context, edge, glow);
+                mEdgeGlowLeft = new EdgeEffect(context);
+                mEdgeGlowRight = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowLeft = null;
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 0ba7889..542a1ef 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -166,7 +166,6 @@
     }
 
     /**
-     * @hide
      * Returns the absolute value of the current velocity.
      *
      * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 09c875b..6a6bc23 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -69,8 +69,8 @@
 
     private final Rect mTempRect = new Rect();
     private OverScroller mScroller;
-    private EdgeGlow mEdgeGlowTop;
-    private EdgeGlow mEdgeGlowBottom;
+    private EdgeEffect mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowBottom;
 
     /**
      * Position of the last motion event.
@@ -1511,11 +1511,8 @@
         if (mode != OVER_SCROLL_NEVER) {
             if (mEdgeGlowTop == null) {
                 Context context = getContext();
-                final Resources res = context.getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowTop = new EdgeGlow(context, edge, glow);
-                mEdgeGlowBottom = new EdgeGlow(context, edge, glow);
+                mEdgeGlowTop = new EdgeEffect(context);
+                mEdgeGlowBottom = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowTop = null;
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index f00640e..a6e83f0 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -196,7 +196,6 @@
     }
     
     /**
-     * @hide
      * Returns the current velocity.
      *
      * @return The original velocity less the deceleration. Result may be
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index ff13dcb..14cbf6f 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -1,4 +1,18 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
+/*
+ * 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.widget;
 
@@ -32,7 +46,7 @@
 
     private final TextView mTextView;
 
-    final SpellCheckerSession spellCheckerSession;
+    final SpellCheckerSession mSpellCheckerSession;
     final int mCookie;
 
     // Paired arrays for the (id, spellCheckSpan) pair. mIndex is the next available position
@@ -49,7 +63,7 @@
 
         final TextServicesManager textServicesManager = (TextServicesManager) textView.getContext().
                 getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
-        spellCheckerSession = textServicesManager.newSpellCheckerSession(
+        mSpellCheckerSession = textServicesManager.newSpellCheckerSession(
                 null /* not currently used by the textServicesManager */, Locale.getDefault(),
                 this, true /* means use the languages defined in Settings */);
         mCookie = hashCode();
@@ -112,6 +126,8 @@
 
     private void scheduleSpellCheck() {
         if (mLength == 0) return;
+        if (mSpellCheckerSession == null) return;
+
         if (mChecker != null) {
             mTextView.removeCallbacks(mChecker);
         }
@@ -155,7 +171,7 @@
                 System.arraycopy(textInfos, 0, textInfosCopy, 0, textInfosCount);
                 textInfos = textInfosCopy;
             }
-            spellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
+            mSpellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
                     false /* TODO Set sequentialWords to true for initial spell check */);
         }
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e9662ae..1aa009b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -63,6 +63,7 @@
 import android.text.TextDirectionHeuristics;
 import android.text.TextPaint;
 import android.text.TextUtils;
+import android.text.TextUtils.TruncateAt;
 import android.text.TextWatcher;
 import android.text.method.AllCapsTransformationMethod;
 import android.text.method.ArrowKeyMovementMethod;
@@ -82,6 +83,7 @@
 import android.text.method.TransformationMethod2;
 import android.text.method.WordIterator;
 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;
@@ -128,6 +130,7 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView.OnItemClickListener;
 import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.util.FastMath;
@@ -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,150 @@
         }
     }
 
+    /**
+     * 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() {
+            LinearLayout linearLayout = new LinearLayout(TextView.this.getContext());
+            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+            mContentView = linearLayout;
+            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 +7957,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 +7993,10 @@
                     + " what=" + what + ": " + buf);
             TextView.this.spanChange(buf, what, s, -1, e, -1);
         }
+
+        private void hideControllers() {
+            mEditTextShortcutController.hide();
+        }
     }
 
     /**
@@ -7945,17 +8179,6 @@
         startStopMarquee(hasWindowFocus);
     }
 
-    private void removeSpans(int start, int end, Class<?> type) {
-        if (mText instanceof Editable) {
-            Editable editable = ((Editable) mText);
-            Object[] spans = editable.getSpans(start, end, type);
-            final int length = spans.length;
-            for (int i = 0; i < length; i++) {
-                editable.removeSpan(spans[i]);
-            }
-        }
-    }
-
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -8261,7 +8484,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 +8514,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 +9153,7 @@
                 stopSelectionActionMode();
             } else {
                 selectCurrentWord();
+                getSelectionController().show();
             }
             handled = true;
         }
@@ -9091,7 +9317,7 @@
 
     private abstract class PinnedPopupWindow implements TextViewPositionListener {
         protected PopupWindow mPopupWindow;
-        protected LinearLayout mContentView;
+        protected ViewGroup mContentView;
         int mPositionX, mPositionY;
 
         protected abstract void createPopupWindow();
@@ -9107,23 +9333,16 @@
             mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
             mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
 
-            mContentView = new LinearLayout(TextView.this.getContext());
-            LayoutParams wrapContent = new LayoutParams(
-                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+            initContentView();
+
+            LayoutParams wrapContent = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT);
             mContentView.setLayoutParams(wrapContent);
 
-            initContentView();
             mPopupWindow.setContentView(mContentView);
         }
 
         public void show() {
-            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
-            mContentView.measure(
-                    View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels,
-                            View.MeasureSpec.AT_MOST),
-                    View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels,
-                            View.MeasureSpec.AT_MOST));
-
             TextView.this.getPositionListener().addSubscriber(this, false);
 
             computeLocalPosition();
@@ -9131,11 +9350,24 @@
             final PositionListener positionListener = TextView.this.getPositionListener();
             updatePosition(positionListener.getPositionX(), positionListener.getPositionY());
         }
+        
+        protected void measureContent() {
+            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+            mContentView.measure(
+                    View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels,
+                            View.MeasureSpec.AT_MOST),
+                    View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels,
+                            View.MeasureSpec.AT_MOST));
+        }
 
+        /* The popup window will be horizontally centered on the getTextOffset() and vertically
+         * positioned according to viewportToContentHorizontalOffset.
+         * 
+         * This method assumes that mContentView has properly been measured from its content. */
         private void computeLocalPosition() {
-            final int offset = getTextOffset();
-
+            measureContent();
             final int width = mContentView.getMeasuredWidth();
+            final int offset = getTextOffset();
             mPositionX = (int) (mLayout.getPrimaryHorizontal(offset) - width / 2.0f);
             mPositionX += viewportToContentHorizontalOffset();
 
@@ -9183,14 +9415,17 @@
         }
     }
 
-    private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnClickListener {
+    private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
         private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
         private static final int NO_SUGGESTIONS = -1;
         private static final float AVERAGE_HIGHLIGHTS_PER_SUGGESTION = 1.4f;
         private WordIterator mSuggestionWordIterator;
         private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan
                 [(int) (AVERAGE_HIGHLIGHTS_PER_SUGGESTION * MAX_NUMBER_SUGGESTIONS)];
+        private SuggestionInfo[] mSuggestionInfos;
+        private int mNumberOfSuggestions;
         private boolean mCursorWasVisibleBeforeSuggestions;
+        private SuggestionAdapter mSuggestionsAdapter;
 
         private class CustomPopupWindow extends PopupWindow {
             public CustomPopupWindow(Context context, int defStyle) {
@@ -9231,29 +9466,16 @@
 
         @Override
         protected void initContentView() {
-            mContentView.setOrientation(LinearLayout.VERTICAL);
-
-            LayoutInflater inflater = (LayoutInflater) TextView.this.mContext.
-                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-            if (inflater == null) {
-                throw new IllegalArgumentException(
-                        "Unable to create inflater for TextEdit suggestions");
-            }
+            ListView listView = new ListView(TextView.this.getContext());
+            mSuggestionsAdapter = new SuggestionAdapter();
+            listView.setAdapter(mSuggestionsAdapter);
+            listView.setOnItemClickListener(this);
+            mContentView = listView;
 
             // Inflate the suggestion items once and for all.
+            mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS];
             for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
-                View childView = inflater.inflate(mTextEditSuggestionItemLayout,
-                        mContentView, false);
-
-                if (! (childView instanceof TextView)) {
-                    throw new IllegalArgumentException(
-                            "Inflated TextEdit suggestion item is not a TextView: " + childView);
-                }
-
-                childView.setTag(new SuggestionInfo());
-                mContentView.addView(childView);
-                childView.setOnClickListener(this);
+                mSuggestionInfos[i] = new SuggestionInfo();
             }
         }
 
@@ -9262,6 +9484,40 @@
             int spanStart, spanEnd; // range in TextView where text should be inserted
             SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
             int suggestionIndex; // the index of the suggestion inside suggestionSpan
+            SpannableStringBuilder text = new SpannableStringBuilder();
+        }
+
+        private class SuggestionAdapter extends BaseAdapter {
+            private LayoutInflater mInflater = (LayoutInflater) TextView.this.mContext.
+                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+            @Override
+            public int getCount() {
+                return mNumberOfSuggestions;
+            }
+
+            @Override
+            public Object getItem(int position) {
+                return mSuggestionInfos[position];
+            }
+
+            @Override
+            public long getItemId(int position) {
+                return position;
+            }
+
+            @Override
+            public View getView(int position, View convertView, ViewGroup parent) {
+                TextView textView = (TextView) convertView;
+
+                if (textView == null) {
+                    textView = (TextView) mInflater.inflate(mTextEditSuggestionItemLayout, parent,
+                            false);
+                }
+
+                textView.setText(mSuggestionInfos[position].text);
+                return textView;
+            }
         }
 
         /**
@@ -9306,6 +9562,36 @@
         }
 
         @Override
+        protected void measureContent() {
+            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+            final int horizontalMeasure = View.MeasureSpec.makeMeasureSpec(
+                    displayMetrics.widthPixels, View.MeasureSpec.AT_MOST);
+            final int verticalMeasure = View.MeasureSpec.makeMeasureSpec(
+                    displayMetrics.heightPixels, View.MeasureSpec.AT_MOST);
+            
+            int width = 0;
+            View view = null;
+            for (int i = 0; i < mNumberOfSuggestions; i++) {
+                view = mSuggestionsAdapter.getView(i, view, mContentView);
+                view.measure(horizontalMeasure, verticalMeasure);
+                width = Math.max(width, view.getMeasuredWidth());
+            }
+
+            // Enforce the width based on actual text widths
+            mContentView.measure(
+                    View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                    verticalMeasure);
+
+            Drawable popupBackground = mPopupWindow.getBackground();
+            if (popupBackground != null) {
+                if (mTempRect == null) mTempRect = new Rect();
+                popupBackground.getPadding(mTempRect);
+                width += mTempRect.left + mTempRect.right;
+            }
+            mPopupWindow.setWidth(width);
+        }
+
+        @Override
         protected int getTextOffset() {
             return getSelectionStart();
         }
@@ -9333,7 +9619,7 @@
 
             final int nbSpans = suggestionSpans.length;
 
-            int totalNbSuggestions = 0;
+            mNumberOfSuggestions = 0;
             int spanUnionStart = mText.length();
             int spanUnionEnd = 0;
 
@@ -9347,17 +9633,15 @@
                 String[] suggestions = suggestionSpan.getSuggestions();
                 int nbSuggestions = suggestions.length;
                 for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
-                    TextView textView = (TextView) mContentView.getChildAt(
-                            totalNbSuggestions);
-                    textView.setText(suggestions[suggestionIndex]);
-                    SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
+                    SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions];
                     suggestionInfo.spanStart = spanStart;
                     suggestionInfo.spanEnd = spanEnd;
                     suggestionInfo.suggestionSpan = suggestionSpan;
                     suggestionInfo.suggestionIndex = suggestionIndex;
+                    suggestionInfo.text = new SpannableStringBuilder(suggestions[suggestionIndex]);
 
-                    totalNbSuggestions++;
-                    if (totalNbSuggestions == MAX_NUMBER_SUGGESTIONS) {
+                    mNumberOfSuggestions++;
+                    if (mNumberOfSuggestions == MAX_NUMBER_SUGGESTIONS) {
                         // Also end outer for loop
                         spanIndex = nbSpans;
                         break;
@@ -9365,23 +9649,18 @@
                 }
             }
 
-            if (totalNbSuggestions == 0) return false;
+            if (mNumberOfSuggestions == 0) return false;
 
-            if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+            if (mSuggestionRangeSpan == null) mSuggestionRangeSpan =
+                    new SuggestionRangeSpan(mHighlightColor);
+
             ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
-            for (int i = 0; i < totalNbSuggestions; i++) {
-                final TextView textView = (TextView) mContentView.getChildAt(i);
-                highlightTextDifferences(textView, spanUnionStart, spanUnionEnd);
+            for (int i = 0; i < mNumberOfSuggestions; i++) {
+                highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd);
             }
-
-            for (int i = 0; i < totalNbSuggestions; i++) {
-                mContentView.getChildAt(i).setVisibility(VISIBLE);
-            }
-            for (int i = totalNbSuggestions; i < MAX_NUMBER_SUGGESTIONS; i++) {
-                mContentView.getChildAt(i).setVisibility(GONE);
-            }
+            mSuggestionsAdapter.notifyDataSetChanged();
 
             return true;
         }
@@ -9448,13 +9727,13 @@
             return highlightSpan;
         }
 
-        private void highlightTextDifferences(TextView textView, int unionStart, int unionEnd) {
-            SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
+        private void highlightTextDifferences(SuggestionInfo suggestionInfo,
+                int unionStart, int unionEnd) {
             final int spanStart = suggestionInfo.spanStart;
             final int spanEnd = suggestionInfo.spanEnd;
 
             // Remove all text formating by converting to Strings
-            final String text = textView.getText().toString();
+            final String text = suggestionInfo.text.toString();
             final String sourceText = mText.subSequence(spanStart, spanEnd).toString();
 
             long[] sourceWordLimits = getWordLimits(sourceText);
@@ -9529,7 +9808,7 @@
             if (previousCommonWordIndex < words.length - 1) {
                 int firstDifferentPosition = previousCommonWordIndex < 0 ? 0 :
                     extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
-                int lastDifferentPosition = textView.length();
+                int lastDifferentPosition = text.length();
                 ssb.setSpan(highlightSpan(nbHighlightSpans++),
                         shift + firstDifferentPosition, shift + lastDifferentPosition,
                         Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -9540,25 +9819,24 @@
 
                 int lastCommonTextWordEnd = previousCommonWordIndex < 0 ? 0 :
                     extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
-                String textSpaces = text.substring(lastCommonTextWordEnd, textView.length());
+                String textSpaces = text.substring(lastCommonTextWordEnd, text.length());
 
                 if (!sourceSpaces.equals(textSpaces) && textSpaces.length() > 0) {
                     ssb.setSpan(highlightSpan(nbHighlightSpans++),
-                            shift + lastCommonTextWordEnd, shift + textView.length(),
+                            shift + lastCommonTextWordEnd, shift + text.length(),
                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                 }
             }
 
             // Final part, text after the current suggestion range.
             ssb.append(mText.subSequence(spanEnd, unionEnd).toString());
-            textView.setText(ssb);
         }
 
         @Override
-        public void onClick(View view) {
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             if (view instanceof TextView) {
                 TextView textView = (TextView) view;
-                SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
+                SuggestionInfo suggestionInfo = mSuggestionInfos[position];
                 final int spanStart = suggestionInfo.spanStart;
                 final int spanEnd = suggestionInfo.spanEnd;
                 if (spanStart != NO_SUGGESTIONS) {
@@ -9919,9 +10197,11 @@
 
         @Override
         protected void initContentView() {
-            mContentView.setOrientation(LinearLayout.HORIZONTAL);
+            LinearLayout linearLayout = new LinearLayout(TextView.this.getContext());
+            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+            mContentView = linearLayout;
             mContentView.setBackgroundResource(
-                    com.android.internal.R.drawable.text_edit_side_paste_window);
+                    com.android.internal.R.drawable.text_edit_paste_window);
 
             LayoutInflater inflater = (LayoutInflater)TextView.this.mContext.
                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -10694,6 +10974,10 @@
     private void hideControllers() {
         hideInsertionPointCursorController();
         stopSelectionActionMode();
+
+        if (mChangeWatcher != null) {
+            mChangeWatcher.hideControllers();
+        }
     }
 
     /**
@@ -11007,7 +11291,7 @@
     private final TextPaint         mTextPaint;
     private boolean                 mUserSetTextScaleX;
     private final Paint             mHighlightPaint;
-    private int                     mHighlightColor = 0xCC475925;
+    private int                     mHighlightColor = 0x4C33B5E5;
     /**
      * This is temporarily visible to fix bug 3085564 in webView. Do not rely on
      * this field being protected. Will be restored as private when lineHeight
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/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index 3961de3..1c47ca88a 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -16,88 +16,369 @@
 
 package com.android.internal.widget;
 
-import com.android.internal.R;
+import java.lang.ref.WeakReference;
 
+import com.android.internal.widget.LockScreenWidgetCallback;
+import com.android.internal.widget.LockScreenWidgetInterface;
+
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
+import android.media.MediaMetadataRetriever;
+import android.media.RemoteControlClient;
+import android.media.IRemoteControlDisplay;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
 
-/**
- * A special widget for displaying audio playback ("transport controls") in LockScreen.
- *
- */
-public class TransportControlView extends LinearLayout implements LockScreenWidgetInterface,
-        OnClickListener {
-    private static final String TAG = "TransportControlView";
-    static final int sViewIds[] = { R.id.control_prev, R.id.control_pauseplay, R.id.control_next };
-    protected static final int AUDIO_FOCUS_CHANGED = 100;
-    private LockScreenWidgetCallback mCallback;
 
+import com.android.internal.R;
+
+public class TransportControlView extends FrameLayout implements OnClickListener,
+        LockScreenWidgetInterface {
+
+    private static final int MSG_UPDATE_STATE = 100;
+    private static final int MSG_SET_METADATA = 101;
+    private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
+    private static final int MSG_SET_ARTWORK = 103;
+    private static final int MSG_SET_GENERATION_ID = 104;
+    private static final int MAXDIM = 512;
+    protected static final boolean DEBUG = true;
+    protected static final String TAG = "TransportControlView";
+
+    private ImageView mAlbumArt;
+    private TextView mTrackTitle;
+    private ImageView mBtnPrev;
+    private ImageView mBtnPlay;
+    private ImageView mBtnNext;
+    private int mClientGeneration;
+    private Metadata mMetadata = new Metadata();
+    private boolean mAttached;
+    private ComponentName mClientName;
+    private int mTransportControlFlags;
+    private int mPlayState;
+    private AudioManager mAudioManager;
+    private LockScreenWidgetCallback mWidgetCallbacks;
+    private IRemoteControlDisplayWeak mIRCD;
+
+    /**
+     * The metadata which should be populated into the view once we've been attached
+     */
+    private Bundle mPopulateMetadataWhenAttached = null;
+
+    // This handler is required to ensure messages from IRCD are handled in sequence and on
+    // the UI thread.
     private Handler mHandler = new Handler() {
+        @Override
         public void handleMessage(Message msg) {
-            switch (msg.what){
-            case AUDIO_FOCUS_CHANGED:
-                handleAudioFocusChange(msg.arg1);
+            switch (msg.what) {
+            case MSG_UPDATE_STATE:
+                if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
+                break;
+
+            case MSG_SET_METADATA:
+                if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
+                break;
+
+            case MSG_SET_TRANSPORT_CONTROLS:
+                if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
+                break;
+
+            case MSG_SET_ARTWORK:
+                if (mClientGeneration == msg.arg1) {
+                    mMetadata.bitmap = (Bitmap) msg.obj;
+                    mAlbumArt.setImageBitmap(mMetadata.bitmap);
+                }
+                break;
+
+            case MSG_SET_GENERATION_ID:
+                if (mWidgetCallbacks != null) {
+                    boolean clearing = msg.arg2 != 0;
+                    if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + clearing);
+                    if (!clearing) {
+                        mWidgetCallbacks.requestShow(TransportControlView.this);
+                    } else {
+                        mWidgetCallbacks.requestHide(TransportControlView.this);
+                    }
+                }
+                mClientGeneration = msg.arg1;
+                mClientName = (ComponentName) msg.obj;
+                break;
+
             }
         }
     };
 
-    AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener =
-        new AudioManager.OnAudioFocusChangeListener() {
-            public void onAudioFocusChange(final int focusChange) {
-                mHandler.obtainMessage(AUDIO_FOCUS_CHANGED, focusChange, 0).sendToTarget();
-            }
-        };
+    /**
+     * This class is required to have weak linkage to the current TransportControlView
+     * because the remote process can hold a strong reference to this binder object and
+     * we can't predict when it will be GC'd in the remote process. Without this code, it
+     * would allow a heavyweight object to be held on this side of the binder when there's
+     * no requirement to run a GC on the other side.
+     */
+    private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
+        private WeakReference<Handler> mLocalHandler;
 
-    public TransportControlView(Context context) {
-        this(context, null);
-    }
+        IRemoteControlDisplayWeak(Handler handler) {
+            mLocalHandler = new WeakReference<Handler>(handler);
+        }
+
+        public void setPlaybackState(int generationId, int state) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
+            }
+        }
+
+        public void setMetadata(int generationId, Bundle metadata) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+            }
+        }
+
+        public void setTransportControlFlags(int generationId, int flags) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
+                        .sendToTarget();
+            }
+        }
+
+        public void setArtwork(int generationId, Bitmap bitmap) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+            }
+        }
+
+        public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+                handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+            }
+        }
+
+        public void setCurrentClientId(int clientGeneration, ComponentName clientEventReceiver,
+                boolean clearing) throws RemoteException {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_GENERATION_ID,
+                    clientGeneration, (clearing ? 1 : 0), clientEventReceiver).sendToTarget();
+            }
+        }
+    };
 
     public TransportControlView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        Log.v(TAG, "Create TCV " + this);
+        mAudioManager = new AudioManager(mContext);
+        mIRCD = new IRemoteControlDisplayWeak(mHandler);
     }
 
-    protected void handleAudioFocusChange(int focusChange) {
-        // TODO
-    }
-
-    public void setCallback(LockScreenWidgetCallback callback) {
-        mCallback = callback;
+    private void updateTransportControls(int transportControlFlags) {
+        mTransportControlFlags = transportControlFlags;
     }
 
     @Override
     public void onFinishInflate() {
-        for (int i = 0; i < sViewIds.length; i++) {
-            View view = findViewById(sViewIds[i]);
-            if (view != null) {
-                view.setOnClickListener(this);
+        super.onFinishInflate();
+        mTrackTitle = (TextView) findViewById(R.id.title);
+        mTrackTitle.setSelected(true); // enable marquee
+        mAlbumArt = (ImageView) findViewById(R.id.albumart);
+        mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
+        mBtnPlay = (ImageView) findViewById(R.id.btn_play);
+        mBtnNext = (ImageView) findViewById(R.id.btn_next);
+        final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext };
+        for (View view : buttons) {
+            view.setOnClickListener(this);
+        }
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mPopulateMetadataWhenAttached != null) {
+            updateMetadata(mPopulateMetadataWhenAttached);
+            mPopulateMetadataWhenAttached = null;
+        }
+        if (!mAttached) {
+            if (DEBUG) Log.v(TAG, "Registering TCV " + this);
+            mAudioManager.registerRemoteControlDisplay(mIRCD);
+        }
+        mAttached = true;
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mAttached) {
+            if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
+            mAudioManager.unregisterRemoteControlDisplay(mIRCD);
+        }
+        mAttached = false;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
+//        Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim);
+//        mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
+    }
+
+    class Metadata {
+        private String artist;
+        private String trackTitle;
+        private String albumTitle;
+        private Bitmap bitmap;
+
+        public String toString() {
+            return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]";
+        }
+    }
+
+    private String getMdString(Bundle data, int id) {
+        return data.getString(Integer.toString(id));
+    }
+
+    private void updateMetadata(Bundle data) {
+        if (mAttached) {
+            mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
+            mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
+            mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
+            populateMetadata();
+        } else {
+            mPopulateMetadataWhenAttached = data;
+        }
+    }
+
+    /**
+     * Populates the given metadata into the view
+     */
+    private void populateMetadata() {
+        StringBuilder sb = new StringBuilder();
+        int trackTitleLength = 0;
+        if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
+            sb.append(mMetadata.trackTitle);
+            trackTitleLength = mMetadata.trackTitle.length();
+        }
+        if (!TextUtils.isEmpty(mMetadata.artist)) {
+            if (sb.length() != 0) {
+                sb.append(" - ");
+            }
+            sb.append(mMetadata.artist);
+        }
+        if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
+            if (sb.length() != 0) {
+                sb.append(" - ");
+            }
+            sb.append(mMetadata.albumTitle);
+        }
+        mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE);
+        Spannable str = (Spannable) mTrackTitle.getText();
+        if (trackTitleLength != 0) {
+            str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength,
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            trackTitleLength++;
+        }
+        if (sb.length() > trackTitleLength) {
+            str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(),
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+
+        mAlbumArt.setImageBitmap(mMetadata.bitmap);
+        final int flags = mTransportControlFlags;
+        setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
+        setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
+        setVisibilityBasedOnFlag(mBtnPrev, flags,
+                RemoteControlClient.FLAG_KEY_MEDIA_PLAY
+                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
+                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
+                | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
+
+        updatePlayPauseState(mPlayState);
+    }
+
+    private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
+        if ((flags & flag) != 0) {
+            view.setVisibility(View.VISIBLE);
+        } else {
+            view.setVisibility(View.GONE);
+        }
+    }
+
+    private void updatePlayPauseState(int state) {
+        if (DEBUG) Log.v(TAG,
+                "updatePlayPauseState(), old=" + mPlayState + ", state=" + state);
+        if (state == mPlayState) {
+            return;
+        }
+        switch (state) {
+            case RemoteControlClient.PLAYSTATE_PLAYING:
+                mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_pause);
+                break;
+
+            case RemoteControlClient.PLAYSTATE_BUFFERING:
+                mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_stop);
+                break;
+
+            case RemoteControlClient.PLAYSTATE_PAUSED:
+            default:
+                mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_play);
+                break;
+        }
+        mPlayState = state;
+    }
+
+    public void onClick(View v) {
+        int keyCode = -1;
+        if (v == mBtnPrev) {
+            keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+        } else if (v == mBtnNext) {
+            keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
+        } else if (v == mBtnPlay) {
+            keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+
+        }
+        if (keyCode != -1) {
+            sendMediaButtonClick(keyCode);
+            if (mWidgetCallbacks != null) {
+                mWidgetCallbacks.userActivity(this);
             }
         }
     }
 
-    public void onClick(View v) {
-        switch (v.getId()) {
-            case R.id.control_prev:
-                // TODO
-                break;
+    private void sendMediaButtonClick(int keyCode) {
+        // TODO: target to specific player based on mClientName
+        KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+        Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        getContext().sendOrderedBroadcast(intent, null);
 
-            case R.id.control_pauseplay:
-                // TODO
-                break;
+        keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
+        intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        getContext().sendOrderedBroadcast(intent, null);
+    }
 
-            case R.id.control_next:
-                // TODO
-                break;
-        }
-        // Have any button click extend lockscreen's timeout.
-        if (mCallback != null) {
-            mCallback.userActivity(this);
-        }
+    public void setCallback(LockScreenWidgetCallback callback) {
+        mWidgetCallbacks = callback;
     }
 
 }
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index ec926e4..cd1f8ba 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.widget.multiwaveview;
 
-import java.util.ArrayList;
-
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
@@ -31,15 +29,20 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Vibrator;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.R;
 
+import java.util.ArrayList;
+
 /**
  * A special widget containing a center and outer ring. Moving the center ring to the outer ring
  * causes an event that can be caught by implementing OnTriggerListener.
@@ -82,6 +85,8 @@
     private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>();
     private ArrayList<Tweener> mChevronAnimations = new ArrayList<Tweener>();
     private ArrayList<Tweener> mTargetAnimations = new ArrayList<Tweener>();
+    private ArrayList<String> mTargetDescriptions;
+    private ArrayList<String> mDirectionDescriptions;
     private Tweener mHandleAnimation;
     private OnTriggerListener mOnTriggerListener;
     private TargetDrawable mHandleDrawable;
@@ -103,6 +108,9 @@
     private boolean mDragging;
     private int mNewTargetResources;
 
+    private boolean mWaveHovered = false;
+    private long mLastHoverExitTimeMillis = 0;
+
     private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
         public void onAnimationEnd(Animator animator) {
             switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
@@ -128,6 +136,8 @@
         }
     };
     private int mTargetResourceId;
+    private int mTargetDescriptionsResourceId;
+    private int mDirectionDescriptionsResourceId;
 
     public MultiWaveView(Context context) {
         this(context, null);
@@ -177,6 +187,25 @@
             throw new IllegalStateException("Must specify at least one target drawable");
         }
 
+        // Read array of target descriptions
+        if (a.getValue(R.styleable.MultiWaveView_targetDescriptions, outValue)) {
+            final int resourceId = outValue.resourceId;
+            if (resourceId == 0) {
+                throw new IllegalStateException("Must specify target descriptions");
+            }
+            setTargetDescriptionsResourceId(resourceId);
+        }
+
+        // Read array of direction descriptions
+        if (a.getValue(R.styleable.MultiWaveView_directionDescriptions, outValue)) {
+            final int resourceId = outValue.resourceId;
+            if (resourceId == 0) {
+                throw new IllegalStateException("Must specify direction descriptions");
+            }
+            setDirectionDescriptionsResourceId(resourceId);
+        }
+
+        a.recycle();
         setVibrateEnabled(mVibrationDuration > 0);
     }
 
@@ -247,6 +276,9 @@
                 showTargets(true);
                 mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
                 setGrabbedState(OnTriggerListener.CENTER_HANDLE);
+                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                    announceTargets();
+                }
                 break;
 
             case STATE_TRACKING:
@@ -347,6 +379,13 @@
         }
     }
 
+    private void dispatchGrabbedEvent(int whichHandler) {
+        vibrate();
+        if (mOnTriggerListener != null) {
+            mOnTriggerListener.onGrabbed(this, whichHandler);
+        }
+    }
+
     private void doFinish() {
         final int activeTarget = mActiveTarget;
         boolean targetHit =  activeTarget != -1;
@@ -475,6 +514,7 @@
             Drawable drawable = array.getDrawable(i);
             targetDrawables.add(new TargetDrawable(res, drawable));
         }
+        array.recycle();
         mTargetResourceId = resourceId;
         mTargetDrawables = targetDrawables;
         updateTargetPositions();
@@ -499,6 +539,48 @@
     }
 
     /**
+     * Sets the resource id specifying the target descriptions for accessibility.
+     *
+     * @param resourceId The resource id.
+     */
+    public void setTargetDescriptionsResourceId(int resourceId) {
+        mTargetDescriptionsResourceId = resourceId;
+        if (mTargetDescriptions != null) {
+            mTargetDescriptions.clear();
+        }
+    }
+
+    /**
+     * Gets the resource id specifying the target descriptions for accessibility.
+     *
+     * @return The resource id.
+     */
+    public int getTargetDescriptionsResourceId() {
+        return mTargetDescriptionsResourceId;
+    }
+
+    /**
+     * Sets the resource id specifying the target direction descriptions for accessibility.
+     *
+     * @param resourceId The resource id.
+     */
+    public void setDirectionDescriptionsResourceId(int resourceId) {
+        mDirectionDescriptionsResourceId = resourceId;
+        if (mDirectionDescriptions != null) {
+            mDirectionDescriptions.clear();
+        }
+    }
+
+    /**
+     * Gets the resource id specifying the target direction descriptions.
+     *
+     * @return The resource id.
+     */
+    public int getDirectionDescriptionsResourceId() {
+        return mDirectionDescriptionsResourceId;
+    }
+
+    /**
      * Enable or disable vibrate on touch.
      *
      * @param enabled
@@ -593,6 +675,43 @@
         }
     }
 
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
+            final int action = event.getAction();
+            switch (action) {
+                case MotionEvent.ACTION_HOVER_ENTER:
+                case MotionEvent.ACTION_HOVER_MOVE:
+                    final float dx = event.getX() - mWaveCenterX;
+                    final float dy = event.getY() - mWaveCenterY;
+                    if (dist2(dx,dy) <= square(mTapRadius)) {
+                        if (!mWaveHovered) {
+                            mWaveHovered = true;
+                            final long timeSinceLastHoverExitMillis =
+                                event.getEventTime() - mLastHoverExitTimeMillis;
+                            final long recurringEventsInterval =
+                                ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
+                            if (timeSinceLastHoverExitMillis > recurringEventsInterval) {
+                                String text =
+                                    mContext.getString(R.string.content_description_sliding_handle);
+                                announceText(text);
+                            }
+                        }
+                    } else {
+                        mWaveHovered = false;
+                    }
+                    break;
+                case MotionEvent.ACTION_HOVER_EXIT:
+                    mLastHoverExitTimeMillis = event.getEventTime();
+                    mWaveHovered = false;
+                    break;
+                default:
+                    mWaveHovered = false;
+            }
+        }
+        return super.onHoverEvent(event);
+    }
+
     private void handleUp(MotionEvent event) {
         if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
         switchToState(STATE_FINISH, event.getX(), event.getY());
@@ -663,7 +782,11 @@
         invalidateGlobalRegion(mHandleDrawable);
 
         if (mActiveTarget != activeTarget && activeTarget != -1) {
-            vibrate();
+            dispatchGrabbedEvent(activeTarget);
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                String targetContentDescription = getTargetDescription(activeTarget);
+                announceText(targetContentDescription);
+            }
         }
         mActiveTarget = activeTarget;
     }
@@ -771,4 +894,62 @@
         return dx*dx + dy*dy;
     }
 
-}
\ No newline at end of file
+    private void announceTargets() {
+        StringBuilder utterance = new StringBuilder();
+        final int targetCount = mTargetDrawables.size();
+        for (int i = 0; i < targetCount; i++) {
+            String targetDescription = getTargetDescription(i);
+            String directionDescription = getDirectionDescription(i);
+            if (!TextUtils.isEmpty(targetDescription)
+                    && !TextUtils.isEmpty(directionDescription)) {
+                String text = String.format(directionDescription, targetDescription);
+                utterance.append(text);
+            }
+            if (utterance.length() > 0) {
+                announceText(utterance.toString());
+            }
+        }
+    }
+
+    private void announceText(String text) {
+        setContentDescription(text);
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+        setContentDescription(null);
+    }
+
+    private String getTargetDescription(int index) {
+        if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
+            mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
+            if (mTargetDrawables.size() != mTargetDescriptions.size()) {
+                Log.w(TAG, "The number of target drawables must be"
+                        + " euqal to the number of target descriptions.");
+                return null;
+            }
+        }
+        return mTargetDescriptions.get(index);
+    }
+
+    private String getDirectionDescription(int index) {
+        if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
+            mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
+            if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
+                Log.w(TAG, "The number of target drawables must be"
+                        + " euqal to the number of direction descriptions.");
+                return null;
+            }
+        }
+        return mDirectionDescriptions.get(index);
+    }
+
+    private ArrayList<String> loadDescriptions(int resourceId) {
+        TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
+        final int count = array.length();
+        ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
+        for (int i = 0; i < count; i++) {
+            String contentDescription = array.getString(i);
+            targetContentDescriptions.add(contentDescription);
+        }
+        array.recycle();
+        return targetContentDescriptions;
+    }
+}
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index ddac820..419e464 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -352,7 +352,6 @@
             char buf[32];
             int len;
             snprintf(buf, sizeof(buf), "%lld", value);
-            jchar* dst = env->GetCharArrayElements(buffer, NULL);
             sizeCopied = charToJchar(buf, dst, bufferSize);
          }
     } else if (type == FIELD_TYPE_FLOAT) {
@@ -360,7 +359,6 @@
         if (window->getDouble(row, column, &value)) {
             char tempbuf[32];
             snprintf(tempbuf, sizeof(tempbuf), "%g", value);
-            jchar* dst = env->GetCharArrayElements(buffer, NULL);
             sizeCopied = charToJchar(tempbuf, dst, bufferSize);
         }
     } else if (type == FIELD_TYPE_NULL) {
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 3cbd912..84a50f0 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -124,6 +124,11 @@
     return (jboolean)(::wifi_start_supplicant() == 0);
 }
 
+static jboolean android_net_wifi_startP2pSupplicant(JNIEnv* env, jobject)
+{
+    return (jboolean)(::wifi_start_p2p_supplicant() == 0);
+}
+
 static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject)
 {
     return doBooleanCommand("OK", "TERMINATE");
@@ -581,6 +586,7 @@
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded},
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
     { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
+    { "startP2pSupplicant", "()Z",  (void *)android_net_wifi_startP2pSupplicant },
     { "stopSupplicant", "()Z", (void*) android_net_wifi_stopSupplicant },
     { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "()Z",  (void *)android_net_wifi_connectToSupplicant },
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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 540c65a..f50cecd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -93,6 +93,9 @@
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
 
+    <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+    <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
+
     <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
     <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" />
     <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED" />
@@ -917,7 +920,7 @@
     <!-- Allows applications to write the apn settings -->
     <permission android:name="android.permission.WRITE_APN_SETTINGS"
                 android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-                android:protectionLevel="dangerous"
+                android:protectionLevel="signatureOrSystem"
                 android:description="@string/permdesc_writeApnSettings"
                 android:label="@string/permlab_writeApnSettings" />
 
diff --git a/core/res/res/anim/wallpaper_close_exit.xml b/core/res/res/anim/wallpaper_close_exit.xml
index 7e0e05f..987fd89 100644
--- a/core/res/res/anim/wallpaper_close_exit.xml
+++ b/core/res/res/anim/wallpaper_close_exit.xml
@@ -29,4 +29,7 @@
             android:fillEnabled="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quint"
             android:duration="300" />
+    <!-- This is needed to keep the animation running while wallpaper_close_enter completes -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:duration="600" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
index 075831b..1804fa8 100644
--- a/core/res/res/anim/wallpaper_open_exit.xml
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -29,5 +29,7 @@
                 android:interpolator="@interpolator/accelerate_quint"
                 android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
                 android:duration="200" />
-
+        <!-- This is needed to keep the animation running while wallpaper_open_enter completes -->
+        <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+                android:duration="500" />
 </set>
\ No newline at end of file
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_lockscreen_player_background.9.png b/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png
new file mode 100644
index 0000000..b187358
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_embed_play.png b/core/res/res/drawable-hdpi/ic_media_embed_play.png
index 05778c1..23ac7e4 100644
--- a/core/res/res/drawable-hdpi/ic_media_embed_play.png
+++ b/core/res/res/drawable-hdpi/ic_media_embed_play.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-hdpi/text_edit_paste_window.9.png b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
index 6654e4d..b74f37b 100644
--- a/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
+++ b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png b/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png
index c97514f..b74f37b 100644
--- a/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png
+++ b/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.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_lockscreen_player_background.9.png b/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png
new file mode 100644
index 0000000..8cfd1af
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_embed_play.png b/core/res/res/drawable-mdpi/ic_media_embed_play.png
index 3576ce5..fc5d8c6 100644
--- a/core/res/res/drawable-mdpi/ic_media_embed_play.png
+++ b/core/res/res/drawable-mdpi/ic_media_embed_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_ff.png b/core/res/res/drawable-mdpi/ic_media_ff.png
index 170dd2d..892772e 100644
--- a/core/res/res/drawable-mdpi/ic_media_ff.png
+++ b/core/res/res/drawable-mdpi/ic_media_ff.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_fullscreen.png b/core/res/res/drawable-mdpi/ic_media_fullscreen.png
index 960aa85..1c60e15 100644
--- a/core/res/res/drawable-mdpi/ic_media_fullscreen.png
+++ b/core/res/res/drawable-mdpi/ic_media_fullscreen.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_next.png b/core/res/res/drawable-mdpi/ic_media_next.png
index a6feed0..bbe311b 100644
--- a/core/res/res/drawable-mdpi/ic_media_next.png
+++ b/core/res/res/drawable-mdpi/ic_media_next.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_pause.png b/core/res/res/drawable-mdpi/ic_media_pause.png
index 548ba02..e4e8d86 100644
--- a/core/res/res/drawable-mdpi/ic_media_pause.png
+++ b/core/res/res/drawable-mdpi/ic_media_pause.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_play.png b/core/res/res/drawable-mdpi/ic_media_play.png
index 0fe6806..8eaf962 100644
--- a/core/res/res/drawable-mdpi/ic_media_play.png
+++ b/core/res/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_previous.png b/core/res/res/drawable-mdpi/ic_media_previous.png
index 0163d09..e9abc7f 100644
--- a/core/res/res/drawable-mdpi/ic_media_previous.png
+++ b/core/res/res/drawable-mdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_rew.png b/core/res/res/drawable-mdpi/ic_media_rew.png
index 5489180..a5eb94a 100644
--- a/core/res/res/drawable-mdpi/ic_media_rew.png
+++ b/core/res/res/drawable-mdpi/ic_media_rew.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-mdpi/text_edit_paste_window.9.png b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
index 41886eb..16f623d 100644
--- a/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
+++ b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png
index 88be6e1..16f623d 100644
--- a/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.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_lockscreen_player_background.9.png b/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png
new file mode 100644
index 0000000..7fb0cbc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.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/drawable-xhdpi/text_edit_paste_window.9.png b/core/res/res/drawable-xhdpi/text_edit_paste_window.9.png
new file mode 100644
index 0000000..5c043b6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_edit_suggestions_window.9.png b/core/res/res/drawable-xhdpi/text_edit_suggestions_window.9.png
new file mode 100644
index 0000000..5c043b6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/text_edit_suggestions_window.9.png
Binary files differ
diff --git a/core/res/res/drawable/switch_inner_holo_dark.xml b/core/res/res/drawable/switch_inner_holo_dark.xml
index 3eb55ee..67584bc 100644
--- a/core/res/res/drawable/switch_inner_holo_dark.xml
+++ b/core/res/res/drawable/switch_inner_holo_dark.xml
@@ -17,5 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false" android:drawable="@drawable/switch_thumb_disabled_holo_dark" />
     <item android:state_pressed="true"  android:drawable="@drawable/switch_thumb_pressed_holo_dark" />
+    <item android:state_checked="true"  android:drawable="@drawable/switch_thumb_activated_holo_dark" />
     <item                               android:drawable="@drawable/switch_thumb_holo_dark" />
 </selector>
diff --git a/core/res/res/drawable/switch_inner_holo_light.xml b/core/res/res/drawable/switch_inner_holo_light.xml
index 9b287cf..95df0e88 100644
--- a/core/res/res/drawable/switch_inner_holo_light.xml
+++ b/core/res/res/drawable/switch_inner_holo_light.xml
@@ -17,5 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false" android:drawable="@drawable/switch_thumb_disabled_holo_light" />
     <item android:state_pressed="true"  android:drawable="@drawable/switch_thumb_pressed_holo_light" />
+    <item android:state_checked="true"  android:drawable="@drawable/switch_thumb_activated_holo_light" />
     <item                               android:drawable="@drawable/switch_thumb_holo_light" />
 </selector>
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/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 12df99e..694db50 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -186,6 +186,8 @@
         android:layout_rowSpan="6"
         android:layout_columnSpan="1"
         android:layout_gravity="fill"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
         />
 
 </GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 6145e47..cf3bd42 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -174,6 +174,8 @@
         android:layout_rowSpan="3"
         android:layout_columnSpan="1"
         android:layout_gravity="fill"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
         />
 
 </GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index 6016d4e..0368530 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -130,6 +130,8 @@
             android:layout_alignParentBottom="true"
 
             android:targetDrawables="@array/lockscreen_targets_with_camera"
+            android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
+            android:directionDescriptions="@array/lockscreen_direction_descriptions"
             android:handleDrawable="@drawable/ic_lockscreen_handle"
             android:waveDrawable="@drawable/ic_lockscreen_outerring"
             android:outerRadius="@dimen/multiwaveview_target_placement_radius"
@@ -188,6 +190,8 @@
         android:layout_rowSpan="4"
         android:layout_columnSpan="1"
         android:layout_gravity="fill"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
         />
 
 </GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 0568dd9..2849376 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -135,6 +135,8 @@
         android:layout_rowSpan="7"
 
         android:targetDrawables="@array/lockscreen_targets_with_camera"
+        android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
+        android:directionDescriptions="@array/lockscreen_direction_descriptions"
         android:handleDrawable="@drawable/ic_lockscreen_handle"
         android:waveDrawable="@drawable/ic_lockscreen_outerring"
         android:outerRadius="@dimen/multiwaveview_target_placement_radius"
@@ -155,6 +157,8 @@
         android:layout_rowSpan="5"
         android:layout_columnSpan="1"
         android:layout_gravity="fill"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
         />
 
 </GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index 9b28731..d71dbff 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -156,6 +156,8 @@
         android:layout_rowSpan="5"
         android:layout_columnSpan="1"
         android:layout_gravity="fill"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
         />
 
 </GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 433dda7..64c479f 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -167,6 +167,8 @@
         android:layout_rowSpan="4"
         android:layout_columnSpan="1"
         android:layout_gravity="fill"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
         />
 
 </GridLayout>
diff --git a/core/res/res/layout/keyguard_transport_control.xml b/core/res/res/layout/keyguard_transport_control.xml
index 6308b02..2ebe5fc 100644
--- a/core/res/res/layout/keyguard_transport_control.xml
+++ b/core/res/res/layout/keyguard_transport_control.xml
@@ -1,53 +1,102 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2008, 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.
-*/
+<!-- 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.
 -->
 
+<!-- Note: This file is meant to be included in various password unlock screens. As such,
+     LayoutParams (layout_*) for TransportControlView should *NOT* be specified here,
+     but rather as include tags for this file or the layout will break. -->
 <com.android.internal.widget.TransportControlView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:gravity="bottom"
-    android:orientation="horizontal"
-    android:background="#a0808080"
-    android:visibility="gone">
+    android:id="@+id/transport_controls"
+    android:background="@drawable/ic_lockscreen_player_background">
 
-    <Button android:id="@+id/control_prev"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="#80ff0000"
+    <ImageView
+        android:id="@+id/albumart"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="fill"
+        android:scaleType="centerCrop"
+        android:adjustViewBounds="false"
         />
 
-    <Space android:layout_width="0dip"
-        android:layout_height="0dip"
-        android:layout_weight="1"/>
-
-    <Button android:id="@+id/control_pauseplay"
-        android:layout_width="wrap_content"
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="#8000ff00"
+        android:background="#c8000000"
+        android:layout_gravity="bottom">
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dip"
+            android:layout_marginLeft="16dip"
+            android:layout_marginRight="16dip"
+            android:gravity="center_horizontal"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:textAppearance="?android:attr/textAppearanceMedium"
         />
-
-    <Space android:layout_width="0dip"
-        android:layout_height="0dip"
-        android:layout_weight="1"/>
-
-    <Button android:id="@+id/control_next"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="#800000ff"
-        />
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginTop="5dip">
+            <FrameLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1">
+                <ImageView
+                    android:id="@+id/btn_prev"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:src="@drawable/ic_media_rew"
+                    android:clickable="true"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:padding="10dip"/>
+            </FrameLayout>
+            <FrameLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1">
+                <ImageView
+                    android:id="@+id/btn_play"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:clickable="true"
+                    android:src="@drawable/ic_media_play"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:padding="10dip"/>
+            </FrameLayout>
+            <FrameLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1">
+                <ImageView
+                    android:id="@+id/btn_next"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:clickable="true"
+                    android:src="@drawable/ic_media_ff"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:padding="10dip"/>
+            </FrameLayout>
+        </LinearLayout>
+    </LinearLayout>
 
 </com.android.internal.widget.TransportControlView>
diff --git a/core/res/res/layout/text_edit_action_popup_text.xml b/core/res/res/layout/text_edit_action_popup_text.xml
index aa6a5e1..42e6f2e 100644
--- a/core/res/res/layout/text_edit_action_popup_text.xml
+++ b/core/res/res/layout/text_edit_action_popup_text.xml
@@ -25,4 +25,5 @@
     android:textAppearance="?android:attr/textAppearanceSmallInverse"
     android:textColor="@android:color/black"
     android:textAllCaps="true"
+    android:textStyle="bold"
 />
diff --git a/core/res/res/values-land/arrays.xml b/core/res/res/values-land/arrays.xml
index fd492ec..68e5cfd 100644
--- a/core/res/res/values-land/arrays.xml
+++ b/core/res/res/values-land/arrays.xml
@@ -27,13 +27,34 @@
         <item>@drawable/ic_lockscreen_soundon</item>
     </array>
 
+    <array name="lockscreen_target_descriptions_when_silent">
+        <item>@null</item>
+        <item>@string/description_target_unlock</item>
+        <item>@null</item>
+        <item>@string/description_target_soundon</item>
+    </array>
+
+    <array name="lockscreen_direction_descriptions">
+        <item>@null</item>
+        <item>@string/description_direction_up</item>
+        <item>@null</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
     <array name="lockscreen_targets_when_soundon">
-        <item>@null</item>"
+        <item>@null</item>
         <item>@drawable/ic_lockscreen_unlock</item>
         <item>@null</item>
         <item>@drawable/ic_lockscreen_silent</item>
     </array>
 
+    <array name="lockscreen_target_descriptions_when_soundon">
+        <item>@null</item>
+        <item>@string/description_target_unlock</item>
+        <item>@null</item>
+        <item>@string/description_target_silent</item>
+    </array>
+
     <array name="lockscreen_targets_with_camera">
         <item>@null</item>
         <item>@drawable/ic_lockscreen_unlock</item>
@@ -41,4 +62,11 @@
         <item>@drawable/ic_lockscreen_camera</item>
     </array>
 
+    <array name="lockscreen_target_descriptions_with_camera">
+        <item>@null</item>
+        <item>@string/description_target_unlock</item>
+        <item>@null</item>
+        <item>@string/description_target_camera</item>
+    </array>
+
 </resources>
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/arrays.xml b/core/res/res/values/arrays.xml
index 57e9bbf..8d5bd0b 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -349,18 +349,46 @@
         <item>@null</item>
     </array>
 
+    <array name="lockscreen_target_descriptions_when_silent">
+        <item>@string/description_target_unlock</item>
+        <item>@null</item>
+        <item>@string/description_target_soundon</item>
+        <item>@null</item>
+    </array>
+
+    <array name="lockscreen_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</item>
+        <item>@null</item>
+    </array>
+
     <array name="lockscreen_targets_when_soundon">
         <item>@drawable/ic_lockscreen_unlock</item>
         <item>@null</item>
         <item>@drawable/ic_lockscreen_silent</item>
-        <item>@null</item>"
+        <item>@null</item>
+    </array>
+
+    <array name="lockscreen_target_descriptions_when_soundon">
+        <item>@string/description_target_unlock</item>
+        <item>@null</item>
+        <item>@string/description_target_silent</item>
+        <item>@null</item>
     </array>
 
     <array name="lockscreen_targets_with_camera">
         <item>@drawable/ic_lockscreen_unlock</item>
         <item>@null</item>
         <item>@drawable/ic_lockscreen_camera</item>
-        <item>@null</item>"
+        <item>@null</item>
+    </array>
+
+    <array name="lockscreen_target_descriptions_with_camera">
+        <item>@string/description_target_unlock</item>
+        <item>@null</item>
+        <item>@string/description_target_camera</item>
+        <item>@null</item>
     </array>
 
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dae9f70..b50c063 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -160,9 +160,8 @@
 
         <!--  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 +3148,7 @@
     </declare-styleable>
     <declare-styleable name="SuggestionSpan">
         <attr name="textUnderlineColor" />
-        <attr name="textUnderlineThicknessPercentage" />
+        <attr name="textUnderlineThickness" />
     </declare-styleable>
     <!-- An <code>input-extras</code> is a container for extra data to supply to
          an input method.  Contains
@@ -5169,6 +5168,12 @@
         <!-- Reference to an array resource that be shown as targets around a circle. -->
         <attr name="targetDrawables" format="reference"/>
 
+        <!-- Reference to an array resource that be used as description for the targets around the circle. -->
+        <attr name="targetDescriptions" format="reference"/>
+
+        <!-- Reference to an array resource that be used to announce the directions with targets around the circle. -->
+        <attr name="directionDescriptions" format="reference"/>
+
         <!-- Sets a drawable as the drag center. -->
         <attr name="handleDrawable" format="reference" />
 
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index aee7ea4..fd525f3 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -54,8 +54,6 @@
     <color name="dim_foreground_light_inverse">#bebebe</color>
     <color name="dim_foreground_light_inverse_disabled">#80bebebe</color>
     <color name="hint_foreground_light">#808080</color>
-    <color name="highlight_background">#cc475925</color>
-    <color name="highlight_background_inverse">#ccd2e461</color>
     <color name="highlighted_text_dark">#9983CC39</color>
     <color name="highlighted_text_light">#9983CC39</color>
     <color name="link_text_dark">#5c5cff</color>
@@ -135,10 +133,8 @@
     <color name="dim_foreground_inverse_holo_light">#bebebe</color>
     <color name="dim_foreground_inverse_disabled_holo_light">#80bebebe</color>
     <color name="hint_foreground_holo_light">#808080</color>
-    <color name="highlight_background_holo">#cc475925</color>
-    <color name="highlight_background_inverse_holo">#ccd2e461</color>
-    <color name="highlighted_text_holo_dark">#9983CC39</color>
-    <color name="highlighted_text_holo_light">#9983CC39</color>
+    <color name="highlighted_text_holo_dark">#4c33b5e5</color>
+    <color name="highlighted_text_holo_light">#4c33b5e5</color>
     <color name="link_text_holo_dark">#5c5cff</color>
     <color name="link_text_holo_light">#0000ee</color>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b155b803..051ed14 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -189,6 +189,9 @@
 
     <!-- Boolean indicating whether the wifi chipset has p2p support -->
     <bool translatable="false" name="config_wifi_p2p_support">false</bool>
+    <!-- Device type information conforming to Annex B format in WiFi Direct specification.
+         The default represents a dual-mode smartphone -->
+    <string translatable="false" name="config_wifi_p2p_device_type">10-0050F204-5</string>
 
     <!-- Boolean indicating whether the wifi chipset supports background scanning mechanism.
          This mechanism allows the host to remain in suspend state and the dongle to actively
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/public.xml b/core/res/res/values/public.xml
index fcd3bba..6988f6b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2006,4 +2006,8 @@
   <public type="color" name="holo_orange_dark" />
   <public type="color" name="holo_purple" />
   <public type="color" name="holo_blue_bright" />
+
+  <public type="attr" name="targetDescriptions" />
+  <public type="attr" name="directionDescriptions" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8e0f300..32e8827 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>
 
@@ -3129,6 +3132,29 @@
     <!-- Description of the Enter button in a KeyboardView. [CHAR LIMIT=NONE] -->
     <string name="keyboardview_keycode_enter">Enter</string>
 
+    <!-- Slide lock screen -->
+
+    <!-- Description of the sliding handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="content_description_sliding_handle">"Sliding handle. Tap and hold."</string>
+
+    <!-- Description of the up direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_direction_up">Up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+    <!-- Description of the down direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_direction_down">Down for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+    <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_direction_left">"Left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+    <!-- Description of the right direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_direction_right">Right for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+
+    <!-- Description of the unlock target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_target_unlock">Unlock</string>
+    <!-- Description of the camera target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_target_camera">Camera</string>
+    <!-- Description of the silent target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_target_silent">Silent</string>
+    <!-- Description of the sound on target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="description_target_soundon">Sound on</string>
+
     <!-- Announce that a headset is required to hear keyboard keys while typing a password. [CHAR LIMIT=NONE] -->
     <string name="keyboard_headset_required_to_hear_password">Key. Headset required to hear
     keys while typing a password.</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 052a040..fba1cd1 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">
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..56bd2ce 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -14,15 +14,14 @@
 
 # Warning: this is actually a product definition, to be inherited from
 
+# On space-constrained devices, we include a subset of fonts:
+# First, core/required fonts
 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/DroidSans.ttf:system/fonts/DroidSans.ttf \
-    frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.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/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
-    frameworks/base/data/fonts/DroidSansEthiopic-Regular.ttf:system/fonts/DroidSansEthiopic-Regular.ttf \
     frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \
     frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \
     frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \
@@ -33,9 +32,22 @@
     frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
     frameworks/base/data/fonts/Lohit_Hindi.ttf:system/fonts/Lohit_Hindi.ttf \
     frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
-    frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf \
     frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \
     frameworks/base/data/fonts/AndroidClock_Highlight.ttf:system/fonts/AndroidClock_Highlight.ttf \
     frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf \
     frameworks/base/data/fonts/system_fonts.xml:system/etc/system_fonts.xml \
     frameworks/base/data/fonts/fallback_fonts.xml:system/etc/fallback_fonts.xml
+
+# Next, include additional fonts, depending on how much space we have
+ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+# Smaller fonts alternatives
+PRODUCT_COPY_FILES += \
+    frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf
+else
+# Full font set alternatives
+PRODUCT_COPY_FILES += \
+    frameworks/base/data/fonts/DroidSansFallbackFull.ttf:system/fonts/DroidSansFallback.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/DroidSansEthiopic-Regular.ttf:system/fonts/DroidSansEthiopic-Regular.ttf
+endif
diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk
index fd6bf17..610e821 100755
--- a/data/sounds/AudioPackage6.mk
+++ b/data/sounds/AudioPackage6.mk
@@ -41,29 +41,4 @@
 	$(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
 	$(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
 	$(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Bootes.ogg:system/media/audio/ringtones/Bootes.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cassiopeia.ogg:system/media/audio/ringtones/Cassiopeia.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Iridium.ogg:system/media/audio/ringtones/Iridium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
 	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/alarms/ogg/Copernicium.ogg b/data/sounds/alarms/ogg/Copernicium.ogg
index c619e8b..65d2867 100644
--- a/data/sounds/alarms/ogg/Copernicium.ogg
+++ b/data/sounds/alarms/ogg/Copernicium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Curium.ogg b/data/sounds/alarms/ogg/Curium.ogg
index ebce391..125a236 100644
--- a/data/sounds/alarms/ogg/Curium.ogg
+++ b/data/sounds/alarms/ogg/Curium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Fermium.ogg b/data/sounds/alarms/ogg/Fermium.ogg
index 6132565..6940442 100644
--- a/data/sounds/alarms/ogg/Fermium.ogg
+++ b/data/sounds/alarms/ogg/Fermium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Hassium.ogg b/data/sounds/alarms/ogg/Hassium.ogg
index 408b7c2..86b2b71 100644
--- a/data/sounds/alarms/ogg/Hassium.ogg
+++ b/data/sounds/alarms/ogg/Hassium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Neptunium.ogg b/data/sounds/alarms/ogg/Neptunium.ogg
index 058e2db..1a99141 100644
--- a/data/sounds/alarms/ogg/Neptunium.ogg
+++ b/data/sounds/alarms/ogg/Neptunium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Nobelium.ogg b/data/sounds/alarms/ogg/Nobelium.ogg
index 33878c9..4309bc6 100644
--- a/data/sounds/alarms/ogg/Nobelium.ogg
+++ b/data/sounds/alarms/ogg/Nobelium.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Altair.ogg b/data/sounds/notifications/ogg/Altair.ogg
old mode 100755
new mode 100644
index d84b59e..407aeb9
--- a/data/sounds/notifications/ogg/Altair.ogg
+++ b/data/sounds/notifications/ogg/Altair.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Antares.ogg b/data/sounds/notifications/ogg/Antares.ogg
old mode 100755
new mode 100644
index 9d60917..409c684
--- a/data/sounds/notifications/ogg/Antares.ogg
+++ b/data/sounds/notifications/ogg/Antares.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Betelgeuse.ogg b/data/sounds/notifications/ogg/Betelgeuse.ogg
index 83c7722..488d1e8 100644
--- a/data/sounds/notifications/ogg/Betelgeuse.ogg
+++ b/data/sounds/notifications/ogg/Betelgeuse.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Deneb.ogg b/data/sounds/notifications/ogg/Deneb.ogg
old mode 100755
new mode 100644
index e58b3b6..b84eae3
--- a/data/sounds/notifications/ogg/Deneb.ogg
+++ b/data/sounds/notifications/ogg/Deneb.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Hojus.ogg b/data/sounds/notifications/ogg/Hojus.ogg
index fc8f73f..65b780c 100644
--- a/data/sounds/notifications/ogg/Hojus.ogg
+++ b/data/sounds/notifications/ogg/Hojus.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Lalande.ogg b/data/sounds/notifications/ogg/Lalande.ogg
old mode 100755
new mode 100644
index b6e253a..eda9c9d
--- a/data/sounds/notifications/ogg/Lalande.ogg
+++ b/data/sounds/notifications/ogg/Lalande.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Mira.ogg b/data/sounds/notifications/ogg/Mira.ogg
index f21e3c4..f5a6e94 100644
--- a/data/sounds/notifications/ogg/Mira.ogg
+++ b/data/sounds/notifications/ogg/Mira.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Proxima.ogg b/data/sounds/notifications/ogg/Proxima.ogg
index 235b5ca..53bf899 100644
--- a/data/sounds/notifications/ogg/Proxima.ogg
+++ b/data/sounds/notifications/ogg/Proxima.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Upsilon.ogg b/data/sounds/notifications/ogg/Upsilon.ogg
index 036dcad..e970422 100644
--- a/data/sounds/notifications/ogg/Upsilon.ogg
+++ b/data/sounds/notifications/ogg/Upsilon.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Cassiopeia.ogg b/data/sounds/ringtones/ogg/Cassiopeia.ogg
index 61c4d27..b871940 100644
--- a/data/sounds/ringtones/ogg/Cassiopeia.ogg
+++ b/data/sounds/ringtones/ogg/Cassiopeia.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Lyra.ogg b/data/sounds/ringtones/ogg/Lyra.ogg
index b7f740d..d170bc0 100644
--- a/data/sounds/ringtones/ogg/Lyra.ogg
+++ b/data/sounds/ringtones/ogg/Lyra.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Sceptrum.ogg b/data/sounds/ringtones/ogg/Sceptrum.ogg
index 89d64d70..e94abe0 100644
--- a/data/sounds/ringtones/ogg/Sceptrum.ogg
+++ b/data/sounds/ringtones/ogg/Sceptrum.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Solarium.ogg b/data/sounds/ringtones/ogg/Solarium.ogg
index 361367a..8dac71e 100644
--- a/data/sounds/ringtones/ogg/Solarium.ogg
+++ b/data/sounds/ringtones/ogg/Solarium.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/UrsaMinor.ogg b/data/sounds/ringtones/ogg/UrsaMinor.ogg
index a80801d..a90d1de 100644
--- a/data/sounds/ringtones/ogg/UrsaMinor.ogg
+++ b/data/sounds/ringtones/ogg/UrsaMinor.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Vespa.ogg b/data/sounds/ringtones/ogg/Vespa.ogg
index 1f75ec8..f637831 100644
--- a/data/sounds/ringtones/ogg/Vespa.ogg
+++ b/data/sounds/ringtones/ogg/Vespa.ogg
Binary files differ
diff --git a/docs/html/guide/topics/usb/index.jd b/docs/html/guide/topics/usb/index.jd
index 3e2a18b..6dc8ec5 100644
--- a/docs/html/guide/topics/usb/index.jd
+++ b/docs/html/guide/topics/usb/index.jd
@@ -6,9 +6,9 @@
       <h2>Topics</h2>
 
       <ol>
-        <li><a href="{@docRoot}guide/topics/USB/accessory.jd">USB Accessory</a></li>
+        <li><a href="{@docRoot}guide/topics/usb/accessory.html">USB Accessory</a></li>
 
-        <li><a href="{@docRoot}guide/topics/USB/host.jd">USB Host</a></li>
+        <li><a href="{@docRoot}guide/topics/usb/host.html">USB Host</a></li>
       </ol>
     </div>
   </div>
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 cde997e..ee65223 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -358,22 +358,7 @@
      */
     public Paint(Paint paint) {
         mNativePaint = native_initWithPaint(paint.mNativePaint);
-        mHasCompatScaling = paint.mHasCompatScaling;
-        mCompatScaling = paint.mCompatScaling;
-        mInvCompatScaling = paint.mInvCompatScaling;
-        mBidiFlags = paint.mBidiFlags;
-        hasShadow = paint.hasShadow;
-        mColorFilter = paint.mColorFilter;
-        mMaskFilter = paint.mMaskFilter;
-        mPathEffect = paint.mPathEffect;
-        mRasterizer = paint.mRasterizer;
-        mShader = paint.mShader;
-        mTypeface = paint.mTypeface;
-        mXfermode = paint.mXfermode;
-        shadowColor = paint.shadowColor;
-        shadowDx = paint.shadowDx;
-        shadowDy = paint.shadowDy;
-        shadowRadius = paint.shadowRadius;
+        setClassVariablesFrom(paint);
     }
 
     /** Restores the paint to its default settings. */
@@ -396,21 +381,36 @@
         if (this != src) {
             // copy over the native settings
             native_set(mNativePaint, src.mNativePaint);
-            // copy over our java settings
-            mColorFilter    = src.mColorFilter;
-            mMaskFilter     = src.mMaskFilter;
-            mPathEffect     = src.mPathEffect;
-            mRasterizer     = src.mRasterizer;
-            mShader         = src.mShader;
-            mTypeface       = src.mTypeface;
-            mXfermode       = src.mXfermode;
-            mHasCompatScaling = src.mHasCompatScaling;
-            mCompatScaling    = src.mCompatScaling;
-            mInvCompatScaling = src.mInvCompatScaling;
-            mBidiFlags      = src.mBidiFlags;
+            setClassVariablesFrom(src);
         }
     }
 
+    /**
+     * Set all class variables using current values from the given
+     * {@link Paint}.
+     */
+    private void setClassVariablesFrom(Paint paint) {
+        mColorFilter = paint.mColorFilter;
+        mMaskFilter = paint.mMaskFilter;
+        mPathEffect = paint.mPathEffect;
+        mRasterizer = paint.mRasterizer;
+        mShader = paint.mShader;
+        mTypeface = paint.mTypeface;
+        mXfermode = paint.mXfermode;
+
+        mHasCompatScaling = paint.mHasCompatScaling;
+        mCompatScaling = paint.mCompatScaling;
+        mInvCompatScaling = paint.mInvCompatScaling;
+
+        hasShadow = paint.hasShadow;
+        shadowDx = paint.shadowDx;
+        shadowDy = paint.shadowDy;
+        shadowRadius = paint.shadowRadius;
+        shadowColor = paint.shadowColor;
+
+        mBidiFlags = paint.mBidiFlags;
+    }
+
     /** @hide */
     public void setCompatibilityScaling(float factor) {
         if (factor == 1.0) {
@@ -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();
         }
 
@@ -1594,7 +1594,7 @@
     }
 
     /**
-     * Return the glypth Ids for the characters in the string.
+     * Return the glyph Ids for the characters in the string.
      *
      * @param text   The text to measure
      * @param start  The index of the first char to to measure
@@ -1613,7 +1613,7 @@
      *
      * Used only for BiDi / RTL Tests
      */
-    public int getTextGlypths(String text, int start, int end, int contextStart, int contextEnd,
+    public int getTextGlyphs(String text, int start, int end, int contextStart, int contextEnd,
             int flags, char[] glyphs) {
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index 2b3aa33..00e9609 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.io.PrintWriter;
+
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.FloatMath;
@@ -81,8 +83,37 @@
         return "RectF(" + left + ", " + top + ", "
                       + right + ", " + bottom + ")";
     }
+
+    /**
+     * Return a string representation of the rectangle in a compact form.
+     */
+    public String toShortString() {
+        return toShortString(new StringBuilder(32));
+    }
     
     /**
+     * Return a string representation of the rectangle in a compact form.
+     * @hide
+     */
+    public String toShortString(StringBuilder sb) {
+        sb.setLength(0);
+        sb.append('['); sb.append(left); sb.append(',');
+        sb.append(top); sb.append("]["); sb.append(right);
+        sb.append(','); sb.append(bottom); sb.append(']');
+        return sb.toString();
+    }
+    
+    /**
+     * Print short representation to given writer.
+     * @hide
+     */
+    public void printShortString(PrintWriter pw) {
+        pw.print('['); pw.print(left); pw.print(',');
+        pw.print(top); pw.print("]["); pw.print(right);
+        pw.print(','); pw.print(bottom); pw.print(']');
+    }
+
+    /**
      * Returns true if the rectangle is empty (left >= right or top >= bottom)
      */
     public final boolean isEmpty() {
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/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 6c91dfc..4a4bcfb 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -495,6 +495,25 @@
     // Example value: "true" or "false". Read/write.
     static const char KEY_RECORDING_HINT[];
 
+    // Returns true if video snapshot is supported. That is, applications
+    // can call Camera.takePicture during recording. Applications do not need to
+    // call Camera.startPreview after taking a picture. The preview will be
+    // still active. Other than that, taking a picture during recording is
+    // identical to taking a picture normally. All settings and methods related
+    // to takePicture work identically. Ex: KEY_PICTURE_SIZE,
+    // KEY_SUPPORTED_PICTURE_SIZES, KEY_JPEG_QUALITY, KEY_ROTATION, and etc.
+    // The picture will have an EXIF header. FLASH_MODE_AUTO and FLASH_MODE_ON
+    // also still work, but the video will record the flash.
+    //
+    // Applications can set shutter callback as null to avoid the shutter
+    // sound. It is also recommended to set raw picture and post view callbacks
+    // to null to avoid the interrupt of preview display.
+    //
+    // Field-of-view of the recorded video may be different from that of the
+    // captured pictures.
+    // Example value: "true" or "false". Read only.
+    static const char KEY_VIDEO_SNAPSHOT_SUPPORTED[];
+
     // Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
     static const char TRUE[];
     static const char FALSE[];
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..d0940bb 100644
--- a/include/media/stagefright/SurfaceMediaSource.h
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -34,7 +34,7 @@
 class SurfaceMediaSource : public BnSurfaceTexture, public MediaSource,
                                             public MediaBufferObserver {
 public:
-    enum { MIN_UNDEQUEUED_BUFFERS = 3 };
+    enum { MIN_UNDEQUEUED_BUFFERS = 4 };
     enum {
         MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
         MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
@@ -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/include/utils/threads.h b/include/utils/threads.h
index c84a9b4..c685625 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -143,6 +143,13 @@
 // in either case errno is set.  Thread ID zero means current thread.
 extern int androidSetThreadPriority(pid_t tid, int prio);
 
+// Get the current scheduling group of a particular thread. Normally returns
+// one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT.
+// Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if
+// scheduling groups are disabled.  Returns INVALID_OPERATION if unexpected error.
+// Thread ID zero means current thread.
+extern int androidGetThreadSchedulingGroup(pid_t tid);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 0eb5d50..0dcab6b 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -87,6 +87,7 @@
 const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected-faces-hw";
 const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
 const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
+const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
 
 const char CameraParameters::TRUE[] = "true";
 const char CameraParameters::FALSE[] = "false";
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/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp
index 44ea79c..0755fb7 100644
--- a/libs/rs/driver/rsdBcc.cpp
+++ b/libs/rs/driver/rsdBcc.cpp
@@ -37,6 +37,7 @@
 struct DrvScript {
     int (*mRoot)();
     void (*mInit)();
+    void (*mFreeChildren)();
 
     BCCScriptRef mBccScript;
 
@@ -125,6 +126,7 @@
 
     drv->mRoot = reinterpret_cast<int (*)()>(bccGetFuncAddr(drv->mBccScript, "root"));
     drv->mInit = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, "init"));
+    drv->mFreeChildren = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, ".rs.dtor"));
 
     exportFuncCount = drv->ME->getExportFuncCount();
     if (exportFuncCount > 0) {
@@ -430,6 +432,13 @@
     }
 }
 
+void rsdScriptInvokeFreeChildren(const Context *dc, Script *script) {
+    DrvScript *drv = (DrvScript *)script->mHal.drv;
+
+    if (drv->mFreeChildren) {
+        drv->mFreeChildren();
+    }
+}
 
 void rsdScriptInvokeFunction(const Context *dc, Script *script,
                             uint32_t slot,
diff --git a/libs/rs/driver/rsdBcc.h b/libs/rs/driver/rsdBcc.h
index 67929bc..5f83ed2 100644
--- a/libs/rs/driver/rsdBcc.h
+++ b/libs/rs/driver/rsdBcc.h
@@ -43,6 +43,8 @@
                         android::renderscript::Script *script);
 void rsdScriptInvokeInit(const android::renderscript::Context *dc,
                          android::renderscript::Script *script);
+void rsdScriptInvokeFreeChildren(const android::renderscript::Context *dc,
+                                 android::renderscript::Script *script);
 
 void rsdScriptSetGlobalVar(const android::renderscript::Context *,
                            const android::renderscript::Script *,
diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp
index 171d045..a38fff77 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -60,6 +60,7 @@
         rsdScriptInvokeRoot,
         rsdScriptInvokeForEach,
         rsdScriptInvokeInit,
+        rsdScriptInvokeFreeChildren,
         rsdScriptSetGlobalVar,
         rsdScriptSetGlobalBind,
         rsdScriptSetGlobalObj,
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
index f62c72e..93513fe 100644
--- a/libs/rs/rsScript.cpp
+++ b/libs/rs/rsScript.cpp
@@ -72,6 +72,12 @@
     mRSC->mHal.funcs.script.setGlobalObj(mRSC, this, slot, val);
 }
 
+bool Script::freeChildren() {
+    incSysRef();
+    mRSC->mHal.funcs.script.invokeFreeChildren(mRSC, this);
+    return decSysRef();
+}
+
 namespace android {
 namespace renderscript {
 
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index c0324dd..d645421 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -73,6 +73,8 @@
     void setVar(uint32_t slot, const void *val, size_t len);
     void setVarObj(uint32_t slot, ObjectBase *val);
 
+    virtual bool freeChildren();
+
     virtual void runForEach(Context *rsc,
                             const Allocation * ain,
                             Allocation * aout,
diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h
index 21dff218..b8d7351 100644
--- a/libs/rs/rs_hal.h
+++ b/libs/rs/rs_hal.h
@@ -90,6 +90,7 @@
                               uint32_t usrLen,
                               const RsScriptCall *sc);
         void (*invokeInit)(const Context *rsc, Script *s);
+        void (*invokeFreeChildren)(const Context *rsc, Script *s);
 
         void (*setGlobalVar)(const Context *rsc, const Script *s,
                              uint32_t slot,
diff --git a/libs/rs/scriptc/rs_cl.rsh b/libs/rs/scriptc/rs_cl.rsh
index e402b86..bbc8fc5 100644
--- a/libs/rs/scriptc/rs_cl.rsh
+++ b/libs/rs/scriptc/rs_cl.rsh
@@ -15,7 +15,7 @@
  */
 
 /** @file rs_cl.rsh
- *  \brief Additional compute routines
+ *  \brief Basic math functions
  *
  *
  */
@@ -111,225 +111,555 @@
         fnc(float4 v1, float4 v2, int4 *v3);
 
 
+/**
+ * Return the inverse cosine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) acos(float);
 FN_FUNC_FN(acos)
 
+/**
+ * Return the inverse hyperbolic cosine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) acosh(float);
 FN_FUNC_FN(acosh)
 
+/**
+ * Return the inverse cosine divided by PI.
+ *
+ * Supports float, float2, float3, float4
+ */
 _RS_RUNTIME float __attribute__((overloadable)) acospi(float v);
-
-
 FN_FUNC_FN(acospi)
 
+/**
+ * Return the inverse sine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) asin(float);
 FN_FUNC_FN(asin)
 
+/**
+ * Return the inverse hyperbolic sine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) asinh(float);
 FN_FUNC_FN(asinh)
 
 
+/**
+ * Return the inverse sine divided by PI.
+ *
+ * Supports float, float2, float3, float4
+ */
 _RS_RUNTIME float __attribute__((overloadable)) asinpi(float v);
 FN_FUNC_FN(asinpi)
 
+/**
+ * Return the inverse tangent.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) atan(float);
 FN_FUNC_FN(atan)
 
-extern float __attribute__((overloadable)) atan2(float, float);
+/**
+ * Return the inverse tangent of y / x.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ *
+ * @param y
+ * @param x
+ */
+extern float __attribute__((overloadable)) atan2(float y, float x);
 FN_FUNC_FN_FN(atan2)
 
+/**
+ * Return the inverse hyperbolic tangent.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) atanh(float);
 FN_FUNC_FN(atanh)
 
-
+/**
+ * Return the inverse tangent divided by PI.
+ *
+ * Supports float, float2, float3, float4
+ */
 _RS_RUNTIME float __attribute__((overloadable)) atanpi(float v);
 FN_FUNC_FN(atanpi)
 
-
+/**
+ * Return the inverse tangent of y / x, divided by PI.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ *
+ * @param y
+ * @param x
+ */
 _RS_RUNTIME float __attribute__((overloadable)) atan2pi(float y, float x);
 FN_FUNC_FN_FN(atan2pi)
 
+
+/**
+ * Return the cube root.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) cbrt(float);
 FN_FUNC_FN(cbrt)
 
+/**
+ * Return the smallest integer not less than a value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) ceil(float);
 FN_FUNC_FN(ceil)
 
-extern float __attribute__((overloadable)) copysign(float, float);
+/**
+ * Copy the sign bit from y to x.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ *
+ * @param x
+ * @param y
+ */
+extern float __attribute__((overloadable)) copysign(float x, float y);
 FN_FUNC_FN_FN(copysign)
 
+/**
+ * Return the cosine.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) cos(float);
 FN_FUNC_FN(cos)
 
+/**
+ * Return the hypebolic cosine.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) cosh(float);
 FN_FUNC_FN(cosh)
 
-
+/**
+ * Return the cosine of the value * PI.
+ *
+ * Supports float, float2, float3, float4.
+ */
 _RS_RUNTIME float __attribute__((overloadable)) cospi(float v);
 FN_FUNC_FN(cospi)
 
+/**
+ * Return the complementary error function.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) erfc(float);
 FN_FUNC_FN(erfc)
 
+/**
+ * Return the error function.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) erf(float);
 FN_FUNC_FN(erf)
 
+/**
+ * Return e ^ value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) exp(float);
 FN_FUNC_FN(exp)
 
+/**
+ * Return 2 ^ value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) exp2(float);
 FN_FUNC_FN(exp2)
 
-extern float __attribute__((overloadable)) pow(float, float);
+/**
+ * Return x ^ y.
+ *
+ * Supports float, float2, float3, float4. Both arguments must be of the same
+ * type.
+ */
+extern float __attribute__((overloadable)) pow(float x, float y);
+FN_FUNC_FN_FN(pow)
 
+/**
+ * Return 10 ^ value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 _RS_RUNTIME float __attribute__((overloadable)) exp10(float v);
 FN_FUNC_FN(exp10)
 
+/**
+ * Return (e ^ value) - 1.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) expm1(float);
 FN_FUNC_FN(expm1)
 
+/**
+ * Return the absolute value of a value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) fabs(float);
 FN_FUNC_FN(fabs)
 
+/**
+ * Return the positive difference between two values.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ */
 extern float __attribute__((overloadable)) fdim(float, float);
 FN_FUNC_FN_FN(fdim)
 
+/**
+ * Return the smallest integer not greater than a value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) floor(float);
 FN_FUNC_FN(floor)
 
-extern float __attribute__((overloadable)) fma(float, float, float);
+/**
+ * Return a*b + c.
+ *
+ * Supports float, float2, float3, float4.
+ */
+extern float __attribute__((overloadable)) fma(float a, float b, float c);
 FN_FUNC_FN_FN_FN(fma)
 
-extern float __attribute__((overloadable)) fmax(float, float);
+/**
+ * Return (x < y ? y : x)
+ *
+ * Supports float, float2, float3, float4.
+ * @param x: may be float, float2, float3, float4
+ * @param y: may be float or vector.  If vector must match type of x.
+ */
+extern float __attribute__((overloadable)) fmax(float x, float y);
 FN_FUNC_FN_FN(fmax);
 FN_FUNC_FN_F(fmax);
 
-extern float __attribute__((overloadable)) fmin(float, float);
+/**
+ * Return (x > y ? y : x)
+ *
+ * @param x: may be float, float2, float3, float4
+ * @param y: may be float or vector.  If vector must match type of x.
+ */
+extern float __attribute__((overloadable)) fmin(float x, float y);
 FN_FUNC_FN_FN(fmin);
 FN_FUNC_FN_F(fmin);
 
-extern float __attribute__((overloadable)) fmod(float, float);
+/**
+ * Return the remainder from x / y
+ *
+ * Supports float, float2, float3, float4.
+ */
+extern float __attribute__((overloadable)) fmod(float x, float y);
 FN_FUNC_FN_FN(fmod)
 
 
+/**
+ * Return fractional part of v
+ *
+ * @param iptr  iptr[0] will be set to the floor of the input value.
+ * Supports float, float2, float3, float4.
+ */
 _RS_RUNTIME float __attribute__((overloadable)) fract(float v, float *iptr);
 FN_FUNC_FN_PFN(fract)
 
-extern float __attribute__((overloadable)) frexp(float, int *);
+/**
+ * Return the mantissa and place the exponent into iptr[0]
+ *
+ * @param v Supports float, float2, float3, float4.
+ * @param iptr  Must have the same vector size as v.
+ */
+extern float __attribute__((overloadable)) frexp(float v, int *iptr);
 FN_FUNC_FN_PIN(frexp)
 
-extern float __attribute__((overloadable)) hypot(float, float);
+/**
+ * Return sqrt(x*x + y*y)
+ *
+ * Supports float, float2, float3, float4.
+ */
+extern float __attribute__((overloadable)) hypot(float x, float y);
 FN_FUNC_FN_FN(hypot)
 
+/**
+ * Return the integer exponent of a value
+ *
+ * Supports 1,2,3,4 components
+ */
 extern int __attribute__((overloadable)) ilogb(float);
 IN_FUNC_FN(ilogb)
 
-extern float __attribute__((overloadable)) ldexp(float, int);
+/**
+ * Return (x * 2^y)
+ *
+ * @param x Supports 1,2,3,4 components
+ * @param y Supports single component or matching vector.
+ */
+extern float __attribute__((overloadable)) ldexp(float x, int y);
 FN_FUNC_FN_IN(ldexp)
 FN_FUNC_FN_I(ldexp)
 
+/**
+ * Return the log gamma
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) lgamma(float);
 FN_FUNC_FN(lgamma)
-extern float __attribute__((overloadable)) lgamma(float, int*);
+
+/**
+ * Return the log gamma and sign
+ *
+ * @param x Supports 1,2,3,4 components
+ * @param y Supports matching vector.
+ */
+extern float __attribute__((overloadable)) lgamma(float x, int* y);
 FN_FUNC_FN_PIN(lgamma)
 
+/**
+ * Return the natural logarithm
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) log(float);
 FN_FUNC_FN(log)
 
-
+/**
+ * Return the base 10 logarithm
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) log10(float);
 FN_FUNC_FN(log10)
 
-
+/**
+ * Return the base 2 logarithm
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) log2(float v);
 FN_FUNC_FN(log2)
 
-extern float __attribute__((overloadable)) log1p(float);
+/**
+ * Return the natural logarithm of (v + 1.0f)
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) log1p(float v);
 FN_FUNC_FN(log1p)
 
+/**
+ * Compute the exponent of the value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) logb(float);
 FN_FUNC_FN(logb)
 
-extern float __attribute__((overloadable)) mad(float, float, float);
+/**
+ * Compute (a * b) + c
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) mad(float a, float b, float c);
 FN_FUNC_FN_FN_FN(mad)
 
-extern float __attribute__((overloadable)) modf(float, float *);
+/**
+ * Return the integral and fractional components of a number
+ * Supports 1,2,3,4 components
+ *
+ * @param x Source value
+ * @param iret iret[0] will be set to the integral portion of the number.
+ * @return The floating point portion of the value.
+ */
+extern float __attribute__((overloadable)) modf(float x, float *iret);
 FN_FUNC_FN_PFN(modf);
 
 //extern float __attribute__((overloadable)) nan(uint);
 
-extern float __attribute__((overloadable)) nextafter(float, float);
+/**
+ * Return the next floating point number from x towards y.
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) nextafter(float x, float y);
 FN_FUNC_FN_FN(nextafter)
 
-FN_FUNC_FN_FN(pow)
-
+/**
+ * Return (v ^ p).
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) pown(float v, int p);
-_RS_RUNTIME float2 __attribute__((overloadable)) pown(float2 v, int2 p);
-_RS_RUNTIME float3 __attribute__((overloadable)) pown(float3 v, int3 p);
-_RS_RUNTIME float4 __attribute__((overloadable)) pown(float4 v, int4 p);
+FN_FUNC_FN_IN(pown)
 
+/**
+ * Return (v ^ p).
+ * @param v must be greater than 0.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) powr(float v, float p);
-_RS_RUNTIME float2 __attribute__((overloadable)) powr(float2 v, float2 p);
-_RS_RUNTIME float3 __attribute__((overloadable)) powr(float3 v, float3 p);
-_RS_RUNTIME float4 __attribute__((overloadable)) powr(float4 v, float4 p);
+FN_FUNC_FN_FN(powr)
 
-extern float __attribute__((overloadable)) remainder(float, float);
+/**
+ * Return round x/y to the nearest integer then compute the remander.
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) remainder(float x, float y);
 FN_FUNC_FN_FN(remainder)
 
+// document once we know the precision of bionic
 extern float __attribute__((overloadable)) remquo(float, float, int *);
 FN_FUNC_FN_FN_PIN(remquo)
 
+/**
+ * Round to the nearest integral value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) rint(float);
 FN_FUNC_FN(rint)
 
+/**
+ * Compute the Nth root of a value.
+ *
+ * Supports 1,2,3,4 components
+ */
+_RS_RUNTIME float __attribute__((overloadable)) rootn(float v, int n);
+FN_FUNC_FN_IN(rootn)
 
-_RS_RUNTIME float __attribute__((overloadable)) rootn(float v, int r);
-_RS_RUNTIME float2 __attribute__((overloadable)) rootn(float2 v, int2 r);
-_RS_RUNTIME float3 __attribute__((overloadable)) rootn(float3 v, int3 r);
-_RS_RUNTIME float4 __attribute__((overloadable)) rootn(float4 v, int4 r);
-
-
+/**
+ * Round to the nearest integral value.  Half values are rounded away from zero.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) round(float);
 FN_FUNC_FN(round)
 
-
+/**
+ * Return the square root of a value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) sqrt(float);
+FN_FUNC_FN(sqrt)
+
+/**
+ * Return (1 / sqrt(value)).
+ *
+ * @param v The incoming value in radians
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) rsqrt(float v);
 FN_FUNC_FN(rsqrt)
 
-extern float __attribute__((overloadable)) sin(float);
+/**
+ * Return the sine of a value specified in radians.
+ *
+ * @param v The incoming value in radians
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) sin(float v);
 FN_FUNC_FN(sin)
 
+/**
+ * Return the sine and cosine of a value.
+ *
+ * @return sine
+ * @param v The incoming value in radians
+ * @param *cosptr cosptr[0] will be set to the cosine value.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) sincos(float v, float *cosptr);
-_RS_RUNTIME float2 __attribute__((overloadable)) sincos(float2 v, float2 *cosptr);
-_RS_RUNTIME float3 __attribute__((overloadable)) sincos(float3 v, float3 *cosptr);
-_RS_RUNTIME float4 __attribute__((overloadable)) sincos(float4 v, float4 *cosptr);
+FN_FUNC_FN_PFN(sincos);
 
+/**
+ * Return the hyperbolic sine of a value specified in radians.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) sinh(float);
 FN_FUNC_FN(sinh)
 
+/**
+ * Return the sin(v * PI).
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) sinpi(float v);
 FN_FUNC_FN(sinpi)
 
-FN_FUNC_FN(sqrt)
-
-extern float __attribute__((overloadable)) tan(float);
+/**
+ * Return the tangent of a value.
+ *
+ * Supports 1,2,3,4 components
+ * @param v The incoming value in radians
+ */
+extern float __attribute__((overloadable)) tan(float v);
 FN_FUNC_FN(tan)
 
+/**
+ * Return the hyperbolic tangent of a value.
+ *
+ * Supports 1,2,3,4 components
+ * @param v The incoming value in radians
+ */
 extern float __attribute__((overloadable)) tanh(float);
 FN_FUNC_FN(tanh)
 
+/**
+ * Return tan(v * PI)
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) tanpi(float v);
 FN_FUNC_FN(tanpi)
 
-
+/**
+ * Compute the gamma function of a value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) tgamma(float);
 FN_FUNC_FN(tgamma)
 
+/**
+ * Round to integral using truncation.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) trunc(float);
 FN_FUNC_FN(trunc)
 
-// Int ops (partial), 6.11.3
 
 #define XN_FUNC_YN(typeout, fnc, typein)                                \
 extern typeout __attribute__((overloadable)) fnc(typein);               \
@@ -373,14 +703,29 @@
 UIN_FUNC_IN(abs)
 IN_FUNC_IN(clz)
 
+/**
+ * Return the minimum of two values.
+ *
+ * Supports 1,2,3,4 components of uchar, char, ushort, short, uint, int, float.
+ */
 IN_FUNC_IN_IN_BODY(min, (v1 < v2 ? v1 : v2))
 FN_FUNC_FN_F(min)
 
+/**
+ * Return the maximum of two values.
+ *
+ * Supports 1,2,3,4 components of uchar, char, ushort, short, uint, int, float.
+ */
 IN_FUNC_IN_IN_BODY(max, (v1 > v2 ? v1 : v2))
 FN_FUNC_FN_F(max)
 
-// 6.11.4
-
+/**
+ *  Clamp a value to a specified high and low bound.
+ *
+ * @param amount value to be clamped.  Supports 1,2,3,4 components
+ * @param low Lower bound, must be scalar or matching vector.
+ * @param high High bound, must match type of low
+ */
 _RS_RUNTIME float __attribute__((overloadable)) clamp(float amount, float low, float high);
 _RS_RUNTIME float2 __attribute__((overloadable)) clamp(float2 amount, float2 low, float2 high);
 _RS_RUNTIME float3 __attribute__((overloadable)) clamp(float3 amount, float3 low, float3 high);
@@ -389,9 +734,19 @@
 _RS_RUNTIME float3 __attribute__((overloadable)) clamp(float3 amount, float low, float high);
 _RS_RUNTIME float4 __attribute__((overloadable)) clamp(float4 amount, float low, float high);
 
+/**
+ * Convert from radians to degrees.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) degrees(float radians);
 FN_FUNC_FN(degrees)
 
+/**
+ * return start + ((stop - start) * amount);
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) mix(float start, float stop, float amount);
 _RS_RUNTIME float2 __attribute__((overloadable)) mix(float2 start, float2 stop, float2 amount);
 _RS_RUNTIME float3 __attribute__((overloadable)) mix(float3 start, float3 stop, float3 amount);
@@ -400,9 +755,22 @@
 _RS_RUNTIME float3 __attribute__((overloadable)) mix(float3 start, float3 stop, float amount);
 _RS_RUNTIME float4 __attribute__((overloadable)) mix(float4 start, float4 stop, float amount);
 
+/**
+ * Convert from degrees to radians.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) radians(float degrees);
 FN_FUNC_FN(radians)
 
+/**
+ * if (v < edge)
+ *     return 0.f;
+ * else
+ *     return 1.f;
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) step(float edge, float v);
 _RS_RUNTIME float2 __attribute__((overloadable)) step(float2 edge, float2 v);
 _RS_RUNTIME float3 __attribute__((overloadable)) step(float3 edge, float3 v);
@@ -411,6 +779,7 @@
 _RS_RUNTIME float3 __attribute__((overloadable)) step(float3 edge, float v);
 _RS_RUNTIME float4 __attribute__((overloadable)) step(float4 edge, float v);
 
+// not implemented
 extern float __attribute__((overloadable)) smoothstep(float, float, float);
 extern float2 __attribute__((overloadable)) smoothstep(float2, float2, float2);
 extern float3 __attribute__((overloadable)) smoothstep(float3, float3, float3);
@@ -419,29 +788,59 @@
 extern float3 __attribute__((overloadable)) smoothstep(float, float, float3);
 extern float4 __attribute__((overloadable)) smoothstep(float, float, float4);
 
+/**
+ * if (v < 0) return -1.f;
+ * else if (v > 0) return 1.f;
+ * else return 0.f;
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) sign(float v);
 FN_FUNC_FN(sign)
 
-// 6.11.5
+/**
+ * Compute the cross product of two vectors.
+ *
+ * Supports 3,4 components
+ */
 _RS_RUNTIME float3 __attribute__((overloadable)) cross(float3 lhs, float3 rhs);
-
 _RS_RUNTIME float4 __attribute__((overloadable)) cross(float4 lhs, float4 rhs);
 
+/**
+ * Compute the dot product of two vectors.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) dot(float lhs, float rhs);
 _RS_RUNTIME float __attribute__((overloadable)) dot(float2 lhs, float2 rhs);
 _RS_RUNTIME float __attribute__((overloadable)) dot(float3 lhs, float3 rhs);
 _RS_RUNTIME float __attribute__((overloadable)) dot(float4 lhs, float4 rhs);
 
+/**
+ * Compute the length of a vector.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) length(float v);
 _RS_RUNTIME float __attribute__((overloadable)) length(float2 v);
 _RS_RUNTIME float __attribute__((overloadable)) length(float3 v);
 _RS_RUNTIME float __attribute__((overloadable)) length(float4 v);
 
+/**
+ * Compute the distance between two points.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) distance(float lhs, float rhs);
 _RS_RUNTIME float __attribute__((overloadable)) distance(float2 lhs, float2 rhs);
 _RS_RUNTIME float __attribute__((overloadable)) distance(float3 lhs, float3 rhs);
 _RS_RUNTIME float __attribute__((overloadable)) distance(float4 lhs, float4 rhs);
 
+/**
+ * Normalize a vector.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) normalize(float v);
 _RS_RUNTIME float2 __attribute__((overloadable)) normalize(float2 v);
 _RS_RUNTIME float3 __attribute__((overloadable)) normalize(float3 v);
diff --git a/libs/rs/scriptc/rs_quaternion.rsh b/libs/rs/scriptc/rs_quaternion.rsh
index 36e6736..23945ae 100644
--- a/libs/rs/scriptc/rs_quaternion.rsh
+++ b/libs/rs/scriptc/rs_quaternion.rsh
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-/** @file rs_matrix.rsh
+/** @file rs_quaternion.rsh
  *  \brief Quaternion routines
  *
  *
diff --git a/libs/rs/scriptc/rs_time.rsh b/libs/rs/scriptc/rs_time.rsh
index f8f297d..60e3dee 100644
--- a/libs/rs/scriptc/rs_time.rsh
+++ b/libs/rs/scriptc/rs_time.rsh
@@ -15,43 +15,96 @@
  */
 
 /** @file rs_time.rsh
- *  \brief Time routines
+ *  \brief Renderscript time routines
  *
- *
+ *  This file contains Renderscript functions relating to time and date
+ *  manipulation.
  */
 
 #ifndef __RS_TIME_RSH__
 #define __RS_TIME_RSH__
 
+/**
+ * Calendar time interpreted as seconds elapsed since the Epoch (00:00:00 on
+ * January 1, 1970, Coordinated Universal Time (UTC)).
+ */
 typedef int rs_time_t;
 
+/**
+ * Data structure for broken-down time components.
+ *
+ * tm_sec   - Seconds after the minute. This ranges from 0 to 59, but possibly
+ *            up to 60 for leap seconds.
+ * tm_min   - Minutes after the hour. This ranges from 0 to 59.
+ * tm_hour  - Hours past midnight. This ranges from 0 to 23.
+ * tm_mday  - Day of the month. This ranges from 1 to 31.
+ * tm_mon   - Months since January. This ranges from 0 to 11.
+ * tm_year  - Years since 1900.
+ * tm_wday  - Days since Sunday. This ranges from 0 to 6.
+ * tm_yday  - Days since January 1. This ranges from 0 to 365.
+ * tm_isdst - Flag to indicate whether daylight saving time is in effect. The
+ *            value is positive if it is in effect, zero if it is not, and
+ *            negative if the information is not available.
+ */
 typedef struct {
-    int tm_sec;
-    int tm_min;
-    int tm_hour;
-    int tm_mday;
-    int tm_mon;
-    int tm_year;
-    int tm_wday;
-    int tm_yday;
-    int tm_isdst;
+    int tm_sec;     ///< seconds
+    int tm_min;     ///< minutes
+    int tm_hour;    ///< hours
+    int tm_mday;    ///< day of the month
+    int tm_mon;     ///< month
+    int tm_year;    ///< year
+    int tm_wday;    ///< day of the week
+    int tm_yday;    ///< day of the year
+    int tm_isdst;   ///< daylight savings time
 } rs_tm;
 
+/**
+ * Returns the number of seconds since the Epoch (00:00:00 UTC, January 1,
+ * 1970). If @p timer is non-NULL, the result is also stored in the memory
+ * pointed to by this variable. If an error occurs, a value of -1 is returned.
+ *
+ * @param timer Location to also store the returned calendar time.
+ *
+ * @return Seconds since the Epoch.
+ */
 extern rs_time_t __attribute__((overloadable))
     rsTime(rs_time_t *timer);
 
+/**
+ * Converts the time specified by @p timer into broken-down time and stores it
+ * in @p local. This function also returns a pointer to @p local. If @p local
+ * is NULL, this function does nothing and returns NULL.
+ *
+ * @param local Broken-down time.
+ * @param timer Input time as calendar time.
+ *
+ * @return Pointer to broken-down time (same as input @p local).
+ */
 extern rs_tm * __attribute__((overloadable))
     rsLocaltime(rs_tm *local, const rs_time_t *timer);
 
-// Return the current system clock in milliseconds
+/**
+ * Returns the current system clock (uptime) in milliseconds.
+ *
+ * @return Uptime in milliseconds.
+ */
 extern int64_t __attribute__((overloadable))
     rsUptimeMillis(void);
 
-// Return the current system clock in nanoseconds
+/**
+ * Returns the current system clock (uptime) in nanoseconds.
+ *
+ * @return Uptime in nanoseconds.
+ */
 extern int64_t __attribute__((overloadable))
     rsUptimeNanos(void);
 
-// Return the time in seconds since function was last called in this script.
+/**
+ * Returns the time in seconds since this function was last called in this
+ * script.
+ *
+ * @return Time in seconds.
+ */
 extern float __attribute__((overloadable))
     rsGetDt(void);
 
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 875beb9..5d5df60 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -89,7 +89,7 @@
  */
 typedef uint32_t uint;
 /**
- * Typedef for unsigned char (use for 64-bit unsigned integers)
+ * Typedef for unsigned long (use for 64-bit unsigned integers)
  */
 typedef uint64_t ulong;
 /**
@@ -102,67 +102,67 @@
 typedef int32_t ssize_t;
 
 /**
- * \brief Opaque handle to a RenderScript element.
+ * \brief Opaque handle to a Renderscript element.
  *
  * See: android.renderscript.Element
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_element;
 /**
- * \brief Opaque handle to a RenderScript type.
+ * \brief Opaque handle to a Renderscript type.
  *
  * See: android.renderscript.Type
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_type;
 /**
- * \brief Opaque handle to a RenderScript allocation.
+ * \brief Opaque handle to a Renderscript allocation.
  *
  * See: android.renderscript.Allocation
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_allocation;
 /**
- * \brief Opaque handle to a RenderScript sampler object.
+ * \brief Opaque handle to a Renderscript sampler object.
  *
  * See: android.renderscript.Sampler
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_sampler;
 /**
- * \brief Opaque handle to a RenderScript script object.
+ * \brief Opaque handle to a Renderscript script object.
  *
  * See: android.renderscript.ScriptC
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_script;
 /**
- * \brief Opaque handle to a RenderScript mesh object.
+ * \brief Opaque handle to a Renderscript mesh object.
  *
  * See: android.renderscript.Mesh
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_mesh;
 /**
- * \brief Opaque handle to a RenderScript ProgramFragment object.
+ * \brief Opaque handle to a Renderscript ProgramFragment object.
  *
  * See: android.renderscript.ProgramFragment
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_fragment;
 /**
- * \brief Opaque handle to a RenderScript ProgramVertex object.
+ * \brief Opaque handle to a Renderscript ProgramVertex object.
  *
  * See: android.renderscript.ProgramVertex
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_vertex;
 /**
- * \brief Opaque handle to a RenderScript sampler object.
+ * \brief Opaque handle to a Renderscript ProgramRaster object.
  *
- * See: android.renderscript.Sampler
+ * See: android.renderscript.ProgramRaster
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_raster;
 /**
- * \brief Opaque handle to a RenderScript ProgramStore object.
+ * \brief Opaque handle to a Renderscript ProgramStore object.
  *
  * See: android.renderscript.ProgramStore
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_store;
 /**
- * \brief Opaque handle to a RenderScript font object.
+ * \brief Opaque handle to a Renderscript font object.
  *
  * See: android.renderscript.Font
  */
@@ -170,163 +170,163 @@
 
 /**
  * Vector version of the basic float type.
- * Provides two float fields packed into a single 64bit field with 64 bit
+ * Provides two float fields packed into a single 64 bit field with 64 bit
  * alignment.
  */
 typedef float float2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic float type. Provides three float fields packed
- * into a single 128bit field with 128 bit alignment.
+ * into a single 128 bit field with 128 bit alignment.
  */
 typedef float float3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic float type.
- * Provides four float fields packed into a single 128bit field with 128bit
+ * Provides four float fields packed into a single 128 bit field with 128 bit
  * alignment.
  */
 typedef float float4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic double type. Provides two double fields packed
- * into a single 128bit field with 128bit alignment.
+ * into a single 128 bit field with 128 bit alignment.
  */
 typedef double double2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic double type. Provides three double fields packed
- * into a single 256bit field with 256bit alignment.
+ * into a single 256 bit field with 256 bit alignment.
  */
 typedef double double3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic double type. Provides four double fields packed
- * into a single 256bit field with 256bit alignment.
+ * into a single 256 bit field with 256 bit alignment.
  */
 typedef double double4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic uchar type. Provides two uchar fields packed
- * into a single 16bit field with 16bit alignment.
+ * into a single 16 bit field with 16 bit alignment.
  */
 typedef uchar uchar2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic uchar type. Provides three uchar fields packed
- * into a single 32bit field with 32bit alignment.
+ * into a single 32 bit field with 32 bit alignment.
  */
 typedef uchar uchar3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic uchar type. Provides four uchar fields packed
- * into a single 32bit field with 32bit alignment.
+ * into a single 32 bit field with 32 bit alignment.
  */
 typedef uchar uchar4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic ushort type. Provides two ushort fields packed
- * into a single 32bit field with 32bit alignment.
+ * into a single 32 bit field with 32 bit alignment.
  */
 typedef ushort ushort2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic ushort type. Provides three ushort fields packed
- * into a single 64bit field with 64bit alignment.
+ * into a single 64 bit field with 64 bit alignment.
  */
 typedef ushort ushort3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic ushort type. Provides four ushort fields packed
- * into a single 64bit field with 64bit alignment.
+ * into a single 64 bit field with 64 bit alignment.
  */
 typedef ushort ushort4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic uint type. Provides two uint fields packed into a
- * single 64bit field with 64bit alignment.
+ * single 64 bit field with 64 bit alignment.
  */
 typedef uint uint2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic uint type. Provides three uint fields packed into
- * a single 128bit field with 128bit alignment.
+ * a single 128 bit field with 128 bit alignment.
  */
 typedef uint uint3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic uint type. Provides four uint fields packed into
- * a single 128bit field with 128bit alignment.
+ * a single 128 bit field with 128 bit alignment.
  */
 typedef uint uint4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic ulong type. Provides two ulong fields packed into
- * a single 128bit field with 128bit alignment.
+ * a single 128 bit field with 128 bit alignment.
  */
 typedef ulong ulong2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic ulong type. Provides three ulong fields packed
- * into a single 256bit field with 256bit alignment.
+ * into a single 256 bit field with 256 bit alignment.
  */
 typedef ulong ulong3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic ulong type. Provides four ulong fields packed
- * into a single 256bit field with 256bit alignment.
+ * into a single 256 bit field with 256 bit alignment.
  */
 typedef ulong ulong4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic char type. Provides two char fields packed into a
- * single 16bit field with 16bit alignment.
+ * single 16 bit field with 16 bit alignment.
  */
 typedef char char2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic char type. Provides three char fields packed into
- * a single 32bit field with 32bit alignment.
+ * a single 32 bit field with 32 bit alignment.
  */
 typedef char char3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic char type. Provides four char fields packed into
- * a single 32bit field with 32bit alignment.
+ * a single 32 bit field with 32 bit alignment.
  */
 typedef char char4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic short type. Provides two short fields packed into
- * a single 32bit field with 32bit alignment.
+ * a single 32 bit field with 32 bit alignment.
  */
 typedef short short2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic short type. Provides three short fields packed
- * into a single 64bit field with 64bit alignment.
+ * into a single 64 bit field with 64 bit alignment.
  */
 typedef short short3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic short type. Provides four short fields packed
- * into a single 64bit field with 64bit alignment.
+ * into a single 64 bit field with 64 bit alignment.
  */
 typedef short short4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic int type. Provides two int fields packed into a
- * single 64bit field with 64bit alignment.
+ * single 64 bit field with 64 bit alignment.
  */
 typedef int int2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic int type. Provides three int fields packed into a
- * single 128bit field with 128bit alignment.
+ * single 128 bit field with 128 bit alignment.
  */
 typedef int int3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic int type. Provides two four fields packed into a
- * single 128bit field with 128bit alignment.
+ * single 128 bit field with 128 bit alignment.
  */
 typedef int int4 __attribute__((ext_vector_type(4)));
 
 /**
  * Vector version of the basic long type. Provides two long fields packed into a
- * single 128bit field with 128bit alignment.
+ * single 128 bit field with 128 bit alignment.
  */
 typedef long long2 __attribute__((ext_vector_type(2)));
 /**
  * Vector version of the basic long type. Provides three long fields packed into
- * a single 256bit field with 256bit alignment.
+ * a single 256 bit field with 256 bit alignment.
  */
 typedef long long3 __attribute__((ext_vector_type(3)));
 /**
  * Vector version of the basic long type. Provides four long fields packed into
- * a single 256bit field with 256bit alignment.
+ * a single 256 bit field with 256 bit alignment.
  */
 typedef long long4 __attribute__((ext_vector_type(4)));
 
@@ -369,8 +369,6 @@
 
 /**
  * \brief Enum for selecting cube map faces
- *
- * Used todo-alexst
  */
 typedef enum {
     RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X = 0,
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index d18c0a2..02c380b 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -368,6 +368,41 @@
     return rc;
 }
 
+int androidGetThreadSchedulingGroup(pid_t tid)
+{
+    int ret = ANDROID_TGROUP_DEFAULT;
+
+#if defined(HAVE_PTHREADS)
+    // convention is to not call get/set_sched_policy methods if disabled by property
+    pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
+    if (gDoSchedulingGroup) {
+        SchedPolicy policy;
+        // get_sched_policy does not support tid == 0
+        if (tid == 0) {
+            tid = androidGetTid();
+        }
+        if (get_sched_policy(tid, &policy) < 0) {
+            ret = INVALID_OPERATION;
+        } else {
+            switch (policy) {
+            case SP_BACKGROUND:
+                ret = ANDROID_TGROUP_BG_NONINTERACT;
+                break;
+            case SP_FOREGROUND:
+                ret = ANDROID_TGROUP_FG_BOOST;
+                break;
+            default:
+                // should not happen, as enum SchedPolicy does not have any other values
+                ret = INVALID_OPERATION;
+                break;
+            }
+        }
+    }
+#endif
+
+    return ret;
+}
+
 namespace android {
 
 /*
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index dbe4115..cd15718 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_syncAfRcs(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_syncAfRcs(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_syncAfRcs(RC_INFO_ALL);
             }
         }//synchronized(mAudioFocusLock)
 
@@ -3182,7 +3183,7 @@
      * Helper function:
      * Called synchronized on mRCStack
      */
-    private void clearRemoteControlDisplay() {
+    private void clearRemoteControlDisplay_syncAfRcs() {
         synchronized(mCurrentRcLock) {
             mCurrentRcClient = null;
         }
@@ -3191,18 +3192,21 @@
     }
 
     /**
-     * Helper function:
-     * Called synchronized on mRCStack
-     * mRCStack.empty() is false
+     * Helper function for code readability: only to be called from
+     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
+     *    this method.
+     * Preconditions:
+     *    - called synchronized mAudioFocusLock then on mRCStack
+     *    - mRCStack.isEmpty() is false
      */
-    private void updateRemoteControlDisplay(int infoChangedFlags) {
+    private void updateRemoteControlDisplay_syncAfRcs(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_syncAfRcs();
             return;
         }
         synchronized(mCurrentRcLock) {
@@ -3219,17 +3223,17 @@
 
     /**
      * Helper function:
-     * Called synchronized on mFocusLock, then mRCStack
+     * Called synchronized on mAudioFocusLock, then mRCStack
      * Check whether the remote control display should be updated, triggers the update if required
      * @param infoChangedFlags the flags corresponding to the remote control client information
      *     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_syncAfRcs(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_syncAfRcs();
             return;
         }
         // if the top of the two stacks belong to different packages, there is a mismatch, clear
@@ -3237,17 +3241,18 @@
                 && (mFocusStack.peek().mPackageName != null)
                 && !(mRCStack.peek().mCallingPackageName.compareTo(
                         mFocusStack.peek().mPackageName) == 0)) {
-            clearRemoteControlDisplay();
+            clearRemoteControlDisplay_syncAfRcs();
             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_syncAfRcs();
             return;
         }
         // refresh conditions were verified: update the remote controls
-        updateRemoteControlDisplay(infoChangedFlags);
+        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
+        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
     }
 
     /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
@@ -3258,7 +3263,7 @@
             synchronized(mRCStack) {
                 pushMediaButtonReceiver(eventReceiver);
                 // new RC client, assume every type of information shall be queried
-                checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
         }
     }
@@ -3273,7 +3278,7 @@
                 removeMediaButtonReceiver(eventReceiver);
                 if (topOfStackWillChange) {
                     // current RC client will change, assume every type of info needs to be queried
-                    checkUpdateRemoteControlDisplay(RC_INFO_ALL);
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                 }
             }
         }
@@ -3282,6 +3287,7 @@
     /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
     public void registerRemoteControlClient(ComponentName eventReceiver,
             IRemoteControlClient rcClient, String clientName, String callingPackageName) {
+        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
                 // store the new display information
@@ -3329,7 +3335,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_syncAfRcs(RC_INFO_ALL);
                 }
             }
         }
@@ -3426,51 +3432,43 @@
     }
 
     /**
-     * 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.
      */
     public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
         if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
-        synchronized(mRCStack) {
-            if ((mRcDisplay == rcd) || (rcd == null)) {
-                return;
-            }
-            // if we had a display before, stop monitoring its death
-            rcDisplay_stopDeathMonitor_syncRcStack();
-            mRcDisplay = rcd;
-            // new display, start monitoring its death
-            rcDisplay_startDeathMonitor_syncRcStack();
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                if ((mRcDisplay == rcd) || (rcd == null)) {
+                    return;
+                }
+                // if we had a display before, stop monitoring its death
+                rcDisplay_stopDeathMonitor_syncRcStack();
+                mRcDisplay = rcd;
+                // new display, start monitoring its death
+                rcDisplay_startDeathMonitor_syncRcStack();
 
-            // let all the remote control clients there is a new display
-            // no need to unplug the previous because we only support one display
-            // and the clients don't track the death of the display
-            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-            while(stackIterator.hasNext()) {
-                RemoteControlStackEntry rcse = stackIterator.next();
-                if(rcse.mRcClient != null) {
-                    try {
-                        rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error connecting remote control display to client: " + e);
-                        e.printStackTrace();
+                // let all the remote control clients there is a new display
+                // no need to unplug the previous because we only support one display
+                // and the clients don't track the death of the display
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mRcClient != null) {
+                        try {
+                            rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error connecting remote control display to client: " + e);
+                            e.printStackTrace();
+                        }
                     }
                 }
-            }
 
-            // 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;
-                    }
-                }
+                // we have a new display, of which all the clients are now aware: have it be updated
+                checkUpdateRemoteControlDisplay_syncAfRcs(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/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index b7d129d..2b4e85f 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -29,6 +29,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.Matrix;
 import android.media.videoeditor.VideoEditor.ExportProgressListener;
 import android.media.videoeditor.VideoEditor.PreviewProgressListener;
 import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
@@ -1050,6 +1051,10 @@
          */
          public int rgbWidth;
          public int rgbHeight;
+         /**
+         * Video rotation degree.
+         */
+         public int rotationDegree;
     }
 
     /**
@@ -1700,6 +1705,11 @@
          */
         public int audioVolumeValue;
 
+        /**
+         * Video rotation degree.
+         */
+        public int videoRotation;
+
         public String Id;
     }
 
@@ -2254,6 +2264,7 @@
         lclipSettings.panZoomTopLeftXEnd = 0;
         lclipSettings.panZoomTopLeftYEnd = 0;
         lclipSettings.mediaRendering = 0;
+        lclipSettings.rotationDegree = 0;
     }
 
 
@@ -3784,7 +3795,8 @@
      **/
     void getPixelsList(String filename, final int width, final int height,
             long startMs, long endMs, int thumbnailCount, int[] indices,
-            final MediaItem.GetThumbnailListCallback callback) {
+            final MediaItem.GetThumbnailListCallback callback,
+            final int videoRotation) {
         /* Make width and height as even */
         final int newWidth = (width + 1) & 0xFFFFFFFE;
         final int newHeight = (height + 1) & 0xFFFFFFFE;
@@ -3799,7 +3811,7 @@
         final int[] rgb888 = new int[thumbnailSize];
         final IntBuffer tmpBuffer = IntBuffer.allocate(thumbnailSize);
         nativeGetPixelsList(filename, rgb888, newWidth, newHeight,
-                thumbnailCount, startMs, endMs, indices,
+                thumbnailCount, videoRotation, startMs, endMs, indices,
                 new NativeGetPixelsListCallback() {
             public void onThumbnail(int index) {
                 Bitmap bitmap = Bitmap.createBitmap(
@@ -3821,7 +3833,21 @@
 
                     canvas.setBitmap(null);
                 }
-                callback.onThumbnail(bitmap, index);
+
+                if (videoRotation == 0) {
+                    callback.onThumbnail(bitmap, index);
+                } else {
+                    Matrix mtx = new Matrix();
+                    mtx.postRotate(videoRotation);
+                    Bitmap rotatedBmp =
+                        Bitmap.createBitmap(bitmap, 0, 0, width, height, mtx, false);
+                    callback.onThumbnail(rotatedBmp, index);
+
+                    if (bitmap != null) {
+                        bitmap.recycle();
+                    }
+                }
+
             }
         });
 
@@ -3943,8 +3969,8 @@
             long timeMS);
 
     private native int nativeGetPixelsList(String fileName, int[] pixelArray,
-            int width, int height, int nosofTN, long startTimeMs, long endTimeMs,
-            int[] indices, NativeGetPixelsListCallback callback);
+            int width, int height, int nosofTN, int videoRotation, long startTimeMs,
+            long endTimeMs, int[] indices, NativeGetPixelsListCallback callback);
 
     /**
      * Releases the JNI and cleans up the core native module.. Should be called
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index b2a279a..65a9e19 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -638,7 +638,7 @@
             }
 
             mMANativeHelper.getPixelsList(getGeneratedImageClip(), width,
-                height, startMs, endMs, thumbnailCount, indices, callback);
+                height, startMs, endMs, thumbnailCount, indices, callback, 0);
         }
     }
 
diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index fea751b..2ce857c 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -57,6 +57,7 @@
     private String mAudioWaveformFilename;
     private MediaArtistNativeHelper mMANativeHelper;
     private VideoEditorImpl mVideoEditor;
+    private final int mVideoRotationDegree;
     /**
      *  The audio waveform data
      */
@@ -190,6 +191,7 @@
         } else {
             mWaveformData = null;
         }
+        mVideoRotationDegree = properties.videoRotation;
     }
 
     /**
@@ -317,7 +319,8 @@
         }
 
         mMANativeHelper.getPixelsList(super.getFilename(), width,
-                height, startMs, endMs, thumbnailCount, indices, callback);
+                height, startMs, endMs, thumbnailCount, indices, callback,
+                mVideoRotationDegree);
     }
 
     /*
@@ -425,7 +428,12 @@
      */
     @Override
     public int getWidth() {
-        return mWidth;
+        if (mVideoRotationDegree == 90 ||
+             mVideoRotationDegree == 270) {
+            return mHeight;
+        } else {
+            return mWidth;
+        }
     }
 
     /*
@@ -433,7 +441,12 @@
      */
     @Override
     public int getHeight() {
-        return mHeight;
+        if (mVideoRotationDegree == 90 ||
+             mVideoRotationDegree == 270) {
+            return mWidth;
+        } else {
+            return mHeight;
+        }
     }
 
     /*
@@ -725,6 +738,7 @@
         clipSettings.beginCutTime = (int)getBoundaryBeginTime();
         clipSettings.endCutTime = (int)getBoundaryEndTime();
         clipSettings.mediaRendering = mMANativeHelper.getMediaItemRenderingMode(getRenderingMode());
+        clipSettings.rotationDegree = mVideoRotationDegree;
 
         return clipSettings;
     }
diff --git a/media/java/android/media/videoeditor/VideoEditorProfile.java b/media/java/android/media/videoeditor/VideoEditorProfile.java
index ecdcdfb..202a2df 100755
--- a/media/java/android/media/videoeditor/VideoEditorProfile.java
+++ b/media/java/android/media/videoeditor/VideoEditorProfile.java
@@ -91,7 +91,7 @@
             case MediaProperties.VCODEC_H263:
             case MediaProperties.VCODEC_H264:
             case MediaProperties.VCODEC_MPEG4:
-                 level = native_get_videoeditor_export_profile(vidCodec);
+                 level = native_get_videoeditor_export_level(vidCodec);
                  break;
             default :
                throw new IllegalArgumentException("Unsupported video codec" + vidCodec);
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index 69735ca..4e0e0f2 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -490,7 +490,8 @@
     VIDEOEDIT_JAVA_FIELD_INIT("audioDuration",          "I"),
     VIDEOEDIT_JAVA_FIELD_INIT("audioBitrate",           "I"),
     VIDEOEDIT_JAVA_FIELD_INIT("audioChannels",          "I"),
-    VIDEOEDIT_JAVA_FIELD_INIT("audioSamplingFrequency", "I")
+    VIDEOEDIT_JAVA_FIELD_INIT("audioSamplingFrequency", "I"),
+    VIDEOEDIT_JAVA_FIELD_INIT("videoRotation",          "I")
 };
 
 VIDEOEDIT_JAVA_DEFINE_FIELD_CLASS(Properties, PROPERTIES_CLASS_NAME)
@@ -540,7 +541,8 @@
     VIDEOEDIT_JAVA_FIELD_INIT("panZoomTopLeftYEnd",   "I"                 ),
     VIDEOEDIT_JAVA_FIELD_INIT("mediaRendering",       "I"                 ),
     VIDEOEDIT_JAVA_FIELD_INIT("rgbWidth",           "I"                 ),
-    VIDEOEDIT_JAVA_FIELD_INIT("rgbHeight",          "I"                 )
+    VIDEOEDIT_JAVA_FIELD_INIT("rgbHeight",          "I"                 ),
+    VIDEOEDIT_JAVA_FIELD_INIT("rotationDegree",     "I"                 )
 };
 
 VIDEOEDIT_JAVA_DEFINE_FIELD_CLASS(ClipSettings, CLIP_SETTINGS_CLASS_NAME)
@@ -1402,6 +1404,10 @@
             VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", \
                 "getClipSettings-- rgbFileHeight %d ",
                 pSettings->ClipProperties.uiStillPicHeight);
+
+            // Set the video rotation degree
+            pSettings->ClipProperties.videoRotationDegrees =
+                (M4OSA_UInt32)pEnv->GetIntField(object, fieldIds.rotationDegree);
         }
 
         // Check if settings could be set.
@@ -1513,6 +1519,10 @@
                 pSettings->ClipProperties.uiStillPicWidth ,
                 pSettings->ClipProperties.uiStillPicHeight);
 
+            // Set the video rotation
+            pEnv->SetIntField(object, fieldIds.rotationDegree,
+                pSettings->ClipProperties.videoRotationDegrees);
+
             // Return the object.
             (*pObject) = object;
         }
@@ -1609,6 +1619,9 @@
             pEnv->SetIntField(object, fieldIds.audioSamplingFrequency,
                 pProperties->uiSamplingFrequency);
 
+            // Set the video rotation field.
+            pEnv->SetIntField(object, fieldIds.videoRotation, pProperties->uiRotation);
+
             // Return the object.
             (*pObject) = object;
         }
diff --git a/media/jni/mediaeditor/VideoEditorClasses.h b/media/jni/mediaeditor/VideoEditorClasses.h
index 3c10b1d..a4c82a8 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.h
+++ b/media/jni/mediaeditor/VideoEditorClasses.h
@@ -145,6 +145,7 @@
     M4OSA_UInt32 uiAudioBitrate;
     M4OSA_UInt32 uiNbChannels;
     M4OSA_UInt32 uiSamplingFrequency;
+    M4OSA_UInt32 uiRotation;
 } VideoEditPropClass_Properties;
 
 typedef struct
@@ -166,6 +167,7 @@
     jfieldID audioBitrate;
     jfieldID audioChannels;
     jfieldID audioSamplingFrequency;
+    jfieldID videoRotation;
 } VideoEditJava_PropertiesFieldIds;
 
 
@@ -187,6 +189,7 @@
     jfieldID mediaRendering;
     jfieldID rgbFileWidth;
     jfieldID rgbFileHeight;
+    jfieldID rotationDegree;
 } VideoEditJava_ClipSettingsFieldIds;
 
 typedef struct
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index b737e5d..4e73581 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -185,6 +185,7 @@
                                      M4OSA_UInt32             width,
                                      M4OSA_UInt32             height,
                                      M4OSA_UInt32             noOfThumbnails,
+                                     M4OSA_UInt32             videoRotation,
                                      jlong                    startTime,
                                      jlong                    endTime,
                                      jintArray                indexArray,
@@ -291,7 +292,7 @@
                                 (void *)videoEditor_release            },
     {"nativeGetPixels",         "(Ljava/lang/String;[IIIJ)I",
                                 (void*)videoEditor_getPixels               },
-    {"nativeGetPixelsList",     "(Ljava/lang/String;[IIIIJJ[ILandroid/media/videoeditor/MediaArtistNativeHelper$NativeGetPixelsListCallback;)I",
+    {"nativeGetPixelsList",     "(Ljava/lang/String;[IIIIIJJ[ILandroid/media/videoeditor/MediaArtistNativeHelper$NativeGetPixelsListCallback;)I",
                                 (void*)videoEditor_getPixelsList           },
     {"getMediaProperties",
     "(Ljava/lang/String;)Landroid/media/videoeditor/MediaArtistNativeHelper$Properties;",
@@ -375,6 +376,12 @@
                     pEnv->GetIntField(object,fid);
    M4OSA_TRACE1_1("audioVolumeValue = %d",
                     pSettings->ClipProperties.uiClipAudioVolumePercentage);
+
+   fid = pEnv->GetFieldID(clazz,"videoRotation","I");
+   pSettings->ClipProperties.videoRotationDegrees =
+                    pEnv->GetIntField(object,fid);
+   M4OSA_TRACE1_1("videoRotation = %d",
+                    pSettings->ClipProperties.videoRotationDegrees);
 }
 
 static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType,
@@ -789,6 +796,8 @@
             pContext->pEditSettings->pClipList[iCurrentClipIndex]->ClipProperties.uiVideoWidth,
             (M4OSA_Void **)&frameStr.pBuffer);
             tnTimeMs = (M4OSA_UInt32)timeMs;
+
+          frameStr.videoRotationDegree = 0;
     } else {
         /* Handle 3gp/mp4 Clips here */
         /* get thumbnail*/
@@ -913,6 +922,9 @@
 
         /* Fill up the render structure*/
         frameStr.pBuffer = (M4OSA_Void*)yuvPlane[0].pac_data;
+
+        frameStr.videoRotationDegree = pContext->pEditSettings->\
+            pClipList[iCurrentClipIndex]->ClipProperties.videoRotationDegrees;
     }
 
     frameStr.timeMs = timeMs;    /* timestamp on storyboard*/
@@ -976,13 +988,12 @@
     videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv,
             (M4NO_ERROR != result), result);
 
-    if (pContext->pEditSettings->pClipList[iCurrentClipIndex]->FileType ==\
-         /*M4VIDEOEDITING_kFileType_JPG */ M4VIDEOEDITING_kFileType_ARGB8888) {
-            free(frameStr.pBuffer);
-    } else {
-        free(yuvPlane[0].pac_data);
+    free(frameStr.pBuffer);
+    if (pContext->pEditSettings->pClipList[iCurrentClipIndex]->FileType !=
+            M4VIDEOEDITING_kFileType_ARGB8888) {
         free(yuvPlane);
     }
+
     return tnTimeMs;
 }
 
@@ -2275,6 +2286,7 @@
                 M4OSA_UInt32            width,
                 M4OSA_UInt32            height,
                 M4OSA_UInt32            noOfThumbnails,
+                M4OSA_UInt32            videoRotation,
                 jlong                   startTime,
                 jlong                   endTime,
                 jintArray               indexArray,
diff --git a/media/jni/mediaeditor/VideoEditorMain.h b/media/jni/mediaeditor/VideoEditorMain.h
index ca4a945..4c3b517 100755
--- a/media/jni/mediaeditor/VideoEditorMain.h
+++ b/media/jni/mediaeditor/VideoEditorMain.h
@@ -71,6 +71,7 @@
     M4OSA_Bool      bApplyEffect;        /* Apply video effects before render*/
     M4OSA_UInt32    clipBeginCutTime;  /* Clip begin cut time relative to storyboard */
     M4OSA_UInt32    clipEndCutTime;    /* Clip end cut time relative to storyboard */
+    M4OSA_UInt32    videoRotationDegree; /* Video rotation degree */
 
 } VideoEditor_renderPreviewFrameStr;
 #endif /*__VIDEO_EDITOR_API_H__*/
diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
index 2ca3a08..c8fb263 100755
--- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
@@ -258,6 +258,8 @@
                 pProperties->uiAudioBitrate      = pClipProperties->uiAudioBitrate;
                 pProperties->uiNbChannels        = pClipProperties->uiNbChannels;
                 pProperties->uiSamplingFrequency = pClipProperties->uiSamplingFrequency;
+                pProperties->uiRotation          = pClipProperties->videoRotationDegrees;
+
             }
 
             // Free the clip properties.
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/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 0251baf..605d056 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -124,7 +124,14 @@
                             : ATSParser::DISCONTINUITY_FORMATCHANGE,
                         extra);
             } else {
-                mTSParser->feedTSPacket(buffer, sizeof(buffer));
+                status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer));
+
+                if (err != OK) {
+                    LOGE("TS Parser returned error %d", err);
+                    mTSParser->signalEOS(err);
+                    mEOS = true;
+                    break;
+                }
             }
 
             mOffset += n;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 1f08a91..ee77f47 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -663,6 +663,19 @@
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
 
+    if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) {
+        // We're currently attempting to flush the decoder, in order
+        // to complete this, the decoder wants all its buffers back,
+        // so we don't want any output buffers it sent us (from before
+        // we initiated the flush) to be stuck in the renderer's queue.
+
+        LOGV("we're still flushing the %s decoder, sending its output buffer"
+             " right back.", audio ? "audio" : "video");
+
+        reply->post();
+        return;
+    }
+
     sp<RefBase> obj;
     CHECK(msg->findObject("buffer", &obj));
 
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index a6a3a18..a741987 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();
 }
@@ -86,7 +86,15 @@
                             : ATSParser::DISCONTINUITY_FORMATCHANGE,
                         extra);
             } else {
-                mTSParser->feedTSPacket(buffer, sizeof(buffer));
+                status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer));
+
+                if (err != OK) {
+                    LOGE("TS Parser returned error %d", err);
+
+                    mTSParser->signalEOS(err);
+                    mEOS = true;
+                    break;
+                }
             }
         }
     }
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..47224cc 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.
@@ -977,7 +978,7 @@
             CHECK(!mAudioPlayer->isSeeking());
 
             // We will have finished the seek while starting the audio player.
-            postAudioSeekComplete_l();
+            postAudioSeekComplete();
         }
     } else {
         mAudioPlayer->resume();
@@ -1876,7 +1877,8 @@
     mQueue.postEventWithDelay(mVideoLagEvent, 1000000ll);
 }
 
-void AwesomePlayer::postCheckAudioStatusEvent_l(int64_t delayUs) {
+void AwesomePlayer::postCheckAudioStatusEvent(int64_t delayUs) {
+    Mutex::Autolock autoLock(mAudioLock);
     if (mAudioStatusEventPending) {
         return;
     }
@@ -1885,14 +1887,18 @@
 }
 
 void AwesomePlayer::onCheckAudioStatus() {
-    Mutex::Autolock autoLock(mLock);
-    if (!mAudioStatusEventPending) {
-        // Event was dispatched and while we were blocking on the mutex,
-        // has already been cancelled.
-        return;
+    {
+        Mutex::Autolock autoLock(mAudioLock);
+        if (!mAudioStatusEventPending) {
+            // Event was dispatched and while we were blocking on the mutex,
+            // has already been cancelled.
+            return;
+        }
+
+        mAudioStatusEventPending = false;
     }
 
-    mAudioStatusEventPending = false;
+    Mutex::Autolock autoLock(mLock);
 
     if (mWatchForAudioSeekComplete && !mAudioPlayer->isSeeking()) {
         mWatchForAudioSeekComplete = false;
@@ -2238,17 +2244,11 @@
 }
 
 void AwesomePlayer::postAudioEOS(int64_t delayUs) {
-    Mutex::Autolock autoLock(mLock);
-    postCheckAudioStatusEvent_l(delayUs);
+    postCheckAudioStatusEvent(delayUs);
 }
 
 void AwesomePlayer::postAudioSeekComplete() {
-    Mutex::Autolock autoLock(mLock);
-    postAudioSeekComplete_l();
-}
-
-void AwesomePlayer::postAudioSeekComplete_l() {
-    postCheckAudioStatusEvent_l(0 /* delayUs */);
+    postCheckAudioStatusEvent(0);
 }
 
 status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 7f09319..d5b013d 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1990,7 +1990,7 @@
     CHECK(mIsEncoder);
 
     if (mDecodingTimeList.empty()) {
-        CHECK(mNoMoreOutputData);
+        CHECK(mSignalledEOS || mNoMoreOutputData);
         // No corresponding input frame available.
         // This could happen when EOS is reached.
         return 0;
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/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 24cf77c..8e73121 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -148,6 +148,7 @@
     mutable Mutex mLock;
     Mutex mMiscStateLock;
     mutable Mutex mStatsLock;
+    Mutex mAudioLock;
 
     OMXClient mClient;
     TimedEventQueue mQueue;
@@ -223,7 +224,7 @@
     void postVideoEvent_l(int64_t delayUs = -1);
     void postBufferingEvent_l();
     void postStreamDoneEvent_l(status_t status);
-    void postCheckAudioStatusEvent_l(int64_t delayUs);
+    void postCheckAudioStatusEvent(int64_t delayUs);
     void postVideoLagEvent_l();
     status_t play_l();
 
@@ -295,7 +296,6 @@
     void ensureCacheIsFetching_l();
 
     status_t startAudioPlayer_l(bool sendErrorNotification = true);
-    void postAudioSeekComplete_l();
 
     void shutdownVideoDecoder_l();
     status_t setNativeWindow_l(const sp<ANativeWindow> &native);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 5bbc2b4..e13464e 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -48,7 +48,7 @@
 
     bool parsePID(
             unsigned pid, unsigned payload_unit_start_indicator,
-            ABitReader *br);
+            ABitReader *br, status_t *err);
 
     void signalDiscontinuity(
             DiscontinuityType type, const sp<AMessage> &extra);
@@ -77,7 +77,7 @@
     bool mFirstPTSValid;
     uint64_t mFirstPTS;
 
-    void parseProgramMap(ABitReader *br);
+    status_t parseProgramMap(ABitReader *br);
 
     DISALLOW_EVIL_CONSTRUCTORS(Program);
 };
@@ -89,7 +89,7 @@
     unsigned pid() const { return mElementaryPID; }
     void setPID(unsigned pid) { mElementaryPID = pid; }
 
-    void parse(
+    status_t parse(
             unsigned payload_unit_start_indicator,
             ABitReader *br);
 
@@ -111,13 +111,11 @@
     sp<ABuffer> mBuffer;
     sp<AnotherPacketSource> mSource;
     bool mPayloadStarted;
-    DiscontinuityType mPendingDiscontinuity;
-    sp<AMessage> mPendingDiscontinuityExtra;
 
     ElementaryStreamQueue *mQueue;
 
-    void flush();
-    void parsePES(ABitReader *br);
+    status_t flush();
+    status_t parsePES(ABitReader *br);
 
     void onPayloadData(
             unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
@@ -125,9 +123,6 @@
 
     void extractAACFrames(const sp<ABuffer> &buffer);
 
-    void deferDiscontinuity(
-            DiscontinuityType type, const sp<AMessage> &extra);
-
     DISALLOW_EVIL_CONSTRUCTORS(Stream);
 };
 
@@ -145,14 +140,17 @@
 
 bool ATSParser::Program::parsePID(
         unsigned pid, unsigned payload_unit_start_indicator,
-        ABitReader *br) {
+        ABitReader *br, status_t *err) {
+    *err = OK;
+
     if (pid == mProgramMapPID) {
         if (payload_unit_start_indicator) {
             unsigned skip = br->getBits(8);
             br->skipBits(skip * 8);
         }
 
-        parseProgramMap(br);
+        *err = parseProgramMap(br);
+
         return true;
     }
 
@@ -161,7 +159,7 @@
         return false;
     }
 
-    mStreams.editValueAt(index)->parse(
+    *err = mStreams.editValueAt(index)->parse(
             payload_unit_start_indicator, br);
 
     return true;
@@ -185,7 +183,7 @@
     unsigned mPID;
 };
 
-void ATSParser::Program::parseProgramMap(ABitReader *br) {
+status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
     unsigned table_id = br->getBits(8);
     LOGV("  table_id = %u", table_id);
     CHECK_EQ(table_id, 0x02u);
@@ -288,7 +286,60 @@
     }
 
     if (PIDsChanged) {
-        mStreams.clear();
+#if 0
+        LOGI("before:");
+        for (size_t i = 0; i < mStreams.size(); ++i) {
+            sp<Stream> stream = mStreams.editValueAt(i);
+
+            LOGI("PID 0x%08x => type 0x%02x", stream->pid(), stream->type());
+        }
+
+        LOGI("after:");
+        for (size_t i = 0; i < infos.size(); ++i) {
+            StreamInfo &info = infos.editItemAt(i);
+
+            LOGI("PID 0x%08x => type 0x%02x", info.mPID, info.mType);
+        }
+#endif
+
+        // The only case we can recover from is if we have two streams
+        // and they switched PIDs.
+
+        bool success = false;
+
+        if (mStreams.size() == 2 && infos.size() == 2) {
+            const StreamInfo &info1 = infos.itemAt(0);
+            const StreamInfo &info2 = infos.itemAt(1);
+
+            sp<Stream> s1 = mStreams.editValueAt(0);
+            sp<Stream> s2 = mStreams.editValueAt(1);
+
+            bool caseA =
+                info1.mPID == s1->pid() && info1.mType == s2->type()
+                    && info2.mPID == s2->pid() && info2.mType == s1->type();
+
+            bool caseB =
+                info1.mPID == s2->pid() && info1.mType == s1->type()
+                    && info2.mPID == s1->pid() && info2.mType == s2->type();
+
+            if (caseA || caseB) {
+                unsigned pid1 = s1->pid();
+                unsigned pid2 = s2->pid();
+                s1->setPID(pid2);
+                s2->setPID(pid1);
+
+                mStreams.clear();
+                mStreams.add(s1->pid(), s1);
+                mStreams.add(s2->pid(), s2);
+
+                success = true;
+            }
+        }
+
+        if (!success) {
+            LOGI("Stream PIDs changed and we cannot recover.");
+            return ERROR_MALFORMED;
+        }
     }
 
     for (size_t i = 0; i < infos.size(); ++i) {
@@ -299,13 +350,10 @@
         if (index < 0) {
             sp<Stream> stream = new Stream(this, info.mPID, info.mType);
             mStreams.add(info.mPID, stream);
-
-            if (PIDsChanged) {
-                sp<AMessage> extra;
-                stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE, extra);
-            }
         }
     }
+
+    return OK;
 }
 
 sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
@@ -325,14 +373,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;
@@ -345,12 +395,8 @@
     : mProgram(program),
       mElementaryPID(elementaryPID),
       mStreamType(streamType),
-      mBuffer(new ABuffer(192 * 1024)),
       mPayloadStarted(false),
-      mPendingDiscontinuity(DISCONTINUITY_NONE),
       mQueue(NULL) {
-    mBuffer->setRange(0, 0);
-
     switch (mStreamType) {
         case STREAMTYPE_H264:
             mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::H264);
@@ -380,6 +426,11 @@
     }
 
     LOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType);
+
+    if (mQueue != NULL) {
+        mBuffer = new ABuffer(192 * 1024);
+        mBuffer->setRange(0, 0);
+    }
 }
 
 ATSParser::Stream::~Stream() {
@@ -387,22 +438,30 @@
     mQueue = NULL;
 }
 
-void ATSParser::Stream::parse(
+status_t ATSParser::Stream::parse(
         unsigned payload_unit_start_indicator, ABitReader *br) {
+    if (mQueue == NULL) {
+        return OK;
+    }
+
     if (payload_unit_start_indicator) {
         if (mPayloadStarted) {
             // Otherwise we run the danger of receiving the trailing bytes
             // of a PES packet that we never saw the start of and assuming
             // we have a a complete PES packet.
 
-            flush();
+            status_t err = flush();
+
+            if (err != OK) {
+                return err;
+            }
         }
 
         mPayloadStarted = true;
     }
 
     if (!mPayloadStarted) {
-        return;
+        return OK;
     }
 
     size_t payloadSizeBits = br->numBitsLeft();
@@ -423,10 +482,16 @@
 
     memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
     mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
+
+    return OK;
 }
 
 void ATSParser::Stream::signalDiscontinuity(
         DiscontinuityType type, const sp<AMessage> &extra) {
+    if (mQueue == NULL) {
+        return;
+    }
+
     mPayloadStarted = false;
     mBuffer->setRange(0, 0);
 
@@ -451,8 +516,6 @@
 
             if (mSource != NULL) {
                 mSource->queueDiscontinuity(type, extra);
-            } else {
-                deferDiscontinuity(type, extra);
             }
             break;
         }
@@ -463,22 +526,13 @@
     }
 }
 
-void ATSParser::Stream::deferDiscontinuity(
-        DiscontinuityType type, const sp<AMessage> &extra) {
-    if (type > mPendingDiscontinuity) {
-        // Only upgrade discontinuities.
-        mPendingDiscontinuity = type;
-        mPendingDiscontinuityExtra = extra;
-    }
-}
-
 void ATSParser::Stream::signalEOS(status_t finalResult) {
     if (mSource != NULL) {
         mSource->signalEOS(finalResult);
     }
 }
 
-void ATSParser::Stream::parsePES(ABitReader *br) {
+status_t ATSParser::Stream::parsePES(ABitReader *br) {
     unsigned packet_startcode_prefix = br->getBits(24);
 
     LOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
@@ -486,7 +540,8 @@
     if (packet_startcode_prefix != 1) {
         LOGV("Supposedly payload_unit_start=1 unit does not start "
              "with startcode.");
-        return;
+
+        return ERROR_MALFORMED;
     }
 
     CHECK_EQ(packet_startcode_prefix, 0x000001u);
@@ -613,6 +668,14 @@
             unsigned dataLength =
                 PES_packet_length - 3 - PES_header_data_length;
 
+            if (br->numBitsLeft() < dataLength * 8) {
+                LOGE("PES packet does not carry enough data to contain "
+                     "payload. (numBitsLeft = %d, required = %d)",
+                     br->numBitsLeft(), dataLength * 8);
+
+                return ERROR_MALFORMED;
+            }
+
             CHECK_GE(br->numBitsLeft(), dataLength * 8);
 
             onPayloadData(
@@ -636,19 +699,24 @@
         CHECK_NE(PES_packet_length, 0u);
         br->skipBits(PES_packet_length * 8);
     }
+
+    return OK;
 }
 
-void ATSParser::Stream::flush() {
+status_t ATSParser::Stream::flush() {
     if (mBuffer->size() == 0) {
-        return;
+        return OK;
     }
 
     LOGV("flushing stream 0x%04x size = %d", mElementaryPID, mBuffer->size());
 
     ABitReader br(mBuffer->data(), mBuffer->size());
-    parsePES(&br);
+
+    status_t err = parsePES(&br);
 
     mBuffer->setRange(0, 0);
+
+    return err;
 }
 
 void ATSParser::Stream::onPayloadData(
@@ -656,10 +724,6 @@
         const uint8_t *data, size_t size) {
     LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
 
-    if (mQueue == NULL) {
-        return;
-    }
-
     CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
     int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
 
@@ -679,14 +743,6 @@
                      mElementaryPID, mStreamType);
 
                 mSource = new AnotherPacketSource(meta);
-
-                if (mPendingDiscontinuity != DISCONTINUITY_NONE) {
-                    mSource->queueDiscontinuity(
-                            mPendingDiscontinuity, mPendingDiscontinuityExtra);
-                    mPendingDiscontinuity = DISCONTINUITY_NONE;
-                    mPendingDiscontinuityExtra.clear();
-                }
-
                 mSource->queueAccessUnit(accessUnit);
             }
         } else if (mQueue->getFormat() != NULL) {
@@ -734,17 +790,18 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-ATSParser::ATSParser() {
+ATSParser::ATSParser(uint32_t flags)
+    : mFlags(flags) {
 }
 
 ATSParser::~ATSParser() {
 }
 
-void ATSParser::feedTSPacket(const void *data, size_t size) {
+status_t ATSParser::feedTSPacket(const void *data, size_t size) {
     CHECK_EQ(size, kTSPacketSize);
 
     ABitReader br((const uint8_t *)data, kTSPacketSize);
-    parseTS(&br);
+    return parseTS(&br);
 }
 
 void ATSParser::signalDiscontinuity(
@@ -822,7 +879,7 @@
     MY_LOGV("  CRC = 0x%08x", br->getBits(32));
 }
 
-void ATSParser::parsePID(
+status_t ATSParser::parsePID(
         ABitReader *br, unsigned PID,
         unsigned payload_unit_start_indicator) {
     if (PID == 0) {
@@ -831,13 +888,18 @@
             br->skipBits(skip * 8);
         }
         parseProgramAssociationTable(br);
-        return;
+        return OK;
     }
 
     bool handled = false;
     for (size_t i = 0; i < mPrograms.size(); ++i) {
+        status_t err;
         if (mPrograms.editItemAt(i)->parsePID(
-                    PID, payload_unit_start_indicator, br)) {
+                    PID, payload_unit_start_indicator, br, &err)) {
+            if (err != OK) {
+                return err;
+            }
+
             handled = true;
             break;
         }
@@ -846,6 +908,8 @@
     if (!handled) {
         LOGV("PID 0x%04x not handled.", PID);
     }
+
+    return OK;
 }
 
 void ATSParser::parseAdaptationField(ABitReader *br) {
@@ -855,7 +919,7 @@
     }
 }
 
-void ATSParser::parseTS(ABitReader *br) {
+status_t ATSParser::parseTS(ABitReader *br) {
     LOGV("---");
 
     unsigned sync_byte = br->getBits(8);
@@ -886,8 +950,10 @@
     }
 
     if (adaptation_field_control == 1 || adaptation_field_control == 3) {
-        parsePID(br, PID, payload_unit_start_indicator);
+        return parsePID(br, PID, payload_unit_start_indicator);
     }
+
+    return OK;
 }
 
 sp<MediaSource> ATSParser::getSource(SourceType type) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 1e6451d..388cb54 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -38,9 +38,18 @@
         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
+    };
 
-    void feedTSPacket(const void *data, size_t size);
+    ATSParser(uint32_t flags = 0);
+
+    status_t feedTSPacket(const void *data, size_t size);
 
     void signalDiscontinuity(
             DiscontinuityType type, const sp<AMessage> &extra);
@@ -73,18 +82,19 @@
     struct Program;
     struct Stream;
 
+    uint32_t mFlags;
     Vector<sp<Program> > mPrograms;
 
     void parseProgramAssociationTable(ABitReader *br);
     void parseProgramMap(ABitReader *br);
     void parsePES(ABitReader *br);
 
-    void parsePID(
+    status_t parsePID(
         ABitReader *br, unsigned PID,
         unsigned payload_unit_start_indicator);
 
     void parseAdaptationField(ABitReader *br);
-    void parseTS(ABitReader *br);
+    status_t parseTS(ABitReader *br);
 
     DISALLOW_EVIL_CONSTRUCTORS(ATSParser);
 };
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 8250ad1..17cf45a 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -210,13 +210,10 @@
 
     if (n < (ssize_t)kTSPacketSize) {
         return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
-    } else {
-        mParser->feedTSPacket(packet, kTSPacketSize);
     }
 
     mOffset += n;
-
-    return OK;
+    return mParser->feedTSPacket(packet, kTSPacketSize);
 }
 
 void MPEG2TSExtractor::setLiveSession(const sp<LiveSession> &liveSession) {
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index d643a0b..d663602 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -106,13 +106,14 @@
             mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                     window.get(), NULL);
         } else {
-            EGLint pbufferAttribs[] = {
-                EGL_WIDTH, getSurfaceWidth(),
-                EGL_HEIGHT, getSurfaceHeight(),
-                EGL_NONE };
+            LOGV("No actual display. Choosing EGLSurface based on SurfaceMediaSource");
+            sp<SurfaceMediaSource> sms = new SurfaceMediaSource(
+                    getSurfaceWidth(), getSurfaceHeight());
+            sp<SurfaceTextureClient> stc = new SurfaceTextureClient(sms);
+            sp<ANativeWindow> window = stc;
 
-            mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
-                    pbufferAttribs);
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+                    window.get(), NULL);
         }
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
         ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
@@ -408,7 +409,6 @@
         mSTC.clear();
         mANW.clear();
         GLTest::TearDown();
-        eglDestroySurface(mEglDisplay, mSmsEglSurface);
     }
 
     void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr);
@@ -419,8 +419,6 @@
     sp<SurfaceMediaSource> mSMS;
     sp<SurfaceTextureClient> mSTC;
     sp<ANativeWindow> mANW;
-    EGLConfig  mSMSGlConfig;
-    EGLSurface  mSmsEglSurface;
 };
 
 /////////////////////////////////////////////////////////////////////
@@ -462,7 +460,7 @@
     glClear(GL_COLOR_BUFFER_BIT);
 
     // The following call dequeues and queues the buffer
-    eglSwapBuffers(mEglDisplay, mSmsEglSurface);
+    eglSwapBuffers(mEglDisplay, mEglSurface);
     glDisable(GL_SCISSOR_TEST);
 }
 
@@ -488,19 +486,12 @@
     mSTC = new SurfaceTextureClient(iST);
     mANW = mSTC;
 
-    EGLint numConfigs = 0;
-    EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
-            1, &numConfigs));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    LOGV("Native Window = %p, mSTC = %p", mANW.get(), mSTC.get());
-
-    mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+    mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                                 mANW.get(), NULL);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+    ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
 
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
             mEglContext));
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 }
@@ -778,9 +769,9 @@
 
 // Test to examine whether we can choose the Recordable Android GLConfig
 // DummyRecorder used- no real encoding here
-TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWrite) {
+TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWriter) {
     LOGV("Test # %d", testId++);
-    LOGV("Test to verify creating a surface w/ right config *********");
+    LOGV("Verify creating a surface w/ right config + dummy writer*********");
 
     mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
     mSTC = new SurfaceTextureClient(mSMS);
@@ -789,17 +780,12 @@
     DummyRecorder writer(mSMS);
     writer.start();
 
-    EGLint numConfigs = 0;
-    EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
-            1, &numConfigs));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+    mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                                 mANW.get(), NULL);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+    ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
 
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
             mEglContext));
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
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/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index b396223..0810643 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -181,9 +181,10 @@
     }
 
     // Note: This test is to assume the mediaserver's pid is 34
-    private void stressVideoRecord(int frameRate, int width, int height, int videoFormat,
+    private boolean stressVideoRecord(int frameRate, int width, int height, int videoFormat,
             int outFormat, String outFile, boolean videoOnly) {
         // Video recording
+        boolean doesTestFail = false;
         for (int i = 0; i < NUM_PLAYBACk_IN_EACH_LOOP; i++) {
             MediaRecorder mRecorder = new MediaRecorder();
             try {
@@ -212,8 +213,11 @@
             } catch (Exception e) {
                 Log.v("record video failed ", e.toString());
                 mRecorder.release();
+                doesTestFail = true;
+                break;
             }
         }
+        return !doesTestFail;
     }
 
     public void stressAudioRecord(String filePath) {
@@ -366,8 +370,8 @@
         Writer output = new BufferedWriter(new FileWriter(videoH263RecordOnlyMemoryOut, true));
         output.write("H263 video record only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
-            stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263,
-                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true);
+            assertTrue(stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263,
+                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true));
             getMemoryWriteToLog(output, i);
         }
         output.write("\n");
@@ -386,8 +390,8 @@
         Writer output = new BufferedWriter(new FileWriter(videoMp4RecordOnlyMemoryOut, true));
         output.write("MPEG4 video record only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
-            stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP,
-                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true);
+            assertTrue(stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP,
+                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true));
             getMemoryWriteToLog(output, i);
         }
         output.write("\n");
@@ -407,8 +411,8 @@
         Writer output = new BufferedWriter(new FileWriter(videoRecordAudioMemoryOut, true));
         output.write("Audio and h263 video record\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
-            stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263,
-                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false);
+            assertTrue(stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263,
+                    MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false));
             getMemoryWriteToLog(output, i);
         }
         output.write("\n");
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index ddad2d3..ca62908 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -95,12 +95,12 @@
         if (fgets(cmdline, sizeof(cmdline) - 1, file))
         {
             if (!strcmp(value, cmdline))
-                sEGLTraceLevel = 1;
+                gEGLDebugLevel = 1;
         }
         fclose(file);
     }
 
-    if (sEGLTraceLevel > 0)
+    if (gEGLDebugLevel > 0)
     {
         property_get("debug.egl.debug_port", value, "5039");
         const unsigned short port = (unsigned short)atoi(value);
@@ -117,7 +117,7 @@
     if (sEGLTraceLevel > 0) {
         setGlTraceThreadSpecific(value);
         setGlThreadSpecific(&gHooksTrace);
-    } else if (sEGLTraceLevel > 0 && value != &gHooksNoContext) {
+    } else if (gEGLDebugLevel > 0 && value != &gHooksNoContext) {
         setGlTraceThreadSpecific(value);
         setGlThreadSpecific(&gHooksDebug);
     } else {
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png
deleted file mode 100644
index 87c7be6..0000000
--- a/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png
index 4f4ae78..4362836 100644
--- a/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_callout_line.9.png b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.9.png
new file mode 100644
index 0000000..335d5a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png
deleted file mode 100644
index 5f4c035..0000000
--- a/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.9.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.9.png
new file mode 100644
index 0000000..1ad16f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png
deleted file mode 100644
index 23aabce..0000000
--- a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.9.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.9.png
new file mode 100644
index 0000000..6e806ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.png
deleted file mode 100644
index 0b0765b..0000000
--- a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg_press.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/app_icon.png b/packages/SystemUI/res/drawable-large-hdpi/app_icon.png
deleted file mode 100644
index 52354bd..0000000
--- a/packages/SystemUI/res/drawable-large-hdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-large-hdpi/recents_bg_protect_tile.png
deleted file mode 100644
index ce01276..0000000
--- a/packages/SystemUI/res/drawable-large-hdpi/recents_bg_protect_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-large-hdpi/recents_blue_glow.9.png
deleted file mode 100644
index 1848fcd..0000000
--- a/packages/SystemUI/res/drawable-large-hdpi/recents_blue_glow.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-large-hdpi/recents_callout_line.png
deleted file mode 100644
index 61a3f87..0000000
--- a/packages/SystemUI/res/drawable-large-hdpi/recents_callout_line.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-large-hdpi/recents_thumbnail_bg.png
deleted file mode 100644
index b6aca49..0000000
--- a/packages/SystemUI/res/drawable-large-hdpi/recents_thumbnail_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-large-hdpi/recents_thumbnail_bg_press.png
deleted file mode 100644
index 226aaac..0000000
--- a/packages/SystemUI/res/drawable-large-hdpi/recents_thumbnail_bg_press.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/app_icon.png b/packages/SystemUI/res/drawable-large-mdpi/app_icon.png
deleted file mode 100644
index 001811f..0000000
--- a/packages/SystemUI/res/drawable-large-mdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-large-mdpi/recents_bg_protect_tile.png
deleted file mode 100644
index 3d0fbf2..0000000
--- a/packages/SystemUI/res/drawable-large-mdpi/recents_bg_protect_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-large-mdpi/recents_blue_glow.9.png
deleted file mode 100644
index 4362836..0000000
--- a/packages/SystemUI/res/drawable-large-mdpi/recents_blue_glow.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-large-mdpi/recents_callout_line.png
deleted file mode 100644
index f4ccd7e..0000000
--- a/packages/SystemUI/res/drawable-large-mdpi/recents_callout_line.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg.png
deleted file mode 100644
index 6392fa1..0000000
--- a/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg_press.png
deleted file mode 100644
index f6ee596..0000000
--- a/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg_press.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png
deleted file mode 100644
index 87c7be6..0000000
--- a/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png
index 4f4ae78..4362836 100644
--- a/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_callout_line.9.png b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.9.png
new file mode 100644
index 0000000..724a5cd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png
deleted file mode 100644
index 5f4c035..0000000
--- a/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.9.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.9.png
new file mode 100644
index 0000000..82ba091
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png
deleted file mode 100644
index 23aabce..0000000
--- a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.9.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.9.png
new file mode 100644
index 0000000..7376085
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png
deleted file mode 100644
index 0b0765b..0000000
--- a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/recents_blue_glow.9.png
new file mode 100644
index 0000000..4ac131a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_blue_glow.9.png
index 4f4ae78..4362836 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_blue_glow.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_callout_line.png
deleted file mode 100644
index 5f4c035..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_callout_line.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_thumbnail_bg.png
deleted file mode 100644
index 87a67c9..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_thumbnail_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_thumbnail_bg_press.png
deleted file mode 100644
index a1c39e6..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/recents_thumbnail_bg_press.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/recents_bg_protect_tile.png
new file mode 100644
index 0000000..59908ad
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/recents_blue_glow.9.png
new file mode 100644
index 0000000..3938502
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_blue_glow.9.png
new file mode 100644
index 0000000..e1e08c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_callout_line.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_callout_line.9.png
new file mode 100644
index 0000000..1bd018a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_callout_line.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg.9.png
new file mode 100644
index 0000000..0352aca
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png
new file mode 100644
index 0000000..507ee22
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-hdpi/app_icon.png b/packages/SystemUI/res/drawable-xlarge-hdpi/app_icon.png
deleted file mode 100644
index 52354bd..0000000
--- a/packages/SystemUI/res/drawable-xlarge-hdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-xlarge-hdpi/recents_bg_protect_tile.png
deleted file mode 100644
index ce01276..0000000
--- a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_bg_protect_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-xlarge-hdpi/recents_blue_glow.9.png
deleted file mode 100644
index 1848fcd..0000000
--- a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_blue_glow.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-xlarge-hdpi/recents_callout_line.png
deleted file mode 100644
index 846bc49..0000000
--- a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_callout_line.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-xlarge-hdpi/recents_thumbnail_bg.png
deleted file mode 100644
index a983e12..0000000
--- a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_thumbnail_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-xlarge-hdpi/recents_thumbnail_bg_press.png
deleted file mode 100644
index 7c6e44e..0000000
--- a/packages/SystemUI/res/drawable-xlarge-hdpi/recents_thumbnail_bg_press.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/app_icon.png b/packages/SystemUI/res/drawable-xlarge-mdpi/app_icon.png
deleted file mode 100644
index 001811f..0000000
--- a/packages/SystemUI/res/drawable-xlarge-mdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_bg_protect_tile.png
deleted file mode 100644
index 3d0fbf2..0000000
--- a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_bg_protect_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_blue_glow.9.png
deleted file mode 100644
index 4362836..0000000
--- a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_blue_glow.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_callout_line.png
deleted file mode 100644
index f4ccd7e..0000000
--- a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_callout_line.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png
deleted file mode 100644
index 6392fa1..0000000
--- a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png
deleted file mode 100644
index f6ee596..0000000
--- a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml b/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml
index 200bac4..6d05095 100644
--- a/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml
+++ b/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml
@@ -15,5 +15,6 @@
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:drawable="@drawable/recents_thumbnail_bg_press" android:state_pressed="true" />
+    <item android:drawable="@drawable/recents_thumbnail_bg" android:state_activated="true" />
     <item android:drawable="@*android:color/transparent"/>
 </selector>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 16008a3..e99888c 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -19,70 +19,74 @@
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
-<RelativeLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
-    android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+    android:layout_height="match_parent"
+    android:layout_width="wrap_content">
 
-    <FrameLayout android:id="@+id/app_thumbnail"
-        android:layout_width="wrap_content"
+    <RelativeLayout android:id="@+id/recent_item"
+        android:layout_gravity="bottom"
         android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
-        android:scaleType="center"
-        android:clickable="true"
-        android:background="@drawable/recents_thumbnail_bg"
-        android:foreground="@drawable/recents_thumbnail_overlay">
-        <ImageView android:id="@+id/app_thumbnail_image"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:paddingBottom="@*android:dimen/status_bar_height">
+
+        <FrameLayout android:id="@+id/app_thumbnail"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+            android:scaleType="center"
+            android:clickable="true"
+            android:background="@drawable/recents_thumbnail_overlay">
+            <ImageView android:id="@+id/app_thumbnail_image"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="invisible"
+            />
+        </FrameLayout>
+
+        <ImageView android:id="@+id/app_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_alignTop="@id/app_thumbnail"
+            android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
+            android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
+            android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
+            android:adjustViewBounds="true"
             android:visibility="invisible"
         />
-    </FrameLayout>
 
-    <ImageView android:id="@+id/app_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_alignTop="@id/app_thumbnail"
-        android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
-        android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
-        android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
-        android:adjustViewBounds="true"
-        android:visibility="invisible"
-    />
+        <TextView android:id="@+id/app_label"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_label_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_below="@id/app_thumbnail"
+            android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:visibility="invisible"
+        />
 
-    <TextView android:id="@+id/app_label"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_label_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_below="@id/app_thumbnail"
-        android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
-        android:layout_marginLeft="@dimen/recents_thumbnail_bg_padding_left"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:visibility="invisible"
-    />
+        <TextView android:id="@+id/app_description"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_description_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_below="@id/app_label"
+            android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+        />
 
-    <TextView android:id="@+id/app_description"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_description_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_below="@id/app_label"
-        android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
-        android:layout_marginLeft="@dimen/recents_thumbnail_bg_padding_left"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-    />
-
-</RelativeLayout>
+    </RelativeLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index 20ef7cf..f84cc19 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -30,13 +30,12 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignParentBottom="true"
-        android:paddingBottom="@*android:dimen/status_bar_height"
         android:clipToPadding="false"
         android:clipChildren="false">
 
         <LinearLayout android:id="@+id/recents_glow"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_gravity="bottom|right"
             android:orientation="horizontal"
             android:clipToPadding="false"
@@ -44,7 +43,7 @@
             >
             <com.android.systemui.recent.RecentsHorizontalScrollView android:id="@+id/recents_container"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
                 android:divider="@null"
                 android:stackFromBottom="true"
@@ -58,7 +57,7 @@
 
                 <LinearLayout android:id="@+id/recents_linear_layout"
                     android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
+                    android:layout_height="match_parent"
                     android:orientation="horizontal"
                     android:clipToPadding="false"
                     android:clipChildren="false">
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index c0fce71..73ca335 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -19,79 +19,83 @@
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
-<RelativeLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+    android:layout_width="match_parent">
 
-    <FrameLayout android:id="@+id/app_thumbnail"
-        android:layout_width="wrap_content"
+    <RelativeLayout android:id="@+id/recent_item"
         android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:clickable="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
-        android:scaleType="center"
-        android:background="@drawable/recents_thumbnail_bg"
-        android:foreground="@drawable/recents_thumbnail_overlay">
-        <ImageView android:id="@+id/app_thumbnail_image"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="invisible"
+        android:layout_width="match_parent">
+
+        <FrameLayout android:id="@+id/app_thumbnail"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:clickable="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+            android:scaleType="center"
+            android:background="@drawable/recents_thumbnail_overlay">
+            <ImageView android:id="@+id/app_thumbnail_image"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="invisible"
+            />
+        </FrameLayout>
+
+        <ImageView android:id="@+id/app_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_alignTop="@id/app_thumbnail"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
+            android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
+            android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
+            android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
+            android:adjustViewBounds="true"
         />
-    </FrameLayout>
 
-    <ImageView android:id="@+id/app_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_alignTop="@id/app_thumbnail"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
-        android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-        android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
-        android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
-        android:adjustViewBounds="true"
-    />
+        <TextView android:id="@+id/app_label"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_label_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignParentLeft="true"
+            android:layout_alignTop="@id/app_icon"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+        />
 
-    <TextView android:id="@+id/app_label"
-        android:layout_width="@dimen/status_bar_recents_app_label_width"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_label_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignParentLeft="true"
-        android:layout_alignTop="@id/app_icon"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-    />
+        <View android:id="@+id/recents_callout_line"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="1dip"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
+            android:layout_toLeftOf="@id/app_thumbnail"
+            android:layout_below="@id/app_label"
+            android:layout_marginRight="3dip"
+            android:layout_marginTop="3dip"
+            android:background="@drawable/recents_callout_line"
+        />
 
-    <View android:id="@+id/recents_callout_line"
-        android:layout_width="@dimen/status_bar_recents_app_label_width"
-        android:layout_height="1dip"
-        android:layout_alignParentLeft="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
-        android:layout_toLeftOf="@id/app_thumbnail"
-        android:layout_below="@id/app_label"
-        android:layout_marginRight="3dip"
-        android:layout_marginTop="3dip"
-        android:background="@drawable/recents_callout_line"
-    />
+        <TextView android:id="@+id/app_description"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_description_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
+            android:layout_below="@id/recents_callout_line"
+            android:layout_marginTop="3dip"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+        />
 
-    <TextView android:id="@+id/app_description"
-        android:layout_width="@dimen/status_bar_recents_app_label_width"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_description_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignParentLeft="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
-        android:layout_below="@id/recents_callout_line"
-        android:layout_marginTop="3dip"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-    />
-
-</RelativeLayout>
+    </RelativeLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index c680b8e..dd25cf9 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -40,7 +40,7 @@
             android:layout_marginTop="@*android:dimen/status_bar_height">
             <com.android.systemui.recent.RecentsVerticalScrollView
                 android:id="@+id/recents_container"
-                android:layout_width="@dimen/status_bar_recents_width"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginRight="0dp"
                 android:divider="@null"
@@ -53,7 +53,7 @@
                 android:clipChildren="false">
 
                 <LinearLayout android:id="@+id/recents_linear_layout"
-                    android:layout_width="wrap_content"
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:orientation="vertical"
                     android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index 5306508..cab90fd 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -19,21 +19,20 @@
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
-<RelativeLayout
+<RelativeLayout android:id="@+id/recent_item"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+    android:layout_width="wrap_content">
 
     <FrameLayout android:id="@+id/app_thumbnail"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
+        android:clickable="true"
         android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
         android:scaleType="center"
-        android:clickable="true"
-        android:background="@drawable/recents_thumbnail_bg"
-        android:foreground="@drawable/recents_thumbnail_overlay">
+        android:background="@drawable/recents_thumbnail_overlay">
         <ImageView android:id="@+id/app_thumbnail_image"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -48,8 +47,8 @@
         android:layout_alignTop="@id/app_thumbnail"
         android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
         android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-        android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
-        android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
+        android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
+        android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
         android:adjustViewBounds="true"
     />
 
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
index 2c9a152..4ef602e 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
@@ -47,7 +47,7 @@
             android:clipChildren="false"
             >
             <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
-                android:layout_width="@dimen/status_bar_recents_width"
+                android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
                 android:divider="@null"
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/res/values-hdpi/dimens.xml b/packages/SystemUI/res/values-hdpi/dimens.xml
index 287e0d1..6b6fd4d 100644
--- a/packages/SystemUI/res/values-hdpi/dimens.xml
+++ b/packages/SystemUI/res/values-hdpi/dimens.xml
@@ -16,12 +16,6 @@
 */
 -->
 <resources>
-    <!-- Offsets for rendering thumbnails over drawable recents_thumbnail_bg -->
-    <dimen name="recents_thumbnail_bg_padding_left">6px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_top">7px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_right">6px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_bottom">6px</dimen>
-
     <!-- thickness (height) of each notification row, including any separators or padding -->
     <!-- Note: this is 64dip + 1px divider = 97px. -->
     <dimen name="notification_height">97px</dimen>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 3919685..ca74b8b 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -20,18 +20,14 @@
     <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
 
     <!-- Recent Applications parameters -->
-    <!-- Width of a recent app view, including all content -->
-    <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
     <!-- How far the thumbnail for a recent app appears from left edge -->
     <dimen name="status_bar_recents_thumbnail_left_margin">8dp</dimen>
     <!-- How far the thumbnail for a recent app appears from top edge -->
     <dimen name="status_bar_recents_thumbnail_top_margin">12dp</dimen>
-    <!-- Width of scrollable area in recents -->
-    <dimen name="status_bar_recents_width">128dp</dimen>
     <!-- Padding for text descriptions -->
     <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
     <!-- Width of application label text -->
-    <dimen name="status_bar_recents_app_label_width">97dip</dimen>
+    <dimen name="status_bar_recents_app_label_width">156dip</dimen>
     <!-- Left margin of application label text -->
     <dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
     <!-- Margin between recents container and glow on the right -->
diff --git a/packages/SystemUI/res/values-mdpi/dimens.xml b/packages/SystemUI/res/values-mdpi/dimens.xml
deleted file mode 100644
index 741b75a..0000000
--- a/packages/SystemUI/res/values-mdpi/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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>
-    <!-- Offsets for rendering thumbnails over drawable recents_thumbnail_bg -->
-    <dimen name="recents_thumbnail_bg_padding_left">6px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_top">7px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_right">6px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_bottom">6px</dimen>
-</resources>
diff --git a/packages/SystemUI/res/values-port/dimens.xml b/packages/SystemUI/res/values-port/dimens.xml
index 54c25fa..b89a610 100644
--- a/packages/SystemUI/res/values-port/dimens.xml
+++ b/packages/SystemUI/res/values-port/dimens.xml
@@ -17,18 +17,14 @@
 -->
 <resources>
     <!-- Recent Applications parameters -->
-    <!-- Width of a recent app view, including all content -->
-    <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
     <!-- How far the thumbnail for a recent app appears from left edge -->
     <dimen name="status_bar_recents_thumbnail_left_margin">110dp</dimen>
-    <!-- Width of scrollable area in recents -->
-    <dimen name="status_bar_recents_width">356dp</dimen>
     <!-- Padding for text descriptions -->
     <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
     <!-- Width of application label text -->
     <dimen name="status_bar_recents_app_label_width">97dip</dimen>
     <!-- Left margin of application label text -->
-    <dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
+    <dimen name="status_bar_recents_app_label_left_margin">8dip</dimen>
     <!-- Margin between recents container and glow on the right -->
     <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..3e2ec59
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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>
+
+    <!-- Whether we're using the tablet-optimized recents interface (we use this
+     value at runtime for some things) -->
+    <bool name="config_recents_interface_for_tablets">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index a5bea5c..fe9245d 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -30,16 +30,19 @@
     <dimen name="panel_float">56dp</dimen>
 
     <!-- Recent Applications parameters -->
-    <!-- Width of a recent app view, including all content -->
-    <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
     <!-- How far the thumbnail for a recent app appears from left edge -->
-    <dimen name="status_bar_recents_thumbnail_left_margin">110dp</dimen>
+    <dimen name="status_bar_recents_thumbnail_left_margin">121dp</dimen>
     <!-- Upper width limit for application icon -->
-    <dimen name="status_bar_recents_thumbnail_max_width">64dp</dimen>
+    <dimen name="status_bar_recents_app_icon_max_width">64dp</dimen>
     <!-- Upper height limit for application icon -->
-    <dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen>
-    <!-- Width of scrollable area in recents -->
-    <dimen name="status_bar_recents_width">356dp</dimen>
+    <dimen name="status_bar_recents_app_icon_max_height">64dp</dimen>
+
+    <!-- Size of application icon -->
+    <dimen name="status_bar_recents_thumbnail_width">245dp</dimen>
+    <dimen name="status_bar_recents_thumbnail_height">144dp</dimen>
+
+    <!-- Width of recents panel -->
+    <dimen name="status_bar_recents_width">600dp</dimen>
     <!-- Padding for text descriptions -->
     <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
     <!-- Size of application label text -->
@@ -55,12 +58,6 @@
     <!-- Margin between recents container and glow on the right -->
     <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
 
-    <!-- Offsets for rendering thumbnails over drawable recents_thumbnail_bg -->
-    <dimen name="recents_thumbnail_bg_padding_left">15px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_top">8px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_right">12px</dimen>
-    <dimen name="recents_thumbnail_bg_padding_bottom">8px</dimen>
-
     <!-- Where to place the app icon over the thumbnail -->
     <dimen name="status_bar_recents_app_icon_left_margin">13dp</dimen>
     <dimen name="status_bar_recents_app_icon_top_margin">13dp</dimen>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5d14fa8..4c222f9 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -22,5 +22,6 @@
     <drawable name="ticker_background_color">#ff1d1d1d</drawable>
     <drawable name="status_bar_background">#ff000000</drawable>
     <drawable name="status_bar_recents_background">#b3000000</drawable>
+    <drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
     <drawable name="status_bar_notification_row_background_color">#ff000000</drawable>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d7d7817..4ac89b2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -21,6 +21,10 @@
      for different hardware and product builds. -->
 <resources>
 
+    <!-- Whether we're using the tablet-optimized recents interface (we use this
+     value at runtime for some things) -->
+    <bool name="config_recents_interface_for_tablets">false</bool>
+
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f633825c..d0ece6c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -21,17 +21,21 @@
 
     <!-- Recent Applications parameters -->
     <!-- Upper width limit for application icon -->
-    <dimen name="status_bar_recents_thumbnail_max_width">64dp</dimen>
+    <dimen name="status_bar_recents_app_icon_max_width">64dp</dimen>
     <!-- Upper height limit for application icon -->
-    <dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen>
+    <dimen name="status_bar_recents_app_icon_max_height">64dp</dimen>
     <!-- Where to place the app icon over the thumbnail -->
     <dimen name="status_bar_recents_app_icon_left_margin">13dp</dimen>
     <dimen name="status_bar_recents_app_icon_top_margin">13dp</dimen>
 
+    <!-- Size of application thumbnail -->
+    <dimen name="status_bar_recents_thumbnail_width">164dp</dimen>
+    <dimen name="status_bar_recents_thumbnail_height">164dp</dimen>
+
     <!-- Size of application label text -->
-    <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
+    <dimen name="status_bar_recents_app_label_text_size">16dip</dimen>
     <!-- Size of application description text -->
-    <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
+    <dimen name="status_bar_recents_app_description_text_size">16dip</dimen>
     <!-- Size of fading edge for scroll effect -->
     <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
     <!-- Margin between recents container and glow on the right -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7779703..bad7e1f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -36,10 +36,10 @@
     <string name="status_bar_please_disturb_button">Show notifications</string>
 
     <!-- Title shown in recents popup for removing an application from the list -->
-    <string name="status_bar_recent_remove_item_title">Remove</string>
+    <string name="status_bar_recent_remove_item_title">Remove from list</string>
 
     <!-- Title shown in recents popup for inspecting an application's properties -->
-    <string name="status_bar_recent_inspect_item_title">Inspect</string>
+    <string name="status_bar_recent_inspect_item_title">App info</string>
 
     <!-- The label in the bar at the top of the status bar when there are no notifications
          showing.  [CHAR LIMIT=40]-->
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index e7ed052..14743f4 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -33,20 +33,19 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_INVALIDATE = false;
     private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
+    private static final boolean CONSTRAIN_SWIPE = true;
+    private static final boolean FADE_OUT_DURING_SWIPE = true;
+    private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
 
     public static final int X = 0;
     public static final int Y = 1;
 
-    private boolean CONSTRAIN_SWIPE = true;
-    private boolean FADE_OUT_DURING_SWIPE = true;
-    private boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int MAX_ESCAPE_ANIMATION_DURATION = 500; // ms
     private int MAX_DISMISS_VELOCITY = 1000; // dp/sec
     private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; // ms
 
-    public static float ALPHA_FADE_START = 0.8f; // fraction of thumbnail width
+    public static float ALPHA_FADE_START = 0f; // fraction of thumbnail width
                                                  // where fade starts
     static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
                                               // beyond which alpha->0
@@ -59,6 +58,8 @@
     private float mInitialTouchPos;
     private boolean mDragging;
     private View mCurrView;
+    private View mCurrAnimView;
+    private boolean mCanCurrViewBeDimissed;
     private float mDensityScale;
 
     public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
@@ -82,8 +83,8 @@
         return mSwipeDirection == X ? ev.getX() : ev.getY();
     }
 
-    private float getPos(View v) {
-        return mSwipeDirection == X ? v.getX() : v.getY();
+    private float getTranslation(View v) {
+        return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
     }
 
     private float getVelocity(VelocityTracker vt) {
@@ -115,19 +116,15 @@
                 v.getMeasuredHeight();
     }
 
-    private float getContentSize(View v) {
-        View content = mCallback.getChildContentView(v);
-        return getSize(content);
-    }
-
-    private float getAlphaForOffset(View view, float thumbSize) {
-        final float fadeSize = ALPHA_FADE_END * thumbSize;
+    private float getAlphaForOffset(View view) {
+        float viewSize = getSize(view);
+        final float fadeSize = ALPHA_FADE_END * viewSize;
         float result = 1.0f;
-        float pos = getPos(view);
-        if (pos >= thumbSize * ALPHA_FADE_START) {
-            result = 1.0f - (pos - thumbSize * ALPHA_FADE_START) / fadeSize;
-        } else if (pos < thumbSize * (1.0f - ALPHA_FADE_START)) {
-            result = 1.0f + (thumbSize * ALPHA_FADE_START + pos) / fadeSize;
+        float pos = getTranslation(view);
+        if (pos >= viewSize * ALPHA_FADE_START) {
+            result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
+        } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
+            result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
         }
         return result;
     }
@@ -168,6 +165,8 @@
             case MotionEvent.ACTION_DOWN:
                 mDragging = false;
                 mCurrView = mCallback.getChildAtPosition(ev);
+                mCurrAnimView = mCallback.getChildContentView(mCurrView);
+                mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
                 mVelocityTracker.clear();
                 mVelocityTracker.addMovement(ev);
                 mInitialTouchPos = getPos(ev);
@@ -180,21 +179,24 @@
                     if (Math.abs(delta) > mPagingTouchSlop) {
                         mCallback.onBeginDrag(mCurrView);
                         mDragging = true;
-                        mInitialTouchPos = getPos(ev) - getPos(mCurrView);
+                        mInitialTouchPos = getPos(ev) - getTranslation(mCurrAnimView);
                     }
                 }
                 break;
             case MotionEvent.ACTION_UP:
                 mDragging = false;
                 mCurrView = null;
+                mCurrAnimView = null;
                 break;
         }
         return mDragging;
     }
 
-    public void dismissChild(final View animView, float velocity) {
+    public void dismissChild(final View view, float velocity) {
+        final View animView = mCallback.getChildContentView(view);
+        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
         float newPos;
-        if (velocity < 0 || (velocity == 0 && getPos(animView) < 0)) {
+        if (velocity < 0 || (velocity == 0 && getTranslation(animView) < 0)) {
             newPos = -getSize(animView);
         } else {
             newPos = getSize(animView);
@@ -202,7 +204,7 @@
         int duration = MAX_ESCAPE_ANIMATION_DURATION;
         if (velocity != 0) {
             duration = Math.min(duration,
-                                (int) (Math.abs(newPos - getPos(animView)) * 1000f / Math
+                                (int) (Math.abs(newPos - getTranslation(animView)) * 1000f / Math
                                         .abs(velocity)));
         }
         ObjectAnimator anim = createTranslationAnimation(animView, newPos);
@@ -216,17 +218,17 @@
             }
 
             public void onAnimationEnd(Animator animation) {
-                mCallback.onChildDismissed(animView);
+                mCallback.onChildDismissed(view);
             }
 
             public void onAnimationCancel(Animator animation) {
-                mCallback.onChildDismissed(animView);
+                mCallback.onChildDismissed(view);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE) {
-                    animView.setAlpha(getAlphaForOffset(animView, getContentSize(animView)));
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    animView.setAlpha(getAlphaForOffset(animView));
                 }
                 invalidateGlobalRegion(animView);
             }
@@ -234,14 +236,16 @@
         anim.start();
     }
 
-    public void snapChild(final View animView, float velocity) {
+    public void snapChild(final View view, float velocity) {
+        final View animView = mCallback.getChildContentView(view);
+        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(animView);
         ObjectAnimator anim = createTranslationAnimation(animView, 0);
         int duration = SNAP_ANIM_LEN;
         anim.setDuration(duration);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE) {
-                    animView.setAlpha(getAlphaForOffset(animView, getContentSize(animView)));
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    animView.setAlpha(getAlphaForOffset(animView));
                 }
                 invalidateGlobalRegion(animView);
             }
@@ -264,7 +268,7 @@
                     // don't let items that can't be dismissed be dragged more than
                     // maxScrollDistance
                     if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
-                        float size = getSize(mCurrView);
+                        float size = getSize(mCurrAnimView);
                         float maxScrollDistance = 0.15f * size;
                         if (Math.abs(delta) >= size) {
                             delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
@@ -272,9 +276,9 @@
                             delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
                         }
                     }
-                    setTranslation(mCurrView, delta);
-                    if (FADE_OUT_DURING_SWIPE) {
-                        mCurrView.setAlpha(getAlphaForOffset(mCurrView, getContentSize(mCurrView)));
+                    setTranslation(mCurrAnimView, delta);
+                    if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
+                        mCurrAnimView.setAlpha(getAlphaForOffset(mCurrAnimView));
                     }
                     invalidateGlobalRegion(mCurrView);
                 }
@@ -290,10 +294,10 @@
 
                     // Decide whether to dismiss the current view
                     boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
-                            Math.abs(getPos(mCurrView)) > 0.4 * getSize(mCurrView);
+                            Math.abs(getTranslation(mCurrAnimView)) > 0.4 * getSize(mCurrAnimView);
                     boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
                             (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
-                            (velocity > 0) == (getPos(mCurrView) > 0);
+                            (velocity > 0) == (getTranslation(mCurrAnimView) > 0);
 
                     boolean dismissChild = mCallback.canChildBeDismissed(mCurrView) &&
                             (childSwipedFastEnough || childSwipedFarEnough);
@@ -303,6 +307,7 @@
                         dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
                     } else {
                         // snappity
+                        mCallback.onDragCancelled(mCurrView);
                         snapChild(mCurrView, velocity);
                     }
                 }
@@ -321,5 +326,7 @@
         void onBeginDrag(View v);
 
         void onChildDismissed(View v);
+
+        void onDragCancelled(View v);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
index 5609ead..2de4185 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
@@ -27,4 +27,8 @@
     void handleOnClick(View selectedView);
     void handleSwipe(View selectedView);
     void handleLongPress(View selectedView, View anchorView);
+    void handleShowBackground(boolean show);
+
+    // TODO: find another way to get this info from RecentsPanelView
+    boolean isRecentsVisible();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 8da2db6..85cde7c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.DataSetObserver;
+import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -41,6 +42,8 @@
     private RecentsCallback mCallback;
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
+    private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+
     private OnLongClickListener mOnLongClick = new OnLongClickListener() {
         public boolean onLongClick(View v) {
             final View anchorView = v.findViewById(R.id.app_description);
@@ -49,15 +52,12 @@
         }
     };
 
-    public RecentsHorizontalScrollView(Context context) {
-        this(context, null);
-    }
-
     public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop);
+        mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, false);
     }
 
     private int scrollPositionOfMostRecent() {
@@ -71,7 +71,11 @@
             view.setLongClickable(true);
             view.setOnLongClickListener(mOnLongClick);
 
-            final View thumbnail = getChildContentView(view);
+            if (mPerformanceHelper != null) {
+                mPerformanceHelper.addViewCallback(view);
+            }
+
+            final View thumbnail = view.findViewById(R.id.app_thumbnail);
             // thumbnail is set to clickable in the layout file
             thumbnail.setOnClickListener(new OnClickListener() {
                 public void onClick(View v) {
@@ -123,6 +127,11 @@
         // We do this so the underlying ScrollView knows that it won't get
         // the chance to intercept events anymore
         requestDisallowInterceptTouchEvent(true);
+        v.setActivated(true);
+    }
+
+    public void onDragCancelled(View v) {
+        v.setActivated(false);
     }
 
     public View getChildAtPosition(MotionEvent ev) {
@@ -139,7 +148,60 @@
     }
 
     public View getChildContentView(View v) {
-        return v.findViewById(R.id.app_thumbnail);
+        return v.findViewById(R.id.recent_item);
+    }
+
+    @Override
+    protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onLayoutCallback();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+
+        if (mPerformanceHelper != null) {
+            int paddingLeft = mPaddingLeft;
+            final boolean offsetRequired = isPaddingOffsetRequired();
+            if (offsetRequired) {
+                paddingLeft += getLeftPaddingOffset();
+            }
+
+            int left = mScrollX + paddingLeft;
+            int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
+            int top = mScrollY + getFadeTop(offsetRequired);
+            int bottom = top + getFadeHeight(offsetRequired);
+
+            if (offsetRequired) {
+                right += getRightPaddingOffset();
+                bottom += getBottomPaddingOffset();
+            }
+            mPerformanceHelper.drawCallback(canvas,
+                    left, right, top, bottom, mScrollX, mScrollY,
+                    0, 0,
+                    getLeftFadingEdgeStrength(), getRightFadingEdgeStrength());
+        }
+    }
+
+    @Override
+    public int getVerticalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getVerticalFadingEdgeLengthCallback();
+        } else {
+            return super.getVerticalFadingEdgeLength();
+        }
+    }
+
+    @Override
+    public int getHorizontalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getHorizontalFadingEdgeLengthCallback();
+        } else {
+            return super.getHorizontalFadingEdgeLength();
+        }
     }
 
     @Override
@@ -153,6 +215,14 @@
     }
 
     @Override
+    public void onAttachedToWindow() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onAttachedToWindowCallback(
+                    mCallback, mLinearLayout, isHardwareAccelerated());
+        }
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         float densityScale = getResources().getDisplayMetrics().density;
@@ -192,6 +262,12 @@
         });
     }
 
+    public void onRecentsVisibilityChanged() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.updateShowBackground();
+        }
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -220,6 +296,9 @@
 
     @Override
     public void setLayoutTransition(LayoutTransition transition) {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.setLayoutTransitionCallback(transition);
+        }
         // The layout transition applies to our embedded LinearLayout
         mLinearLayout.setLayoutTransition(transition);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
deleted file mode 100644
index d8b086b..0000000
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.systemui.recent;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ListView;
-
-import com.android.systemui.R;
-
-public class RecentsListView extends ListView {
-    private int mLastVisiblePosition;
-    private RecentsCallback mCallback;
-
-    public RecentsListView(Context context) {
-        this(context, null);
-    }
-
-    public RecentsListView(Context context, AttributeSet attrs) {
-        super(context, attrs, 0);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        LayoutInflater inflater = (LayoutInflater)
-                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-        View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer, this, false);
-        setScrollbarFadingEnabled(true);
-        addFooterView(footer, null, false);
-        final int leftPadding = mContext.getResources()
-            .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
-        setOverScrollEffectPadding(leftPadding, 0);
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        // Keep track of the last visible item in the list so we can restore it
-        // to the bottom when the orientation changes.
-        final int childCount = getChildCount();
-        if (childCount > 0) {
-            mLastVisiblePosition = getFirstVisiblePosition() + childCount - 1;
-            View view = getChildAt(childCount - 1);
-            final int distanceFromBottom = getHeight() - view.getTop();
-
-            // This has to happen post-layout, so run it "in the future"
-            post(new Runnable() {
-                public void run() {
-                    setSelectionFromTop(mLastVisiblePosition, getHeight() - distanceFromBottom);
-                }
-            });
-        }
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        // scroll to bottom after reloading
-        int count = getAdapter().getCount();
-        mLastVisiblePosition = count - 1;
-        if (visibility == View.VISIBLE && changedView == this) {
-            post(new Runnable() {
-                public void run() {
-                    setSelection(mLastVisiblePosition);
-                }
-            });
-        }
-    }
-
-    public void setCallback(RecentsCallback callback) {
-        mCallback = callback;
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 9cc2c29..e59c109 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -56,7 +56,6 @@
 import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
 import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
@@ -64,6 +63,7 @@
 import android.widget.RelativeLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.StatusBar;
@@ -73,7 +73,7 @@
 
 public class RecentsPanelView extends RelativeLayout
         implements OnItemClickListener, RecentsCallback, StatusBarPanel, Animator.AnimatorListener {
-    static final String TAG = "RecentsListView";
+    static final String TAG = "RecentsPanelView";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
     private static final int DISPLAY_TASKS = 20;
     private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
@@ -84,12 +84,8 @@
     private View mRecentsScrim;
     private View mRecentsGlowView;
     private ViewGroup mRecentsContainer;
-    private Bitmap mGlowBitmap;
-    // TODO: add these widgets attributes to the layout file
-    private int mGlowBitmapPaddingLeftPx;
-    private int mGlowBitmapPaddingTopPx;
-    private int mGlowBitmapPaddingRightPx;
-    private int mGlowBitmapPaddingBottomPx;
+    private Bitmap mAppThumbnailBackground;
+
     private boolean mShowing;
     private Choreographer mChoreo;
     private View mRecentsDismissButton;
@@ -129,7 +125,7 @@
         }
 
         public void setThumbnail(Bitmap thumbnail) {
-            mThumbnail = compositeBitmap(mGlowBitmap, thumbnail);
+            mThumbnail = compositeBitmap(mAppThumbnailBackground, thumbnail);
         }
 
         public Bitmap getThumbnail() {
@@ -178,7 +174,7 @@
         public View getView(int position, View convertView, ViewGroup parent) {
             ViewHolder holder;
             if (convertView == null) {
-                convertView = mInflater.inflate(R.layout.status_bar_recent_item, null);
+                convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
                 holder = new ViewHolder();
                 holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
                 holder.thumbnailViewImage = (ImageView) convertView.findViewById(
@@ -247,6 +243,28 @@
         }
     }
 
+    public void hide(boolean animate) {
+        mShowing = false;
+        if (!animate) {
+            setVisibility(View.GONE);
+        }
+        if (mBar != null) {
+            mBar.animateCollapse();
+        }
+    }
+
+    public void handleShowBackground(boolean show) {
+        if (show) {
+            mRecentsScrim.setBackgroundResource(R.drawable.status_bar_recents_background);
+        } else {
+            mRecentsScrim.setBackgroundDrawable(null);
+        }
+    }
+
+    public boolean isRecentsVisible() {
+        return getVisibility() == VISIBLE;
+    }
+
     public void onAnimationCancel(Animator animation) {
     }
 
@@ -315,15 +333,12 @@
 
         mIconDpi = xlarge ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
 
-        mGlowBitmap = BitmapFactory.decodeResource(res, R.drawable.recents_thumbnail_bg);
-        mGlowBitmapPaddingLeftPx =
-                res.getDimensionPixelSize(R.dimen.recents_thumbnail_bg_padding_left);
-        mGlowBitmapPaddingTopPx =
-                res.getDimensionPixelSize(R.dimen.recents_thumbnail_bg_padding_top);
-        mGlowBitmapPaddingRightPx =
-                res.getDimensionPixelSize(R.dimen.recents_thumbnail_bg_padding_right);
-        mGlowBitmapPaddingBottomPx =
-                res.getDimensionPixelSize(R.dimen.recents_thumbnail_bg_padding_bottom);
+        int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width);
+        int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height);
+        int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
+        mAppThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(mAppThumbnailBackground);
+        c.drawColor(color);
     }
 
     @Override
@@ -332,12 +347,7 @@
         mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
         mListAdapter = new ActivityDescriptionAdapter(mContext);
-        if (mRecentsContainer instanceof RecentsListView) {
-            RecentsListView listView = (RecentsListView) mRecentsContainer;
-            listView.setAdapter(mListAdapter);
-            listView.setOnItemClickListener(this);
-            listView.setCallback(this);
-        } else if (mRecentsContainer instanceof RecentsHorizontalScrollView){
+        if (mRecentsContainer instanceof RecentsHorizontalScrollView){
             RecentsHorizontalScrollView scrollView
                     = (RecentsHorizontalScrollView) mRecentsContainer;
             scrollView.setAdapter(mListAdapter);
@@ -349,7 +359,7 @@
             scrollView.setCallback(this);
         }
         else {
-            throw new IllegalArgumentException("missing RecentsListView/RecentsScrollView");
+            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
         }
 
 
@@ -382,6 +392,14 @@
         if (visibility == View.VISIBLE && changedView == this) {
             refreshApplicationList();
         }
+
+        if (mRecentsContainer instanceof RecentsHorizontalScrollView) {
+            ((RecentsHorizontalScrollView) mRecentsContainer).onRecentsVisibilityChanged();
+        } else if (mRecentsContainer instanceof RecentsVerticalScrollView) {
+            ((RecentsVerticalScrollView) mRecentsContainer).onRecentsVisibilityChanged();
+        } else {
+            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
+        }
     }
 
     Drawable getFullResDefaultActivityIcon() {
@@ -555,6 +573,9 @@
             mThumbnailLoader = null;
         }
         mActivityDescriptions = getRecentTasks();
+        for (ActivityDescription ad : mActivityDescriptions) {
+            ad.setThumbnail(mAppThumbnailBackground);
+        }
         mListAdapter.notifyDataSetInvalidated();
         if (mActivityDescriptions.size() > 0) {
             if (DEBUG) Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps");
@@ -629,14 +650,8 @@
             paint.setAntiAlias(true);
             paint.setFilterBitmap(true);
             paint.setAlpha(255);
-            final int srcWidth = thumbnail.getWidth();
-            final int srcHeight = thumbnail.getHeight();
-            if (DEBUG) Log.v(TAG, "Source thumb: " + srcWidth + "x" + srcHeight);
-            canvas.drawBitmap(thumbnail,
-                    new Rect(0, 0, srcWidth-1, srcHeight-1),
-                    new RectF(mGlowBitmapPaddingLeftPx, mGlowBitmapPaddingTopPx,
-                            outBitmap.getWidth() - mGlowBitmapPaddingRightPx,
-                            outBitmap.getHeight() - mGlowBitmapPaddingBottomPx), paint);
+            canvas.drawBitmap(thumbnail, null,
+                    new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint);
             canvas.setBitmap(null);
         }
         return outBitmap;
@@ -649,15 +664,6 @@
         mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
     }
 
-    public void hide(boolean animate) {
-        if (!animate) {
-            setVisibility(View.GONE);
-        }
-        if (mBar != null) {
-            mBar.animateCollapse();
-        }
-    }
-
     public void handleOnClick(View view) {
         ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
         final Context context = view.getContext();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
new file mode 100644
index 0000000..b7e656e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
@@ -0,0 +1,304 @@
+/*
+ * 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.systemui.recent;
+
+import android.animation.LayoutTransition;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+public class RecentsScrollViewPerformanceHelper {
+    public static final boolean OPTIMIZE_SW_RENDERED_RECENTS = true;
+    public static final boolean USE_DARK_FADE_IN_HW_ACCELERATED_MODE = true;
+    private View mScrollView;
+    private LinearLayout mLinearLayout;
+    private RecentsCallback mCallback;
+
+    private boolean mShowBackground = false;
+    private int mFadingEdgeLength;
+    private Drawable.ConstantState mBackgroundDrawable;
+    private Context mContext;
+    private boolean mIsVertical;
+    private boolean mFirstTime = true;
+    private boolean mSoftwareRendered = false;
+    private boolean mAttachedToWindow = false;
+
+    public static RecentsScrollViewPerformanceHelper create(Context context,
+            AttributeSet attrs, View scrollView, boolean isVertical) {
+        boolean isTablet = context.getResources().
+                getBoolean(R.bool.config_recents_interface_for_tablets);
+        if (!isTablet && (OPTIMIZE_SW_RENDERED_RECENTS || USE_DARK_FADE_IN_HW_ACCELERATED_MODE)) {
+            return new RecentsScrollViewPerformanceHelper(context, attrs, scrollView, isVertical);
+        } else {
+            return null;
+        }
+    }
+
+    public RecentsScrollViewPerformanceHelper(Context context,
+            AttributeSet attrs, View scrollView, boolean isVertical) {
+        mScrollView = scrollView;
+        mContext = context;
+        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View);
+        mFadingEdgeLength = a.getDimensionPixelSize(android.R.styleable.View_fadingEdgeLength,
+                ViewConfiguration.get(context).getScaledFadingEdgeLength());
+        mIsVertical = isVertical;
+    }
+
+    public void onAttachedToWindowCallback(
+            RecentsCallback callback, LinearLayout layout, boolean hardwareAccelerated) {
+        mSoftwareRendered = !hardwareAccelerated;
+        if ((mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS)
+                || USE_DARK_FADE_IN_HW_ACCELERATED_MODE) {
+            mScrollView.setVerticalFadingEdgeEnabled(false);
+            mScrollView.setHorizontalFadingEdgeEnabled(false);
+        }
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            mCallback = callback;
+            mLinearLayout = layout;
+            mAttachedToWindow = true;
+            mBackgroundDrawable = mContext.getResources()
+                .getDrawable(R.drawable.status_bar_recents_background).getConstantState();
+            updateShowBackground();
+        }
+
+    }
+
+    public void addViewCallback(View newLinearLayoutChild) {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            final View view = newLinearLayoutChild;
+            if (mShowBackground) {
+                view.setBackgroundDrawable(mBackgroundDrawable.newDrawable());
+                view.setDrawingCacheEnabled(true);
+                view.buildDrawingCache();
+            } else {
+                view.setBackgroundDrawable(null);
+                view.setDrawingCacheEnabled(false);
+                view.destroyDrawingCache();
+            }
+        }
+    }
+
+    public void onLayoutCallback() {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            mScrollView.post(new Runnable() {
+                public void run() {
+                    updateShowBackground();
+                }
+            });
+        }
+    }
+
+    public void drawCallback(Canvas canvas,
+            int left, int right, int top, int bottom, int scrollX, int scrollY,
+            float topFadingEdgeStrength, float bottomFadingEdgeStrength,
+            float leftFadingEdgeStrength, float rightFadingEdgeStrength) {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            if (mIsVertical) {
+                if (scrollY < 0) {
+                    Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                    d.setBounds(0, scrollY, mScrollView.getWidth(), 0);
+                    d.draw(canvas);
+                } else {
+                    final int childHeight = mLinearLayout.getHeight();
+                    if (scrollY + mScrollView.getHeight() > childHeight) {
+                        Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                        d.setBounds(0, childHeight, mScrollView.getWidth(),
+                                scrollY + mScrollView.getHeight());
+                        d.draw(canvas);
+                    }
+                }
+            } else {
+                if (scrollX < 0) {
+                    Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                    d.setBounds(scrollX, 0, 0, mScrollView.getHeight());
+                    d.draw(canvas);
+                } else {
+                    final int childWidth = mLinearLayout.getWidth();
+                    if (scrollX + mScrollView.getWidth() > childWidth) {
+                        Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                        d.setBounds(childWidth, 0,
+                                scrollX + mScrollView.getWidth(), mScrollView.getHeight());
+                        d.draw(canvas);
+                    }
+                }
+            }
+        }
+
+        if ((mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS)
+                || USE_DARK_FADE_IN_HW_ACCELERATED_MODE) {
+            Paint p = new Paint();
+            Matrix matrix = new Matrix();
+            // use use a height of 1, and then wack the matrix each time we
+            // actually use it.
+            Shader fade = new LinearGradient(0, 0, 0, 1, 0xCC000000, 0, Shader.TileMode.CLAMP);
+            // PULL OUT THIS CONSTANT
+
+            p.setShader(fade);
+
+            // draw the fade effect
+            boolean drawTop = false;
+            boolean drawBottom = false;
+            boolean drawLeft = false;
+            boolean drawRight = false;
+
+            float topFadeStrength = 0.0f;
+            float bottomFadeStrength = 0.0f;
+            float leftFadeStrength = 0.0f;
+            float rightFadeStrength = 0.0f;
+
+            final float fadeHeight = mFadingEdgeLength;
+            int length = (int) fadeHeight;
+
+            // clip the fade length if top and bottom fades overlap
+            // overlapping fades produce odd-looking artifacts
+            if (mIsVertical && (top + length > bottom - length)) {
+                length = (bottom - top) / 2;
+            }
+
+            // also clip horizontal fades if necessary
+            if (!mIsVertical && (left + length > right - length)) {
+                length = (right - left) / 2;
+            }
+
+            if (mIsVertical) {
+                topFadeStrength = Math.max(0.0f, Math.min(1.0f, topFadingEdgeStrength));
+                drawTop = topFadeStrength * fadeHeight > 1.0f;
+                bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, bottomFadingEdgeStrength));
+                drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
+            }
+
+            if (!mIsVertical) {
+                leftFadeStrength = Math.max(0.0f, Math.min(1.0f, leftFadingEdgeStrength));
+                drawLeft = leftFadeStrength * fadeHeight > 1.0f;
+                rightFadeStrength = Math.max(0.0f, Math.min(1.0f, rightFadingEdgeStrength));
+                drawRight = rightFadeStrength * fadeHeight > 1.0f;
+            }
+
+            if (drawTop) {
+                matrix.setScale(1, fadeHeight * topFadeStrength);
+                matrix.postTranslate(left, top);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(left, top, right, top + length, p);
+            }
+
+            if (drawBottom) {
+                matrix.setScale(1, fadeHeight * bottomFadeStrength);
+                matrix.postRotate(180);
+                matrix.postTranslate(left, bottom);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(left, bottom - length, right, bottom, p);
+            }
+
+            if (drawLeft) {
+                matrix.setScale(1, fadeHeight * leftFadeStrength);
+                matrix.postRotate(-90);
+                matrix.postTranslate(left, top);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(left, top, left + length, bottom, p);
+            }
+
+            if (drawRight) {
+                matrix.setScale(1, fadeHeight * rightFadeStrength);
+                matrix.postRotate(90);
+                matrix.postTranslate(right, top);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(right - length, top, right, bottom, p);
+            }
+        }
+    }
+
+    public int getVerticalFadingEdgeLengthCallback() {
+        return mFadingEdgeLength;
+    }
+
+    public int getHorizontalFadingEdgeLengthCallback() {
+        return mFadingEdgeLength;
+    }
+
+    public void setLayoutTransitionCallback(LayoutTransition transition) {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            if (transition != null) {
+                transition.addTransitionListener(new LayoutTransition.TransitionListener() {
+                    @Override
+                    public void startTransition(LayoutTransition transition,
+                            ViewGroup container, View view, int transitionType) {
+                        updateShowBackground();
+                    }
+
+                    @Override
+                    public void endTransition(LayoutTransition transition,
+                            ViewGroup container, View view, int transitionType) {
+                        updateShowBackground();
+                    }
+                });
+            }
+        }
+    }
+
+    // Turn on/off drawing the background in our ancestor, and turn on/off drawing
+    // in the items in LinearLayout contained by this scrollview.
+    // Moving the background drawing to our children, and turning on a drawing cache
+    // for each of them, gives us a ~20fps gain when Recents is rendered in software
+    public void updateShowBackground() {
+        if (!mAttachedToWindow) {
+            // We haven't been initialized yet-- we'll get called again when we are
+            return;
+        }
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            LayoutTransition transition = mLinearLayout.getLayoutTransition();
+            int linearLayoutSize =
+                mIsVertical ? mLinearLayout.getHeight() : mLinearLayout.getWidth();
+            int scrollViewSize =
+                mIsVertical ? mScrollView.getHeight() : mScrollView.getWidth();
+            boolean show = !mScrollView.isHardwareAccelerated() &&
+                (linearLayoutSize > scrollViewSize) &&
+                !(transition != null && transition.isRunning()) &&
+                mCallback.isRecentsVisible();
+
+            if (!mFirstTime && show == mShowBackground) return;
+            mShowBackground = show;
+            mFirstTime = false;
+
+            mCallback.handleShowBackground(!show);
+            for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+                View v = mLinearLayout.getChildAt(i);
+                if (show) {
+                    v.setBackgroundDrawable(mBackgroundDrawable.newDrawable());
+                    v.setDrawingCacheEnabled(true);
+                    v.buildDrawingCache();
+                } else {
+                    v.setDrawingCacheEnabled(false);
+                    v.destroyDrawingCache();
+                    v.setBackgroundDrawable(null);
+                }
+            }
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index b1a30d9..3acef08 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.DataSetObserver;
+import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -40,6 +41,7 @@
     private RecentsCallback mCallback;
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
+    private RecentsScrollViewPerformanceHelper mPerformanceHelper;
 
     private OnLongClickListener mOnLongClick = new OnLongClickListener() {
         public boolean onLongClick(View v) {
@@ -49,15 +51,13 @@
         }
     };
 
-    public RecentsVerticalScrollView(Context context) {
-        this(context, null);
-    }
-
     public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+
+        mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, true);
     }
 
     private int scrollPositionOfMostRecent() {
@@ -77,11 +77,15 @@
             }
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
+            if (mPerformanceHelper != null) {
+                mPerformanceHelper.addViewCallback(view);
+            }
+
             if (old == null) {
                 view.setClickable(true);
                 view.setOnLongClickListener(mOnLongClick);
 
-                final View thumbnail = getChildContentView(view);
+                final View thumbnail = view.findViewById(R.id.app_thumbnail);
                 // thumbnail is set to clickable in the layout file
                 thumbnail.setOnClickListener(new OnClickListener() {
                     public void onClick(View v) {
@@ -138,6 +142,11 @@
         // We do this so the underlying ScrollView knows that it won't get
         // the chance to intercept events anymore
         requestDisallowInterceptTouchEvent(true);
+        v.setActivated(true);
+    }
+
+    public void onDragCancelled(View v) {
+        v.setActivated(false);
     }
 
     public View getChildAtPosition(MotionEvent ev) {
@@ -155,7 +164,60 @@
     }
 
     public View getChildContentView(View v) {
-        return v.findViewById(R.id.app_thumbnail);
+        return v.findViewById(R.id.recent_item);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onLayoutCallback();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+
+        if (mPerformanceHelper != null) {
+            int paddingLeft = mPaddingLeft;
+            final boolean offsetRequired = isPaddingOffsetRequired();
+            if (offsetRequired) {
+                paddingLeft += getLeftPaddingOffset();
+            }
+
+            int left = mScrollX + paddingLeft;
+            int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
+            int top = mScrollY + getFadeTop(offsetRequired);
+            int bottom = top + getFadeHeight(offsetRequired);
+
+            if (offsetRequired) {
+                right += getRightPaddingOffset();
+                bottom += getBottomPaddingOffset();
+            }
+            mPerformanceHelper.drawCallback(canvas,
+                    left, right, top, bottom, mScrollX, mScrollY,
+                    getTopFadingEdgeStrength(), getBottomFadingEdgeStrength(),
+                    0, 0);
+        }
+    }
+
+    @Override
+    public int getVerticalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getVerticalFadingEdgeLengthCallback();
+        } else {
+            return super.getVerticalFadingEdgeLength();
+        }
+    }
+
+    @Override
+    public int getHorizontalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getHorizontalFadingEdgeLengthCallback();
+        } else {
+            return super.getHorizontalFadingEdgeLength();
+        }
     }
 
     @Override
@@ -169,6 +231,14 @@
     }
 
     @Override
+    public void onAttachedToWindow() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onAttachedToWindowCallback(
+                    mCallback, mLinearLayout, isHardwareAccelerated());
+        }
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         float densityScale = getResources().getDisplayMetrics().density;
@@ -208,6 +278,12 @@
         });
     }
 
+    public void onRecentsVisibilityChanged() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.updateShowBackground();
+        }
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -236,6 +312,9 @@
 
     @Override
     public void setLayoutTransition(LayoutTransition transition) {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.setLayoutTransitionCallback(transition);
+        }
         // The layout transition applies to our embedded LinearLayout
         mLinearLayout.setLayoutTransition(transition);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index e6c0b96..98dca92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -70,6 +70,14 @@
         return mCurrentView.findViewById(R.id.menu);
     }
 
+    public View getBackButton() {
+        return mCurrentView.findViewById(R.id.back);
+    }
+
+    public View getHomeButton() {
+        return mCurrentView.findViewById(R.id.home);
+    }
+
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
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..3d23abe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -380,7 +380,7 @@
     }
 
     protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
-        boolean translucent = false;
+        boolean opaque = false;
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 layoutParams.width,
                 layoutParams.height,
@@ -388,7 +388,7 @@
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                (translucent ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
+                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
         if (ActivityManager.isHighEndGfx(mDisplay)) {
             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
         }
@@ -404,7 +404,7 @@
         // Recents Panel
         boolean visible = false;
         if (mRecentsPanel != null) {
-            visible = mRecentsPanel.getVisibility() == View.VISIBLE;
+            visible = mRecentsPanel.isShowing();
             WindowManagerImpl.getDefault().removeView(mRecentsPanel);
         }
 
@@ -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");
@@ -1081,18 +1091,9 @@
             }
         }
 
-        if ((diff & StatusBarManager.DISABLE_NAVIGATION) != 0) {
-            if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) {
-                Slog.d(TAG, "DISABLE_NAVIGATION: yes");
-
-                // close recents if it's visible
-                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
-                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
-            }
-
-            if (mNavigationBarView != null) {
-                mNavigationBarView.setEnabled((state & StatusBarManager.DISABLE_NAVIGATION) == 0);
-            }
+        if ((diff & (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK)) != 0) {
+            setNavigationVisibility(state &
+                    (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK));
         }
 
         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
@@ -1117,6 +1118,32 @@
         }
     }
 
+    private void setNavigationVisibility(int visibility) {
+        boolean disableNavigation = ((visibility & StatusBarManager.DISABLE_NAVIGATION) != 0);
+        boolean disableBack = ((visibility & StatusBarManager.DISABLE_BACK) != 0);
+
+        Slog.i(TAG, "DISABLE_BACK: " + (disableBack ? "yes" : "no"));
+        Slog.i(TAG, "DISABLE_NAVIGATION: " + (disableNavigation ? "yes" : "no"));
+
+        if (mNavigationBarView != null) {
+            if (disableNavigation && disableBack) {
+                mNavigationBarView.setEnabled(false);
+            } else {
+                mNavigationBarView.getBackButton().setEnabled(!disableBack);
+                mNavigationBarView.getHomeButton().setEnabled(!disableNavigation);
+                mNavigationBarView.getRecentsButton().setEnabled(!disableNavigation);
+
+                mNavigationBarView.setEnabled(true);
+            }
+        }
+
+        if (disableNavigation) {
+            // close recents if it's visible
+            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
+            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
+        }
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 60dfdac..3c85814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -130,6 +130,8 @@
     int mLastDataTypeIconId = -1;
     String mLastLabel = "";
 
+    private boolean mHasMobileDataFeature;
+
     boolean mDataAndWifiStacked = false;
 
     // yuck -- stop doing this here and put it in the framework
@@ -147,6 +149,10 @@
     public NetworkController(Context context) {
         mContext = context;
 
+        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        mHasMobileDataFeature = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
         // set up the default wifi icon, used when no radios have ever appeared
         updateWifiIcons();
 
@@ -229,7 +235,7 @@
                 mWifiIconId,
                 mWifiActivityIconId);
         cluster.setMobileDataIndicators(
-                hasMobileDataFeature(),
+                mHasMobileDataFeature,
                 mPhoneSignalIconId,
                 mMobileActivityIconId,
                 mDataTypeIconId);
@@ -376,12 +382,6 @@
         }
     }
 
-    private boolean hasMobileDataFeature() {
-        // XXX: HAX: replace when a more reliable method is available
-        return (! "wifi-only".equals(SystemProperties.get("ro.carrier")));
-    }
-
-
     private void updateAirplaneMode() {
         mAirplaneMode = (Settings.System.getInt(mContext.getContentResolver(),
             Settings.System.AIRPLANE_MODE_ON, 0) == 1);
@@ -828,8 +828,8 @@
             label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
             // On devices without mobile radios, we want to show the wifi icon
             combinedSignalIconId =
-                hasMobileDataFeature() ? mDataSignalIconId : mWifiIconId;
-            mContentDescriptionCombinedSignal = hasMobileDataFeature()
+                mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId;
+            mContentDescriptionCombinedSignal = mHasMobileDataFeature
                 ? mContentDescriptionDataType : mContentDescriptionWifi;
             mDataTypeIconId = 0;
         }
@@ -866,7 +866,7 @@
                         mWifiIconId,
                         mWifiActivityIconId);
                 cluster.setMobileDataIndicators(
-                        hasMobileDataFeature(),
+                        mHasMobileDataFeature,
                         mPhoneSignalIconId,
                         mMobileActivityIconId,
                         mDataTypeIconId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 469b462..e287b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -124,6 +124,9 @@
         requestDisallowInterceptTouchEvent(true);
     }
 
+    public void onDragCancelled(View v) {
+    }
+
     public View getChildAtPosition(MotionEvent ev) {
         // find the view under the pointer, accounting for GONE views
         final int count = getChildCount();
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/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 2ab667d..cc73d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -306,7 +306,7 @@
         mStatusBarView.setIgnoreChildren(2, mRecentButton, mRecentsPanel);
 
         lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
+                (int) res.getDimension(R.dimen.status_bar_recents_width),
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
@@ -949,29 +949,34 @@
                 mTicker.halt();
             }
         }
-        if ((diff & StatusBarManager.DISABLE_NAVIGATION) != 0) {
-            if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) {
-                Slog.i(TAG, "DISABLE_NAVIGATION: yes");
-                mNavigationArea.setVisibility(View.INVISIBLE);
-                mInputMethodSwitchButton.setScreenLocked(true);
-            } else {
-                Slog.i(TAG, "DISABLE_NAVIGATION: no");
-                mNavigationArea.setVisibility(View.VISIBLE);
-                mInputMethodSwitchButton.setScreenLocked(false);
-            }
+        if ((diff & (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK)) != 0) {
+            setNavigationVisibility(state &
+                    (StatusBarManager.DISABLE_NAVIGATION | StatusBarManager.DISABLE_BACK));
         }
-        if ((diff & StatusBarManager.DISABLE_BACK) != 0) {
-            if ((state & StatusBarManager.DISABLE_BACK) != 0) {
-                Slog.i(TAG, "DISABLE_BACK: yes");
-                mBackButton.setEnabled(false);
-                mInputMethodSwitchButton.setScreenLocked(true);
-            } else {
-                Slog.i(TAG, "DISABLE_BACK: no");
-                mBackButton.setEnabled(true);
-                mInputMethodSwitchButton.setScreenLocked(false);
-            }
+    }
+
+    private void setNavigationVisibility(int visibility) {
+        boolean disableNavigation = ((visibility & StatusBarManager.DISABLE_NAVIGATION) != 0);
+        boolean disableBack = ((visibility & StatusBarManager.DISABLE_BACK) != 0);
+
+        Slog.i(TAG, "DISABLE_BACK: " + (disableBack ? "yes" : "no"));
+        Slog.i(TAG, "DISABLE_NAVIGATION: " + (disableNavigation ? "yes" : "no"));
+
+        if (disableNavigation && disableBack) {
+            mNavigationArea.setVisibility(View.INVISIBLE);
+        } else {
+            int backVisiblity = (disableBack ? View.INVISIBLE : View.VISIBLE);
+            int navVisibility = (disableNavigation ? View.INVISIBLE : View.VISIBLE);
+
+            mBackButton.setVisibility(backVisiblity);
+            mHomeButton.setVisibility(navVisibility);
+            mRecentButton.setVisibility(navVisibility);
+            // don't change menu button visibility here
+
+            mNavigationArea.setVisibility(View.VISIBLE);
         }
 
+        mInputMethodSwitchButton.setScreenLocked(disableNavigation);
     }
 
     private boolean hasTicker(Notification n) {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index 9629702..40cc7d8 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -20,6 +20,7 @@
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccCard.State;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.TransportControlView;
 import com.android.internal.policy.impl.KeyguardUpdateMonitor.SimStateCallback;
 
 import java.util.ArrayList;
@@ -52,8 +53,6 @@
     public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
     private static final long INSTRUCTION_RESET_DELAY = 2000; // time until instruction text resets
 
-    private static final int SHOW_WIDGET = 8;
-    private static final int HIDE_WIDGET = 9;
     private static final int INSTRUCTION_TEXT = 10;
     private static final int CARRIER_TEXT = 11;
     private static final int CARRIER_HELP_TEXT = 12;
@@ -71,7 +70,7 @@
     private TextView mStatus1View;
     private TextView mOwnerInfoView;
     private TextView mAlarmStatusView;
-    private View mTransportView;
+    private TransportControlView mTransportView;
 
     // Top-level container view for above views
     private View mContainer;
@@ -162,7 +161,7 @@
         mStatus1View = (TextView) findViewById(R.id.status1);
         mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
         mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
-        mTransportView = findViewById(R.id.transport);
+        mTransportView = (TransportControlView) findViewById(R.id.transport);
         mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
         if (mEmergencyCallButton != null) {
             mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
@@ -192,20 +191,6 @@
                 mUpdateMonitor.getTelephonyPlmn(), mUpdateMonitor.getTelephonySpn()));
     }
 
-    public void enterWidgetMode() {
-        if (mTransportView != null) {
-            mTransportView.setVisibility(View.VISIBLE);
-            update(SHOW_WIDGET, null);
-        }
-    }
-
-    public void leaveWidgetMode() {
-        if (mTransportView != null) {
-            mTransportView.setVisibility(View.GONE);
-            update(HIDE_WIDGET, null);
-        }
-    }
-
     private boolean inWidgetMode() {
         return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE;
     }
@@ -248,7 +233,8 @@
      * @param lockIcon
      */
     public void setHelpMessage(int textResId, int lockIcon) {
-        mHelpMessageText = getText(textResId).toString();
+        final CharSequence tmp = getText(textResId);
+        mHelpMessageText = tmp == null ? null : tmp.toString();
         update(HELP_MESSAGE_TEXT, mHelpMessageText);
     }
 
@@ -603,15 +589,6 @@
         public void onPhoneStateChanged(String newState) {
             updateEmergencyCallButtonState();
         }
-
-        public void onTransportControlStateChanged(int state) {
-            // TODO: define what state means
-            if (state == 0) {
-                leaveWidgetMode();
-            } else {
-                enterWidgetMode();
-            }
-        }
     };
 
     private SimStateCallback mSimStateCallback = new SimStateCallback() {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 77f1932..2955de3 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -25,6 +25,7 @@
 import static android.os.BatteryManager.BATTERY_STATUS_FULL;
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
 import android.media.AudioManager;
+import android.media.IRemoteControlClient;
 import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Message;
@@ -93,8 +94,6 @@
     private static final int MSG_SIM_STATE_CHANGE = 304;
     private static final int MSG_RINGER_MODE_CHANGED = 305;
     private static final int MSG_PHONE_STATE_CHANGED = 306;
-    private static final int MSG_TRANSPORT_CONTROL_STATE_CHANGED = 307;
-
 
     /**
      * When we receive a
@@ -171,9 +170,6 @@
                     case MSG_PHONE_STATE_CHANGED:
                         handlePhoneStateChanged((String)msg.obj);
                         break;
-                    case MSG_TRANSPORT_CONTROL_STATE_CHANGED:
-                        handleTransportControlStateChanged(msg.arg1);
-                        break;
                 }
             }
         };
@@ -263,23 +259,10 @@
                     String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
                 }
-                // TODO
-                else if ("android.media.TRANSPORT_CONTROL_CHANGED".equals(action)) {
-                    int state = intent.getIntExtra("state", 0);
-                    mHandler.sendMessage(mHandler.obtainMessage(MSG_TRANSPORT_CONTROL_STATE_CHANGED,
-                            state));
-                }
             }
         }, filter);
     }
 
-    protected void handleTransportControlStateChanged(int state) {
-        if (DEBUG) Log.d(TAG, "handleTransportControlStateChanged()");
-        for (int i = 0; i < mInfoCallbacks.size(); i++) {
-            mInfoCallbacks.get(i).onTransportControlStateChanged(state);
-        }
-    }
-
     protected void handlePhoneStateChanged(String newState) {
         if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
         for (int i = 0; i < mInfoCallbacks.size(); i++) {
@@ -465,11 +448,6 @@
          */
         void onPhoneStateChanged(String newState);
 
-        /**
-         * Called when AudioService informs us of a change to the transport control client.
-         *
-         */
-        void onTransportControlStateChanged(int state);
     }
 
     /**
@@ -567,4 +545,5 @@
     public void reportFailedAttempt() {
         mFailedAttempts++;
     }
+
 }
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 5661116..a544167 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,29 @@
                 }
             }
 
-            // 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) {
+                // disable navigation status bar components if lock screen is up
+                flags |= StatusBarManager.DISABLE_NAVIGATION;
+                if (!mHidden) {
+                    // showing lockscreen exclusively (no activities in front of it)
+                    // disable clock and back button too
+                    flags |= StatusBarManager.DISABLE_BACK;
+                    flags |= StatusBarManager.DISABLE_CLOCK;
+                }
+                if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
+                    // showing secure lockscreen; disable expanding.
+                    flags |= StatusBarManager.DISABLE_EXPAND;
+                }
             }
-            mStatusBarManager.disable(enable ?
-                         StatusBarManager.DISABLE_NONE :
-                         ( StatusBarManager.DISABLE_EXPAND
-                         | StatusBarManager.DISABLE_NAVIGATION
-                         | StatusBarManager.DISABLE_CLOCK));
+
+            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/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 70772eb..9c14734 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -17,8 +17,11 @@
 package com.android.internal.policy.impl;
 
 import com.android.internal.R;
+import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockScreenWidgetCallback;
+import com.android.internal.widget.TransportControlView;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
@@ -31,6 +34,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
@@ -64,6 +68,8 @@
  */
 public class LockPatternKeyguardView extends KeyguardViewBase {
 
+    private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000;
+
     static final boolean DEBUG_CONFIGURATION = false;
 
     // time after launching EmergencyDialer before the screen goes blank.
@@ -84,6 +90,8 @@
     private boolean mScreenOn = false;
     private boolean mEnableFallback = false; // assume no fallback UI until we know better
 
+    private boolean mShowLockBeforeUnlock = false;
+
     /**
      * The current {@link KeyguardScreen} will use this to communicate back to us.
      */
@@ -148,7 +156,7 @@
      * Keeps track of what mode the current unlock screen is (cached from most recent computation in
      * {@link #getUnlockMode}).
      */
-    private UnlockMode mUnlockScreenMode;
+    private UnlockMode mUnlockScreenMode = UnlockMode.Unknown;
 
     private boolean mForgotPattern;
 
@@ -164,8 +172,6 @@
      */
     private final LockPatternUtils mLockPatternUtils;
 
-    private UnlockMode mCurrentUnlockMode = UnlockMode.Unknown;
-
     /**
      * The current configuration.
      */
@@ -173,7 +179,23 @@
 
     private Runnable mRecreateRunnable = new Runnable() {
         public void run() {
-            recreateScreens();
+            updateScreen(mMode, false);
+        }
+    };
+
+    private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() {
+        public void userActivity(View self) {
+            mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT);
+        }
+
+        public void requestShow(View view) {
+            if (DEBUG) Log.v(TAG, "View " + view + " requested show transports");
+            view.setVisibility(View.VISIBLE);
+        }
+
+        public void requestHide(View view) {
+            if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports");
+            view.setVisibility(View.GONE);
         }
     };
 
@@ -204,16 +226,11 @@
 
         mConfiguration = context.getResources().getConfiguration();
         mEnableFallback = false;
-
-        mRequiresSim =
-                TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
-
+        mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
         mUpdateMonitor = updateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mWindowController = controller;
 
-        mMode = getInitialMode();
-
         mKeyguardScreenCallback = new KeyguardScreenCallback() {
 
             public void goToLockScreen() {
@@ -224,7 +241,7 @@
                     mIsVerifyUnlockOnly = false;
                     getCallback().keyguardDone(false);
                 } else {
-                    updateScreen(Mode.LockScreen);
+                    updateScreen(Mode.LockScreen, false);
                 }
             }
 
@@ -240,14 +257,14 @@
                 if (!isSecure()) {
                     getCallback().keyguardDone(true);
                 } else {
-                    updateScreen(Mode.UnlockScreen);
+                    updateScreen(Mode.UnlockScreen, false);
                 }
             }
 
             public void forgotPattern(boolean isForgotten) {
                 if (mEnableFallback) {
                     mForgotPattern = isForgotten;
-                    updateScreen(Mode.UnlockScreen);
+                    updateScreen(Mode.UnlockScreen, false);
                 }
             }
 
@@ -260,7 +277,6 @@
             }
 
             public void recreateMe(Configuration config) {
-                mConfiguration = config;
                 removeCallbacks(mRecreateRunnable);
                 post(mRecreateRunnable);
             }
@@ -330,7 +346,7 @@
                         showAlmostAtAccountLoginDialog();
                     } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
                         mLockPatternUtils.setPermanentlyLocked(true);
-                        updateScreen(mMode);
+                        updateScreen(mMode, false);
                     }
                 } else {
                     final boolean showTimeout =
@@ -358,21 +374,8 @@
         setFocusableInTouchMode(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
 
-        // create both the lock and unlock screen so they are quickly available
-        // when the screen turns on
-        mLockScreen = createLockScreen();
-        addView(mLockScreen);
-        final UnlockMode unlockMode = getUnlockMode();
-        if (DEBUG) Log.d(TAG,
-            "LockPatternKeyguardView ctor: about to createUnlockScreenFor; mEnableFallback="
-            + mEnableFallback);
-        mUnlockScreen = createUnlockScreenFor(unlockMode);
-        mUnlockScreenMode = unlockMode;
-
+        updateScreen(getInitialMode(), false);
         maybeEnableFallback(context);
-
-        addView(mUnlockScreen);
-        updateScreen(mMode);
     }
 
     private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
@@ -449,7 +452,7 @@
     public void reset() {
         mIsVerifyUnlockOnly = false;
         mForgotPattern = false;
-        updateScreen(getInitialMode());
+        updateScreen(getInitialMode(), false);
     }
 
     @Override
@@ -457,7 +460,7 @@
         mScreenOn = false;
         mForgotPattern = false;
         if (mMode == Mode.LockScreen) {
-           ((KeyguardScreen) mLockScreen).onPause();
+            ((KeyguardScreen) mLockScreen).onPause();
         } else {
             ((KeyguardScreen) mUnlockScreen).onPause();
         }
@@ -467,50 +470,54 @@
     public void onScreenTurnedOn() {
         mScreenOn = true;
         if (mMode == Mode.LockScreen) {
-           ((KeyguardScreen) mLockScreen).onResume();
+            ((KeyguardScreen) mLockScreen).onResume();
         } else {
             ((KeyguardScreen) mUnlockScreen).onResume();
         }
     }
 
     private void recreateLockScreen() {
-        if (mLockScreen.getVisibility() == View.VISIBLE) {
-            ((KeyguardScreen) mLockScreen).onPause();
+        if (mLockScreen != null) {
+            if (mLockScreen.getVisibility() == View.VISIBLE) {
+                ((KeyguardScreen) mLockScreen).onPause();
+            }
+            ((KeyguardScreen) mLockScreen).cleanUp();
+            removeView(mLockScreen);
         }
-        ((KeyguardScreen) mLockScreen).cleanUp();
-        removeView(mLockScreen);
 
         mLockScreen = createLockScreen();
         mLockScreen.setVisibility(View.INVISIBLE);
         addView(mLockScreen);
     }
 
-    private void recreateUnlockScreen() {
-        if (mUnlockScreen.getVisibility() == View.VISIBLE) {
-            ((KeyguardScreen) mUnlockScreen).onPause();
+    private void recreateUnlockScreen(UnlockMode unlockMode) {
+        if (mUnlockScreen != null) {
+            if (mUnlockScreen.getVisibility() == View.VISIBLE) {
+                ((KeyguardScreen) mUnlockScreen).onPause();
+            }
+            ((KeyguardScreen) mUnlockScreen).cleanUp();
+            removeView(mUnlockScreen);
         }
-        ((KeyguardScreen) mUnlockScreen).cleanUp();
-        removeView(mUnlockScreen);
 
-        final UnlockMode unlockMode = getUnlockMode();
         mUnlockScreen = createUnlockScreenFor(unlockMode);
         mUnlockScreen.setVisibility(View.INVISIBLE);
-        mUnlockScreenMode = unlockMode;
         addView(mUnlockScreen);
     }
 
-    private void recreateScreens() {
-        recreateLockScreen();
-        recreateUnlockScreen();
-        updateScreen(mMode);
-    }
-
     @Override
     protected void onDetachedFromWindow() {
         removeCallbacks(mRecreateRunnable);
         super.onDetachedFromWindow();
     }
 
+    protected void onConfigurationChanged(Configuration newConfig) {
+        Resources resources = getResources();
+        mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
+        mConfiguration = newConfig;
+        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed");
+        updateScreen(mMode, true /* force */);
+    }
+
     @Override
     protected boolean dispatchHoverEvent(MotionEvent event) {
         // Do not let the screen to get locked while the user is disabled and touch
@@ -529,7 +536,7 @@
         if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
                 && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) {
             if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
-            updateScreen(Mode.UnlockScreen);
+            updateScreen(Mode.UnlockScreen, false);
             getCallback().pokeWakelock();
         } else {
             if (DEBUG) Log.d(TAG, "poking wake lock immediately");
@@ -542,24 +549,31 @@
         if (!isSecure()) {
             // non-secure keyguard screens are successfull by default
             getCallback().keyguardDone(true);
-        } else if (mUnlockScreenMode != UnlockMode.Pattern) {
-            // can only verify unlock when in pattern mode
+        } else if (mUnlockScreenMode != UnlockMode.Pattern
+                && mUnlockScreenMode != UnlockMode.Password) {
+            // can only verify unlock when in pattern/password mode
             getCallback().keyguardDone(false);
         } else {
             // otherwise, go to the unlock screen, see if they can verify it
             mIsVerifyUnlockOnly = true;
-            updateScreen(Mode.UnlockScreen);
+            updateScreen(Mode.UnlockScreen, false);
         }
     }
 
     @Override
     public void cleanUp() {
-        ((KeyguardScreen) mLockScreen).onPause();
-        ((KeyguardScreen) mLockScreen).cleanUp();
-        this.removeView(mLockScreen);
-        ((KeyguardScreen) mUnlockScreen).onPause();
-        ((KeyguardScreen) mUnlockScreen).cleanUp();
-        this.removeView(mUnlockScreen);
+        if (mLockScreen != null) {
+            ((KeyguardScreen) mLockScreen).onPause();
+            ((KeyguardScreen) mLockScreen).cleanUp();
+            this.removeView(mLockScreen);
+            mLockScreen = null;
+        }
+        if (mUnlockScreen != null) {
+            ((KeyguardScreen) mUnlockScreen).onPause();
+            ((KeyguardScreen) mUnlockScreen).cleanUp();
+            this.removeView(mUnlockScreen);
+            mUnlockScreen = null;
+        }
     }
 
     private boolean isSecure() {
@@ -587,19 +601,30 @@
         return secure;
     }
 
-    private void updateScreen(final Mode mode) {
+    private void updateScreen(Mode mode, boolean force) {
 
         if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
                 + " last mode=" + mMode, new RuntimeException());
 
         mMode = mode;
 
-        // Re-create the unlock screen if necessary. This is primarily required to properly handle
-        // SIM state changes. This typically happens when this method is called by reset()
-        if (mode == Mode.UnlockScreen && mCurrentUnlockMode != getUnlockMode()) {
-            recreateUnlockScreen();
+        // Re-create the lock screen if necessary
+        if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
+            if (force || mLockScreen == null) {
+                recreateLockScreen();
+            }
         }
 
+        // Re-create the unlock screen if necessary. This is primarily required to properly handle
+        // SIM state changes. This typically happens when this method is called by reset()
+        if (mode == Mode.UnlockScreen) {
+            final UnlockMode unlockMode = getUnlockMode();
+            if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
+                recreateUnlockScreen(unlockMode);
+            }
+        }
+
+        // visibleScreen should never be null
         final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
         final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
 
@@ -613,7 +638,7 @@
         }
 
         if (mScreenOn) {
-            if (goneScreen.getVisibility() == View.VISIBLE) {
+            if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) {
                 ((KeyguardScreen) goneScreen).onPause();
             }
             if (visibleScreen.getVisibility() != View.VISIBLE) {
@@ -621,7 +646,9 @@
             }
         }
 
-        goneScreen.setVisibility(View.GONE);
+        if (goneScreen != null) {
+            goneScreen.setVisibility(View.GONE);
+        }
         visibleScreen.setVisibility(View.VISIBLE);
         requestLayout();
 
@@ -632,12 +659,14 @@
     }
 
     View createLockScreen() {
-        return new LockScreen(
+        View lockView = new LockScreen(
                 mContext,
                 mConfiguration,
                 mLockPatternUtils,
                 mUpdateMonitor,
                 mKeyguardScreenCallback);
+        initializeTransportControlView(lockView);
+        return lockView;
     }
 
     View createUnlockScreenFor(UnlockMode unlockMode) {
@@ -691,7 +720,7 @@
                 // regular pattern unlock UI, regardless of the value of
                 // mUnlockScreenMode or whether or not we're in the
                 // "permanently locked" state.)
-                unlockView = createUnlockScreenFor(UnlockMode.Pattern);
+                return createUnlockScreenFor(UnlockMode.Pattern);
             }
         } else if (unlockMode == UnlockMode.Password) {
             unlockView = new PasswordUnlockScreen(
@@ -703,10 +732,22 @@
         } else {
             throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
         }
-        mCurrentUnlockMode = unlockMode;
+        initializeTransportControlView(unlockView);
+        mUnlockScreenMode = unlockMode;
         return unlockView;
     }
 
+    private void initializeTransportControlView(View view) {
+        com.android.internal.widget.TransportControlView tcv =
+                (TransportControlView) view.findViewById(R.id.transport);
+        if (tcv == null) {
+            if (DEBUG) Log.w(TAG, "Couldn't find transport control widget");
+        } else {
+            tcv.setVisibility(View.GONE); // hide tcv until we get the callback below to show it.
+            tcv.setCallback(mWidgetCallback);
+        }
+    }
+
     /**
      * Given the current state of things, what should be the initial mode of
      * the lock screen (lock or unlock).
@@ -718,16 +759,10 @@
                         !mLockPatternUtils.isPukUnlockScreenEnable())) {
             return Mode.LockScreen;
         } else {
-            // Show LockScreen first for any screen other than Pattern unlock.
-            final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
-                    == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
-
-            boolean showLockBeforeUnlock = getResources()
-                    .getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
-            if (isSecure() && (usingLockPattern || !showLockBeforeUnlock)) {
-                return Mode.UnlockScreen;
-            } else {
+            if (!isSecure() || mShowLockBeforeUnlock) {
                 return Mode.LockScreen;
+            } else {
+                return Mode.UnlockScreen;
             }
         }
     }
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index ce3bc74..2f2d3b7 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -347,7 +347,8 @@
 
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
         // Check if this was the result of hitting the enter key
-        if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
+        if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
+                || actionId == EditorInfo.IME_ACTION_NEXT) {
             verifyPasswordAndUnlock();
             return true;
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index dfd1b00..2f5deba 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -38,6 +38,7 @@
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -266,6 +267,7 @@
     int mLidOpen = LID_ABSENT;
 
     boolean mSystemReady;
+    boolean mSystemBooted;
     boolean mHdmiPlugged;
     int mUiMode = Configuration.UI_MODE_TYPE_NORMAL;
     int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -900,6 +902,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) {
@@ -2262,7 +2265,7 @@
             // incorrectly think it does cover it when it doesn't.  We'll revisit
             // this later when we re-do the phone status bar.
             if (mStatusBar != null && mStatusBar.isVisibleLw()) {
-                Rect rect = new Rect(mStatusBar.getShownFrameLw());
+                RectF rect = new RectF(mStatusBar.getShownFrameLw());
                 for (int i=mStatusBarPanels.size()-1; i>=0; i--) {
                     WindowState w = mStatusBarPanels.get(i);
                     if (w.isVisibleLw()) {
@@ -2495,6 +2498,11 @@
                                         mKeyguardMediator.isShowingAndNotHidden() :
                                         mKeyguardMediator.isShowing());
 
+        if (!mSystemBooted) {
+            // If we have not yet booted, don't let key events do anything.
+            return 0;
+        }
+
         if (false) {
             Log.d(TAG, "interceptKeyTq keycode=" + keyCode
                   + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);
@@ -3098,6 +3106,13 @@
         }
     }
 
+    /** {@inheritDoc} */
+    public void systemBooted() {
+        synchronized (mLock) {
+            mSystemBooted = true;
+        }
+    }
+
     ProgressDialog mBootMsgDialog = null;
 
     /** {@inheritDoc} */
@@ -3490,7 +3505,8 @@
 
     public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
-                pw.print(" mSystemRead="); pw.println(mSystemReady);
+                pw.print(" mSystemReady="); pw.print(mSystemReady);
+                pw.print(" mSystemBooted="); pw.println(mSystemBooted);
         pw.print(prefix); pw.print("mLidOpen="); pw.print(mLidOpen);
                 pw.print(" mLidOpenRotation="); pw.print(mLidOpenRotation);
                 pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 744fa50..94efa74 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -151,7 +151,7 @@
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
         mPrimaryHardwareDev(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
-        mBtNrec(false)
+        mBtNrecIsOff(false)
 {
 }
 
@@ -751,15 +751,15 @@
         String8 value;
         if (param.get(String8(AUDIO_PARAMETER_KEY_BT_NREC), value) == NO_ERROR) {
             Mutex::Autolock _l(mLock);
-            bool btNrec = (value == AUDIO_PARAMETER_VALUE_ON);
-            if (mBtNrec != btNrec) {
+            bool btNrecIsOff = (value == AUDIO_PARAMETER_VALUE_OFF);
+            if (mBtNrecIsOff != btNrecIsOff) {
                 for (size_t i = 0; i < mRecordThreads.size(); i++) {
                     sp<RecordThread> thread = mRecordThreads.valueAt(i);
                     RecordThread::RecordTrack *track = thread->track();
                     if (track != NULL) {
                         audio_devices_t device = (audio_devices_t)(
                                 thread->device() & AUDIO_DEVICE_IN_ALL);
-                        bool suspend = audio_is_bluetooth_sco_device(device) && btNrec;
+                        bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
                         thread->setEffectSuspended(FX_IID_AEC,
                                                    suspend,
                                                    track->sessionId());
@@ -768,7 +768,7 @@
                                                    track->sessionId());
                     }
                 }
-                mBtNrec = btNrec;
+                mBtNrecIsOff = btNrecIsOff;
             }
         }
         return final_result;
@@ -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()
@@ -4394,7 +4411,7 @@
         mTrack = track.get();
         // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
         bool suspend = audio_is_bluetooth_sco_device(
-                (audio_devices_t)(mDevice & AUDIO_DEVICE_IN_ALL)) && mAudioFlinger->btNrec();
+                (audio_devices_t)(mDevice & AUDIO_DEVICE_IN_ALL)) && mAudioFlinger->btNrecIsOff();
         setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
         setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
     }
@@ -4619,7 +4636,7 @@
                 // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
                 if (mTrack != NULL) {
                     bool suspend = audio_is_bluetooth_sco_device(
-                            (audio_devices_t)value) && mAudioFlinger->btNrec();
+                            (audio_devices_t)value) && mAudioFlinger->btNrecIsOff();
                     setEffectSuspended_l(FX_IID_AEC, suspend, mTrack->sessionId());
                     setEffectSuspended_l(FX_IID_NS, suspend, mTrack->sessionId());
                 }
@@ -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 1141f6c..2e05593 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -210,7 +210,7 @@
 
                 uint32_t    getMode() { return mMode; }
 
-                bool        btNrec() { return mBtNrec; }
+                bool        btNrecIsOff() { return mBtNrecIsOff; }
 
 private:
                             AudioFlinger();
@@ -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:
@@ -1389,7 +1393,7 @@
                 DefaultKeyedVector< pid_t, sp<NotificationClient> >    mNotificationClients;
                 volatile int32_t                    mNextUniqueId;
                 uint32_t                            mMode;
-                bool                                mBtNrec;
+                bool                                mBtNrecIsOff;
 
                 Vector<AudioSessionRef*> mAudioSessionRefs;
 };
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e193be0..b178fd9 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -769,11 +769,6 @@
     status_t result = checkPidAndHardware();
     if (result != NO_ERROR) return result;
 
-    if (mHardware->recordingEnabled()) {
-        LOGE("Cannot take picture during recording.");
-        return INVALID_OPERATION;
-    }
-
     if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
         (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
         LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
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..80ee28e 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -78,6 +78,40 @@
     return value ? "true" : "false";
 }
 
+// --- Global Functions ---
+
+uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
+    // Touch devices get dibs on touch-related axes.
+    if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
+        switch (axis) {
+        case ABS_X:
+        case ABS_Y:
+        case ABS_PRESSURE:
+        case ABS_TOOL_WIDTH:
+        case ABS_DISTANCE:
+        case ABS_TILT_X:
+        case ABS_TILT_Y:
+        case ABS_MT_SLOT:
+        case ABS_MT_TOUCH_MAJOR:
+        case ABS_MT_TOUCH_MINOR:
+        case ABS_MT_WIDTH_MAJOR:
+        case ABS_MT_WIDTH_MINOR:
+        case ABS_MT_ORIENTATION:
+        case ABS_MT_POSITION_X:
+        case ABS_MT_POSITION_Y:
+        case ABS_MT_TOOL_TYPE:
+        case ABS_MT_BLOB_ID:
+        case ABS_MT_TRACKING_ID:
+        case ABS_MT_PRESSURE:
+        case ABS_MT_DISTANCE:
+            return INPUT_DEVICE_CLASS_TOUCH;
+        }
+    }
+
+    // Joystick devices get the rest.
+    return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK;
+}
+
 // --- EventHub::Device ---
 
 EventHub::Device::Device(int fd, int32_t id, const String8& path,
@@ -936,13 +970,17 @@
     }
 
     // See if this device is a joystick.
-    // Ignore touchscreens because they use the same absolute axes for other purposes.
     // Assumes that joysticks always have gamepad buttons in order to distinguish them
     // from other devices such as accelerometers that also have absolute axes.
-    if (haveGamepadButtons
-            && !(device->classes & INPUT_DEVICE_CLASS_TOUCH)
-            && containsNonZeroByte(device->absBitmask, 0, sizeof_bit_array(ABS_MAX + 1))) {
-        device->classes |= INPUT_DEVICE_CLASS_JOYSTICK;
+    if (haveGamepadButtons) {
+        uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
+        for (int i = 0; i <= ABS_MAX; i++) {
+            if (test_bit(i, device->absBitmask)
+                    && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+                device->classes = assumedClasses;
+                break;
+            }
+        }
     }
 
     // Check whether this device has switches.
@@ -1094,9 +1132,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/EventHub.h b/services/input/EventHub.h
index fae5d4f..d37549a 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -112,6 +112,12 @@
 };
 
 /*
+ * Gets the class that owns an axis, in cases where multiple classes might claim
+ * the same axis for different purposes.
+ */
+extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses);
+
+/*
  * Grand Central Station for events.
  *
  * The event hub aggregates input events received across all known input
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/InputReader.cpp b/services/input/InputReader.cpp
index bfcf8e0..88c19a4 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -390,7 +390,7 @@
 
 InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
         const String8& name, uint32_t classes) {
-    InputDevice* device = new InputDevice(&mContext, deviceId, name);
+    InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);
 
     // External devices.
     if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
@@ -842,9 +842,10 @@
 
 // --- InputDevice ---
 
-InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
-        mContext(context), mId(id), mName(name), mSources(0),
-        mIsExternal(false), mDropUntilNextSync(false) {
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name,
+        uint32_t classes) :
+        mContext(context), mId(id), mName(name), mClasses(classes),
+        mSources(0), mIsExternal(false), mDropUntilNextSync(false) {
 }
 
 InputDevice::~InputDevice() {
@@ -2491,7 +2492,8 @@
 
     bool resetNeeded = false;
     if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
-            | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT))) {
+            | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
+            | InputReaderConfiguration::CHANGE_SHOW_TOUCHES))) {
         // Configure device sources, surface dimensions, orientation and
         // scaling factors.
         configureSurface(when, &resetNeeded);
@@ -2523,17 +2525,17 @@
         }
     }
 
-    if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)
+    if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
+        // The device is a touch screen.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
+        // The device is a pointing device like a track pad.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+    } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)
             || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
         // The device is a cursor device with a touch pad attached.
         // By default don't use the touch pad to move the pointer.
         mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
-    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
-        // The device is a pointing device like a track pad.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
-    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
-        // The device is a touch screen.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
     } else {
         // The device is a touch pad of unknown purpose.
         mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
@@ -2681,18 +2683,19 @@
     bool deviceModeChanged;
     if (mDeviceMode != oldDeviceMode) {
         deviceModeChanged = true;
-
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
-            if (mPointerController == NULL) {
-                mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-            }
-        } else {
-            mPointerController.clear();
-        }
-
         mOrientedRanges.clear();
     }
 
+    // Create pointer controller if needed.
+    if (mDeviceMode == DEVICE_MODE_POINTER ||
+            (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
+        if (mPointerController == NULL) {
+            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+        }
+    } else {
+        mPointerController.clear();
+    }
+
     bool orientationChanged = mSurfaceOrientation != orientation;
     if (orientationChanged) {
         mSurfaceOrientation = orientation;
@@ -3380,7 +3383,7 @@
         cookPointerData();
 
         // Dispatch the touches either directly or by translation through a pointer on screen.
-        if (mPointerController != NULL) {
+        if (mDeviceMode == DEVICE_MODE_POINTER) {
             for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); ) {
                 uint32_t id = idBits.clearFirstMarkedBit();
                 const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
@@ -3418,6 +3421,17 @@
 
             dispatchPointerUsage(when, policyFlags, pointerUsage);
         } else {
+            if (mDeviceMode == DEVICE_MODE_DIRECT
+                    && mConfig.showTouches && mPointerController != NULL) {
+                mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+                mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+
+                mPointerController->setButtonState(mCurrentButtonState);
+                mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
+                        mCurrentCookedPointerData.idToIndex,
+                        mCurrentCookedPointerData.touchingIdBits);
+            }
+
             dispatchHoverExit(when, policyFlags);
             dispatchTouches(when, policyFlags);
             dispatchHoverEnterAndMove(when, policyFlags);
@@ -3442,7 +3456,7 @@
 }
 
 void TouchInputMapper::timeoutExpired(nsecs_t when) {
-    if (mPointerController != NULL) {
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
         if (mPointerUsage == POINTER_USAGE_GESTURES) {
             dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
         }
@@ -5746,6 +5760,11 @@
     if (!changes) { // first time only
         // Collect all axes.
         for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
+            if (!(getAbsAxisUsage(abs, getDevice()->getClasses())
+                    & INPUT_DEVICE_CLASS_JOYSTICK)) {
+                continue; // axis must be claimed by a different device
+            }
+
             RawAbsoluteAxisInfo rawAxisInfo;
             getAbsoluteAxisInfo(abs, &rawAxisInfo);
             if (rawAxisInfo.valid) {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index bad96df..a122c97 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -56,6 +56,9 @@
         // The display size or orientation changed.
         CHANGE_DISPLAY_INFO = 1 << 2,
 
+        // The visible touches option changed.
+        CHANGE_SHOW_TOUCHES = 1 << 3,
+
         // All devices must be reopened.
         CHANGE_MUST_REOPEN = 1 << 31,
     };
@@ -140,6 +143,9 @@
     // will cover this portion of the display diagonal.
     float pointerGestureZoomSpeedRatio;
 
+    // True to show the location of touches on the touch screen as spots.
+    bool showTouches;
+
     InputReaderConfiguration() :
             virtualKeyQuietTime(0),
             pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
@@ -155,7 +161,8 @@
             pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
             pointerGestureSwipeMaxWidthRatio(0.25f),
             pointerGestureMovementSpeedRatio(0.8f),
-            pointerGestureZoomSpeedRatio(0.3f) { }
+            pointerGestureZoomSpeedRatio(0.3f),
+            showTouches(false) { }
 
     bool getDisplayInfo(int32_t displayId, bool external,
             int32_t* width, int32_t* height, int32_t* orientation) const;
@@ -423,12 +430,13 @@
 /* Represents the state of a single input device. */
 class InputDevice {
 public:
-    InputDevice(InputReaderContext* context, int32_t id, const String8& name);
+    InputDevice(InputReaderContext* context, int32_t id, const String8& name, uint32_t classes);
     ~InputDevice();
 
     inline InputReaderContext* getContext() { return mContext; }
     inline int32_t getId() { return mId; }
     inline const String8& getName() { return mName; }
+    inline uint32_t getClasses() { return mClasses; }
     inline uint32_t getSources() { return mSources; }
 
     inline bool isExternal() { return mIsExternal; }
@@ -476,10 +484,11 @@
 private:
     InputReaderContext* mContext;
     int32_t mId;
+    String8 mName;
+    uint32_t mClasses;
 
     Vector<InputMapper*> mMappers;
 
-    String8 mName;
     uint32_t mSources;
     bool mIsExternal;
     bool mDropUntilNextSync;
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/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 32f948b..a086208 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -852,8 +852,8 @@
         mNextDevice = device;
     }
 
-    InputDevice* newDevice(int32_t deviceId, const String8& name) {
-        return new InputDevice(&mContext, deviceId, name);
+    InputDevice* newDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+        return new InputDevice(&mContext, deviceId, name, classes);
     }
 
 protected:
@@ -912,7 +912,7 @@
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId,
             const String8& name, uint32_t classes, uint32_t sources,
             const PropertyMap* configuration) {
-        InputDevice* device = mReader->newDevice(deviceId, name);
+        InputDevice* device = mReader->newDevice(deviceId, name, classes);
         FakeInputMapper* mapper = new FakeInputMapper(device, sources);
         device->addMapper(mapper);
         mReader->setNextDevice(device);
@@ -1211,6 +1211,7 @@
 protected:
     static const char* DEVICE_NAME;
     static const int32_t DEVICE_ID;
+    static const uint32_t DEVICE_CLASSES;
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -1226,7 +1227,7 @@
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
 
         mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
+        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME), DEVICE_CLASSES);
     }
 
     virtual void TearDown() {
@@ -1241,10 +1242,13 @@
 
 const char* InputDeviceTest::DEVICE_NAME = "device";
 const int32_t InputDeviceTest::DEVICE_ID = 1;
+const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
+        | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
     ASSERT_STREQ(DEVICE_NAME, mDevice->getName());
+    ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
 }
 
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
@@ -1390,6 +1394,7 @@
 protected:
     static const char* DEVICE_NAME;
     static const int32_t DEVICE_ID;
+    static const uint32_t DEVICE_CLASSES;
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -1402,7 +1407,7 @@
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new FakeInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
+        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME), DEVICE_CLASSES);
 
         mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
     }
@@ -1483,6 +1488,7 @@
 
 const char* InputMapperTest::DEVICE_NAME = "device";
 const int32_t InputMapperTest::DEVICE_ID = 1;
+const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
 
 
 // --- SwitchInputMapperTest ---
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/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index bfca851..2348d76 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -17,6 +17,8 @@
 package com.android.server;
 
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -701,6 +703,12 @@
         return result.toArray(new NetworkInfo[result.size()]);
     }
 
+    @Override
+    public boolean isNetworkSupported(int networkType) {
+        enforceAccessPermission();
+        return (isNetworkTypeValid(networkType) && (mNetTrackers[networkType] != null));
+    }
+
     /**
      * Return LinkProperties for the active (i.e., connected) default
      * network interface.  It is assumed that at most one default network
@@ -1418,6 +1426,9 @@
         // do this before we broadcast the change
         handleConnectivityChange(prevNetType, doReset);
 
+        final Intent immediateIntent = new Intent(intent);
+        immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
+        sendStickyBroadcast(immediateIntent);
         sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
         /*
          * If the failover network is already connected, then immediately send
@@ -1476,11 +1487,13 @@
     }
 
     private void sendConnectedBroadcast(NetworkInfo info) {
-        sendGeneralBroadcast(info, ConnectivityManager.CONNECTIVITY_ACTION);
+        sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
+        sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
     private void sendConnectedBroadcastDelayed(NetworkInfo info, int delayMs) {
-        sendGeneralBroadcastDelayed(info, ConnectivityManager.CONNECTIVITY_ACTION, delayMs);
+        sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
+        sendGeneralBroadcastDelayed(info, CONNECTIVITY_ACTION, delayMs);
     }
 
     private void sendInetConditionBroadcast(NetworkInfo info) {
@@ -1559,6 +1572,10 @@
         }
 
         intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
+
+        final Intent immediateIntent = new Intent(intent);
+        immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
+        sendStickyBroadcast(immediateIntent);
         sendStickyBroadcast(intent);
         /*
          * If the failover network is already connected, then immediately send
@@ -1576,8 +1593,7 @@
             }
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             if (DBG) {
-                log("sendStickyBroadcast: NetworkInfo=" +
-                    intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
+                log("sendStickyBroadcast: action=" + intent.getAction());
             }
 
             mContext.sendStickyBroadcast(intent);
@@ -1588,7 +1604,10 @@
         if (delayMs <= 0) {
             sendStickyBroadcast(intent);
         } else {
-            if (DBG) log("sendStickyBroadcastDelayed: delayMs=" + delayMs + " intent=" + intent);
+            if (DBG) {
+                log("sendStickyBroadcastDelayed: delayMs=" + delayMs + ", action="
+                        + intent.getAction());
+            }
             mHandler.sendMessageDelayed(mHandler.obtainMessage(
                     EVENT_SEND_STICKY_BROADCAST_INTENT, intent), delayMs);
         }
@@ -2281,7 +2300,6 @@
                 case EVENT_SEND_STICKY_BROADCAST_INTENT:
                 {
                     Intent intent = (Intent)msg.obj;
-                    log("EVENT_SEND_STICKY_BROADCAST_INTENT: sendStickyBroadcast intent=" + intent);
                     sendStickyBroadcast(intent);
                     break;
                 }
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/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d0e8b5e..2714fc5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -350,7 +350,6 @@
                 Slog.i(TAG, "Wi-Fi Service");
                 wifi = new WifiService(context);
                 ServiceManager.addService(Context.WIFI_SERVICE, wifi);
-                wifi.checkAndStartWifi();
             } catch (Throwable e) {
                 reportWtf("starting Wi-Fi Service", e);
             }
@@ -361,6 +360,7 @@
                 ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
                 networkStats.bindConnectivityManager(connectivity);
                 networkPolicy.bindConnectivityManager(connectivity);
+                wifi.checkAndStartWifi();
                 wifiP2p.connectivityServiceReady();
             } catch (Throwable e) {
                 reportWtf("starting Connectivity Service", e);
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/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 77f53c2..8af90ff 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -26,7 +26,7 @@
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -51,6 +51,7 @@
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readBooleanAttribute;
 import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readIntAttribute;
 import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readLongAttribute;
@@ -60,7 +61,6 @@
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 
 import android.app.IActivityManager;
 import android.app.INotificationManager;
@@ -321,7 +321,7 @@
         mContext.registerReceiver(mScreenReceiver, screenFilter, null, mHandler);
 
         // watch for network interfaces to be claimed
-        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
+        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
         mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
 
         // listen for package/uid changes to update policy
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index bb0a0d1..e0dc96f 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -24,7 +24,7 @@
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
@@ -239,7 +239,7 @@
         }
 
         // watch for network interfaces to be claimed
-        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
+        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
         mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
 
         // listen for periodic polling events
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index b8797d18..bfb244b 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -78,8 +78,6 @@
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.FileUtils;
-import android.os.FileUtils.FileStatus;
-import android.os.Debug;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -706,6 +704,7 @@
                         Runtime.getRuntime().gc();
                     }
                     if (msg.obj != null) {
+                        @SuppressWarnings("unchecked")
                         Set<SdInstallArgs> args = (Set<SdInstallArgs>) msg.obj;
                         if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers");
                         // Unload containers
@@ -3039,10 +3038,6 @@
             return null;
         }
         mScanningPath = scanFile;
-        if (pkg == null) {
-            mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
-            return null;
-        }
 
         if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java
index 36f5dcb..10e294b 100644
--- a/services/java/com/android/server/wm/BlackFrame.java
+++ b/services/java/com/android/server/wm/BlackFrame.java
@@ -51,8 +51,8 @@
             mTmpMatrix.setTranslate(left, top);
             mTmpMatrix.postConcat(matrix);
             mTmpMatrix.getValues(mTmpFloats);
-            surface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
-                    (int)mTmpFloats[Matrix.MTRANS_Y]);
+            surface.setPosition(mTmpFloats[Matrix.MTRANS_X],
+                    mTmpFloats[Matrix.MTRANS_Y]);
             surface.setMatrix(
                     mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
                     mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index b37d1c2..dd440bf 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;
         }
     }
 
@@ -270,7 +273,7 @@
         if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
         Surface.openTransaction();
         try {
-            mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY));
+            mSurface.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DRAG "
                     + mSurface + ": pos=(" +
                     (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
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/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index c8b18c8..60333a3 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -94,6 +94,7 @@
     private static native boolean nativeTransferTouchFocus(InputChannel fromChannel,
             InputChannel toChannel);
     private static native void nativeSetPointerSpeed(int speed);
+    private static native void nativeSetShowTouches(boolean enabled);
     private static native String nativeDump();
     private static native void nativeMonitor();
     
@@ -147,7 +148,10 @@
         nativeStart();
 
         registerPointerSpeedSettingObserver();
+        registerShowTouchesSettingObserver();
+
         updatePointerSpeedFromSettings();
+        updateShowTouchesFromSettings();
     }
     
     public void setDisplaySize(int displayId, int width, int height,
@@ -454,6 +458,32 @@
         return speed;
     }
 
+    public void updateShowTouchesFromSettings() {
+        int setting = getShowTouchesSetting(0);
+        nativeSetShowTouches(setting != 0);
+    }
+
+    private void registerShowTouchesSettingObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
+                new ContentObserver(mWindowManagerService.mH) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateShowTouchesFromSettings();
+                    }
+                });
+    }
+
+    private int getShowTouchesSetting(int defaultValue) {
+        int result = defaultValue;
+        try {
+            result = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.SHOW_TOUCHES);
+        } catch (SettingNotFoundException snfe) {
+        }
+        return result;
+    }
+
     public void dump(PrintWriter pw) {
         String dumpStr = nativeDump();
         if (dumpStr != null) {
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/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 16af151..3c475a0 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -148,8 +148,8 @@
     void setSnapshotTransform(Matrix matrix, float alpha) {
         if (mSurface != null) {
             matrix.getValues(mTmpFloats);
-            mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
-                    (int)mTmpFloats[Matrix.MTRANS_Y]);
+            mSurface.setPosition(mTmpFloats[Matrix.MTRANS_X],
+                    mTmpFloats[Matrix.MTRANS_Y]);
             mSurface.setMatrix(
                     mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
                     mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 50b251f..2e0c9ab 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -281,8 +281,8 @@
             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
             Surface.openTransaction();
             try {
-                surface.setPosition((int)(touchX - thumbCenterX),
-                        (int)(touchY - thumbCenterY));
+                surface.setPosition(touchX - thumbCenterX,
+                        touchY - thumbCenterY);
                 surface.setAlpha(.7071f);
                 surface.setLayer(mService.mDragState.getDragLayerLw());
                 surface.show();
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..755a268 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);
@@ -2497,6 +2498,10 @@
             int attrChanges = 0;
             int flagChanges = 0;
             if (attrs != null) {
+                if (win.mAttrs.type != attrs.type) {
+                    throw new IllegalArgumentException(
+                            "Window type can not be changed after the window is added.");
+                }
                 flagChanges = win.mAttrs.flags ^= attrs.flags;
                 attrChanges = win.mAttrs.copyFrom(attrs);
             }
@@ -2533,6 +2538,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;
@@ -4701,6 +4707,8 @@
             mH.sendMessageDelayed(msg, 30*1000);
         }
 
+        mPolicy.systemBooted();
+
         performEnableScreen();
     }
 
@@ -7919,17 +7927,32 @@
                 if (mWindowDetachedWallpaper != windowDetachedWallpaper) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Detached wallpaper changed from " + mWindowDetachedWallpaper
-                            + windowDetachedWallpaper);
+                            + " to " + windowDetachedWallpaper);
                     mWindowDetachedWallpaper = windowDetachedWallpaper;
                     wallpaperMayChange = true;
                 }
 
                 if (windowAnimationBackgroundColor != 0) {
+                    // If the 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();
@@ -8438,8 +8461,8 @@
                                 + Integer.toHexString(diff));
                     }
                     win.mConfiguration = mCurConfiguration;
-                    win.mClient.resized(win.mSurfaceW, win.mSurfaceH, win.mLastContentInsets,
-                            win.mLastVisibleInsets, win.mDrawPending,
+                    win.mClient.resized((int)win.mSurfaceW, (int)win.mSurfaceH,
+                            win.mLastContentInsets, win.mLastVisibleInsets, win.mDrawPending,
                             configChanged ? win.mConfiguration : null);
                     win.mContentInsetsChanged = false;
                     win.mVisibleInsetsChanged = false;
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index cdd0047..bb36d3a7 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -30,6 +30,7 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -110,7 +111,7 @@
      * are in the screen's coordinate space (WITH the compatibility scale
      * applied).
      */
-    final Rect mShownFrame = new Rect();
+    final RectF mShownFrame = new RectF();
 
     /**
      * Set when we have changed the size of the surface, to know that
@@ -267,7 +268,7 @@
 
     // For debugging, this is the last information given to the surface flinger.
     boolean mSurfaceShown;
-    int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
+    float mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
     int mSurfaceLayer;
     float mSurfaceAlpha;
     
@@ -518,7 +519,7 @@
         return mFrame;
     }
 
-    public Rect getShownFrameLw() {
+    public RectF getShownFrameLw() {
         return mShownFrame;
     }
 
@@ -1128,8 +1129,8 @@
             mDtDx = tmpFloats[Matrix.MSKEW_Y];
             mDsDy = tmpFloats[Matrix.MSKEW_X];
             mDtDy = tmpFloats[Matrix.MSCALE_Y];
-            int x = (int)tmpFloats[Matrix.MTRANS_X];
-            int y = (int)tmpFloats[Matrix.MTRANS_Y];
+            float x = tmpFloats[Matrix.MTRANS_X];
+            float y = tmpFloats[Matrix.MTRANS_Y];
             int w = frame.width();
             int h = frame.height();
             mShownFrame.set(x, y, x+w, y+h);
@@ -1236,7 +1237,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 +1374,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 +1391,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 f2a0a71..f976301 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -179,6 +179,7 @@
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiVisibility(int32_t visibility);
     void setPointerSpeed(int32_t speed);
+    void setShowTouches(bool enabled);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -233,6 +234,9 @@
         // True if pointer gestures are enabled.
         bool pointerGesturesEnabled;
 
+        // Show touches feature enable/disable.
+        bool showTouches;
+
         // Sprite controller singleton, created on first use.
         sp<SpriteController> spriteController;
 
@@ -276,6 +280,7 @@
         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
         mLocked.pointerSpeed = 0;
         mLocked.pointerGesturesEnabled = true;
+        mLocked.showTouches = false;
     }
 
     sp<EventHub> eventHub = new EventHub();
@@ -431,6 +436,8 @@
                 * POINTER_SPEED_EXPONENT);
         outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
 
+        outConfig->showTouches = mLocked.showTouches;
+
         outConfig->setDisplayInfo(0, false /*external*/,
                 mLocked.displayWidth, mLocked.displayHeight, mLocked.displayOrientation);
         outConfig->setDisplayInfo(0, true /*external*/,
@@ -611,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;
         }
     }
@@ -678,6 +686,22 @@
             InputReaderConfiguration::CHANGE_POINTER_SPEED);
 }
 
+void NativeInputManager::setShowTouches(bool enabled) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mLocked.showTouches == enabled) {
+            return;
+        }
+
+        LOGI("Setting show touches feature to %s.", enabled ? "enabled" : "disabled");
+        mLocked.showTouches = enabled;
+    } // release lock
+
+    mInputManager->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+}
+
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -1063,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;
     }
 
@@ -1090,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());
     }
 }
 
@@ -1276,6 +1302,15 @@
     gNativeInputManager->setPointerSpeed(speed);
 }
 
+static void android_server_InputManager_nativeSetShowTouches(JNIEnv* env,
+        jclass clazz, jboolean enabled) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setShowTouches(enabled);
+}
+
 static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
     if (checkInputManagerUnitialized(env)) {
         return NULL;
@@ -1343,6 +1378,8 @@
             (void*) android_server_InputManager_nativeTransferTouchFocus },
     { "nativeSetPointerSpeed", "(I)V",
             (void*) android_server_InputManager_nativeSetPointerSpeed },
+    { "nativeSetShowTouches", "(Z)V",
+            (void*) android_server_InputManager_nativeSetShowTouches },
     { "nativeDump", "()Ljava/lang/String;",
             (void*) android_server_InputManager_nativeDump },
     { "nativeMonitor", "()V",
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/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index f67d251..e892b5e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -18,7 +18,7 @@
 
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
@@ -511,7 +511,7 @@
         future = expectMeteredIfacesChanged();
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         future.get();
         verifyAndReset();
 
@@ -615,7 +615,7 @@
             future = expectMeteredIfacesChanged(TEST_IFACE);
 
             replay();
-            mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+            mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
             future.get();
             verifyAndReset();
         }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 6dd8cd6..54f3bb0 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -18,7 +18,7 @@
 
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
@@ -180,7 +180,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -232,7 +232,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -322,7 +322,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         verifyAndReset();
 
         // modify some number on wifi, and trigger poll event
@@ -372,7 +372,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         verifyAndReset();
 
         // create some traffic on first network
@@ -410,7 +410,7 @@
         expectNetworkStatsPoll();
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
         verifyAndReset();
 
@@ -452,7 +452,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         verifyAndReset();
 
         // create some traffic
@@ -509,7 +509,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         verifyAndReset();
 
         // create some traffic
@@ -541,7 +541,7 @@
         expectNetworkStatsPoll();
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
         verifyAndReset();
 
@@ -575,7 +575,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         verifyAndReset();
 
         // create some traffic for two apps
@@ -637,7 +637,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
 
         replay();
-        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
         verifyAndReset();
 
         // create some initial traffic
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/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 3e13a86..bd35058 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.os.AsyncResult;
@@ -230,8 +231,6 @@
 
     Object     mLastNITZTimeInfo;
 
-    private static final String WIFI_ONLY_CARRIER = "wifi-only";
-
     //***** Events
 
     static final int EVENT_SEND                 = 1;
@@ -626,10 +625,9 @@
         Looper looper = mSenderThread.getLooper();
         mSender = new RILSender(looper);
 
-        // TODO: Provide a common API for determining if a
-        // device is wifi-only. bug: 3480713
-        String carrier = SystemProperties.get("ro.carrier");
-        if (WIFI_ONLY_CARRIER.equals(carrier)) {
+        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
             riljLog("Not starting RILReceiver: wifi-only");
         } else {
             riljLog("Starting RILReceiver");
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 134227a..e92a276 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -39,6 +39,7 @@
     CDMALTEPhone mCdmaLtePhone;
 
     private ServiceState  mLteSS;  // The last LTE state from Voice Registration
+    private boolean mNeedToRegForSimLoaded = true;
 
     public CdmaLteServiceStateTracker(CDMALTEPhone phone) {
         super(phone);
@@ -71,7 +72,10 @@
             isSubscriptionFromRuim = false;
             // Register SIM_RECORDS_LOADED dynamically.
             // This is to avoid confilct with RUIM_READY scenario)
-            phone.mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
+            if (mNeedToRegForSimLoaded) {
+                phone.mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
+                mNeedToRegForSimLoaded = false;
+            }
             pollState();
             // Signal strength polling stops when radio is off.
             queueNextSignalStrengthPoll();
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/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp
index 92f5b64..fb4067a 100644
--- a/tools/aidl/aidl.cpp
+++ b/tools/aidl/aidl.cpp
@@ -575,12 +575,19 @@
 
 // ==========================================================
 void
-generate_dep_file(const Options& options)
+generate_dep_file(const Options& options, const document_item_type* items)
 {
-   /* we open the file in binary mode to ensure that the same output is
-    * generated on all platforms !!
-    */
-    FILE* to = fopen(options.depFileName.c_str(), "wb");
+    /* we open the file in binary mode to ensure that the same output is
+     * generated on all platforms !!
+     */
+    FILE* to = NULL;
+    if (options.autoDepFile) {
+        string fileName = options.outputFileName + ".d";
+        to = fopen(fileName.c_str(), "wb");
+    } else {
+        to = fopen(options.depFileName.c_str(), "wb");
+    }
+
     if (to == NULL) {
         return;
     }
@@ -591,7 +598,12 @@
         slash = "";
     }
 
-    fprintf(to, "%s: \\\n", options.outputFileName.c_str());
+    if (items->item_type == INTERFACE_TYPE) {
+        fprintf(to, "%s: \\\n", options.outputFileName.c_str());
+    } else {
+        // parcelable: there's no output file.
+        fprintf(to, " : \\\n");
+    }
     fprintf(to, "  %s %s\n", options.inputFileName.c_str(), slash);
 
     while (import) {
@@ -611,44 +623,60 @@
 
 // ==========================================================
 static string
-generate_outputFileName(const Options& options, const document_item_type* items)
+generate_outputFileName2(const Options& options, const buffer_type& name, const char* package)
 {
     string result;
 
-    // items has already been checked to have only one interface.
-    if (items->item_type == INTERFACE_TYPE) {
-        interface_type* type = (interface_type*)items;
+    // create the path to the destination folder based on the
+    // interface package name
+    result = options.outputBaseFolder;
+    result += OS_PATH_SEPARATOR;
 
-        // create the path to the destination folder based on the
-        // interface package name
-        result = options.outputBaseFolder;
-        result += OS_PATH_SEPARATOR;
-
-        string package = type->package;
-        size_t len = package.length();
-        for (size_t i=0; i<len; i++) {
-            if (package[i] == '.') {
-                package[i] = OS_PATH_SEPARATOR;
-            }
+    string packageStr = package;
+    size_t len = packageStr.length();
+    for (size_t i=0; i<len; i++) {
+        if (packageStr[i] == '.') {
+            packageStr[i] = OS_PATH_SEPARATOR;
         }
-
-        result += package;
-        
-        // add the filename by replacing the .aidl extension to .java
-        const char* p = strchr(type->name.data, '.');
-        len = p ? p-type->name.data : strlen(type->name.data);
-
-        result += OS_PATH_SEPARATOR;
-        result.append(type->name.data, len);
-        result += ".java";
     }
 
+    result += packageStr;
+
+    // add the filename by replacing the .aidl extension to .java
+    const char* p = strchr(name.data, '.');
+    len = p ? p-name.data : strlen(name.data);
+
+    result += OS_PATH_SEPARATOR;
+    result.append(name.data, len);
+    result += ".java";
+
     return result;
 }
 
 // ==========================================================
+static string
+generate_outputFileName(const Options& options, const document_item_type* items)
+{
+    // items has already been checked to have only one interface.
+    if (items->item_type == INTERFACE_TYPE) {
+        interface_type* type = (interface_type*)items;
+
+        return generate_outputFileName2(options, type->name, type->package);
+    } else if (items->item_type == PARCELABLE_TYPE) {
+        parcelable_type* type = (parcelable_type*)items;
+        return generate_outputFileName2(options, type->name, type->package);
+    }
+
+    // I don't think we can come here, but safer than returning NULL.
+    string result;
+    return result;
+}
+
+
+
+// ==========================================================
 static void
-check_outputFileName(const string& path) {
+check_outputFilePath(const string& path) {
     size_t len = path.length();
     for (size_t i=0; i<len ; i++) {
         if (path[i] == OS_PATH_SEPARATOR) {
@@ -756,7 +784,7 @@
 
 // ==========================================================
 static int
-compile_aidl(const Options& options)
+compile_aidl(Options& options)
 {
     int err = 0, N;
 
@@ -850,27 +878,30 @@
         return 1;
     }
 
+    // if needed, generate the outputFileName from the outputBaseFolder
+    if (options.outputFileName.length() == 0 &&
+            options.outputBaseFolder.length() > 0) {
+        options.outputFileName = generate_outputFileName(options, mainDoc);
+    }
+
+    // if we were asked to, generate a make dependency file
+    // unless it's a parcelable *and* it's supposed to fail on parcelable
+    if ((options.autoDepFile || options.depFileName != "") &&
+            !(onlyParcelable && options.failOnParcelable)) {
+        // make sure the folders of the output file all exists
+        check_outputFilePath(options.outputFileName);
+        generate_dep_file(options, mainDoc);
+    }
+
     // they didn't ask to fail on parcelables, so just exit quietly.
     if (onlyParcelable && !options.failOnParcelable) {
         return 0;
     }
 
-    // if we were asked to, generate a make dependency file
-    if (options.depFileName != "") {
-        generate_dep_file(options);
-    }
-
-    // if needed, generate the outputFileName from the outputBaseFolder
-    string outputFileName = options.outputFileName;
-    if (outputFileName.length() == 0 &&
-            options.outputBaseFolder.length() > 0) {
-        outputFileName = generate_outputFileName(options, mainDoc);
-    }
-    
     // make sure the folders of the output file all exists
-    check_outputFileName(outputFileName);
+    check_outputFilePath(options.outputFileName);
 
-    err = generate_java(outputFileName, options.inputFileName.c_str(),
+    err = generate_java(options.outputFileName, options.inputFileName.c_str(),
                         (interface_type*)mainDoc);
 
     return err;
diff --git a/tools/aidl/options.cpp b/tools/aidl/options.cpp
index 0aa7db2..7b2daeb 100644
--- a/tools/aidl/options.cpp
+++ b/tools/aidl/options.cpp
@@ -15,6 +15,7 @@
             "OPTIONS:\n"
             "   -I<DIR>    search path for import statements.\n"
             "   -d<FILE>   generate dependency file.\n"
+            "   -a         generate dependency file next to the output file with the name based on the input file.\n"
             "   -p<FILE>   file created by --preprocess to import.\n"
             "   -o<FOLDER> base output folder for generated files.\n"
             "   -b         fail when trying to compile a parcelable.\n"
@@ -49,6 +50,7 @@
 
     options->task = COMPILE_AIDL;
     options->failOnParcelable = false;
+    options->autoDepFile = false;
 
     // OPTIONS
     while (i < argc) {
@@ -73,6 +75,9 @@
                         return usage();
                     }
                 }
+                else if (s[1] == 'a') {
+                    options->autoDepFile = true;
+                }
                 else if (s[1] == 'p') {
                     if (len > 2) {
                         options->preprocessedFiles.push_back(s+2);
diff --git a/tools/aidl/options.h b/tools/aidl/options.h
index d88d988..387e37d 100644
--- a/tools/aidl/options.h
+++ b/tools/aidl/options.h
@@ -23,6 +23,7 @@
     string outputFileName;
     string outputBaseFolder;
     string depFileName;
+    bool autoDepFile;
 
     vector<string> filesToPreprocess;
 };
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 c1f6785..450f816 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -60,7 +60,9 @@
 
     public native static boolean startSupplicant();
 
-    /* Does a graceful shutdown of supplicant.
+    public native static boolean startP2pSupplicant();
+
+    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
      *
      * Note that underneath we use a harsh-sounding "terminate" supplicant command
      * for a graceful stop and a mild-sounding "stop" interface
@@ -215,6 +217,19 @@
         return doBooleanCommand("WPS_PIN any " + pin);
     }
 
+    public static boolean setPersistentReconnect(boolean enabled) {
+        int value = (enabled == true) ? 1 : 0;
+        return WifiNative.doBooleanCommand("SET persistent_reconnect " + value);
+    }
+
+    public static boolean setDeviceName(String name) {
+        return WifiNative.doBooleanCommand("SET device_name " + name);
+    }
+
+    public static boolean setDeviceType(String type) {
+        return WifiNative.doBooleanCommand("SET device_type " + type);
+    }
+
     public static boolean p2pFind() {
         return doBooleanCommand("P2P_FIND");
     }
@@ -269,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 c6f7da2..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.
@@ -2524,23 +2530,11 @@
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
             switch (message.what) {
-               case CMD_START_DRIVER:
-                   mWakeLock.acquire();
-                   WifiNative.startDriverCommand();
-                   mWakeLock.release();
-                   break;
-                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
-                    SupplicantState state = handleSupplicantStateChange(message);
-                    /* A driver start causes supplicant to first report an INTERFACE_DISABLED
-                     * state before transitioning out of it for connection. Stay in
-                     * DriverStoppedState until we get an INTERFACE_DISABLED state and transition
-                     * to DriverStarting upon getting that
-                     * TODO: Fix this when the supplicant can be made to just transition out of
-                     * INTERFACE_DISABLED state when driver gets started
-                     */
-                    if (state == SupplicantState.INTERFACE_DISABLED) {
-                        transitionTo(mDriverStartingState);
-                    }
+                case CMD_START_DRIVER:
+                    mWakeLock.acquire();
+                    WifiNative.startDriverCommand();
+                    mWakeLock.release();
+                    transitionTo(mDriverStartingState);
                     break;
                 default:
                     return NOT_HANDLED;
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 274edae..c52142d 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -36,6 +36,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.util.Slog;
+import android.util.Log;
 
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
@@ -174,7 +175,7 @@
      * It triggers a disableNetwork call if a DNS check fails.
      */
     public boolean mDisableAPNextFailure = false;
-    private ConnectivityManager mConnectivityManager;
+    private static boolean sWifiOnly = false;
     private boolean mNotificationShown;
     public boolean mHasConnectedWifiManager = false;
 
@@ -219,9 +220,14 @@
 
     public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) {
         ContentResolver contentResolver = context.getContentResolver();
+
+        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
+
         // Disable for wifi only devices.
         if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null &&
-                "wifi-only".equals(SystemProperties.get("ro.carrier"))) {
+                sWifiOnly) {
             putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false);
         }
         WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
@@ -508,22 +514,6 @@
         }
     }
 
-    /**
-     * @return true if there is definitely no mobile data (we'll be less aggressive)
-     */
-    private boolean hasNoMobileData() {
-        if (mConnectivityManager == null) {
-            mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
-                    Context.CONNECTIVITY_SERVICE);
-        }
-        NetworkInfo mobileNetInfo = mConnectivityManager.getNetworkInfo(
-                ConnectivityManager.TYPE_MOBILE);
-        if (mobileNetInfo == null || !mobileNetInfo.isAvailable()) {
-            return true;
-        }
-        return false;
-    }
-
     class DefaultState extends State {
         @Override
         public boolean processMessage(Message msg) {
@@ -941,7 +931,7 @@
 
             if (mDisableAPNextFailure || mNumCheckFailures >= mBssids.size()
                     || mNumCheckFailures >= mMaxSsidBlacklists) {
-                if (hasNoMobileData()) {
+                if (sWifiOnly) {
                     Slog.w(WWSM_TAG, "Would disable bad network, but device has no mobile data!" +
                             "  Going idle...");
                     // This state should be called idle -- will be changing flow.
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index adf13be..f67680d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -50,6 +50,7 @@
 import android.os.Messenger;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -126,6 +127,8 @@
     private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 11;
 
     private final boolean mP2pSupported;
+    private final String mDeviceType;
+    private String mDeviceName;
 
     private NetworkInfo mNetworkInfo;
 
@@ -142,6 +145,9 @@
 
         mP2pSupported = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_wifi_p2p_support);
+        mDeviceType = mContext.getResources().getString(
+                com.android.internal.R.string.config_wifi_p2p_device_type);
+        mDeviceName = getDefaultDeviceName();
 
         mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
         mP2pStateMachine.start();
@@ -182,6 +188,14 @@
                 "WifiP2pService");
     }
 
+    /* We use the 4 digits of the ANDROID_ID to have a friendly
+     * default that has low likelihood of collision with a peer */
+    private String getDefaultDeviceName() {
+        String id = Settings.Secure.getString(mContext.getContentResolver(),
+                    Settings.Secure.ANDROID_ID);
+        return "Android_" + id.substring(0,4);
+    }
+
     /**
      * Get a reference to handler. This is used by a client to establish
      * an AsyncChannel communication with WifiP2pService
@@ -514,7 +528,7 @@
                         if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
                     }
 
-                    if (WifiNative.startSupplicant()) {
+                    if (WifiNative.startP2pSupplicant()) {
                         mWifiMonitor.startMonitoring();
                         transitionTo(mP2pEnablingState);
                     } else {
@@ -577,6 +591,7 @@
             mNetworkInfo.setIsAvailable(true);
             //Start listening for new connections
             WifiNative.p2pListen();
+            initializeP2pSettings();
         }
 
         @Override
@@ -637,6 +652,22 @@
                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
                     transitionTo(mP2pDisabledState);
                     sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
+                    break;
+                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+                    mGroup = (WifiP2pGroup) message.obj;
+                    if (DBG) logd(getName() + " group started");
+                    if (mGroup.isGroupOwner()) {
+                        startDhcpServer(mGroup.getInterface());
+                    } else {
+                        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
+                                P2pStateMachine.this, mGroup.getInterface());
+                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+                        WifiP2pDevice groupOwner = mGroup.getOwner();
+                        updateDeviceStatus(groupOwner.deviceAddress, Status.CONNECTED);
+                        sendP2pPeersChangedBroadcast();
+                    }
+                    transitionTo(mGroupCreatedState);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -708,21 +739,6 @@
                     sendP2pPeersChangedBroadcast();
                     transitionTo(mInactiveState);
                     break;
-                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
-                    mGroup = (WifiP2pGroup) message.obj;
-                    if (DBG) logd(getName() + " group started");
-                    if (mGroup.isGroupOwner()) {
-                        startDhcpServer(mGroup.getInterface());
-                    } else {
-                        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
-                                P2pStateMachine.this, mGroup.getInterface());
-                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
-                        WifiP2pDevice groupOwner = mGroup.getOwner();
-                        updateDeviceStatus(groupOwner.deviceAddress, Status.CONNECTED);
-                        sendP2pPeersChangedBroadcast();
-                    }
-                    transitionTo(mGroupCreatedState);
-                    break;
                 case GROUP_NEGOTIATION_TIMED_OUT:
                     if (mGroupNegotiationTimeoutIndex == message.arg1) {
                         if (DBG) logd("Group negotiation timed out");
@@ -760,25 +776,33 @@
                     //After a GO setup, STA connected event comes with interface address
                     String interfaceAddress = (String) message.obj;
                     String deviceAddress = getDeviceAddress(interfaceAddress);
-                    mGroup.addClient(deviceAddress);
-                    updateDeviceStatus(deviceAddress, Status.CONNECTED);
-                    if (DBG) logd(getName() + " ap sta connected");
-                    sendP2pPeersChangedBroadcast();
+                    if (deviceAddress != null) {
+                        mGroup.addClient(deviceAddress);
+                        updateDeviceStatus(deviceAddress, Status.CONNECTED);
+                        if (DBG) logd(getName() + " ap sta connected");
+                        sendP2pPeersChangedBroadcast();
+                    } else {
+                        loge("Connect on unknown device address : " + interfaceAddress);
+                    }
                     break;
                 case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
                     interfaceAddress = (String) message.obj;
                     deviceAddress = getDeviceAddress(interfaceAddress);
-                    updateDeviceStatus(deviceAddress, Status.AVAILABLE);
-                    if (mGroup.removeClient(deviceAddress)) {
-                        if (DBG) logd("Removed client " + deviceAddress);
-                        sendP2pPeersChangedBroadcast();
-                    } else {
-                        if (DBG) logd("Failed to remove client " + deviceAddress);
-                        for (WifiP2pDevice c : mGroup.getClientList()) {
-                            if (DBG) logd("client " + c.deviceAddress);
+                    if (deviceAddress != null) {
+                        updateDeviceStatus(deviceAddress, Status.AVAILABLE);
+                        if (mGroup.removeClient(deviceAddress)) {
+                            if (DBG) logd("Removed client " + deviceAddress);
+                            sendP2pPeersChangedBroadcast();
+                        } else {
+                            if (DBG) logd("Failed to remove client " + deviceAddress);
+                            for (WifiP2pDevice c : mGroup.getClientList()) {
+                                if (DBG) logd("client " + c.deviceAddress);
+                            }
                         }
+                        if (DBG) loge(getName() + " ap sta disconnected");
+                    } else {
+                        loge("Disconnect on unknown device address : " + interfaceAddress);
                     }
-                    if (DBG) loge(getName() + " ap sta disconnected");
                     break;
                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                     DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj;
@@ -857,6 +881,9 @@
                 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
                     notifyP2pProvDiscPinRequest((WifiP2pDevice) message.obj);
                     break;
+                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+                    Slog.e(TAG, "Duplicate group creation event notice, ignore");
+                    break;
                 case WifiP2pManager.WPS_PBC:
                     WifiNative.wpsPbc();
                     break;
@@ -1117,6 +1144,12 @@
         return null;
     }
 
+    private void initializeP2pSettings() {
+        WifiNative.setPersistentReconnect(true);
+        WifiNative.setDeviceName(mDeviceName);
+        WifiNative.setDeviceType(mDeviceType);
+    }
+
     //State machine initiated requests can have replyTo set to null indicating
     //there are no recepients, we ignore those reply actions
     private void replyToMessage(Message msg, int what) {