Merge "Use the local timezone for CDMA SMS timestamps."
diff --git a/api/current.txt b/api/current.txt
index e379f8b..91d85a3 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();
@@ -16052,7 +16057,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 +16584,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 +16692,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 +16730,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";
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1271ddd..0e3eaaa 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.
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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9c96883..3441217 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)}.
      *
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/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/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index 89b6f32..f8d0142 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;
             }
@@ -453,7 +453,7 @@
         }
 
         final AudioTrack audioTrack = params.mAudioTrack;
-        final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat);
+        final int bytesPerFrame = params.mBytesPerFrame;
         final int lengthInBytes = params.mBytesWritten;
         final int lengthInFrames = lengthInBytes / bytesPerFrame;
 
@@ -511,16 +511,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..3e905d6 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,16 @@
     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;
+    // Not volatile, accessed only from the audio playback thread.
     int mAudioBufferSize;
+    // Always synchronized on "this".
+    int mUnconsumedBytes;
 
     private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
 
@@ -53,6 +62,8 @@
         mPan = pan;
         mLogger = logger;
 
+        mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount;
+
         // initially null.
         mAudioTrack = null;
         mBytesWritten = 0;
@@ -64,18 +75,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 +114,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/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/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2aa481c..4748522 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -9124,7 +9124,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 */
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index ff13dcb..b89b8ec 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -112,6 +112,8 @@
 
     private void scheduleSpellCheck() {
         if (mLength == 0) return;
+        if (spellCheckerSession == null) return;
+
         if (mChecker != null) {
             mTextView.removeCallbacks(mChecker);
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e9662ae..94f1604 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8928,6 +8928,7 @@
                 stopSelectionActionMode();
             } else {
                 selectCurrentWord();
+                getSelectionController().show();
             }
             handled = true;
         }
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..76bc535 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)) {
+                utterance.append(targetDescription);
+                utterance.append(" ");
+                utterance.append(directionDescription);
+                utterance.append(".");
+            }
+        }
+        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_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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 540c65a..01f2a8f 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" />
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-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-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/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/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..4c8c0d1 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_with_camera"
             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..ba55f0c0 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_with_camera"
         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/values-land/arrays.xml b/core/res/res/values-land/arrays.xml
index fd492ec..57aafc8 100644
--- a/core/res/res/values-land/arrays.xml
+++ b/core/res/res/values-land/arrays.xml
@@ -27,13 +27,41 @@
         <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_when_silent">
+        <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_direction_descriptions_when_soundon">
+        <item>@null</item>
+        <item>@string/description_direction_up</item>
+        <item>@null</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
     <array name="lockscreen_targets_with_camera">
         <item>@null</item>
         <item>@drawable/ic_lockscreen_unlock</item>
@@ -41,4 +69,18 @@
         <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>
+
+    <array name="lockscreen_direction_descriptions_with_camera">
+        <item>@null</item>
+        <item>@string/description_direction_up</item>
+        <item>@null</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
 </resources>
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/arrays.xml b/core/res/res/values/arrays.xml
index 57e9bbf..c9043ba 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -349,18 +349,60 @@
         <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_when_silent">
+        <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_direction_descriptions_when_soundon">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</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>
+
+    <array name="lockscreen_direction_descriptions_with_camera">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</item>
+        <item>@null</item>
     </array>
 
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dae9f70..8c0d826 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5169,6 +5169,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/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/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..547e1fc 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>
@@ -3129,6 +3129,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</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</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</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</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/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..b4d94f3 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) {
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/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/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/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/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/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/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/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/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index bfca851..3815c3b 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;
@@ -1418,6 +1420,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 +1481,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 +1566,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 +1587,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 +1598,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 +2294,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/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/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/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/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/tests/BandwidthTests/Android.mk b/tests/BandwidthTests/Android.mk
new file mode 100644
index 0000000..2cc2009
--- /dev/null
+++ b/tests/BandwidthTests/Android.mk
@@ -0,0 +1,22 @@
+#
+# 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_PACKAGE_NAME := BandwidthEnforcementTest
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
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/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index c1f6785..2cd54d9 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");
     }
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) {