Merge "Update code to use location aware isEmergencyNumber."
diff --git a/api/14.txt b/api/14.txt
index ce78f9e..45bb882 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -2072,6 +2072,7 @@
     method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public void invalidateAuthToken(java.lang.String, java.lang.String);
+    method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public java.lang.String peekAuthToken(android.accounts.Account, java.lang.String);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
diff --git a/api/current.txt b/api/current.txt
index 0aa94b2..45bb882 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2072,7 +2072,7 @@
     method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public void invalidateAuthToken(java.lang.String, java.lang.String);
-    method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], android.os.Bundle);
+    method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public java.lang.String peekAuthToken(android.accounts.Account, java.lang.String);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 530ecf1..3d3a373 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -1790,22 +1790,43 @@
      * @param allowableAccountTypes an optional string array of account types. These are used
      * both to filter the shown accounts and to filter the list of account types that are shown
      * when adding an account.
-     * @param addAccountOptions This {@link Bundle} is passed as the addAccount options
-     * @return an {@link Intent} that can be used to launch the ChooseAccount activity flow. 
+     * @param alwaysPromptForAccount if set the account chooser screen is always shown, otherwise
+     * it is only shown when there is more than one account from which to choose
+     * @param descriptionOverrideText if set, this string is used as the description in the
+     * accounts chooser screen rather than the default
+     * @param addAccountAuthTokenType This {@link Bundle} is passed as the {@link #addAccount}
+     * authTokenType
+     * @param addAccountRequiredFeatures This {@link Bundle} is passed as the {@link #addAccount}
+     * requiredFeatures
+     * @param addAccountOptions This {@link Bundle} is passed as the {@link #addAccount} options
+     * @return an {@link Intent} that can be used to launch the ChooseAccount activity flow.
      */
     static public Intent newChooseAccountIntent(Account selectedAccount,
             ArrayList<Account> allowableAccounts,
             String[] allowableAccountTypes,
+            boolean alwaysPromptForAccount,
+            String descriptionOverrideText,
+            String addAccountAuthTokenType,
+            String[] addAccountRequiredFeatures,
             Bundle addAccountOptions) {
         Intent intent = new Intent();
         intent.setClassName("android", "android.accounts.ChooseTypeAndAccountActivity");
         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST,
                 allowableAccounts);
-        intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST,
-                allowableAccountTypes != null ? Lists.newArrayList(allowableAccountTypes) : 0);
+        intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
+                allowableAccountTypes);
         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
                 addAccountOptions);
         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_SELECTED_ACCOUNT, selectedAccount);
+        intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT,
+                alwaysPromptForAccount);
+        intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_DESCRIPTION_TEXT_OVERRIDE,
+                descriptionOverrideText);
+        intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
+                addAccountAuthTokenType);
+        intent.putExtra(
+                ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY,
+                addAccountRequiredFeatures);
         return intent;
     }
 
diff --git a/core/java/android/accounts/ChooseAccountTypeActivity.java b/core/java/android/accounts/ChooseAccountTypeActivity.java
index 836164c..f53e6f3 100644
--- a/core/java/android/accounts/ChooseAccountTypeActivity.java
+++ b/core/java/android/accounts/ChooseAccountTypeActivity.java
@@ -52,12 +52,12 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.choose_account);
+        setContentView(R.layout.choose_account_type);
 
         // Read the validAccountTypes, if present, and add them to the setOfAllowableAccountTypes
         Set<String> setOfAllowableAccountTypes = null;
         ArrayList<String> validAccountTypes = getIntent().getStringArrayListExtra(
-                ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST);
+                ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY);
         if (validAccountTypes != null) {
             setOfAllowableAccountTypes = new HashSet<String>(validAccountTypes.size());
             for (String type : validAccountTypes) {
@@ -138,10 +138,14 @@
 
     protected void runAddAccountForAuthenticator(AuthInfo authInfo) {
         Log.d(TAG, "selected account type " + authInfo.name);
-        Bundle options = getIntent().getBundleExtra(
+        final Bundle options = getIntent().getBundleExtra(
                 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE);
-        AccountManager.get(this).addAccount(authInfo.desc.type, null, null, options,
-                this, this, null);
+        final String[] requiredFeatures = getIntent().getStringArrayExtra(
+                ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY);
+        final String authTokenType = getIntent().getStringExtra(
+                ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING);
+        AccountManager.get(this).addAccount(authInfo.desc.type, authTokenType, requiredFeatures,
+                options, this, this, null /* Handler */);
     }
 
     public void run(final AccountManagerFuture<Bundle> accountManagerFuture) {
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index a903399..b4030b9 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -57,25 +58,68 @@
      * that match the types in this list, if this parameter is supplied. This list is also
      * used to filter the allowable account types if add account is selected.
      */
-    public static final String EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST = "allowableAccountTypes";
+    public static final String EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY = "allowableAccountTypes";
 
     /**
-     * This is passed as the options bundle in AccountManager.addAccount() if it is called.
+     * This is passed as the addAccountOptions parameter in AccountManager.addAccount()
+     * if it is called.
      */
     public static final String EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE = "addAccountOptions";
 
     /**
+     * This is passed as the requiredFeatures parameter in AccountManager.addAccount()
+     * if it is called.
+     */
+    public static final String EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY = 
+            "addAccountRequiredFeatures";
+
+    /**
+     * This is passed as the authTokenType string in AccountManager.addAccount()
+     * if it is called.
+     */
+    public static final String EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING = "authTokenType";
+
+    /**
      * If set then the specified account is already "selected".
      */
     public static final String EXTRA_SELECTED_ACCOUNT = "selectedAccount";
 
+    /**
+     * If true then display the account selection list even if there is just
+     * one account to choose from. boolean.
+     */
+    public static final String EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT =
+            "alwaysPromptForAccount";
+
+    /**
+     * If set then this string willb e used as the description rather than
+     * the default.
+     */
+    public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE =
+            "descriptionTextOverride";
+
     private ArrayList<AccountInfo> mAccountInfos;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.choose_type_and_account);
+
+        // save some items we use frequently
         final AccountManager accountManager = AccountManager.get(this);
+        final Intent intent = getIntent();
+
+        // override the description text if supplied
+        final String descriptionOverride =
+                intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);
+        if (!TextUtils.isEmpty(descriptionOverride)) {
+            ((TextView)findViewById(R.id.description)).setText(descriptionOverride);
+        }
+
+        // If the selected account matches one in the list we will place a
+        // checkmark next to it.
+        final Account selectedAccount =
+                (Account)intent.getParcelableExtra(EXTRA_SELECTED_ACCOUNT);
 
         // build an efficiently queryable map of account types to authenticator descriptions
         final HashMap<String, AuthenticatorDescription> typeToAuthDescription =
@@ -87,7 +131,7 @@
         // Read the validAccounts, if present, and add them to the setOfAllowableAccounts
         Set<Account> setOfAllowableAccounts = null;
         final ArrayList<Parcelable> validAccounts =
-                getIntent().getParcelableArrayListExtra(EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST);
+                intent.getParcelableArrayListExtra(EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST);
         if (validAccounts != null) {
             setOfAllowableAccounts = new HashSet<Account>(validAccounts.size());
             for (Parcelable parcelable : validAccounts) {
@@ -98,7 +142,7 @@
         // Read the validAccountTypes, if present, and add them to the setOfAllowableAccountTypes
         Set<String> setOfAllowableAccountTypes = null;
         final ArrayList<String> validAccountTypes =
-                getIntent().getStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST);
+                intent.getStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY);
         if (validAccountTypes != null) {
             setOfAllowableAccountTypes = new HashSet<String>(validAccountTypes.size());
             for (String type : validAccountTypes) {
@@ -121,7 +165,8 @@
                 continue;
             }
             mAccountInfos.add(new AccountInfo(account,
-                    getDrawableForType(typeToAuthDescription, account.type)));
+                    getDrawableForType(typeToAuthDescription, account.type),
+                    account.equals(selectedAccount)));
         }
 
         // If there are no allowable accounts go directly to add account
@@ -131,7 +176,8 @@
         }
 
         // if there is only one allowable account return it
-        if (mAccountInfos.size() == 1) {
+        if (!intent.getBooleanExtra(EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, false)
+                && mAccountInfos.size() == 1) {
             Account account = mAccountInfos.get(0).account;
             setResultAndFinish(account.name, account.type);
             return;
@@ -143,7 +189,6 @@
         list.setAdapter(new AccountArrayAdapter(this,
                 android.R.layout.simple_list_item_1, mAccountInfos));
         list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-        list.setTextFilterEnabled(false);
         list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
                 onListItemClick((ListView)parent, v, position, id);
@@ -173,10 +218,12 @@
                 return;
             }
         }
+        Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: canceled");
         setResult(Activity.RESULT_CANCELED);
         finish();
     }
 
+
     private Drawable getDrawableForType(
             final HashMap<String, AuthenticatorDescription> typeToAuthDescription,
             String accountType) {
@@ -212,31 +259,40 @@
         bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
         bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
         setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
+        Log.d(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: "
+                + "selected account " + accountName + ", " + accountType);
         finish();
     }
 
     private void startChooseAccountTypeActivity() {
         final Intent intent = new Intent(this, ChooseAccountTypeActivity.class);
-        intent.putStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST,
-                getIntent().getStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST));
+        intent.putStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
+                getIntent().getStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY));
         intent.putExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
-                getIntent().getBundleExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST));
+                getIntent().getBundleExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE));
+        intent.putExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY,
+                getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY));
+        intent.putExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
+                getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING));
         startActivityForResult(intent, 0);
     }
 
     private static class AccountInfo {
         final Account account;
         final Drawable drawable;
+        private final boolean checked;
 
-        AccountInfo(Account account, Drawable drawable) {
+        AccountInfo(Account account, Drawable drawable, boolean checked) {
             this.account = account;
             this.drawable = drawable;
+            this.checked = checked;
         }
     }
 
     private static class ViewHolder {
         ImageView icon;
         TextView text;
+        ImageView checkmark;
     }
 
     private static class AccountArrayAdapter extends ArrayAdapter<AccountInfo> {
@@ -256,10 +312,11 @@
             ViewHolder holder;
 
             if (convertView == null) {
-                convertView = mLayoutInflater.inflate(R.layout.choose_account_row, null);
+                convertView = mLayoutInflater.inflate(R.layout.choose_selected_account_row, null);
                 holder = new ViewHolder();
                 holder.text = (TextView) convertView.findViewById(R.id.account_row_text);
                 holder.icon = (ImageView) convertView.findViewById(R.id.account_row_icon);
+                holder.checkmark = (ImageView) convertView.findViewById(R.id.account_row_checkmark);
                 convertView.setTag(holder);
             } else {
                 holder = (ViewHolder) convertView.getTag();
@@ -267,7 +324,9 @@
 
             holder.text.setText(mInfos.get(position).account.name);
             holder.icon.setImageDrawable(mInfos.get(position).drawable);
-
+            final int displayCheckmark =
+                    mInfos.get(position).checked ? View.VISIBLE : View.INVISIBLE;
+            holder.checkmark.setVisibility(displayCheckmark);
             return convertView;
         }
     }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2c2a493..0776e10 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -153,6 +153,7 @@
     final HashMap<IBinder, Service> mServices
             = new HashMap<IBinder, Service>();
     AppBindData mBoundApplication;
+    Profiler mProfiler;
     Configuration mConfiguration;
     Configuration mCompatConfiguration;
     Configuration mResConfiguration;
@@ -364,10 +365,6 @@
         ApplicationInfo appInfo;
         List<ProviderInfo> providers;
         ComponentName instrumentationName;
-        String profileFile;
-        ParcelFileDescriptor profileFd;
-        boolean autoStopProfiler;
-        boolean profiling;
         Bundle instrumentationArgs;
         IInstrumentationWatcher instrumentationWatcher;
         int debugMode;
@@ -375,10 +372,23 @@
         boolean persistent;
         Configuration config;
         CompatibilityInfo compatInfo;
-        boolean handlingProfiling;
+
+        /** Initial values for {@link Profiler}. */
+        String initProfileFile;
+        ParcelFileDescriptor initProfileFd;
+        boolean initAutoStopProfiler;
+
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
+    }
+
+    static final class Profiler {
+        String profileFile;
+        ParcelFileDescriptor profileFd;
+        boolean autoStopProfiler;
+        boolean profiling;
+        boolean handlingProfiling;
         public void setProfiler(String file, ParcelFileDescriptor fd) {
             if (profiling) {
                 if (fd != null) {
@@ -661,8 +671,6 @@
             data.appInfo = appInfo;
             data.providers = providers;
             data.instrumentationName = instrumentationName;
-            data.setProfiler(profileFile, profileFd);
-            data.autoStopProfiler = false;
             data.instrumentationArgs = instrumentationArgs;
             data.instrumentationWatcher = instrumentationWatcher;
             data.debugMode = debugMode;
@@ -670,6 +678,9 @@
             data.persistent = persistent;
             data.config = config;
             data.compatInfo = compatInfo;
+            data.initProfileFile = profileFile;
+            data.initProfileFd = profileFd;
+            data.initAutoStopProfiler = false;
             queueOrSendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -1293,8 +1304,8 @@
         public final boolean queueIdle() {
             ActivityClientRecord a = mNewActivities;
             boolean stopProfiling = false;
-            if (mBoundApplication != null && mBoundApplication.profileFd != null
-                    && mBoundApplication.autoStopProfiler) {
+            if (mBoundApplication != null && mProfiler.profileFd != null
+                    && mProfiler.autoStopProfiler) {
                 stopProfiling = true;
             }
             if (a != null) {
@@ -1320,7 +1331,7 @@
                 } while (a != null);
             }
             if (stopProfiling) {
-                mBoundApplication.stopProfiling();
+                mProfiler.stopProfiling();
             }
             ensureJitEnabled();
             return false;
@@ -1635,12 +1646,12 @@
     }
 
     public boolean isProfiling() {
-        return mBoundApplication != null && mBoundApplication.profileFile != null
-                && mBoundApplication.profileFd == null;
+        return mProfiler != null && mProfiler.profileFile != null
+                && mProfiler.profileFd == null;
     }
 
     public String getProfileFilePath() {
-        return mBoundApplication.profileFile;
+        return mProfiler.profileFile;
     }
 
     public Looper getLooper() {
@@ -1679,6 +1690,9 @@
             ContextImpl context = getSystemContext();
             context.init(new LoadedApk(this, "android", context, info,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO), null, this);
+
+            // give ourselves a default profiler
+            mProfiler = new Profiler();
         }
     }
 
@@ -1947,9 +1961,9 @@
         unscheduleGcIdler();
 
         if (r.profileFd != null) {
-            mBoundApplication.setProfiler(r.profileFile, r.profileFd);
-            mBoundApplication.startProfiling();
-            mBoundApplication.autoStopProfiler = r.autoStopProfiler;
+            mProfiler.setProfiler(r.profileFile, r.profileFd);
+            mProfiler.startProfiling();
+            mProfiler.autoStopProfiler = r.autoStopProfiler;
         }
 
         if (localLOGV) Slog.v(
@@ -3570,10 +3584,10 @@
                     case 1:
                         ViewDebug.startLooperProfiling(pcd.path, pcd.fd.getFileDescriptor());
                         break;
-                    default:
-                        mBoundApplication.setProfiler(pcd.path, pcd.fd);
-                        mBoundApplication.autoStopProfiler = false;
-                        mBoundApplication.startProfiling();
+                    default:                        
+                        mProfiler.setProfiler(pcd.path, pcd.fd);
+                        mProfiler.autoStopProfiler = false;
+                        mProfiler.startProfiling();
                         break;
                 }
             } catch (RuntimeException e) {
@@ -3592,7 +3606,7 @@
                     ViewDebug.stopLooperProfiling();
                     break;
                 default:
-                    mBoundApplication.stopProfiling();
+                    mProfiler.stopProfiling();
                     break;
             }
         }
@@ -3685,6 +3699,11 @@
         mConfiguration = new Configuration(data.config);
         mCompatConfiguration = new Configuration(data.config);
 
+        mProfiler = new Profiler();
+        mProfiler.profileFile = data.initProfileFile;
+        mProfiler.profileFd = data.initProfileFd;
+        mProfiler.autoStopProfiler = data.initAutoStopProfiler;
+
         // send up app name; do this *before* waiting for debugger
         Process.setArgV0(data.processName);
         android.ddm.DdmHandleAppName.setAppName(data.processName);
@@ -3699,8 +3718,8 @@
             }
         }
 
-        if (data.profileFd != null) {
-            data.startProfiling();
+        if (mProfiler.profileFd != null) {
+            mProfiler.startProfiling();
         }
 
         // If the app is Honeycomb MR1 or earlier, switch its AsyncTask
@@ -3841,10 +3860,10 @@
             mInstrumentation.init(this, instrContext, appContext,
                     new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher);
 
-            if (data.profileFile != null && !ii.handleProfiling
-                    && data.profileFd == null) {
-                data.handlingProfiling = true;
-                File file = new File(data.profileFile);
+            if (mProfiler.profileFile != null && !ii.handleProfiling
+                    && mProfiler.profileFd == null) {
+                mProfiler.handlingProfiling = true;
+                File file = new File(mProfiler.profileFile);
                 file.getParentFile().mkdirs();
                 Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
             }
@@ -3896,8 +3915,8 @@
 
     /*package*/ final void finishInstrumentation(int resultCode, Bundle results) {
         IActivityManager am = ActivityManagerNative.getDefault();
-        if (mBoundApplication.profileFile != null && mBoundApplication.handlingProfiling
-                && mBoundApplication.profileFd == null) {
+        if (mProfiler.profileFile != null && mProfiler.handlingProfiling
+                && mProfiler.profileFd == null) {
             Debug.stopMethodTracing();
         }
         //Slog.i(TAG, "am: " + ActivityManagerNative.getDefault()
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 5c4cc87..3290b9d 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -338,7 +338,7 @@
 
     /**
      * Column name for suggestions cursor. <i>Optional.</i> This column may be
-     * used to specify the time in (@link System#currentTimeMillis
+     * used to specify the time in {@link System#currentTimeMillis
      * System.currentTImeMillis()} (wall time in UTC) when an item was last
      * accessed within the results-providing application. If set, this may be
      * used to show more-recently-used items first.
diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java
index 9b0a2d7..fa77bc5 100644
--- a/core/java/android/net/DhcpInfoInternal.java
+++ b/core/java/android/net/DhcpInfoInternal.java
@@ -24,6 +24,7 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 
 /**
  * A simple object for retrieving the results of a DHCP request.
@@ -41,14 +42,18 @@
     public String serverAddress;
     public int leaseDuration;
 
-    private Collection<RouteInfo> routes;
+    private Collection<RouteInfo> mRoutes;
 
     public DhcpInfoInternal() {
-        routes = new ArrayList<RouteInfo>();
+        mRoutes = new ArrayList<RouteInfo>();
     }
 
     public void addRoute(RouteInfo routeInfo) {
-        routes.add(routeInfo);
+        mRoutes.add(routeInfo);
+    }
+
+    public Collection<RouteInfo> getRoutes() {
+        return Collections.unmodifiableCollection(mRoutes);
     }
 
     private int convertToInt(String addr) {
@@ -66,7 +71,7 @@
     public DhcpInfo makeDhcpInfo() {
         DhcpInfo info = new DhcpInfo();
         info.ipAddress = convertToInt(ipAddress);
-        for (RouteInfo route : routes) {
+        for (RouteInfo route : mRoutes) {
             if (route.isDefaultRoute()) {
                 info.gateway = convertToInt(route.getGateway().getHostAddress());
                 break;
@@ -94,14 +99,14 @@
     public LinkProperties makeLinkProperties() {
         LinkProperties p = new LinkProperties();
         p.addLinkAddress(makeLinkAddress());
-        for (RouteInfo route : routes) {
+        for (RouteInfo route : mRoutes) {
             p.addRoute(route);
         }
+        //if empty, connectivity configures default DNS
         if (TextUtils.isEmpty(dns1) == false) {
             p.addDns(NetworkUtils.numericToInetAddress(dns1));
         } else {
-            p.addDns(NetworkUtils.numericToInetAddress(serverAddress));
-            Log.d(TAG, "empty dns1, use dhcp server as dns1!");
+            Log.d(TAG, "makeLinkProperties with empty dns1!");
         }
         if (TextUtils.isEmpty(dns2) == false) {
             p.addDns(NetworkUtils.numericToInetAddress(dns2));
@@ -111,11 +116,33 @@
         return p;
     }
 
+    /* Updates the DHCP fields that need to be retained from
+     * original DHCP request if the DHCP renewal shows them as
+     * being empty
+     */
+    public void updateFromDhcpRequest(DhcpInfoInternal orig) {
+        if (orig == null) return;
+
+        if (TextUtils.isEmpty(dns1)) {
+            dns1 = orig.dns1;
+        }
+
+        if (TextUtils.isEmpty(dns2)) {
+            dns2 = orig.dns2;
+        }
+
+        if (mRoutes.size() == 0) {
+            for (RouteInfo route : orig.getRoutes()) {
+                addRoute(route);
+            }
+        }
+    }
+
     public String toString() {
         String routeString = "";
-        for (RouteInfo route : routes) routeString += route.toString() + " | ";
+        for (RouteInfo route : mRoutes) routeString += route.toString() + " | ";
         return "addr: " + ipAddress + "/" + prefixLength +
-                " routes: " + routeString +
+                " mRoutes: " + routeString +
                 " dns: " + dns1 + "," + dns2 +
                 " dhcpServer: " + serverAddress +
                 " leaseDuration: " + leaseDuration;
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 445b2f7..79c9395 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -63,6 +63,9 @@
     private PowerManager.WakeLock mDhcpRenewWakeLock;
     private static final String WAKELOCK_TAG = "DHCP";
 
+    //Remember DHCP configuration from first request
+    private DhcpInfoInternal mDhcpInfo;
+
     private static final int DHCP_RENEW = 0;
     private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW";
 
@@ -335,9 +338,11 @@
         if (dhcpAction == DhcpAction.START) {
             Log.d(TAG, "DHCP request on " + mInterfaceName);
             success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
+            mDhcpInfo = dhcpInfoInternal;
         } else if (dhcpAction == DhcpAction.RENEW) {
             Log.d(TAG, "DHCP renewal on " + mInterfaceName);
             success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal);
+            dhcpInfoInternal.updateFromDhcpRequest(mDhcpInfo);
         }
 
         if (success) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index eef658e..7046008 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -83,6 +83,12 @@
 
     String[] getTetheredIfaces();
 
+    /**
+     * Return list of interface pairs that are actively tethered.  Even indexes are
+     * remote interface, and odd indexes are corresponding local interfaces.
+     */
+    String[] getTetheredIfacePairs();
+
     String[] getTetheringErroredIfaces();
 
     String[] getTetherableUsbRegexs();
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index d8ac31f..a5cdf70 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -441,10 +441,10 @@
             final long curStart = bucketStart[i];
             final long curEnd = curStart + bucketDuration;
 
-            // bucket is older than record; we're finished
-            if (curEnd < start) break;
-            // bucket is newer than record; keep looking
-            if (curStart > end) continue;
+            // bucket is older than request; we're finished
+            if (curEnd <= start) break;
+            // bucket is newer than request; keep looking
+            if (curStart >= end) continue;
 
             // include full value for active buckets, otherwise only fractional
             final boolean activeBucket = curStart < now && curEnd > now;
@@ -466,7 +466,6 @@
             if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
             if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
         }
-
         return entry;
     }
 
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 47cfa73..18eb9f6 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -53,25 +53,33 @@
     public static final int UID_REMOVED = -4;
 
     /**
+     * Special UID value used when collecting {@link NetworkStatsHistory} for
+     * tethering traffic.
+     *
+     * @hide
+     */
+    public static final int UID_TETHERING = -5;
+
+    /**
      * Default tag value for {@link DownloadManager} traffic.
      *
      * @hide
      */
-    public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFF0001;
+    public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFFFF01;
 
     /**
      * Default tag value for {@link MediaPlayer} traffic.
      *
      * @hide
      */
-    public static final int TAG_SYSTEM_MEDIA = 0xFFFF0002;
+    public static final int TAG_SYSTEM_MEDIA = 0xFFFFFF02;
 
     /**
      * Default tag value for {@link BackupManager} traffic.
      *
      * @hide
      */
-    public static final int TAG_SYSTEM_BACKUP = 0xFFFF0003;
+    public static final int TAG_SYSTEM_BACKUP = 0xFFFFFF03;
 
     /**
      * Snapshot of {@link NetworkStats} when the currently active profiling
@@ -90,6 +98,10 @@
      * <p>
      * Changes only take effect during subsequent calls to
      * {@link #tagSocket(Socket)}.
+     * <p>
+     * Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
+     * used internally by system services like {@link DownloadManager} when
+     * performing traffic on behalf of an application.
      */
     public static void setThreadStatsTag(int tag) {
         NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 4e5645d..66373fe 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -231,6 +231,13 @@
     NetworkStats getNetworkStatsUidDetail(int uid);
 
     /**
+     * Return summary of network statistics for the requested pairs of
+     * tethering interfaces.  Even indexes are remote interface, and odd
+     * indexes are corresponding local interfaces.
+     */
+    NetworkStats getNetworkStatsTethering(in String[] ifacePairs);
+
+    /**
      * Set quota for an interface.
      */
     void setInterfaceQuota(String iface, long quotaBytes);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e5882fc..bc5994e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4031,8 +4031,6 @@
         public static final String NETSTATS_UID_MAX_HISTORY = "netstats_uid_max_history";
         /** {@hide} */
         public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history";
-        /** {@hide} */
-        public static final String NETSTATS_FORCE_COMPLETE_POLL = "netstats_force_complete_poll";
 
         /** Preferred NTP server. {@hide} */
         public static final String NTP_SERVER = "ntp_server";
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 0dc781f..335c66b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.os.IRemoteCallback;
 import android.view.IApplicationToken;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
@@ -220,7 +221,7 @@
     void setPointerSpeed(int speed);
 
     /**
-     * Block until all windows the window manager knows about have been drawn.
+     * Block until the given window has been drawn to the screen.
      */
-    void waitForAllDrawn();
+    void waitForWindowDrawn(IBinder token, in IRemoteCallback callback);
 }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c7b59b8..b678c7d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -290,6 +290,13 @@
     private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
 
     /**
+     * When set, this ViewGroup will not dispatch onAttachedToWindow calls
+     * to children when adding new views. This is used to prevent multiple
+     * onAttached calls when a ViewGroup adds children in its own onAttached method.
+     */
+    private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
+
+    /**
      * Indicates which types of drawing caches are to be kept in memory.
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -2154,8 +2161,12 @@
      */
     @Override
     void dispatchAttachedToWindow(AttachInfo info, int visibility) {
+        mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
         super.dispatchAttachedToWindow(info, visibility);
+        mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
+
         visibility |= mViewFlags & VISIBILITY_MASK;
+
         final int count = mChildrenCount;
         final View[] children = mChildren;
         for (int i = 0; i < count; i++) {
@@ -3321,7 +3332,7 @@
         }
 
         AttachInfo ai = mAttachInfo;
-        if (ai != null) {
+        if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
             boolean lastKeepOn = ai.mKeepScreenOn;
             ai.mKeepScreenOn = false;
             child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index fb87e23..122865e 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -34,10 +34,12 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.System;
 import android.util.Log;
+import android.view.WindowManager.LayoutParams;
 import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -175,20 +177,8 @@
         View view = mView = inflater.inflate(R.layout.volume_adjust, null);
         mView.setOnTouchListener(new View.OnTouchListener() {
             public boolean onTouch(View v, MotionEvent event) {
-                // Dismiss the dialog if the user touches outside the visible area. This is not
-                // handled by the usual dialog dismissing code because there is a region above
-                // the panel (marginTop) that is still within the dialog.
-                if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                    int x = (int) event.getX();
-                    int y = (int) event.getY();
-                    if (x < mPanel.getLeft() || x > mPanel.getRight() || y < mPanel.getTop()
-                            || y > mPanel.getBottom()) {
-                        forceTimeout();
-                        return true;
-                    }
-                }
                 resetTimeout();
-                return true;
+                return false;
             }
         });
         mPanel = (ViewGroup) mView.findViewById(R.id.visible_panel);
@@ -196,7 +186,15 @@
         mMoreButton = (ImageView) mView.findViewById(R.id.expand_button);
         mDivider = (ImageView) mView.findViewById(R.id.expand_button_divider);
 
-        mDialog = new Dialog(context, R.style.Theme_Panel_Volume);
+        mDialog = new Dialog(context, R.style.Theme_Panel_Volume) {
+            public boolean onTouchEvent(MotionEvent event) {
+                if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                    forceTimeout();
+                    return true;
+                }
+                return false;
+            }
+        };
         mDialog.setTitle("Volume control"); // No need to localize
         mDialog.setContentView(mView);
         mDialog.setOnDismissListener(new OnDismissListener() {
@@ -208,11 +206,17 @@
         // Change some window properties
         Window window = mDialog.getWindow();
         window.setGravity(Gravity.TOP);
-        WindowManager.LayoutParams lp = window.getAttributes();
+        LayoutParams lp = window.getAttributes();
         lp.token = null;
-        lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+        // Offset from the top
+        lp.y = mContext.getResources().getDimensionPixelOffset(
+                com.android.internal.R.dimen.volume_panel_top);
+        lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
+        lp.width = LayoutParams.WRAP_CONTENT;
+        lp.height = LayoutParams.WRAP_CONTENT;
         window.setAttributes(lp);
-        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+        window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
 
         mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
         mVibrator = new Vibrator();
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 980e454..fdbda4c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -772,10 +772,16 @@
      */
     public void screenTurnedOff(int why);
 
+    public interface ScreenOnListener {
+        void onScreenOn();
+    };
+
     /**
-     * Called after the screen turns on.
+     * Called when the power manager would like to turn the screen on.
+     * Must call back on the listener to tell it when the higher-level system
+     * is ready for the screen to go on (i.e. the lock screen is shown).
      */
-    public void screenTurnedOn();
+    public void screenTurningOn(ScreenOnListener screenOnListener);
 
     /**
      * Return whether the screen is currently on.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index fae2c6f..f57c6c4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -644,7 +644,7 @@
     private Drawable mSelectHandleLeft;
     private Drawable mSelectHandleRight;
 
-    static final boolean USE_WEBKIT_RINGS = true;
+    static final boolean USE_WEBKIT_RINGS = false;
     // the color used to highlight the touch rectangles
     private static final int HIGHLIGHT_COLOR = 0x6633b5e5;
     // the round corner for the highlight path
@@ -730,6 +730,7 @@
     static final int SELECT_AT                          = 135;
     static final int SCREEN_ON                          = 136;
     static final int ENTER_FULLSCREEN_VIDEO             = 137;
+    static final int UPDATE_SELECTION                   = 138;
 
     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
     private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
@@ -4062,8 +4063,11 @@
         // state.
         // If mNativeClass is 0, we should not reach here, so we do not
         // need to check it again.
+        boolean pressed = (mTouchMode == TOUCH_SHORTPRESS_START_MODE
+                || mTouchMode == TOUCH_INIT_MODE
+                || mTouchMode == TOUCH_SHORTPRESS_MODE);
         nativeRecordButtons(hasFocus() && hasWindowFocus(),
-                (mTouchMode == TOUCH_SHORTPRESS_START_MODE && !USE_WEBKIT_RINGS)
+                (pressed && !USE_WEBKIT_RINGS)
                 || mTrackballDown || mGotCenterDown, false);
         drawCoreAndCursorRing(canvas, mBackgroundColor,
                 mDrawCursorRing && drawRings);
@@ -4280,7 +4284,6 @@
         }
         nativeSetExtendSelection();
         mDrawSelectionPointer = false;
-        mSelectionStarted = true;
         mTouchMode = TOUCH_DRAG_MODE;
         return true;
     }
@@ -4441,6 +4444,7 @@
                 mHeldMotionless = MOTIONLESS_PENDING;
             }
         }
+        int saveCount = canvas.save();
         if (animateZoom) {
             mZoomManager.animateZoom(canvas);
         } else if (!canvas.isHardwareAccelerated()) {
@@ -4491,10 +4495,6 @@
                 nativeUseHardwareAccelSkia(mHardwareAccelSkia);
             }
 
-            if (mSelectingText && USE_JAVA_TEXT_SELECTION) {
-                drawTextSelectionHandles(canvas);
-            }
-
         } else {
             DrawFilter df = null;
             if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
@@ -4512,6 +4512,11 @@
             }
         }
 
+        canvas.restoreToCount(saveCount);
+        if (mSelectingText && USE_JAVA_TEXT_SELECTION) {
+            drawTextSelectionHandles(canvas);
+        }
+
         if (extras == DRAW_EXTRAS_CURSOR_RING) {
             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                 mTouchMode = TOUCH_SHORTPRESS_MODE;
@@ -6530,6 +6535,8 @@
         mLastTouchTime = eventTime;
         mVelocityTracker = VelocityTracker.obtain();
         mSnapScrollMode = SNAP_NONE;
+        mPrivateHandler.sendEmptyMessageDelayed(UPDATE_SELECTION,
+                ViewConfiguration.getTapTimeout());
     }
 
     private void startDrag() {
@@ -7192,10 +7199,15 @@
         return mZoomManager.zoomOut();
     }
 
+    /**
+     * This selects the best clickable target at mLastTouchX and mLastTouchY
+     * and calls showCursorTimed on the native side
+     */
     private void updateSelection() {
         if (mNativeClass == 0) {
             return;
         }
+        mPrivateHandler.removeMessages(UPDATE_SELECTION);
         // mLastTouchX and mLastTouchY are the point in the current viewport
         int contentX = viewToContentX(mLastTouchX + mScrollX);
         int contentY = viewToContentY(mLastTouchY + mScrollY);
@@ -7295,6 +7307,7 @@
             return;
         }
         mTouchMode = TOUCH_DONE_MODE;
+        updateSelection();
         switchOutDrawHistory();
         // mLastTouchX and mLastTouchY are the point in the current viewport
         int contentX = viewToContentX(mLastTouchX + mScrollX);
@@ -8185,6 +8198,14 @@
                             SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
                     break;
                 }
+                case UPDATE_SELECTION: {
+                    if (mTouchMode == TOUCH_INIT_MODE
+                            || mTouchMode == TOUCH_SHORTPRESS_MODE
+                            || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
+                        updateSelection();
+                    }
+                    break;
+                }
                 case SWITCH_TO_SHORTPRESS: {
                     mInitialHitTestResult = null; // set by updateSelection()
                     if (mTouchMode == TOUCH_INIT_MODE) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index cfe4cb7..ba89ef3 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3654,7 +3654,8 @@
                 vt.computeCurrentVelocity(1000, mMaximumVelocity);
                 final float yvel = -vt.getYVelocity(activeId);
 
-                if (scroller.isScrollingInDirection(0, yvel)) {
+                if (Math.abs(yvel) >= mMinimumVelocity
+                        && scroller.isScrollingInDirection(0, yvel)) {
                     // Keep the fling alive a little longer
                     postDelayed(this, FLYWHEEL_TIMEOUT);
                 } else {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 72db8e8..5392c2e 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -277,10 +277,11 @@
      *         called, false otherwise is returned.
      */
     public boolean performItemClick(View view, int position, long id) {
-        view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
-
         if (mOnItemClickListener != null) {
             playSoundEffect(SoundEffectConstants.CLICK);
+            if (view != null) {
+                view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+            }
             mOnItemClickListener.onItemClick(this, view, position, id);
             return true;
         }
@@ -338,8 +339,10 @@
      */
     public interface OnItemSelectedListener {
         /**
-         * Callback method to be invoked when an item in this view has been
-         * selected.
+         * <p>Callback method to be invoked when an item in this view has been
+         * selected. This callback is invoked only when the newly selected
+         * position is different from the previously selected position or if
+         * there was no selected item.</p>
          *
          * Impelmenters can call getItemAtPosition(position) if they need to access the
          * data associated with the selected item.
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 390002b..ba69288 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -87,19 +87,26 @@
  * layout parameters. When the
  * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins}
  * property is set, default margins around children are automatically
- * allocated based on the child's visual characteristics. Each of the
- * margins so defined may be independently overridden by an assignment
+ * allocated based on the prevailing UI style guide for the platform.
+ * Each of the margins so defined may be independently overridden by an assignment
  * to the appropriate layout parameter.
+ * Default values will generally produce a reasonable spacing between components
+ * but values may change between different releases of the platform.
  *
  * <h4>Excess Space Distribution</h4>
  *
+ * GridLayout's distribution of excess space is based on <em>priority</em>
+ * rather than <em>weight</em>.
+ * <p>
  * A child's ability to stretch is inferred from the alignment properties of
  * its row and column groups (which are typically set by setting the
  * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters).
  * If alignment was defined along a given axis then the component
- * is taken as flexible in along that axis. If no alignment was set,
- * the component is instead assumed to be inflexible. Multiple components in
- * the same row or column group are considered to act in <em>parallel</em>. Such a
+ * is taken as <em>flexible</em> in that direction. If no alignment was set,
+ * the component is instead assumed to be <em>inflexible</em>.
+ * <p>
+ * Multiple components in the same row or column group are
+ * considered to act in <em>parallel</em>. Such a
  * group is flexible only if <em>all</em> of the components
  * within it are flexible. Row and column groups that sit either side of a common boundary
  * are instead considered to act in <em>series</em>. The composite group made of these two
@@ -109,6 +116,23 @@
  * gravity. To prevent a column from stretching, ensure that one of the components
  * in the column does not define a gravity.
  * <p>
+ * When the principle of flexibility does not provide complete disambiguation,
+ * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
+ * and <em>bottom</em> edges.
+ *
+ * <h5>Limitations</h5>
+ *
+ * GridLayout does not provide support for the principle of <em>weight</em>, as defined in
+ * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible
+ * to configure a GridLayout to distribute excess space in non-trivial proportions between
+ * multiple rows or columns.
+ * <p>
+ * Some common use-cases may nevertheless be accommodated as follows.
+ * To place equal amounts of space around a component in a cell group;
+ * use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}).
+ * For complete control over excess space distribution in a row or column;
+ * use a {@link LinearLayout} subview to hold the components in the associated cell group.
+ * When using either of these techniques, bear in mind that cell groups may be defined to overlap.
  * <p>
  * See {@link GridLayout.LayoutParams} for a full description of the
  * layout parameters used by GridLayout.
@@ -180,9 +204,11 @@
 
     // Misc constants
 
-    private static final String TAG = GridLayout.class.getName();
-    private static boolean DEBUG = false;
-    private static final int PRF = 1;
+    static final String TAG = GridLayout.class.getName();
+    static final boolean DEBUG = false;
+    static final int PRF = 1;
+    static final int MAX_SIZE = 100000;
+    static final int DEFAULT_CONTAINER_MARGIN = 0;
 
     // Defaults
 
@@ -191,8 +217,6 @@
     private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
     private static final boolean DEFAULT_ORDER_PRESERVED = true;
     private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
-    private static final int DEFAULT_CONTAINER_MARGIN = 0;
-    private static final int MAX_SIZE = 100000;
 
     // TypedArray indices
 
@@ -206,13 +230,13 @@
 
     // Instance variables
 
-    private final Axis mHorizontalAxis = new Axis(true);
-    private final Axis mVerticalAxis = new Axis(false);
-    private boolean mLayoutParamsValid = false;
-    private int mOrientation = DEFAULT_ORIENTATION;
-    private boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
-    private int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
-    private int mDefaultGap;
+    final Axis horizontalAxis = new Axis(true);
+    final Axis verticalAxis = new Axis(false);
+    boolean layoutParamsValid = false;
+    int orientation = DEFAULT_ORIENTATION;
+    boolean useDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
+    int alignmentMode = DEFAULT_ALIGNMENT_MODE;
+    int defaultGap;
 
     // Constructors
 
@@ -224,7 +248,7 @@
         if (DEBUG) {
             setWillNotDraw(false);
         }
-        mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
+        defaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout);
         try {
             setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
@@ -266,13 +290,12 @@
      * @attr ref android.R.styleable#GridLayout_orientation
      */
     public int getOrientation() {
-        return mOrientation;
+        return orientation;
     }
 
     /**
-     * The orientation property does not affect layout. Orientation is used
-     * only to generate default row/column indices when they are not specified
-     * by a component's layout parameters.
+     * Orientation is used only to generate default row/column indices when
+     * they are not specified by a component's layout parameters.
      * <p>
      * The default value of this property is {@link #HORIZONTAL}.
      *
@@ -283,8 +306,9 @@
      * @attr ref android.R.styleable#GridLayout_orientation
      */
     public void setOrientation(int orientation) {
-        if (mOrientation != orientation) {
-            mOrientation = orientation;
+        if (this.orientation != orientation) {
+            this.orientation = orientation;
+            invalidateStructure();
             requestLayout();
         }
     }
@@ -302,13 +326,12 @@
      * @attr ref android.R.styleable#GridLayout_rowCount
      */
     public int getRowCount() {
-        return mVerticalAxis.getCount();
+        return verticalAxis.getCount();
     }
 
     /**
-     * The rowCount property does not affect layout. RowCount is used
-     * only to generate default row/column indices when they are not specified
-     * by a component's layout parameters.
+     * RowCount is used only to generate default row/column indices when
+     * they are not specified by a component's layout parameters.
      *
      * @param rowCount the number of rows
      *
@@ -318,7 +341,9 @@
      * @attr ref android.R.styleable#GridLayout_rowCount
      */
     public void setRowCount(int rowCount) {
-        mVerticalAxis.setCount(rowCount);
+        verticalAxis.setCount(rowCount);
+        invalidateStructure();
+        requestLayout();
     }
 
     /**
@@ -334,13 +359,12 @@
      * @attr ref android.R.styleable#GridLayout_columnCount
      */
     public int getColumnCount() {
-        return mHorizontalAxis.getCount();
+        return horizontalAxis.getCount();
     }
 
     /**
-     * The columnCount property does not affect layout. ColumnCount is used
-     * only to generate default column/column indices when they are not specified
-     * by a component's layout parameters.
+     * ColumnCount is used only to generate default column/column indices when
+     * they are not specified by a component's layout parameters.
      *
      * @param columnCount the number of columns.
      *
@@ -350,7 +374,9 @@
      * @attr ref android.R.styleable#GridLayout_columnCount
      */
     public void setColumnCount(int columnCount) {
-        mHorizontalAxis.setCount(columnCount);
+        horizontalAxis.setCount(columnCount);
+        invalidateStructure();
+        requestLayout();
     }
 
     /**
@@ -364,7 +390,7 @@
      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
      */
     public boolean getUseDefaultMargins() {
-        return mUseDefaultMargins;
+        return useDefaultMargins;
     }
 
     /**
@@ -394,7 +420,7 @@
      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
      */
     public void setUseDefaultMargins(boolean useDefaultMargins) {
-        mUseDefaultMargins = useDefaultMargins;
+        this.useDefaultMargins = useDefaultMargins;
         requestLayout();
     }
 
@@ -411,7 +437,7 @@
      * @attr ref android.R.styleable#GridLayout_alignmentMode
      */
     public int getAlignmentMode() {
-        return mAlignmentMode;
+        return alignmentMode;
     }
 
     /**
@@ -430,7 +456,7 @@
      * @attr ref android.R.styleable#GridLayout_alignmentMode
      */
     public void setAlignmentMode(int alignmentMode) {
-        mAlignmentMode = alignmentMode;
+        this.alignmentMode = alignmentMode;
         requestLayout();
     }
 
@@ -445,7 +471,7 @@
      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
      */
     public boolean isRowOrderPreserved() {
-        return mVerticalAxis.isOrderPreserved();
+        return verticalAxis.isOrderPreserved();
     }
 
     /**
@@ -465,7 +491,7 @@
      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
      */
     public void setRowOrderPreserved(boolean rowOrderPreserved) {
-        mVerticalAxis.setOrderPreserved(rowOrderPreserved);
+        verticalAxis.setOrderPreserved(rowOrderPreserved);
         invalidateStructure();
         requestLayout();
     }
@@ -481,7 +507,7 @@
      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
      */
     public boolean isColumnOrderPreserved() {
-        return mHorizontalAxis.isOrderPreserved();
+        return horizontalAxis.isOrderPreserved();
     }
 
     /**
@@ -501,14 +527,14 @@
      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
      */
     public void setColumnOrderPreserved(boolean columnOrderPreserved) {
-        mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
+        horizontalAxis.setOrderPreserved(columnOrderPreserved);
         invalidateStructure();
         requestLayout();
     }
 
     // Static utility methods
 
-    private static int max2(int[] a, int valueIfEmpty) {
+    static int max2(int[] a, int valueIfEmpty) {
         int result = valueIfEmpty;
         for (int i = 0, N = a.length; i < N; i++) {
             result = Math.max(result, a[i]);
@@ -517,14 +543,14 @@
     }
 
     @SuppressWarnings("unchecked")
-    private static <T> T[] append(T[] a, T[] b) {
+    static <T> T[] append(T[] a, T[] b) {
         T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
         System.arraycopy(a, 0, result, 0, a.length);
         System.arraycopy(b, 0, result, a.length, b.length);
         return result;
     }
 
-    private static Alignment getAlignment(int gravity, boolean horizontal) {
+    static Alignment getAlignment(int gravity, boolean horizontal) {
         int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
         int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
         int flags = (gravity & mask) >> shift;
@@ -547,7 +573,7 @@
         if (c.getClass() == Space.class) {
             return 0;
         }
-        return mDefaultGap / 2;
+        return defaultGap / 2;
     }
 
     private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
@@ -555,18 +581,18 @@
     }
 
     private int getDefaultMarginValue(View c, LayoutParams p, boolean horizontal, boolean leading) {
-        if (!mUseDefaultMargins) {
+        if (!useDefaultMargins) {
             return 0;
         }
         Spec spec = horizontal ? p.columnSpec : p.rowSpec;
-        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
+        Axis axis = horizontal ? horizontalAxis : verticalAxis;
         Interval span = spec.span;
         boolean isAtEdge = leading ? (span.min == 0) : (span.max == axis.getCount());
 
         return getDefaultMargin(c, isAtEdge, horizontal, leading);
     }
 
-    private int getMargin1(View view, boolean horizontal, boolean leading) {
+    int getMargin1(View view, boolean horizontal, boolean leading) {
         LayoutParams lp = getLayoutParams(view);
         int margin = horizontal ?
                 (leading ? lp.leftMargin : lp.rightMargin) :
@@ -575,10 +601,10 @@
     }
 
     private int getMargin(View view, boolean horizontal, boolean leading) {
-        if (mAlignmentMode == ALIGN_MARGINS) {
+        if (alignmentMode == ALIGN_MARGINS) {
             return getMargin1(view, horizontal, leading);
         } else {
-            Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
+            Axis axis = horizontal ? horizontalAxis : verticalAxis;
             int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
             LayoutParams lp = getLayoutParams(view);
             Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
@@ -591,10 +617,6 @@
         return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
     }
 
-    private static int valueIfDefined(int value, int defaultValue) {
-        return (value != UNDEFINED) ? value : defaultValue;
-    }
-
     private static boolean fits(int[] a, int value, int start, int end) {
         if (end > a.length) {
             return false;
@@ -629,9 +651,9 @@
 
     // install default indices for cells that don't define them
     private void validateLayoutParams() {
-        final boolean horizontal = (mOrientation == HORIZONTAL);
-        final int axisCount = horizontal ? mHorizontalAxis.count : mVerticalAxis.count;
-        final int count = valueIfDefined(axisCount, 0);
+        final boolean horizontal = (orientation == HORIZONTAL);
+        final Axis axis = horizontal ? horizontalAxis : verticalAxis;
+        final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
 
         int major = 0;
         int minor = 0;
@@ -640,15 +662,17 @@
         for (int i = 0, N = getChildCount(); i < N; i++) {
             LayoutParams lp = getLayoutParams1(getChildAt(i));
 
-            final Interval majorRange = (horizontal ? lp.rowSpec : lp.columnSpec).span;
-            final boolean majorWasDefined = (majorRange.min != UNDEFINED);
+            final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
+            final Interval majorRange = majorSpec.span;
+            final boolean majorWasDefined = majorSpec.startDefined;
             final int majorSpan = majorRange.size();
             if (majorWasDefined) {
                 major = majorRange.min;
             }
 
-            final Interval minorRange = (horizontal ? lp.columnSpec : lp.rowSpec).span;
-            final boolean minorWasDefined = (minorRange.min != UNDEFINED);
+            final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
+            final Interval minorRange = minorSpec.span;
+            final boolean minorWasDefined = minorSpec.startDefined;
             final int minorSpan = clip(minorRange, minorWasDefined, count);
             if (minorWasDefined) {
                 minor = minorRange.min;
@@ -685,9 +709,9 @@
     }
 
     private void invalidateStructure() {
-        mLayoutParamsValid = false;
-        mHorizontalAxis.invalidateStructure();
-        mVerticalAxis.invalidateStructure();
+        layoutParamsValid = false;
+        horizontalAxis.invalidateStructure();
+        verticalAxis.invalidateStructure();
         // This can end up being done twice. Better twice than not at all.
         invalidateValues();
     }
@@ -695,9 +719,9 @@
     private void invalidateValues() {
         // Need null check because requestLayout() is called in View's initializer,
         // before we are set up.
-        if (mHorizontalAxis != null && mVerticalAxis != null) {
-            mHorizontalAxis.invalidateValues();
-            mVerticalAxis.invalidateValues();
+        if (horizontalAxis != null && verticalAxis != null) {
+            horizontalAxis.invalidateValues();
+            verticalAxis.invalidateValues();
         }
     }
 
@@ -705,10 +729,10 @@
         return (LayoutParams) c.getLayoutParams();
     }
 
-    private LayoutParams getLayoutParams(View c) {
-        if (!mLayoutParamsValid) {
+    final LayoutParams getLayoutParams(View c) {
+        if (!layoutParamsValid) {
             validateLayoutParams();
-            mLayoutParamsValid = true;
+            layoutParamsValid = true;
         }
         return getLayoutParams1(c);
     }
@@ -752,7 +776,7 @@
             paint.setStyle(Paint.Style.STROKE);
             paint.setColor(Color.argb(50, 255, 255, 255));
 
-            int[] xs = mHorizontalAxis.locations;
+            int[] xs = horizontalAxis.locations;
             if (xs != null) {
                 for (int i = 0, length = xs.length; i < length; i++) {
                     int x = xs[i];
@@ -760,7 +784,7 @@
                 }
             }
 
-            int[] ys = mVerticalAxis.locations;
+            int[] ys = verticalAxis.locations;
             if (ys != null) {
                 for (int i = 0, length = ys.length; i < length; i++) {
                     int y = ys[i];
@@ -822,7 +846,7 @@
 
     // Measurement
 
-    private boolean isGone(View c) {
+    final boolean isGone(View c) {
         return c.getVisibility() == View.GONE;
     }
 
@@ -847,8 +871,8 @@
     protected void onMeasure(int widthSpec, int heightSpec) {
         measureChildrenWithMargins(widthSpec, heightSpec);
 
-        int width = getPaddingLeft() + mHorizontalAxis.getMeasure(widthSpec) + getPaddingRight();
-        int height = getPaddingTop() + mVerticalAxis.getMeasure(heightSpec) + getPaddingBottom();
+        int width = getPaddingLeft() + horizontalAxis.getMeasure(widthSpec) + getPaddingRight();
+        int height = getPaddingTop() + verticalAxis.getMeasure(heightSpec) + getPaddingBottom();
 
         int measuredWidth = Math.max(width, getSuggestedMinimumWidth());
         int measuredHeight = Math.max(height, getSuggestedMinimumHeight());
@@ -866,7 +890,7 @@
         return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
     }
 
-    private int getMeasurementIncludingMargin(View c, boolean horizontal) {
+    final int getMeasurementIncludingMargin(View c, boolean horizontal) {
         if (isGone(c)) {
             return 0;
         }
@@ -879,7 +903,7 @@
         invalidateValues();
     }
 
-    private Alignment getAlignment(Alignment alignment, boolean horizontal) {
+    final Alignment getAlignment(Alignment alignment, boolean horizontal) {
         return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
                 (horizontal ? LEFT : BASELINE);
     }
@@ -908,11 +932,11 @@
         int paddingRight = getPaddingRight();
         int paddingBottom = getPaddingBottom();
 
-        mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
-        mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
+        horizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
+        verticalAxis.layout(targetHeight - paddingTop - paddingBottom);
 
-        int[] hLocations = mHorizontalAxis.getLocations();
-        int[] vLocations = mVerticalAxis.getLocations();
+        int[] hLocations = horizontalAxis.getLocations();
+        int[] vLocations = verticalAxis.getLocations();
 
         for (int i = 0, N = getChildCount(); i < N; i++) {
             View c = getChildAt(i);
@@ -941,8 +965,8 @@
 
             int dx, dy;
 
-            Bounds colBounds = mHorizontalAxis.getGroupBounds().getValue(i);
-            Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i);
+            Bounds colBounds = horizontalAxis.getGroupBounds().getValue(i);
+            Bounds rowBounds = verticalAxis.getGroupBounds().getValue(i);
 
             // Gravity offsets: the location of the alignment group relative to its cell group.
             //noinspection NullableProblems
@@ -990,7 +1014,7 @@
      distinguished by the "horizontal" flag which is true for the horizontal axis and false
      for the vertical one.
      */
-    private class Axis {
+    final class Axis {
         private static final int MIN_VALUE = -1000000;
 
         private static final int NEW = 0;
@@ -999,9 +1023,8 @@
 
         public final boolean horizontal;
 
-        public int count = UNDEFINED;
-        public boolean countValid = false;
-        public boolean countWasExplicitySet = false;
+        public int definedCount = UNDEFINED;
+        private int inferredCount = UNDEFINED;
 
         PackedMap<Spec, Bounds> groupBounds;
         public boolean groupBoundsValid = false;
@@ -1024,7 +1047,7 @@
         public int[] locations;
         public boolean locationsValid = false;
 
-        private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED;
+        boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
 
         private MutableInt parentMin = new MutableInt(0);
         private MutableInt parentMax = new MutableInt(-MAX_SIZE);
@@ -1046,25 +1069,27 @@
             return count == -1 ? UNDEFINED : count;
         }
 
-        public int getCount() {
-            if (!countValid) {
-                count = max(0, maxIndex()); // if there are no cells, the count is zero
-                countValid = true;
+        private int getInferredCount() {
+            if (inferredCount == UNDEFINED) {
+                inferredCount = max(0, maxIndex()); // if there are no cells, actual count is zero
             }
-            return count;
+            return inferredCount;
+        }
+
+        public int getCount() {
+            return max(definedCount, getInferredCount());
         }
 
         public void setCount(int count) {
-            this.count = count;
-            this.countWasExplicitySet = count != UNDEFINED;
+            this.definedCount = count;
         }
 
         public boolean isOrderPreserved() {
-            return mOrderPreserved;
+            return orderPreserved;
         }
 
         public void setOrderPreserved(boolean orderPreserved) {
-            mOrderPreserved = orderPreserved;
+            this.orderPreserved = orderPreserved;
             invalidateStructure();
         }
 
@@ -1093,7 +1118,7 @@
             }
         }
 
-        private PackedMap<Spec, Bounds> getGroupBounds() {
+        public PackedMap<Spec, Bounds> getGroupBounds() {
             if (groupBounds == null) {
                 groupBounds = createGroupBounds();
             }
@@ -1183,7 +1208,7 @@
 
         // Group arcs by their first vertex, returning an array of arrays.
         // This is linear in the number of arcs.
-        private Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
+        Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
             int N = getCount() + 1; // the number of vertices
             Arc[][] result = new Arc[N][];
             int[] sizes = new int[N];
@@ -1262,7 +1287,7 @@
             addComponentSizes(maxs, getBackwardLinks());
 
             // Add ordering constraints to prevent row/col sizes from going negative
-            if (mOrderPreserved) {
+            if (orderPreserved) {
                 // Add a constraint for every row/col
                 for (int i = 0; i < getCount(); i++) {
                     include(mins, new Interval(i, i + 1), new MutableInt(0));
@@ -1315,6 +1340,48 @@
             return false;
         }
 
+        private void init(int[] locations) {
+            Arrays.fill(locations, MIN_VALUE);
+            locations[0] = 0;
+        }
+
+        private String arcsToString(List<Arc> arcs) {
+            String var = horizontal ? "c" : "r";
+            StringBuilder result = new StringBuilder();
+            boolean first = false;
+            for(Arc arc : arcs) {
+                if (!first) {
+                    first = true;
+                } else {
+                    result =result.append(", ");
+                }
+                int src = arc.span.min;
+                int dst = arc.span.max;
+                int value = arc.value.value;
+                result.append((src < dst) ?
+                        var + dst + " - " + var + src + " > " + value :
+                        var + src + " - " + var + dst + " < " + -value);
+
+            }
+            return result.toString();
+        }
+
+        private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
+            List<Arc> culprits = new ArrayList<Arc>();
+            List<Arc> removed = new ArrayList<Arc>();
+            for (int c = 0; c < arcs.length; c++) {
+                Arc arc = arcs[c];
+                if (culprits0[c]) {
+                    culprits.add(arc);
+                }
+                if (!arc.valid) {
+                    removed.add(arc);
+                }
+            }
+            Log.d(TAG, axisName + " constraints: " + arcsToString(culprits) + " are inconsistent; "
+                    + "permanently removing: " + arcsToString(removed) + ". ");
+        }
+
         /*
         Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
 
@@ -1350,51 +1417,54 @@
         completes in O(N) steps with very low constants.
         */
         private void solve(Arc[] arcs, int[] locations) {
-            String axis = horizontal ? "horizontal" : "vertical";
+            String axisName = horizontal ? "horizontal" : "vertical";
             int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
+            boolean[] originalCulprits = null;
 
-            // We take one extra pass over traditional Bellman-Ford (and omit their final step)
-            for (int i = 0; i < N; i++) {
-                boolean changed = false;
-                for (int j = 0, length = arcs.length; j < length; j++) {
-                    changed |= relax(locations, arcs[j]);
-                }
-                if (!changed) {
-                    if (DEBUG) {
-                        Log.v(TAG, axis + " iteration completed in " + (1 + i) + " steps of " + N);
+            for (int p = 0; p < arcs.length; p++) {
+                init(locations);
+
+                // We take one extra pass over traditional Bellman-Ford (and omit their final step)
+                for (int i = 0; i < N; i++) {
+                    boolean changed = false;
+                    for (int j = 0, length = arcs.length; j < length; j++) {
+                        changed |= relax(locations, arcs[j]);
                     }
-                    return;
-                }
-            }
-
-            Log.d(TAG, "The " + axis + " constraints contained a contradiction. Resolving... ");
-            Log.d(TAG, Arrays.toString(arcs));
-
-            boolean[] culprits = new boolean[arcs.length];
-            for (int i = 0; i < N; i++) {
-                for (int j = 0, length = arcs.length; j < length; j++) {
-                    culprits[j] |= relax(locations, arcs[j]);
-                }
-            }
-            for (int i = 0; i < culprits.length; i++) {
-                if (culprits[i]) {
-                    Arc arc = arcs[i];
-                    // Only remove max values, min values alone cannot be inconsistent
-                    if (arc.span.min < arc.span.max) {
-                        continue;
+                    if (!changed) {
+                        if (originalCulprits != null) {
+                            logError(axisName, arcs, originalCulprits);
+                        }
+                        if (DEBUG) {
+                            Log.v(TAG, axisName + " iteration completed in " +
+                                    (1 + i) + " steps of " + N);
+                        }
+                        return;
                     }
-                    Log.d(TAG, "Removing: " + arc);
-                    arc.valid = false;
-                    break;
+                }
+
+                boolean[] culprits = new boolean[arcs.length];
+                for (int i = 0; i < N; i++) {
+                    for (int j = 0, length = arcs.length; j < length; j++) {
+                        culprits[j] |= relax(locations, arcs[j]);
+                    }
+                }
+
+                if (p == 0) {
+                    originalCulprits = culprits;
+                }
+
+                for (int i = 0; i < arcs.length; i++) {
+                    if (culprits[i]) {
+                        Arc arc = arcs[i];
+                        // Only remove max values, min values alone cannot be inconsistent
+                        if (arc.span.min < arc.span.max) {
+                            continue;
+                        }
+                        arc.valid = false;
+                        break;
+                    }
                 }
             }
-            solve1(arcs, locations);
-        }
-
-        private void solve1(Arc[] arcs, int[] a) {
-            Arrays.fill(a, MIN_VALUE);
-            a[0] = 0;
-            solve(arcs, a);
         }
 
         private void computeMargins(boolean leading) {
@@ -1410,7 +1480,9 @@
             }
         }
 
-        private int[] getLeadingMargins() {
+        // External entry points
+
+        public int[] getLeadingMargins() {
             if (leadingMargins == null) {
                 leadingMargins = new int[getCount() + 1];
             }
@@ -1421,7 +1493,7 @@
             return leadingMargins;
         }
 
-        private int[] getTrailingMargins() {
+        public int[] getTrailingMargins() {
             if (trailingMargins == null) {
                 trailingMargins = new int[getCount() + 1];
             }
@@ -1433,10 +1505,10 @@
         }
 
         private void computeLocations(int[] a) {
-            solve1(getArcs(), a);
+            solve(getArcs(), a);
         }
 
-        private int[] getLocations() {
+        public int[] getLocations() {
             if (locations == null) {
                 int N = getCount() + 1;
                 locations = new int[N];
@@ -1448,8 +1520,6 @@
             return locations;
         }
 
-        // External entry points
-
         private int size(int[] locations) {
             return max2(locations, 0) - locations[0];
         }
@@ -1465,7 +1535,7 @@
             return size(getLocations());
         }
 
-        private int getMeasure(int measureSpec) {
+        public int getMeasure(int measureSpec) {
             int mode = MeasureSpec.getMode(measureSpec);
             int size = MeasureSpec.getSize(measureSpec);
             switch (mode) {
@@ -1485,13 +1555,13 @@
             }
         }
 
-        private void layout(int size) {
+        public void layout(int size) {
             setParentConstraints(size, size);
             getLocations();
         }
 
-        private void invalidateStructure() {
-            countValid = false;
+        public void invalidateStructure() {
+            inferredCount = UNDEFINED;
 
             groupBounds = null;
             forwardLinks = null;
@@ -1506,7 +1576,7 @@
             invalidateValues();
         }
 
-        private void invalidateValues() {
+        public void invalidateValues() {
             groupBoundsValid = false;
             forwardLinksValid = false;
             backwardLinksValid = false;
@@ -1536,9 +1606,22 @@
      * both aspects of alignment within the cell group. It is also possible to specify a child's
      * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
      * method.
-     * <p>
-     * See {@link GridLayout} for a description of the conventions used by GridLayout
-     * in reference to grid indices.
+     *
+     * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
+     *
+     * Because the default values of the {@link #width} and {@link #height}
+     * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
+     * declared in the layout parameters of GridLayout's children. In addition,
+     * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
+     * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
+     * instead controlled by the principle of <em>flexibility</em>,
+     * as discussed in {@link GridLayout}.
+     *
+     * <h4>Summary</h4>
+     *
+     * You should not need to use either of the special size values:
+     * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
+     * a GridLayout.
      *
      * <h4>Default values</h4>
      *
@@ -1561,12 +1644,17 @@
      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
      *          {@code false}; otherwise {@link #UNDEFINED}, to
      *          indicate that a default value should be computed on demand. </li>
-     *     <li>{@link #rowSpec}{@code .span} = {@code [0, 1]} </li>
-     *     <li>{@link #rowSpec}{@code .alignment} = {@link #BASELINE} </li>
-     *     <li>{@link #columnSpec}{@code .span} = {@code [0, 1]} </li>
-     *     <li>{@link #columnSpec}{@code .alignment} = {@link #LEFT} </li>
+     *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
+     *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
+     *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
+     *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
+     *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
+     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #LEFT} </li>
      * </ul>
      *
+     * See {@link GridLayout} for a more complete description of the conventions
+     * used by GridLayout in the interpretation of the properties of this class.
+     *
      * @attr ref android.R.styleable#GridLayout_Layout_layout_row
      * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
      * @attr ref android.R.styleable#GridLayout_Layout_layout_column
@@ -1606,15 +1694,16 @@
         // Instance variables
 
         /**
-         * The spec that specifies the vertical characteristics of the cell group
+         * The spec that defines the vertical characteristics of the cell group
          * described by these layout parameters.
          */
-        public Spec rowSpec;
+        public Spec rowSpec = Spec.UNDEFINED;
+
         /**
-         * The spec that specifies the horizontal characteristics of the cell group
+         * The spec that defines the horizontal characteristics of the cell group
          * described by these layout parameters.
          */
-        public Spec columnSpec;
+        public Spec columnSpec = Spec.UNDEFINED;
 
         // Constructors
 
@@ -1646,7 +1735,7 @@
          * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
          */
         public LayoutParams() {
-            this(spec(UNDEFINED), spec(UNDEFINED));
+            this(Spec.UNDEFINED, Spec.UNDEFINED);
         }
 
         // Copying constructors
@@ -1670,8 +1759,8 @@
          */
         public LayoutParams(LayoutParams that) {
             super(that);
-            this.rowSpec = new Spec(that.rowSpec);
-            this.columnSpec = new Spec(that.columnSpec);
+            this.rowSpec = that.rowSpec;
+            this.columnSpec = that.columnSpec;
         }
 
         // AttributeSet constructors
@@ -1750,11 +1839,11 @@
             this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
         }
 
-        private void setRowSpecSpan(Interval span) {
+        final void setRowSpecSpan(Interval span) {
             rowSpec = rowSpec.copyWriteSpan(span);
         }
 
-        private void setColumnSpecSpan(Interval span) {
+        final void setColumnSpecSpan(Interval span) {
             columnSpec = columnSpec.copyWriteSpan(span);
         }
     }
@@ -1763,7 +1852,7 @@
     In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
     Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
      */
-    private static class Arc {
+    final static class Arc {
         public final Interval span;
         public final MutableInt value;
         public boolean valid = true;
@@ -1781,18 +1870,18 @@
 
     // A mutable Integer - used to avoid heap allocation during the layout operation
 
-    private static class MutableInt {
+    final static class MutableInt {
         public int value;
 
-        private MutableInt() {
+        public MutableInt() {
             reset();
         }
 
-        private MutableInt(int value) {
+        public MutableInt(int value) {
             this.value = value;
         }
 
-        private void reset() {
+        public void reset() {
             value = Integer.MIN_VALUE;
         }
 
@@ -1802,7 +1891,7 @@
         }
     }
 
-    private static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
+    final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
         private final Class<K> keyType;
         private final Class<V> valueType;
 
@@ -1811,7 +1900,7 @@
             this.valueType = valueType;
         }
 
-        private static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
+        public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
             return new Assoc<K, V>(keyType, valueType);
         }
 
@@ -1847,7 +1936,7 @@
     rather than using (and storing) an implementation of Map<Key, ?>.
      */
     @SuppressWarnings(value = "unchecked")
-    private static class PackedMap<K, V> {
+    final static class PackedMap<K, V> {
         public final int[] index;
         public final K[] keys;
         public final V[] values;
@@ -1859,7 +1948,7 @@
             this.values = compact(values, index);
         }
 
-        private V getValue(int i) {
+        public V getValue(int i) {
             return values[index[i]];
         }
 
@@ -1907,7 +1996,7 @@
     group to Bounds and to loop through all Views in the group taking the maximum
     of the values for each View.
     */
-    private static class Bounds {
+    static class Bounds {
         public int before;
         public int after;
         public int flexibility; // we're flexible iff all included specs are flexible
@@ -1969,7 +2058,7 @@
      * Intervals are often written as {@code [min, max]} and represent the set of values
      * {@code x} such that {@code min <= x < max}.
      */
-    static class Interval {
+    final static class Interval {
         /**
          * The minimum value.
          */
@@ -1995,11 +2084,11 @@
             this.max = max;
         }
 
-        private int size() {
+        int size() {
             return max - min;
         }
 
-        private Interval inverse() {
+        Interval inverse() {
             return new Interval(max, min);
         }
 
@@ -2062,32 +2151,31 @@
      * For column groups, this specifies the horizontal alignment.
      */
     public static class Spec {
+        static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
+
+        final boolean startDefined;
         final Interval span;
         final Alignment alignment;
 
-        private Spec(Interval span, Alignment alignment) {
+        private Spec(boolean startDefined, Interval span, Alignment alignment) {
+            this.startDefined = startDefined;
             this.span = span;
             this.alignment = alignment;
         }
 
-        /* Copying constructor */
-        private Spec(Spec that) {
-            this(that.span, that.alignment);
+        private Spec(boolean startDefined, int start, int size, Alignment alignment) {
+            this(startDefined, new Interval(start, start + size), alignment);
         }
 
-        private Spec(int start, int size, Alignment alignment) {
-            this(new Interval(start, start + size), alignment);
+        final Spec copyWriteSpan(Interval span) {
+            return new Spec(startDefined, span, alignment);
         }
 
-        private Spec copyWriteSpan(Interval span) {
-            return new Spec(span, alignment);
+        final Spec copyWriteAlignment(Alignment alignment) {
+            return new Spec(startDefined, span, alignment);
         }
 
-        private Spec copyWriteAlignment(Alignment alignment) {
-            return new Spec(span, alignment);
-        }
-
-        int getFlexibility() {
+        final int getFlexibility() {
             return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
         }
 
@@ -2143,7 +2231,7 @@
      * @param alignment the alignment
      */
     public static Spec spec(int start, int size, Alignment alignment) {
-        return new Spec(start, size, alignment);
+        return new Spec(start != UNDEFINED, start, size, alignment);
     }
 
     /**
@@ -2246,7 +2334,7 @@
         }
     }
 
-    private static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
+    static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
         public int getAlignmentValue(View view, int viewSize) {
             return UNDEFINED;
         }
@@ -2367,7 +2455,7 @@
         }
     };
 
-    private static boolean canStretch(int flexibility) {
+    static boolean canStretch(int flexibility) {
         return (flexibility & CAN_STRETCH) != 0;
     }
 
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 542a1ef..e571998 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -532,7 +532,7 @@
         final int dx = mScrollerX.mFinal - mScrollerX.mStart;
         final int dy = mScrollerY.mFinal - mScrollerY.mStart;
         return !isFinished() && Math.signum(xvel) == Math.signum(dx) &&
-        Math.signum(yvel) == Math.signum(dy);
+                Math.signum(yvel) == Math.signum(dy);
     }
 
     static class SplineOverScroller {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 4d45c2f..aac20c4 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -796,6 +796,21 @@
      * @param y the popup's y location offset
      */
     public void showAtLocation(View parent, int gravity, int x, int y) {
+        showAtLocation(parent.getWindowToken(), gravity, x, y);
+    }
+
+    /**
+     * Display the content view in a popup window at the specified location.
+     *
+     * @param token Window token to use for creating the new window
+     * @param gravity the gravity which controls the placement of the popup window
+     * @param x the popup's x location offset
+     * @param y the popup's y location offset
+     *
+     * @hide Internal use only. Applications should use
+     *       {@link #showAtLocation(View, int, int, int)} instead.
+     */
+    public void showAtLocation(IBinder token, int gravity, int x, int y) {
         if (isShowing() || mContentView == null) {
             return;
         }
@@ -805,7 +820,7 @@
         mIsShowing = true;
         mIsDropdown = false;
 
-        WindowManager.LayoutParams p = createPopupLayout(parent.getWindowToken());
+        WindowManager.LayoutParams p = createPopupLayout(token);
         p.windowAnimations = computeAnimationResource();
        
         preparePopup(p);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 58e0edd..af820ac 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1193,6 +1193,11 @@
         }
         super.setEnabled(enabled);
         prepareCursorControllers();
+        if (enabled) {
+            // Make sure IME is updated with current editor info.
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) imm.restartInput(this);
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index 29ad15b..f9c2cce 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -21,6 +21,8 @@
 import com.android.internal.widget.LockScreenWidgetCallback;
 import com.android.internal.widget.LockScreenWidgetInterface;
 
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -68,7 +70,7 @@
     private int mClientGeneration;
     private Metadata mMetadata = new Metadata();
     private boolean mAttached;
-    private ComponentName mClientName;
+    private PendingIntent mClientIntent;
     private int mTransportControlFlags;
     private int mPlayState;
     private AudioManager mAudioManager;
@@ -116,7 +118,7 @@
                     }
                 }
                 mClientGeneration = msg.arg1;
-                mClientName = (ComponentName) msg.obj;
+                mClientIntent = (PendingIntent) msg.obj;
                 break;
 
             }
@@ -174,12 +176,12 @@
             }
         }
 
-        public void setCurrentClientId(int clientGeneration, ComponentName clientEventReceiver,
+        public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
                 boolean clearing) throws RemoteException {
             Handler handler = mLocalHandler.get();
             if (handler != null) {
                 handler.obtainMessage(MSG_SET_GENERATION_ID,
-                    clientGeneration, (clearing ? 1 : 0), clientEventReceiver).sendToTarget();
+                    clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
             }
         }
     };
@@ -365,16 +367,27 @@
     }
 
     private void sendMediaButtonClick(int keyCode) {
-        // TODO: target to specific player based on mClientName
+        // use the registered PendingIntent that will be processed by the registered
+        //    media button event receiver, which is the component of mClientIntent
         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);
+        try {
+            mClientIntent.send(getContext(), 0, intent);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Error sending intent for media button down: "+e);
+            e.printStackTrace();
+        }
 
         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);
+        try {
+            mClientIntent.send(getContext(), 0, intent);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Error sending intent for media button up: "+e);
+            e.printStackTrace();
+        }
     }
 
     public void setCallback(LockScreenWidgetCallback callback) {
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index fa9a7b7..84bba9a 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -283,7 +283,7 @@
                                     jint count, jint contextCount, jint dirFlags,
                                     jfloat* resultAdvances, jfloat& resultTotalAdvance) {
     // Compute advances and return them
-    TextLayoutCacheValue::computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
+    computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
             resultAdvances, &resultTotalAdvance);
 }
 
@@ -321,4 +321,73 @@
     }
 }
 
+void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
+        size_t start, size_t count, size_t contextCount, int dirFlags,
+        jfloat* outAdvances, jfloat* outTotalAdvance) {
+    SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
+    jchar* buffer = tempBuffer.get();
+    SkScalar* scalarArray = (SkScalar*)outAdvances;
+
+    // this is where we'd call harfbuzz
+    // for now we just use ushape.c
+    size_t widths;
+    const jchar* text;
+    if (dirFlags & 0x1) { // rtl, call arabic shaping in case
+        UErrorCode status = U_ZERO_ERROR;
+        // Use fixed length since we need to keep start and count valid
+        u_shapeArabic(chars, contextCount, buffer, contextCount,
+                U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
+                U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
+                U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
+        // we shouldn't fail unless there's an out of memory condition,
+        // in which case we're hosed anyway
+        for (int i = start, e = i + count; i < e; ++i) {
+            if (buffer[i] == UNICODE_NOT_A_CHAR) {
+                buffer[i] = UNICODE_ZWSP; // zero-width-space for skia
+            }
+        }
+        text = buffer + start;
+        widths = paint->getTextWidths(text, count << 1, scalarArray);
+    } else {
+        text = chars + start;
+        widths = paint->getTextWidths(text, count << 1, scalarArray);
+    }
+
+    jfloat totalAdvance = 0;
+    if (widths < count) {
+#if DEBUG_ADVANCES
+    LOGD("ICU -- count=%d", widths);
+#endif
+        // Skia operates on code points, not code units, so surrogate pairs return only
+        // one value. Expand the result so we have one value per UTF-16 code unit.
+
+        // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
+        // leaving the remaining widths zero.  Not nice.
+        for (size_t i = 0, p = 0; i < widths; ++i) {
+            totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]);
+            if (p < count &&
+                    text[p] >= UNICODE_FIRST_LOW_SURROGATE &&
+                    text[p] < UNICODE_FIRST_PRIVATE_USE &&
+                    text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE &&
+                    text[p-1] < UNICODE_FIRST_LOW_SURROGATE) {
+                outAdvances[p++] = 0;
+            }
+#if DEBUG_ADVANCES
+            LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
+#endif
+        }
+    } else {
+#if DEBUG_ADVANCES
+    LOGD("ICU -- count=%d", count);
+#endif
+        for (size_t i = 0; i < count; i++) {
+            totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]);
+#if DEBUG_ADVANCES
+            LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
+#endif
+        }
+    }
+    *outTotalAdvance = totalAdvance;
+}
+
 }
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index 0a29d78..2522df8 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -106,5 +106,9 @@
                            UErrorCode &status);
     static void handleText(SkPaint* paint, const jchar* text, jsize len,
                            int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas, SkPath* path);
+
+    static void computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
+            size_t start, size_t count, size_t contextCount, int dirFlags,
+            jfloat* outAdvances, jfloat* outTotalAdvance);
 };
 } // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index d04e059..951e01d 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -52,13 +52,7 @@
     mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     if (mDebugEnabled) {
-        LOGD("Start time: %lld", mCacheStartTime);
-#if RTL_USE_HARFBUZZ
-        LOGD("Using HARFBUZZ");
-#else
-        LOGD("Using ICU");
-#endif
-        LOGD("Initialization is done");
+        LOGD("Initialization is done - Start time: %lld", mCacheStartTime);
     }
 
     mInitialized = true;
@@ -242,6 +236,22 @@
     hinting = paint->getHinting();
 }
 
+TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
+        text(NULL),
+        textCopy(other.textCopy),
+        contextCount(other.contextCount),
+        dirFlags(other.dirFlags),
+        typeface(other.typeface),
+        textSize(other.textSize),
+        textSkewX(other.textSkewX),
+        textScaleX(other.textScaleX),
+        flags(other.flags),
+        hinting(other.hinting) {
+    if (other.text) {
+        textCopy.setTo(other.text);
+    }
+}
+
 bool TextLayoutCacheKey::operator<(const TextLayoutCacheKey& rhs) const {
     LTE_INT(count) {
         LTE_INT(contextCount) {
@@ -253,7 +263,7 @@
                                 LTE_INT(flags) {
                                     LTE_INT(hinting) {
                                         LTE_INT(dirFlags) {
-                                            return strncmp16(text, rhs.text, contextCount) < 0;
+                                            return strncmp16(getText(), rhs.getText(), contextCount) < 0;
                                         }
                                     }
                                 }
@@ -269,7 +279,7 @@
 
 void TextLayoutCacheKey::internalTextCopy() {
     textCopy.setTo(text, contextCount);
-    text = textCopy.string();
+    text = NULL;
 }
 
 size_t TextLayoutCacheKey::getSize() {
@@ -302,13 +312,8 @@
     mAdvancesCount = count;
     mAdvances = new float[count];
 
-#if RTL_USE_HARFBUZZ
     computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
             mAdvances, &mTotalAdvance, &mGlyphs, &mGlyphsCount);
-#else
-    computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
-            mAdvances, &mTotalAdvance);
-#endif
 #if DEBUG_ADVANCES
     LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - "
             "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, mTotalAdvance,
@@ -632,75 +637,6 @@
     HB_FreeFace(shaperItem.face);
 }
 
-void TextLayoutCacheValue::computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
-        size_t start, size_t count, size_t contextCount, int dirFlags,
-        jfloat* outAdvances, jfloat* outTotalAdvance) {
-    SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
-    jchar* buffer = tempBuffer.get();
-    SkScalar* scalarArray = (SkScalar*)outAdvances;
-
-    // this is where we'd call harfbuzz
-    // for now we just use ushape.c
-    size_t widths;
-    const jchar* text;
-    if (dirFlags & 0x1) { // rtl, call arabic shaping in case
-        UErrorCode status = U_ZERO_ERROR;
-        // Use fixed length since we need to keep start and count valid
-        u_shapeArabic(chars, contextCount, buffer, contextCount,
-                U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
-                U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
-                U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
-        // we shouldn't fail unless there's an out of memory condition,
-        // in which case we're hosed anyway
-        for (int i = start, e = i + count; i < e; ++i) {
-            if (buffer[i] == UNICODE_NOT_A_CHAR) {
-                buffer[i] = UNICODE_ZWSP; // zero-width-space for skia
-            }
-        }
-        text = buffer + start;
-        widths = paint->getTextWidths(text, count << 1, scalarArray);
-    } else {
-        text = chars + start;
-        widths = paint->getTextWidths(text, count << 1, scalarArray);
-    }
-
-    jfloat totalAdvance = 0;
-    if (widths < count) {
-#if DEBUG_ADVANCES
-    LOGD("ICU -- count=%d", widths);
-#endif
-        // Skia operates on code points, not code units, so surrogate pairs return only
-        // one value. Expand the result so we have one value per UTF-16 code unit.
-
-        // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
-        // leaving the remaining widths zero.  Not nice.
-        for (size_t i = 0, p = 0; i < widths; ++i) {
-            totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]);
-            if (p < count &&
-                    text[p] >= UNICODE_FIRST_LOW_SURROGATE &&
-                    text[p] < UNICODE_FIRST_PRIVATE_USE &&
-                    text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE &&
-                    text[p-1] < UNICODE_FIRST_LOW_SURROGATE) {
-                outAdvances[p++] = 0;
-            }
-#if DEBUG_ADVANCES
-            LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
-#endif
-        }
-    } else {
-#if DEBUG_ADVANCES
-    LOGD("ICU -- count=%d", count);
-#endif
-        for (size_t i = 0; i < count; i++) {
-            totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]);
-#if DEBUG_ADVANCES
-            LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
-#endif
-        }
-    }
-    *outTotalAdvance = totalAdvance;
-}
-
 void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem* shaperItem) {
     delete[] shaperItem->glyphs;
     delete[] shaperItem->attributes;
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 0d8d71f..9143954 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -72,6 +72,8 @@
             const UChar* text, size_t start, size_t count,
             size_t contextCount, int dirFlags);
 
+    TextLayoutCacheKey(const TextLayoutCacheKey& other);
+
     bool operator<(const TextLayoutCacheKey& rhs) const;
 
     /**
@@ -86,7 +88,7 @@
     size_t getSize();
 
 private:
-    const UChar* text;
+    const UChar* text; // if text is NULL, use textCopy
     String16 textCopy;
     size_t start;
     size_t count;
@@ -98,6 +100,10 @@
     SkScalar textScaleX;
     uint32_t flags;
     SkPaint::Hinting hinting;
+
+    inline const UChar* getText() const {
+        return text ? text : textCopy.string();
+    }
 }; // TextLayoutCacheKey
 
 /*
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 80741eb..9755f22 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1533,19 +1533,14 @@
         <activity android:name="android.accounts.ChooseTypeAndAccountActivity"
                 android:excludeFromRecents="true"
                 android:exported="true"
-                android:theme="@android:style/Theme.Holo.Dialog"
+                android:theme="@android:style/Theme.Holo.DialogWhenLarge.NoActionBar"
                 android:label="@string/choose_account_label"
                 android:process=":ui">
-                <intent-filter>
-                    <action android:name="android.intent.action.PICK" />
-                    <category android:name="android.intent.category.ACCOUNT" />
-                </intent-filter>
         </activity>
 
         <activity android:name="android.accounts.ChooseAccountTypeActivity"
                 android:excludeFromRecents="true"
-                android:exported="true"
-                android:theme="@android:style/Theme.Holo.Dialog"
+                android:theme="@android:style/Theme.Holo.DialogWhenLarge.NoActionBar"
                 android:label="@string/choose_account_label"
                 android:process=":ui">
         </activity>
diff --git a/core/res/res/drawable-hdpi/ic_checkmark_holo_light.png b/core/res/res/drawable-hdpi/ic_checkmark_holo_light.png
new file mode 100644
index 0000000..2c6719b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_checkmark_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png b/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png
index a231195..e0f55bf 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_focused_holo.png b/core/res/res/drawable-hdpi/scrubber_control_focused_holo.png
new file mode 100644
index 0000000..7b264f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_holo.png b/core/res/res/drawable-hdpi/scrubber_control_holo.png
deleted file mode 100644
index fae05e5..0000000
--- a/core/res/res/drawable-hdpi/scrubber_control_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_normal_holo.png b/core/res/res/drawable-hdpi/scrubber_control_normal_holo.png
new file mode 100644
index 0000000..c3a5f7d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_pressed_holo.png b/core/res/res/drawable-hdpi/scrubber_control_pressed_holo.png
index ff4d710..6fa4e00 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_pressed_holo.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_checkmark_holo_light.png b/core/res/res/drawable-mdpi/ic_checkmark_holo_light.png
new file mode 100644
index 0000000..744e964
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_checkmark_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_disabled_holo.png b/core/res/res/drawable-mdpi/scrubber_control_disabled_holo.png
index 9d7b77c..a035f6f 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_disabled_holo.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_disabled_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_focused_holo.png b/core/res/res/drawable-mdpi/scrubber_control_focused_holo.png
new file mode 100644
index 0000000..5c9720f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_holo.png b/core/res/res/drawable-mdpi/scrubber_control_holo.png
deleted file mode 100644
index 832fa07..0000000
--- a/core/res/res/drawable-mdpi/scrubber_control_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_normal_holo.png b/core/res/res/drawable-mdpi/scrubber_control_normal_holo.png
new file mode 100644
index 0000000..2497716
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_pressed_holo.png b/core/res/res/drawable-mdpi/scrubber_control_pressed_holo.png
index 4a518f2..5fc3ca2 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_pressed_holo.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_checkmark_holo_light.png b/core/res/res/drawable-xhdpi/ic_checkmark_holo_light.png
new file mode 100644
index 0000000..1607f35
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_checkmark_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_disabled_holo.png b/core/res/res/drawable-xhdpi/scrubber_control_disabled_holo.png
index 0b0bf24..d70151a 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_disabled_holo.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_disabled_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_focused_holo.png b/core/res/res/drawable-xhdpi/scrubber_control_focused_holo.png
new file mode 100644
index 0000000..017688f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_holo.png b/core/res/res/drawable-xhdpi/scrubber_control_holo.png
deleted file mode 100644
index 45060cb..0000000
--- a/core/res/res/drawable-xhdpi/scrubber_control_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_normal_holo.png b/core/res/res/drawable-xhdpi/scrubber_control_normal_holo.png
new file mode 100644
index 0000000..727fcf1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_pressed_holo.png b/core/res/res/drawable-xhdpi/scrubber_control_pressed_holo.png
index d1fe115..6d03e42 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_pressed_holo.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable/btn_cab_done_holo_dark.xml b/core/res/res/drawable/btn_cab_done_holo_dark.xml
index 2cdb605..65d3496 100644
--- a/core/res/res/drawable/btn_cab_done_holo_dark.xml
+++ b/core/res/res/drawable/btn_cab_done_holo_dark.xml
@@ -14,9 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_window_focused="false" android:state_enabled="true"
-        android:drawable="@drawable/btn_cab_done_default_holo_dark" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_pressed="true"
         android:drawable="@drawable/btn_cab_done_pressed_holo_dark" />
     <item android:state_focused="true" android:state_enabled="true"
diff --git a/core/res/res/drawable/btn_cab_done_holo_light.xml b/core/res/res/drawable/btn_cab_done_holo_light.xml
index 81add4c..f6a63f4 100644
--- a/core/res/res/drawable/btn_cab_done_holo_light.xml
+++ b/core/res/res/drawable/btn_cab_done_holo_light.xml
@@ -14,9 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_window_focused="false" android:state_enabled="true"
-        android:drawable="@drawable/btn_cab_done_default_holo_light" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_pressed="true"
         android:drawable="@drawable/btn_cab_done_pressed_holo_light" />
     <item android:state_focused="true" android:state_enabled="true"
diff --git a/core/res/res/drawable/item_background_holo_dark.xml b/core/res/res/drawable/item_background_holo_dark.xml
index 2d4f20a..f188df7 100644
--- a/core/res/res/drawable/item_background_holo_dark.xml
+++ b/core/res/res/drawable/item_background_holo_dark.xml
@@ -17,8 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
           android:exitFadeDuration="@android:integer/config_mediumAnimTime">
 
-    <item android:state_window_focused="false" android:drawable="@color/transparent" />
-
     <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
     <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_dark" />
     <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_dark" />
diff --git a/core/res/res/drawable/item_background_holo_light.xml b/core/res/res/drawable/item_background_holo_light.xml
index c616d86..ee3f4d8 100644
--- a/core/res/res/drawable/item_background_holo_light.xml
+++ b/core/res/res/drawable/item_background_holo_light.xml
@@ -17,8 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
           android:exitFadeDuration="@android:integer/config_mediumAnimTime">
 
-    <item android:state_window_focused="false" android:drawable="@color/transparent" />
-
     <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
     <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_light" />
     <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_light" />
diff --git a/core/res/res/drawable/scrubber_control_selector_holo.xml b/core/res/res/drawable/scrubber_control_selector_holo.xml
index d146eee..1e6c668 100644
--- a/core/res/res/drawable/scrubber_control_selector_holo.xml
+++ b/core/res/res/drawable/scrubber_control_selector_holo.xml
@@ -15,7 +15,8 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:state_enabled="true" android:drawable="@android:drawable/scrubber_control_pressed_holo" />
-    <item android:state_enabled="true" android:drawable="@android:drawable/scrubber_control_holo" />
-    <item                              android:drawable="@android:drawable/scrubber_control_disabled_holo" />
+    <item android:state_enabled="false" android:drawable="@android:drawable/scrubber_control_disabled_holo" />
+    <item android:state_pressed="true" android:drawable="@android:drawable/scrubber_control_pressed_holo" />
+    <item android:state_selected="true" android:drawable="@android:drawable/scrubber_control_focused_holo" />
+    <item android:drawable="@android:drawable/scrubber_control_normal_holo" />
 </selector>
diff --git a/core/res/res/layout/choose_account_type.xml b/core/res/res/layout/choose_account_type.xml
new file mode 100644
index 0000000..db96dc1
--- /dev/null
+++ b/core/res/res/layout/choose_account_type.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/layout/list_content.xml
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingLeft="16dip"
+    android:paddingRight="16dip">
+
+    <TextView android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_gravity="left"
+        android:text="@string/add_account_label"
+        android:paddingTop="16dip"
+        android:paddingBottom="16dip"
+        android:textColor="@android:color/holo_blue_light"
+    />
+
+    <View android:layout_height="3dip"
+          android:layout_width="match_parent"
+          android:background="#323232"/>
+
+    <ListView android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:drawSelectorOnTop="false"
+        android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/choose_selected_account_row.xml b/core/res/res/layout/choose_selected_account_row.xml
new file mode 100644
index 0000000..d88750d
--- /dev/null
+++ b/core/res/res/layout/choose_selected_account_row.xml
@@ -0,0 +1,46 @@
+<?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:paddingLeft="0dip"
+    android:paddingRight="0dip"
+    android:orientation="horizontal" >
+
+   <ImageView android:id="@+id/account_row_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:paddingRight="8dip" />
+
+   <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/account_row_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:gravity="center_vertical"
+        android:minHeight="?android:attr/listPreferredItemHeight" />
+
+    <ImageView android:id="@+id/account_row_checkmark"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:gravity="right"
+         android:layout_weight="2"
+         android:paddingRight="8dip"
+         android:src="@drawable/ic_checkmark_holo_light" />
+    
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/choose_type_and_account.xml b/core/res/res/layout/choose_type_and_account.xml
index 8be01b42..5a05126 100644
--- a/core/res/res/layout/choose_type_and_account.xml
+++ b/core/res/res/layout/choose_type_and_account.xml
@@ -24,21 +24,44 @@
     android:paddingLeft="16dip"
     android:paddingRight="16dip">
 
-    <ListView xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@android:id/list"
+    <TextView android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_gravity="left"
+        android:text="@string/choose_account_label"
+        android:paddingTop="16dip"
+        android:paddingBottom="16dip"
+        android:textColor="@android:color/holo_blue_light"
+    />
+
+    <View android:layout_height="3dip"
+          android:layout_width="match_parent"
+          android:background="#323232"/>
+    
+    <TextView android:id="@+id/description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_gravity="left|center_vertical"
+        android:text="@string/choose_account_text"
+        android:paddingTop="16dip"
+        android:paddingBottom="16dip"
+    />
+
+    <ListView android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_weight="1"
         android:drawSelectorOnTop="false"
+        android:layout_weight="1"
         android:scrollbarAlwaysDrawVerticalTrack="true" />
 
     <Button android:id="@+id/addAccount"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_weight="2"
         android:layout_marginLeft="2dip"
         android:layout_marginRight="2dip"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textStyle="bold"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/add_account_button_label"
     />
 </LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index cf3bd42..27a51da 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -178,4 +178,17 @@
         android:layout_height="0dip"
         />
 
+    <!-- Area to overlay FaceLock -->
+    <TextView android:id="@+id/faceLockAreaView"
+        android:visibility="gone"
+        android:layout_row="3"
+        android:layout_column="0"
+        android:layout_rowSpan="2"
+        android:layout_columnSpan="1"
+        android:layout_gravity="fill"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
+        android:background="@color/facelock_color_background"
+    />
+
 </GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 64c479f..75ed101 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -171,4 +171,19 @@
         android:layout_height="0dip"
         />
 
+    <!-- Area to overlay FaceLock -->
+    <TextView android:id="@+id/faceLockAreaView"
+        android:visibility="gone"
+        android:layout_row="4"
+        android:layout_column="0"
+        android:layout_rowSpan="1"
+        android:layout_columnSpan="1"
+        android:layout_gravity="fill"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip"
+        android:layout_width="0dip"
+        android:layout_height="0dip"
+        android:background="@color/facelock_color_background"
+    />
+
 </GridLayout>
diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml
index ea4e1f9..c16a12c 100644
--- a/core/res/res/layout/volume_adjust.xml
+++ b/core/res/res/layout/volume_adjust.xml
@@ -21,7 +21,6 @@
         android:id="@+id/visible_panel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="80dp"
         android:background="@android:drawable/dialog_full_holo_dark"
         android:orientation="horizontal"
         >
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index ddb9942..f0c6d09 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -112,6 +112,9 @@
     <color name="lockscreen_clock_am_pm">#ff9a9a9a</color>
     <color name="lockscreen_owner_info">#ff9a9a9a</color>
 
+    <!-- FaceLock -->
+    <color name="facelock_color_background">#000000</color>
+
     <!-- For holo theme -->
       <drawable name="screen_background_holo_light">#fff3f3f3</drawable>
       <drawable name="screen_background_holo_dark">#ff000000</drawable>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f1fc42c..0d15388 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -178,4 +178,6 @@
     <!-- Default width for a textview error popup -->
     <dimen name="textview_error_popup_default_width">240dip</dimen>
 
+    <!-- Volume panel y offset -->
+    <dimen name="volume_panel_top">80dp</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f75c7d5..e093fa9 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3094,6 +3094,12 @@
     <!-- Choose Account Activity label -->
     <string name="choose_account_label">Select an account</string>
 
+    <string name="add_account_label">"Add an account"</string>
+    <string name="choose_account_text">"Which account would you like to use?"</string>
+
+    <!-- Button label to add an account [CHAR LIMIT=20] -->
+    <string name="add_account_button_label">Add account</string>
+
     <!-- NumberPicker - accessibility support -->
     <!-- Description of the button to increment the NumberPicker value. [CHAR LIMIT=NONE] -->
     <string name="number_picker_increment_button">Increment</string>
@@ -3174,6 +3180,9 @@
 
     <!-- Slide lock screen -->
 
+    <!-- Description of the sliding handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="content_description_sliding_handle">"Sliding handle. Tap and hold."</string>
+
     <!-- Description of the up direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
     <string name="description_direction_up">Up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
     <!-- Description of the down direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
@@ -3293,4 +3302,4 @@
     <!-- Delimeter used between each item in a textual list; for example "Alpha, Beta". [CHAR LIMIT=3] -->
     <string name="list_delimeter">", "</string>
 
-</resources>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 346a3d29..88b45fd 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -250,7 +250,7 @@
     </style>
 
     <style name="TextAppearance.EasyCorrectSuggestion" parent="TextAppearance.Suggestion">
-        <item name="android:textUnderlineColor">@color/holo_blue_dark</item>
+        <item name="android:textUnderlineColor">#ff888888</item>
     </style>
 
     <style name="TextAppearance.MisspelledSuggestion" parent="TextAppearance.Suggestion">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 3378dc8..1089969 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -94,7 +94,7 @@
 
         <item name="textAppearanceButton">@android:style/TextAppearance.Widget.Button</item>
         
-        <item name="editTextColor">?android:attr/textColorPrimaryInverse</item>
+        <item name="editTextColor">?android:color/primary_text_light</item>
         <item name="editTextBackground">@android:drawable/edit_text</item>
         
         <item name="candidatesTextStyleSpans">@android:string/candidates_style</item>
@@ -410,7 +410,7 @@
         <item name="textColorLink">@android:color/link_text_light</item>
         <item name="textColorLinkInverse">@android:color/link_text_dark</item>
         
-        <item name="editTextColor">?android:attr/textColorPrimary</item>
+        <item name="editTextColor">@android:color/primary_text_light</item>
         <item name="listChoiceBackgroundIndicator">@android:drawable/list_selector_background</item>
 
         <item name="activatedBackgroundIndicator">@android:drawable/activated_background_light</item>
diff --git a/core/tests/bandwidthtests/Android.mk b/core/tests/bandwidthtests/Android.mk
index 3d56141..6871efd 100644
--- a/core/tests/bandwidthtests/Android.mk
+++ b/core/tests/bandwidthtests/Android.mk
@@ -27,4 +27,4 @@
 
 include $(BUILD_PACKAGE)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index be740d3..9eee2f0 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -120,6 +120,42 @@
     }
 
     /**
+     * Ensure that downloading on wifi reports reasonable stats.
+     */
+    @LargeTest
+    public void testWifiUpload() {
+        assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
+        // Download a file from the server.
+        String ts = Long.toString(System.currentTimeMillis());
+        String targetUrl = BandwidthTestUtil.buildDownloadUrl(
+                mTestServer, FILE_SIZE, mDeviceId, ts);
+        File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME);
+        assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile));
+
+        ts = Long.toString(System.currentTimeMillis());
+        NetworkStats pre_test_stats = fetchDataFromProc(mUid);
+        TrafficStats.startDataProfiling(mContext);
+        assertTrue(BandwidthTestUtil.postFileToServer(mTestServer, mDeviceId, ts, tmpSaveFile));
+        NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext);
+        Log.d(LOG_TAG, prof_stats.toString());
+        NetworkStats post_test_stats = fetchDataFromProc(mUid);
+        NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats);
+
+        // Output measurements to instrumentation out, so that it can be compared to that of
+        // the server.
+        Bundle results = new Bundle();
+        results.putString("device_id", mDeviceId);
+        results.putString("timestamp", ts);
+        results.putInt("size", FILE_SIZE);
+        AddStatsToResults(PROF_LABEL, prof_stats, results);
+        AddStatsToResults(PROC_LABEL, proc_stats, results);
+        getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
+
+        // Clean up.
+        assertTrue(cleanUpFile(tmpSaveFile));
+    }
+
+    /**
      * We want to make sure that if we use the Download Manager to download stuff,
      * accounting still goes to the app making the call and that the numbers still make sense.
      */
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/BandwidthTestUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/BandwidthTestUtil.java
index d850169..7dea9e3 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/BandwidthTestUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/BandwidthTestUtil.java
@@ -18,6 +18,15 @@
 
 import android.util.Log;
 
+import com.android.internal.http.multipart.FilePart;
+import com.android.internal.http.multipart.MultipartEntity;
+import com.android.internal.http.multipart.Part;
+import com.android.internal.http.multipart.StringPart;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.util.ByteArrayBuffer;
 
 import java.io.BufferedInputStream;
@@ -80,7 +89,7 @@
      * Download a given file from a target url to a given destination file.
      * @param targetUrl the url to download
      * @param file the {@link File} location where to save to
-     * @return true if it succeeded.
+     * @return true if it succeeded
      */
     public static boolean DownloadFromUrl(String targetUrl, File file) {
         try {
@@ -106,4 +115,32 @@
         return true;
     }
 
+    /**
+     * Post a given file for a given device and timestamp to the server.
+     * @param postUrl {@link String} url used to upload files
+     * @param deviceId {@link String} device id that is uploading
+     * @param timestamp {@link String} timestamp
+     * @param file {@link File} to upload
+     * @return true if it succeeded
+     */
+    public static boolean postFileToServer(String postUrl, String deviceId, String timestamp,
+            File file) {
+        try {
+            HttpClient httpClient = new DefaultHttpClient();
+            HttpPost postRequest = new HttpPost(postUrl);
+            Part[] parts = {
+                    new StringPart("device_id", deviceId),
+                    new StringPart("timestamp", timestamp),
+                    new FilePart("file", file)
+            };
+            MultipartEntity reqEntity = new MultipartEntity(parts, postRequest.getParams());
+            postRequest.setEntity(reqEntity);
+            HttpResponse res = httpClient.execute(postRequest);
+            res.getEntity().getContent().close();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Could not upload file with error: " + e);
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/data/fonts/AndroidClock.ttf b/data/fonts/AndroidClock.ttf
index 6e0932e..7ebc963 100644
--- a/data/fonts/AndroidClock.ttf
+++ b/data/fonts/AndroidClock.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index f9311fb..c716bbb 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index ae92697..eeb5120 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 109b642..1e88d3e 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 2568835..3033027 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/videos/Disco.240p.mp4 b/data/videos/Disco.240p.mp4
new file mode 100644
index 0000000..c9078b7
--- /dev/null
+++ b/data/videos/Disco.240p.mp4
Binary files differ
diff --git a/data/videos/Disco.480p.lq.mp4 b/data/videos/Disco.480p.lq.mp4
new file mode 100644
index 0000000..411e0d5
--- /dev/null
+++ b/data/videos/Disco.480p.lq.mp4
Binary files differ
diff --git a/data/videos/Disco.480p.mq.mp4 b/data/videos/Disco.480p.mq.mp4
new file mode 100644
index 0000000..3dc4957
--- /dev/null
+++ b/data/videos/Disco.480p.mq.mp4
Binary files differ
diff --git a/data/videos/VideoPackage1.mk b/data/videos/VideoPackage1.mk
index 1e7096a..407a76e 100644
--- a/data/videos/VideoPackage1.mk
+++ b/data/videos/VideoPackage1.mk
@@ -23,5 +23,6 @@
         $(LOCAL_PATH)/AndroidInSpace.240p.mp4:$(TARGET_PATH)/AndroidInSpace.240p.mp4 \
         $(LOCAL_PATH)/AndroidInSpace.480p.lq.mp4:$(TARGET_PATH)/AndroidInSpace.480p.mp4 \
         $(LOCAL_PATH)/Sunset.240p.mp4:$(TARGET_PATH)/Sunset.240p.mp4 \
-        $(LOCAL_PATH)/Sunset.480p.lq.mp4:$(TARGET_PATH)/Sunset.480p.mp4
-
+        $(LOCAL_PATH)/Sunset.480p.lq.mp4:$(TARGET_PATH)/Sunset.480p.mp4 \
+        $(LOCAL_PATH)/Disco.240p.mp4:$(TARGET_PATH)/Disco.240p.mp4 \
+        $(LOCAL_PATH)/Disco.480p.lq.mp4:$(TARGET_PATH)/Disco.480p.mp4
diff --git a/data/videos/VideoPackage2.mk b/data/videos/VideoPackage2.mk
index cb77b3e..c256a2d 100644
--- a/data/videos/VideoPackage2.mk
+++ b/data/videos/VideoPackage2.mk
@@ -23,5 +23,6 @@
         $(LOCAL_PATH)/AndroidInSpace.240p.mp4:$(TARGET_PATH)/AndroidInSpace.240p.mp4 \
         $(LOCAL_PATH)/AndroidInSpace.480p.mq.mp4:$(TARGET_PATH)/AndroidInSpace.480p.mp4 \
         $(LOCAL_PATH)/Sunset.240p.mp4:$(TARGET_PATH)/Sunset.240p.mp4 \
-        $(LOCAL_PATH)/Sunset.480p.mq.mp4:$(TARGET_PATH)/Sunset.480p.mp4
-
+        $(LOCAL_PATH)/Sunset.480p.mq.mp4:$(TARGET_PATH)/Sunset.480p.mp4 \
+        $(LOCAL_PATH)/Disco.240p.mp4:$(TARGET_PATH)/Disco.240p.mp4 \
+        $(LOCAL_PATH)/Disco.480p.mq.mp4:$(TARGET_PATH)/Disco.480p.mp4
diff --git a/docs/html/sdk/android-3.2.jd b/docs/html/sdk/android-3.2.jd
index 5618eae..ea2b4ed 100644
--- a/docs/html/sdk/android-3.2.jd
+++ b/docs/html/sdk/android-3.2.jd
@@ -166,7 +166,7 @@
 reserved by the system.
 </li>
 
-<li>In constrast, a screen's <em>width</em> and <em>height</em> represent the
+<li>In contrast, a screen's <em>width</em> and <em>height</em> represent the
 current horizontal or vertical space available for application layout, measured
 in "dp" units, not including screen areas reserved by the system. The width and
 height of a screen change when the user switches orientation between landscape
@@ -313,7 +313,7 @@
 the system forces the application into screen compatibility mode, to ensure best
 display on the current screen.</li>
 
-<li><code>android:smallestWidthLimitDp="<em>numDp"</em></code> &mdash; This
+<li><code>android:requiresSmallestWidthDp="<em>numDp"</em></code> &mdash; This
 attribute lets you specify the minimum smallestWidth on which the application
 can run. If the current screen is smaller than the value specified, the system
 considers the application incompatible with the device, but does not prevent it
diff --git a/include/utils/threads.h b/include/utils/threads.h
index c685625..ab3e8cd 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -143,6 +143,10 @@
 // in either case errno is set.  Thread ID zero means current thread.
 extern int androidSetThreadPriority(pid_t tid, int prio);
 
+// Get the current priority of a particular thread. Returns one of the
+// ANDROID_PRIORITY constants or a negative result in case of error.
+extern int androidGetThreadPriority(pid_t tid);
+
 // Get the current scheduling group of a particular thread. Normally returns
 // one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT.
 // Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 79a01a3..f2bc81e 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -595,7 +595,7 @@
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
-        LOGE("connect: SurfaceTexture has been abandoned!");
+        LOGE("disconnect: SurfaceTexture has been abandoned!");
         return NO_INIT;
     }
 
diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h
index 6531e78..f079a7b 100644
--- a/libs/hwui/utils/Compare.h
+++ b/libs/hwui/utils/Compare.h
@@ -28,7 +28,7 @@
  */
 #define LTE_FLOAT(a) \
     if (a < rhs.a) return true; \
-    if (ALMOST_EQUAL(a, rhs.a))
+    if (a == rhs.a)
 
 /**
  * Compare integers.
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index a5ba57d..3de75ba 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -1020,6 +1020,8 @@
             return true;
         }
     }
+    *outVx = 0;
+    *outVy = 0;
     return false;
 }
 
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 02c380b..5dbcb75 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -368,6 +368,14 @@
     return rc;
 }
 
+int androidGetThreadPriority(pid_t tid) {
+#if defined(HAVE_PTHREADS)
+    return getpriority(PRIO_PROCESS, tid);
+#else
+    return ANDROID_PRIORITY_NORMAL;
+#endif
+}
+
 int androidGetThreadSchedulingGroup(pid_t tid)
 {
     int ret = ANDROID_TGROUP_DEFAULT;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index cd8bb1d..a0881a7 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -18,8 +18,10 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.os.Binder;
@@ -1684,17 +1686,42 @@
      * Register a component to be the sole receiver of MEDIA_BUTTON intents.
      * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
      *      that will receive the media button intent. This broadcast receiver must be declared
-     *      in the application manifest.
+     *      in the application manifest. The package of the component must match that of
+     *      the context you're registering from.
      */
     public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
         if (eventReceiver == null) {
             return;
         }
+        if (!eventReceiver.getPackageName().equals(mContext.getPackageName())) {
+            Log.e(TAG, "registerMediaButtonEventReceiver() error: " +
+                    "receiver and context package names don't match");
+            return;
+        }
+        // construct a PendingIntent for the media button and register it
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        //     the associated intent will be handled by the component being registered
+        mediaButtonIntent.setComponent(eventReceiver);
+        PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+        registerMediaButtonIntent(pi, eventReceiver);
+    }
+
+    /**
+     * @hide
+     * no-op if (pi == null) or (eventReceiver == null)
+     */
+    public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
+        if ((pi == null) || (eventReceiver == null)) {
+            Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
+            return;
+        }
         IAudioService service = getService();
         try {
-            service.registerMediaButtonEventReceiver(eventReceiver);
+            // pi != null
+            service.registerMediaButtonIntent(pi, eventReceiver);
         } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in registerMediaButtonEventReceiver"+e);
+            Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
         }
     }
 
@@ -1707,14 +1734,26 @@
         if (eventReceiver == null) {
             return;
         }
-        IAudioService service = getService();
-        try {
-            service.unregisterMediaButtonEventReceiver(eventReceiver);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiver"+e);
-        }
+        // construct a PendingIntent for the media button and unregister it
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        //     the associated intent will be handled by the component being registered
+        mediaButtonIntent.setComponent(eventReceiver);
+        PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+        unregisterMediaButtonIntent(pi, eventReceiver);
     }
 
+    /**
+     * @hide
+     */
+    public void unregisterMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
+        IAudioService service = getService();
+        try {
+            service.unregisterMediaButtonIntent(pi, eventReceiver);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
+        }
+    }
 
     /**
      * Registers the remote control client for providing information to display on the remote
@@ -1724,14 +1763,13 @@
      * @see RemoteControlClient
      */
     public void registerRemoteControlClient(RemoteControlClient rcClient) {
-        if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
+        if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
             return;
         }
         IAudioService service = getService();
         try {
-            service.registerRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
+            service.registerRemoteControlClient(rcClient.getRcMediaIntent(),   /* mediaIntent   */
                     rcClient.getIRemoteControlClient(),                        /* rcClient      */
-                    rcClient.toString(),                                       /* clientName    */
                     // used to match media button event receiver and audio focus
                     mContext.getPackageName());                                /* packageName   */
         } catch (RemoteException e) {
@@ -1746,13 +1784,13 @@
      * @see #registerRemoteControlClient(RemoteControlClient)
      */
     public void unregisterRemoteControlClient(RemoteControlClient rcClient) {
-        if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
+        if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
             return;
         }
         IAudioService service = getService();
         try {
-            service.unregisterRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
-                    rcClient.getIRemoteControlClient());                         /* rcClient      */
+            service.unregisterRemoteControlClient(rcClient.getRcMediaIntent(), /* mediaIntent   */
+                    rcClient.getIRemoteControlClient());                       /* rcClient      */
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index db27cfd..8895c9e 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -18,6 +18,8 @@
 
 import android.app.ActivityManagerNative;
 import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
@@ -35,6 +37,7 @@
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
@@ -2084,7 +2087,7 @@
             }
         }
 
-        private void persistMediaButtonReceiver(ComponentName receiver) {
+        private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
             Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
                     receiver == null ? "" : receiver.flattenToString());
         }
@@ -2201,7 +2204,7 @@
                     break;
 
                 case MSG_PERSIST_MEDIABUTTONRECEIVER:
-                    persistMediaButtonReceiver( (ComponentName) msg.obj );
+                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
                     break;
 
                 case MSG_RCDISPLAY_CLEAR:
@@ -2894,14 +2897,22 @@
                 }
                 synchronized(mRCStack) {
                     if (!mRCStack.empty()) {
-                        // create a new intent specifically aimed at the current registered listener
+                        // create a new intent to fill in the extras of the registered PendingIntent
                         Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-                        targetedIntent.putExtras(intent.getExtras());
-                        targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
-                        // trap the current broadcast
-                        abortBroadcast();
-                        //Log.v(TAG, " Sending intent" + targetedIntent);
-                        context.sendBroadcast(targetedIntent, null);
+                        Bundle extras = intent.getExtras();
+                        if (extras != null) {
+                            targetedIntent.putExtras(extras);
+                            // trap the current broadcast
+                            abortBroadcast();
+                            //Log.v(TAG, " Sending intent" + targetedIntent);
+                            // send the intent that was registered by the client
+                            try {
+                                mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
+                            } catch (CanceledException e) {
+                                Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
+                                e.printStackTrace();
+                            }
+                        }
                     }
                 }
             }
@@ -2936,18 +2947,18 @@
      */
     private class RcClientDeathHandler implements IBinder.DeathRecipient {
         private IBinder mCb; // To be notified of client's death
-        private ComponentName mRcEventReceiver;
+        private PendingIntent mMediaIntent;
 
-        RcClientDeathHandler(IBinder cb, ComponentName eventReceiver) {
+        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
             mCb = cb;
-            mRcEventReceiver = eventReceiver;
+            mMediaIntent = pi;
         }
 
         public void binderDied() {
             Log.w(TAG, "  RemoteControlClient died");
             // remote control client died, make sure the displays don't use it anymore
             //  by setting its remote control client to null
-            registerRemoteControlClient(mRcEventReceiver, null, null, null/*ignored*/);
+            registerRemoteControlClient(mMediaIntent, null, null/*ignored*/);
         }
 
         public IBinder getBinder() {
@@ -2956,18 +2967,29 @@
     }
 
     private static class RemoteControlStackEntry {
-        /** the target for the ACTION_MEDIA_BUTTON events */
-        public ComponentName mReceiverComponent;// always non null
+        /**
+         * The target for the ACTION_MEDIA_BUTTON events.
+         * Always non null.
+         */
+        public PendingIntent mMediaIntent;
+        /**
+         * The registered media button event receiver.
+         * Always non null.
+         */
+        public ComponentName mReceiverComponent;
         public String mCallingPackageName;
-        public String mRcClientName;
         public int mCallingUid;
-
-        /** provides access to the information to display on the remote control */
+        /**
+         * Provides access to the information to display on the remote control.
+         * May be null (when a media button event receiver is registered,
+         *     but no remote control client has been registered) */
         public IRemoteControlClient mRcClient;
         public RcClientDeathHandler mRcClientDeathHandler;
 
-        public RemoteControlStackEntry(ComponentName r) {
-            mReceiverComponent = r;
+        /** precondition: mediaIntent != null, eventReceiver != null */
+        public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
+            mMediaIntent = mediaIntent;
+            mReceiverComponent = eventReceiver;
             mCallingUid = -1;
             mRcClient = null;
         }
@@ -3003,7 +3025,8 @@
             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
             while(stackIterator.hasNext()) {
                 RemoteControlStackEntry rcse = stackIterator.next();
-                pw.println("     receiver: " + rcse.mReceiverComponent +
+                pw.println("  pi: " + rcse.mMediaIntent +
+                        "  -- ercvr: " + rcse.mReceiverComponent +
                         "  -- client: " + rcse.mRcClient +
                         "  -- uid: " + rcse.mCallingUid);
             }
@@ -3035,7 +3058,6 @@
                     mAudioHandler.sendMessage(
                             mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
                                     null));
-                    return;
                 } else if (oldTop != mRCStack.peek()) {
                     // the top of the stack has changed, save it in the system settings
                     // by posting a message to persist it
@@ -3049,25 +3071,32 @@
 
     /**
      * Helper function:
-     * Restore remote control receiver from the system settings
+     * Restore remote control receiver from the system settings.
      */
     private void restoreMediaButtonReceiver() {
         String receiverName = Settings.System.getString(mContentResolver,
                 Settings.System.MEDIA_BUTTON_RECEIVER);
         if ((null != receiverName) && !receiverName.isEmpty()) {
-            ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName);
-            registerMediaButtonEventReceiver(receiverComponentName);
+            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
+            // construct a PendingIntent targeted to the restored component name
+            // for the media button and register it
+            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+            //     the associated intent will be handled by the component being registered
+            mediaButtonIntent.setComponent(eventReceiver);
+            PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+            registerMediaButtonIntent(pi, eventReceiver);
         }
-        // upon restoring (e.g. after boot), do we want to refresh all remotes?
     }
 
     /**
      * Helper function:
-     * Set the new remote control receiver at the top of the RC focus stack
+     * Set the new remote control receiver at the top of the RC focus stack.
+     * precondition: mediaIntent != null, target != null
      */
-    private void pushMediaButtonReceiver(ComponentName newReceiver) {
+    private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
         // already at top of stack?
-        if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
+        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
             return;
         }
         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
@@ -3075,31 +3104,32 @@
         boolean wasInsideStack = false;
         while(stackIterator.hasNext()) {
             rcse = (RemoteControlStackEntry)stackIterator.next();
-            if(rcse.mReceiverComponent.equals(newReceiver)) {
+            if(rcse.mMediaIntent.equals(mediaIntent)) {
                 wasInsideStack = true;
                 stackIterator.remove();
                 break;
             }
         }
         if (!wasInsideStack) {
-            rcse = new RemoteControlStackEntry(newReceiver);
+            rcse = new RemoteControlStackEntry(mediaIntent, target);
         }
         mRCStack.push(rcse);
 
         // post message to persist the default media button receiver
         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
-                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, newReceiver/*obj*/) );
+                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
     }
 
     /**
      * Helper function:
-     * Remove the remote control receiver from the RC focus stack
+     * Remove the remote control receiver from the RC focus stack.
+     * precondition: pi != null
      */
-    private void removeMediaButtonReceiver(ComponentName newReceiver) {
+    private void removeMediaButtonReceiver(PendingIntent pi) {
         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
         while(stackIterator.hasNext()) {
             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
-            if(rcse.mReceiverComponent.equals(newReceiver)) {
+            if(rcse.mMediaIntent.equals(pi)) {
                 stackIterator.remove();
                 break;
             }
@@ -3110,8 +3140,8 @@
      * Helper function:
      * Called synchronized on mRCStack
      */
-    private boolean isCurrentRcController(ComponentName eventReceiver) {
-        if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(eventReceiver)) {
+    private boolean isCurrentRcController(PendingIntent pi) {
+        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
             return true;
         }
         return false;
@@ -3124,12 +3154,12 @@
      * Update the remote control displays with the new "focused" client generation
      */
     private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
-            ComponentName newClientEventReceiver, boolean clearing) {
+            PendingIntent newMediaIntent, boolean clearing) {
         // NOTE: Only one IRemoteControlDisplay supported in this implementation
         if (mRcDisplay != null) {
             try {
                 mRcDisplay.setCurrentClientId(
-                        newClientGeneration, newClientEventReceiver, clearing);
+                        newClientGeneration, newMediaIntent, clearing);
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
                 // if we had a display before, stop monitoring its death
@@ -3167,10 +3197,9 @@
      *    where the display should be cleared.
      */
     private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
-            ComponentName newClientEventReceiver, boolean clearing) {
+            PendingIntent newMediaIntent, boolean clearing) {
         // send the new valid client generation ID to all displays
-        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newClientEventReceiver,
-                clearing);
+        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
         // send the new valid client generation ID to all clients
         setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
     }
@@ -3186,7 +3215,7 @@
                 mCurrentRcClientGen++;
                 // synchronously update the displays and clients with the new client generation
                 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
-                        null /*event receiver*/, true /*clearing*/);
+                        null /*newMediaIntent*/, true /*clearing*/);
             }
         }
     }
@@ -3204,7 +3233,7 @@
                     // synchronously update the displays and clients with
                     //      the new client generation
                     setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
-                            rcse.mReceiverComponent /*event receiver*/,
+                            rcse.mMediaIntent /*newMediaIntent*/,
                             false /*clearing*/);
 
                     // tell the current client that it needs to send info
@@ -3301,27 +3330,34 @@
         updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
     }
 
-    /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
-    public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
-        Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);
+    /** 
+     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
+     * precondition: mediaIntent != null, target != null
+     */
+    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
+        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
 
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
-                pushMediaButtonReceiver(eventReceiver);
+                pushMediaButtonReceiver(mediaIntent, eventReceiver);
                 // new RC client, assume every type of information shall be queried
                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
         }
     }
 
-    /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
-    public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
-        Log.i(TAG, "  Remote Control   unregisterMediaButtonEventReceiver() for " + eventReceiver);
+    /** 
+     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
+     * precondition: mediaIntent != null, eventReceiver != null
+     */
+    public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
+    {
+        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
 
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
-                boolean topOfStackWillChange = isCurrentRcController(eventReceiver);
-                removeMediaButtonReceiver(eventReceiver);
+                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
+                removeMediaButtonReceiver(mediaIntent);
                 if (topOfStackWillChange) {
                     // current RC client will change, assume every type of info needs to be queried
                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
@@ -3331,8 +3367,8 @@
     }
 
     /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
-    public void registerRemoteControlClient(ComponentName eventReceiver,
-            IRemoteControlClient rcClient, String clientName, String callingPackageName) {
+    public void registerRemoteControlClient(PendingIntent mediaIntent,
+            IRemoteControlClient rcClient, String callingPackageName) {
         if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
@@ -3340,7 +3376,7 @@
                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
                 while(stackIterator.hasNext()) {
                     RemoteControlStackEntry rcse = stackIterator.next();
-                    if(rcse.mReceiverComponent.equals(eventReceiver)) {
+                    if(rcse.mMediaIntent.equals(mediaIntent)) {
                         // already had a remote control client?
                         if (rcse.mRcClientDeathHandler != null) {
                             // stop monitoring the old client's death
@@ -3357,7 +3393,6 @@
                             }
                         }
                         rcse.mCallingPackageName = callingPackageName;
-                        rcse.mRcClientName = clientName;
                         rcse.mCallingUid = Binder.getCallingUid();
                         if (rcClient == null) {
                             rcse.mRcClientDeathHandler = null;
@@ -3366,7 +3401,7 @@
                         // monitor the new client's death
                         IBinder b = rcClient.asBinder();
                         RcClientDeathHandler rcdh =
-                                new RcClientDeathHandler(b, rcse.mReceiverComponent);
+                                new RcClientDeathHandler(b, rcse.mMediaIntent);
                         try {
                             b.linkToDeath(rcdh, 0);
                         } catch (RemoteException e) {
@@ -3380,7 +3415,7 @@
                 }
                 // if the eventReceiver is at the top of the stack
                 // then check for potential refresh of the remote controls
-                if (isCurrentRcController(eventReceiver)) {
+                if (isCurrentRcController(mediaIntent)) {
                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                 }
             }
@@ -3388,24 +3423,23 @@
     }
 
     /**
-     * see AudioManager.unregisterRemoteControlClient(ComponentName eventReceiver, ...)
+     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
      * rcClient is guaranteed non-null
      */
-    public void unregisterRemoteControlClient(ComponentName eventReceiver,
+    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
             IRemoteControlClient rcClient) {
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
                 while(stackIterator.hasNext()) {
                     RemoteControlStackEntry rcse = stackIterator.next();
-                    if ((rcse.mReceiverComponent.equals(eventReceiver))
+                    if ((rcse.mMediaIntent.equals(mediaIntent))
                             && rcClient.equals(rcse.mRcClient)) {
                         // we found the IRemoteControlClient to unregister
                         // stop monitoring its death
                         rcse.unlinkToRcClientDeath();
                         // reset the client-related fields
                         rcse.mRcClient = null;
-                        rcse.mRcClientName = null;
                         rcse.mRcClientDeathHandler = null;
                         rcse.mCallingPackageName = null;
                     }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7bf9814..5294d36 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.media.IAudioFocusDispatcher;
 import android.media.IRemoteControlClient;
@@ -85,13 +86,12 @@
     
     void unregisterAudioFocusClient(String clientId);
 
-    void registerMediaButtonEventReceiver(in ComponentName eventReceiver);
+    oneway void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
+    oneway void unregisterMediaButtonIntent(in PendingIntent pi,  in ComponentName c);
 
-    void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
-
-    oneway void registerRemoteControlClient(in ComponentName eventReceiver,
-           in IRemoteControlClient rcClient, in String clientName, in String callingPackageName);
-    oneway void unregisterRemoteControlClient(in ComponentName eventReceiver,
+    oneway void registerRemoteControlClient(in PendingIntent mediaIntent,
+           in IRemoteControlClient rcClient, in String callingPackageName);
+    oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
            in IRemoteControlClient rcClient);
 
     oneway void   registerRemoteControlDisplay(in IRemoteControlDisplay rcd);
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
index fd50b7e..e15b07c 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.graphics.Bitmap;
 import android.os.Bundle;
@@ -31,14 +32,12 @@
     /**
      * Sets the generation counter of the current client that is displayed on the remote control.
      * @param clientGeneration the new RemoteControlClient generation
-     * @param clientEventReceiver the media button event receiver associated with the client.
-     *    May be null, which implies there is no registered media button event receiver. This
-     *    parameter is supplied as an optimization so a display can directly target media button
-     *    events to the client.
+     * @param clientMediaIntent the PendingIntent associated with the client.
+     *    May be null, which implies there is no registered media button event receiver.
      * @param clearing true if the new client generation value maps to a remote control update
      *    where the display should be cleared.
      */
-    void setCurrentClientId(int clientGeneration, in ComponentName clientEventReceiver,
+    void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent,
             boolean clearing);
 
     void setPlaybackState(int generationId, int state);
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index f66f1b0..5dea87f 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -34,6 +34,7 @@
 import java.lang.IllegalArgumentException;
 
 /**
+ * TODO javadoc update for ComponentName - PendingIntent change
  * RemoteControlClient enables exposing information meant to be consumed by remote controls
  * capable of displaying metadata, artwork and media transport control buttons.
  * A remote control client object is associated with a media button event receiver. This
@@ -205,50 +206,6 @@
     public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3;
 
     /**
-     * @hide
-     * TODO remove after modifying known (internal) media apps using this API
-     * Class constructor.
-     * @param mediaButtonEventReceiver The receiver for the media button events. It needs to have
-     *     been registered with {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)}
-     *     before this new RemoteControlClient can itself be registered with
-     *     {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
-     * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
-     * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
-     */
-    public RemoteControlClient(ComponentName mediaButtonEventReceiver) {
-        mRcEventReceiver = mediaButtonEventReceiver;
-
-        Looper looper;
-        if ((looper = Looper.myLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else if ((looper = Looper.getMainLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else {
-            mEventHandler = null;
-            Log.e(TAG, "RemoteControlClient() couldn't find main application thread");
-        }
-    }
-
-    /**
-     * @hide
-     * TODO remove after modifying known (internal) media apps using this API
-     * Class constructor for a remote control client whose internal event handling
-     * happens on a user-provided Looper.
-     * @param mediaButtonEventReceiver The receiver for the media button events. It needs to have
-     *     been registered with {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)}
-     *     before this new RemoteControlClient can itself be registered with
-     *     {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
-     * @param looper The Looper running the event loop.
-     * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
-     * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
-     */
-    public RemoteControlClient(ComponentName mediaButtonEventReceiver, Looper looper) {
-        mRcEventReceiver = mediaButtonEventReceiver;
-
-        mEventHandler = new EventHandler(this, looper);
-    }
-
-    /**
      * Class constructor.
      * @param mediaButtonIntent The intent that will be sent for the media button events sent
      *     by remote controls.
@@ -262,8 +219,7 @@
      * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
      */
     public RemoteControlClient(PendingIntent mediaButtonIntent) {
-        // TODO implement using PendingIntent instead of ComponentName
-        mRcEventReceiver = null;
+        mRcMediaIntent = mediaButtonIntent;
 
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
@@ -292,8 +248,7 @@
      * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
      */
     public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) {
-        // TODO implement using PendingIntent instead of ComponentName
-        mRcEventReceiver = null;
+        mRcMediaIntent = mediaButtonIntent;
 
         mEventHandler = new EventHandler(this, looper);
     }
@@ -482,7 +437,11 @@
             synchronized(mCacheLock) {
                 // assign the edited data
                 mMetadata = new Bundle(mEditorMetadata);
+                if ((mArtwork != null) && (!mArtwork.equals(mEditorArtwork))) {
+                    mArtwork.recycle();
+                }
                 mArtwork = mEditorArtwork;
+                mEditorArtwork = null;
                 if (mMetadataChanged & mArtworkChanged) {
                     // send to remote control display if conditions are met
                     sendMetadataWithArtwork_syncCacheLock();
@@ -610,9 +569,10 @@
     private int mInternalClientGenId = -2;
 
     /**
-     * The media button event receiver associated with this remote control client
+     * The media button intent description associated with this remote control client
+     * (can / should include target component for intent handling)
      */
-    private final ComponentName mRcEventReceiver;
+    private final PendingIntent mRcMediaIntent;
 
     /**
      * The remote control display to which this client will send information.
@@ -622,10 +582,10 @@
 
     /**
      * @hide
-     * Accessor to media button event receiver
+     * Accessor to media button intent description (includes target component)
      */
-    public ComponentName getRcEventReceiver() {
-        return mRcEventReceiver;
+    public PendingIntent getRcMediaIntent() {
+        return mRcMediaIntent;
     }
     /**
      * @hide
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index ee77f47..7218faf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -34,17 +34,21 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <surfaceflinger/Surface.h>
 #include <gui/ISurfaceTexture.h>
 
+#include "avc_utils.h"
+
 namespace android {
 
 ////////////////////////////////////////////////////////////////////////////////
 
 NuPlayer::NuPlayer()
     : mUIDValid(false),
+      mVideoIsAVC(false),
       mAudioEOS(false),
       mVideoEOS(false),
       mScanSourcesPending(false),
@@ -52,7 +56,12 @@
       mFlushingAudio(NONE),
       mFlushingVideo(NONE),
       mResetInProgress(false),
-      mResetPostponed(false) {
+      mResetPostponed(false),
+      mSkipRenderingAudioUntilMediaTimeUs(-1ll),
+      mSkipRenderingVideoUntilMediaTimeUs(-1ll),
+      mVideoLateByUs(0ll),
+      mNumFramesTotal(0ll),
+      mNumFramesDropped(0ll) {
 }
 
 NuPlayer::~NuPlayer() {
@@ -185,10 +194,14 @@
         {
             LOGV("kWhatStart");
 
+            mVideoIsAVC = false;
             mAudioEOS = false;
             mVideoEOS = false;
             mSkipRenderingAudioUntilMediaTimeUs = -1;
             mSkipRenderingVideoUntilMediaTimeUs = -1;
+            mVideoLateByUs = 0;
+            mNumFramesTotal = 0;
+            mNumFramesDropped = 0;
 
             mSource->start();
 
@@ -269,6 +282,8 @@
                 } else {
                     CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
                     mFlushingVideo = FLUSHED;
+
+                    mVideoLateByUs = 0;
                 }
 
                 LOGV("decoder %s flush completed", audio ? "audio" : "video");
@@ -299,7 +314,12 @@
                          sampleRate, numChannels);
 
                     mAudioSink->close();
-                    CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
+                    CHECK_EQ(mAudioSink->open(
+                                sampleRate,
+                                numChannels,
+                                AUDIO_FORMAT_PCM_16_BIT,
+                                8 /* bufferCount */),
+                             (status_t)OK);
                     mAudioSink->start();
 
                     mRenderer->signalAudioSinkChanged();
@@ -392,13 +412,18 @@
                 int64_t positionUs;
                 CHECK(msg->findInt64("positionUs", &positionUs));
 
+                CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs));
+
                 if (mDriver != NULL) {
                     sp<NuPlayerDriver> driver = mDriver.promote();
                     if (driver != NULL) {
                         driver->notifyPosition(positionUs);
+
+                        driver->notifyFrameStats(
+                                mNumFramesTotal, mNumFramesDropped);
                     }
                 }
-            } else {
+            } else if (what == Renderer::kWhatFlushComplete) {
                 CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
 
                 int32_t audio;
@@ -560,6 +585,12 @@
         return -EWOULDBLOCK;
     }
 
+    if (!audio) {
+        const char *mime;
+        CHECK(meta->findCString(kKeyMIMEType, &mime));
+        mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime);
+    }
+
     sp<AMessage> notify =
         new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
                      id());
@@ -593,53 +624,70 @@
     }
 
     sp<ABuffer> accessUnit;
-    status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
 
-    if (err == -EWOULDBLOCK) {
-        return err;
-    } else if (err != OK) {
-        if (err == INFO_DISCONTINUITY) {
-            int32_t type;
-            CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
+    bool dropAccessUnit;
+    do {
+        status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
 
-            bool formatChange =
-                type == ATSParser::DISCONTINUITY_FORMATCHANGE;
+        if (err == -EWOULDBLOCK) {
+            return err;
+        } else if (err != OK) {
+            if (err == INFO_DISCONTINUITY) {
+                int32_t type;
+                CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
 
-            LOGV("%s discontinuity (formatChange=%d)",
-                 audio ? "audio" : "video", formatChange);
+                bool formatChange =
+                    type == ATSParser::DISCONTINUITY_FORMATCHANGE;
 
-            if (audio) {
-                mSkipRenderingAudioUntilMediaTimeUs = -1;
-            } else {
-                mSkipRenderingVideoUntilMediaTimeUs = -1;
-            }
+                LOGV("%s discontinuity (formatChange=%d)",
+                     audio ? "audio" : "video", formatChange);
 
-            sp<AMessage> extra;
-            if (accessUnit->meta()->findMessage("extra", &extra)
-                    && extra != NULL) {
-                int64_t resumeAtMediaTimeUs;
-                if (extra->findInt64(
-                            "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
-                    LOGI("suppressing rendering of %s until %lld us",
-                            audio ? "audio" : "video", resumeAtMediaTimeUs);
+                if (audio) {
+                    mSkipRenderingAudioUntilMediaTimeUs = -1;
+                } else {
+                    mSkipRenderingVideoUntilMediaTimeUs = -1;
+                }
 
-                    if (audio) {
-                        mSkipRenderingAudioUntilMediaTimeUs =
-                            resumeAtMediaTimeUs;
-                    } else {
-                        mSkipRenderingVideoUntilMediaTimeUs =
-                            resumeAtMediaTimeUs;
+                sp<AMessage> extra;
+                if (accessUnit->meta()->findMessage("extra", &extra)
+                        && extra != NULL) {
+                    int64_t resumeAtMediaTimeUs;
+                    if (extra->findInt64(
+                                "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
+                        LOGI("suppressing rendering of %s until %lld us",
+                                audio ? "audio" : "video", resumeAtMediaTimeUs);
+
+                        if (audio) {
+                            mSkipRenderingAudioUntilMediaTimeUs =
+                                resumeAtMediaTimeUs;
+                        } else {
+                            mSkipRenderingVideoUntilMediaTimeUs =
+                                resumeAtMediaTimeUs;
+                        }
                     }
                 }
+
+                flushDecoder(audio, formatChange);
             }
 
-            flushDecoder(audio, formatChange);
+            reply->setInt32("err", err);
+            reply->post();
+            return OK;
         }
 
-        reply->setInt32("err", err);
-        reply->post();
-        return OK;
-    }
+        if (!audio) {
+            ++mNumFramesTotal;
+        }
+
+        dropAccessUnit = false;
+        if (!audio
+                && mVideoLateByUs > 100000ll
+                && mVideoIsAVC
+                && !IsAVCReferenceFrame(accessUnit)) {
+            dropAccessUnit = true;
+            ++mNumFramesDropped;
+        }
+    } while (dropAccessUnit);
 
     // LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index cf9185b..a5382b4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -70,19 +70,19 @@
     struct StreamingSource;
 
     enum {
-        kWhatSetDataSource,
-        kWhatSetVideoNativeWindow,
-        kWhatSetAudioSink,
-        kWhatMoreDataQueued,
-        kWhatStart,
-        kWhatScanSources,
-        kWhatVideoNotify,
-        kWhatAudioNotify,
-        kWhatRendererNotify,
-        kWhatReset,
-        kWhatSeek,
-        kWhatPause,
-        kWhatResume,
+        kWhatSetDataSource              = '=DaS',
+        kWhatSetVideoNativeWindow       = '=NaW',
+        kWhatSetAudioSink               = '=AuS',
+        kWhatMoreDataQueued             = 'more',
+        kWhatStart                      = 'strt',
+        kWhatScanSources                = 'scan',
+        kWhatVideoNotify                = 'vidN',
+        kWhatAudioNotify                = 'audN',
+        kWhatRendererNotify             = 'renN',
+        kWhatReset                      = 'rset',
+        kWhatSeek                       = 'seek',
+        kWhatPause                      = 'paus',
+        kWhatResume                     = 'rsme',
     };
 
     wp<NuPlayerDriver> mDriver;
@@ -92,6 +92,7 @@
     sp<NativeWindowWrapper> mNativeWindow;
     sp<MediaPlayerBase::AudioSink> mAudioSink;
     sp<Decoder> mVideoDecoder;
+    bool mVideoIsAVC;
     sp<Decoder> mAudioDecoder;
     sp<Renderer> mRenderer;
 
@@ -119,6 +120,9 @@
     int64_t mSkipRenderingAudioUntilMediaTimeUs;
     int64_t mSkipRenderingVideoUntilMediaTimeUs;
 
+    int64_t mVideoLateByUs;
+    int64_t mNumFramesTotal, mNumFramesDropped;
+
     status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
 
     status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 81b41ef..56c2773 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -59,8 +59,21 @@
         format->setObject("native-window", mNativeWindow);
     }
 
+    // Current video decoders do not return from OMX_FillThisBuffer
+    // quickly, violating the OpenMAX specs, until that is remedied
+    // we need to invest in an extra looper to free the main event
+    // queue.
+    bool needDedicatedLooper = !strncasecmp(mime, "video/", 6);
+
     mCodec = new ACodec;
-    looper()->registerHandler(mCodec);
+
+    if (needDedicatedLooper && mCodecLooper == NULL) {
+        mCodecLooper = new ALooper;
+        mCodecLooper->setName("NuPlayerDecoder");
+        mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+    }
+
+    (needDedicatedLooper ? mCodecLooper : looper())->registerHandler(mCodec);
 
     mCodec->setNotificationMessage(notifyMsg);
     mCodec->initiateSetup(format);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index fabc606..3ab1fcf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -43,13 +43,14 @@
 
 private:
     enum {
-        kWhatCodecNotify,
+        kWhatCodecNotify        = 'cdcN',
     };
 
     sp<AMessage> mNotify;
     sp<NativeWindowWrapper> mNativeWindow;
 
     sp<ACodec> mCodec;
+    sp<ALooper> mCodecLooper;
 
     Vector<sp<ABuffer> > mCSD;
     size_t mCSDIndex;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index c6fca2c..b1e917d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -31,6 +31,8 @@
     : mResetInProgress(false),
       mDurationUs(-1),
       mPositionUs(-1),
+      mNumFramesTotal(0),
+      mNumFramesDropped(0),
       mLooper(new ALooper),
       mState(UNINITIALIZED),
       mStartupSeekTimeUs(-1) {
@@ -292,4 +294,30 @@
     sendEvent(MEDIA_SEEK_COMPLETE);
 }
 
+void NuPlayerDriver::notifyFrameStats(
+        int64_t numFramesTotal, int64_t numFramesDropped) {
+    Mutex::Autolock autoLock(mLock);
+    mNumFramesTotal = numFramesTotal;
+    mNumFramesDropped = numFramesDropped;
+}
+
+status_t NuPlayerDriver::dump(int fd, const Vector<String16> &args) const {
+    Mutex::Autolock autoLock(mLock);
+
+    FILE *out = fdopen(dup(fd), "w");
+
+    fprintf(out, " NuPlayer\n");
+    fprintf(out, "  numFramesTotal(%lld), numFramesDropped(%lld), "
+                 "percentageDropped(%.2f)\n",
+                 mNumFramesTotal,
+                 mNumFramesDropped,
+                 mNumFramesTotal == 0
+                    ? 0.0 : (double)mNumFramesDropped / mNumFramesTotal);
+
+    fclose(out);
+    out = NULL;
+
+    return OK;
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 1bb7ca2..181c37d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -60,16 +60,19 @@
     virtual status_t getMetadata(
             const media::Metadata::Filter& ids, Parcel *records);
 
+    virtual status_t dump(int fd, const Vector<String16> &args) const;
+
     void notifyResetComplete();
     void notifyDuration(int64_t durationUs);
     void notifyPosition(int64_t positionUs);
     void notifySeekComplete();
+    void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped);
 
 protected:
     virtual ~NuPlayerDriver();
 
 private:
-    Mutex mLock;
+    mutable Mutex mLock;
     Condition mCondition;
 
     // The following are protected through "mLock"
@@ -77,6 +80,8 @@
     bool mResetInProgress;
     int64_t mDurationUs;
     int64_t mPositionUs;
+    int64_t mNumFramesTotal;
+    int64_t mNumFramesDropped;
     // <<<
 
     sp<ALooper> mLooper;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index bf83849..07e347e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -47,7 +47,8 @@
       mHasVideo(false),
       mSyncQueues(false),
       mPaused(false),
-      mLastPositionUpdateUs(-1ll) {
+      mLastPositionUpdateUs(-1ll),
+      mVideoLateByUs(0ll) {
 }
 
 NuPlayer::Renderer::~Renderer() {
@@ -118,9 +119,24 @@
 
             mDrainAudioQueuePending = false;
 
-            onDrainAudioQueue();
+            if (onDrainAudioQueue()) {
+                uint32_t numFramesPlayed;
+                CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed),
+                         (status_t)OK);
 
-            postDrainAudioQueue();
+                uint32_t numFramesPendingPlayout =
+                    mNumFramesWritten - numFramesPlayed;
+
+                // This is how long the audio sink will have data to
+                // play back.
+                int64_t delayUs =
+                    mAudioSink->msecsPerFrame()
+                        * numFramesPendingPlayout * 1000ll;
+
+                // Let's give it more data after about half that time
+                // has elapsed.
+                postDrainAudioQueue(delayUs / 2);
+            }
             break;
         }
 
@@ -182,7 +198,7 @@
     }
 }
 
-void NuPlayer::Renderer::postDrainAudioQueue() {
+void NuPlayer::Renderer::postDrainAudioQueue(int64_t delayUs) {
     if (mDrainAudioQueuePending || mSyncQueues || mPaused) {
         return;
     }
@@ -194,19 +210,33 @@
     mDrainAudioQueuePending = true;
     sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
     msg->setInt32("generation", mAudioQueueGeneration);
-    msg->post();
+    msg->post(delayUs);
 }
 
 void NuPlayer::Renderer::signalAudioSinkChanged() {
     (new AMessage(kWhatAudioSinkChanged, id()))->post();
 }
 
-void NuPlayer::Renderer::onDrainAudioQueue() {
-    for (;;) {
-        if (mAudioQueue.empty()) {
-            break;
-        }
+bool NuPlayer::Renderer::onDrainAudioQueue() {
+    uint32_t numFramesPlayed;
+    CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
 
+    ssize_t numFramesAvailableToWrite =
+        mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
+
+#if 0
+    if (numFramesAvailableToWrite == mAudioSink->frameCount()) {
+        LOGI("audio sink underrun");
+    } else {
+        LOGV("audio queue has %d frames left to play",
+             mAudioSink->frameCount() - numFramesAvailableToWrite);
+    }
+#endif
+
+    size_t numBytesAvailableToWrite =
+        numFramesAvailableToWrite * mAudioSink->frameSize();
+
+    while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) {
         QueueEntry *entry = &*mAudioQueue.begin();
 
         if (entry->mBuffer == NULL) {
@@ -216,20 +246,7 @@
 
             mAudioQueue.erase(mAudioQueue.begin());
             entry = NULL;
-            return;
-        }
-
-        uint32_t numFramesPlayed;
-        CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
-
-        ssize_t numFramesAvailableToWrite =
-            mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
-
-        size_t numBytesAvailableToWrite =
-            numFramesAvailableToWrite * mAudioSink->frameSize();
-
-        if (numBytesAvailableToWrite == 0) {
-            break;
+            return false;
         }
 
         if (entry->mOffset == 0) {
@@ -274,10 +291,14 @@
             entry = NULL;
         }
 
-        mNumFramesWritten += copy / mAudioSink->frameSize();
+        numBytesAvailableToWrite -= copy;
+        size_t copiedFrames = copy / mAudioSink->frameSize();
+        mNumFramesWritten += copiedFrames;
     }
 
     notifyPosition();
+
+    return !mAudioQueue.empty();
 }
 
 void NuPlayer::Renderer::postDrainVideoQueue() {
@@ -337,15 +358,26 @@
 
         mVideoQueue.erase(mVideoQueue.begin());
         entry = NULL;
+
+        mVideoLateByUs = 0ll;
+
+        notifyPosition();
         return;
     }
 
-#if 0
     int64_t mediaTimeUs;
     CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
 
-    LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
-#endif
+    int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs;
+    mVideoLateByUs = ALooper::GetNowUs() - realTimeUs;
+
+    bool tooLate = (mVideoLateByUs > 40000);
+
+    if (tooLate) {
+        LOGV("video late by %lld us (%.2f secs)", lateByUs, lateByUs / 1E6);
+    } else {
+        LOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
+    }
 
     entry->mNotifyConsumed->setInt32("render", true);
     entry->mNotifyConsumed->post();
@@ -577,6 +609,7 @@
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatPosition);
     notify->setInt64("positionUs", positionUs);
+    notify->setInt64("videoLateByUs", mVideoLateByUs);
     notify->post();
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 3a641a2..268628b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -45,9 +45,9 @@
     void resume();
 
     enum {
-        kWhatEOS,
-        kWhatFlushComplete,
-        kWhatPosition,
+        kWhatEOS                = 'eos ',
+        kWhatFlushComplete      = 'fluC',
+        kWhatPosition           = 'posi',
     };
 
 protected:
@@ -57,14 +57,14 @@
 
 private:
     enum {
-        kWhatDrainAudioQueue,
-        kWhatDrainVideoQueue,
-        kWhatQueueBuffer,
-        kWhatQueueEOS,
-        kWhatFlush,
-        kWhatAudioSinkChanged,
-        kWhatPause,
-        kWhatResume,
+        kWhatDrainAudioQueue    = 'draA',
+        kWhatDrainVideoQueue    = 'draV',
+        kWhatQueueBuffer        = 'queB',
+        kWhatQueueEOS           = 'qEOS',
+        kWhatFlush              = 'flus',
+        kWhatAudioSinkChanged   = 'auSC',
+        kWhatPause              = 'paus',
+        kWhatResume             = 'resm',
     };
 
     struct QueueEntry {
@@ -101,9 +101,10 @@
     bool mPaused;
 
     int64_t mLastPositionUpdateUs;
+    int64_t mVideoLateByUs;
 
-    void onDrainAudioQueue();
-    void postDrainAudioQueue();
+    bool onDrainAudioQueue();
+    void postDrainAudioQueue(int64_t delayUs = 0);
 
     void onDrainVideoQueue();
     void postDrainVideoQueue();
@@ -118,6 +119,7 @@
     void notifyEOS(bool audio, status_t finalResult);
     void notifyFlushComplete(bool audio);
     void notifyPosition();
+    void notifyVideoLateBy(int64_t lateByUs);
 
     void flushQueue(List<QueueEntry> *queue);
     bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index a741987..7319e4c 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -52,7 +52,7 @@
         return false;
     }
 
-    for (int32_t i = 0; i < 10; ++i) {
+    for (int32_t i = 0; i < 50; ++i) {
         char buffer[188];
         sp<AMessage> extra;
         ssize_t n = mStreamListener->read(buffer, sizeof(buffer), &extra);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2ba2273..a2d9e59 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1377,8 +1377,13 @@
                     memcpy(info->mData->data(), buffer->data(), buffer->size());
                 }
 
-                LOGV("[%s] calling emptyBuffer %p",
-                     mCodec->mComponentName.c_str(), bufferID);
+                if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
+                    LOGV("[%s] calling emptyBuffer %p w/ codec specific data",
+                         mCodec->mComponentName.c_str(), bufferID);
+                } else {
+                    LOGV("[%s] calling emptyBuffer %p w/ time %lld us",
+                         mCodec->mComponentName.c_str(), bufferID, timeUs);
+                }
 
                 CHECK_EQ(mCodec->mOMX->emptyBuffer(
                             mCodec->mNode,
@@ -1396,7 +1401,7 @@
                 LOGV("[%s] Signalling EOS on the input port",
                      mCodec->mComponentName.c_str());
 
-                LOGV("[%s] calling emptyBuffer %p",
+                LOGV("[%s] calling emptyBuffer %p signalling EOS",
                      mCodec->mComponentName.c_str(), bufferID);
 
                 CHECK_EQ(mCodec->mOMX->emptyBuffer(
@@ -1457,8 +1462,8 @@
         int64_t timeUs,
         void *platformPrivate,
         void *dataPtr) {
-    LOGV("[%s] onOMXFillBufferDone %p",
-         mCodec->mComponentName.c_str(), bufferID);
+    LOGV("[%s] onOMXFillBufferDone %p time %lld us",
+         mCodec->mComponentName.c_str(), bufferID, timeUs);
 
     ssize_t index;
     BufferInfo *info =
@@ -1686,7 +1691,11 @@
             ++matchIndex) {
         componentName = matchingCodecs.itemAt(matchIndex).string();
 
+        pid_t tid = androidGetTid();
+        int prevPriority = androidGetThreadPriority(tid);
+        androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
         status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+        androidSetThreadPriority(tid, prevPriority);
 
         if (err == OK) {
             break;
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 8a42e8b..07aa140 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -329,6 +329,28 @@
     return foundIDR;
 }
 
+bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit) {
+    const uint8_t *data = accessUnit->data();
+    size_t size = accessUnit->size();
+
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+        CHECK_GT(nalSize, 0u);
+
+        unsigned nalType = nalStart[0] & 0x1f;
+
+        if (nalType == 5) {
+            return true;
+        } else if (nalType == 1) {
+            unsigned nal_ref_idc = (nalStart[0] >> 5) & 3;
+            return nal_ref_idc != 0;
+        }
+    }
+
+    return true;
+}
+
 sp<MetaData> MakeAACCodecSpecificData(
         unsigned profile, unsigned sampling_freq_index,
         unsigned channel_configuration) {
diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
index de936c4..f15014e 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -74,10 +74,32 @@
     return false;
 }
 
+struct AutoPrioritySaver {
+    AutoPrioritySaver()
+        : mTID(androidGetTid()),
+          mPrevPriority(androidGetThreadPriority(mTID)) {
+        androidSetThreadPriority(mTID, ANDROID_PRIORITY_NORMAL);
+    }
+
+    ~AutoPrioritySaver() {
+        androidSetThreadPriority(mTID, mPrevPriority);
+    }
+
+private:
+    pid_t mTID;
+    int mPrevPriority;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AutoPrioritySaver);
+};
 
 static void InitializeNetworkThreadIfNecessary() {
     Mutex::Autolock autoLock(gNetworkThreadLock);
+
     if (gNetworkThread == NULL) {
+        // Make sure any threads spawned by the chromium framework are
+        // running at normal priority instead of inheriting this thread's.
+        AutoPrioritySaver saver;
+
         gNetworkThread = new base::Thread("network");
         base::Thread::Options options;
         options.message_loop_type = MessageLoop::TYPE_IO;
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 582bdba..f039bc1 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -385,6 +385,15 @@
                             item.u.refValue)->debugString(
                                 indent + strlen(item.mName) + 14).c_str());
                 break;
+            case kTypeRect:
+                tmp = StringPrintf(
+                        "Rect %s(%d, %d, %d, %d)",
+                        item.mName,
+                        item.u.rectValue.mLeft,
+                        item.u.rectValue.mTop,
+                        item.u.rectValue.mRight,
+                        item.u.rectValue.mBottom);
+                break;
             default:
                 TRESPASS();
         }
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index 15cd4d4..e418822 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -50,6 +50,7 @@
 sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
 
 bool IsIDR(const sp<ABuffer> &accessUnit);
+bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit);
 
 const char *AVCProfileToString(uint8_t profile);
 
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index bc24dbb..33d3f30 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -85,7 +85,7 @@
     : mOwner(owner),
       mDone(false) {
     mThread = new CallbackDispatcherThread(this);
-    mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_AUDIO);
+    mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND);
 }
 
 OMX::CallbackDispatcher::~CallbackDispatcher() {
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index f7330f3..b705d00 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -42,7 +42,7 @@
     mLooper->start(
             false, // runOnCallingThread
             false, // canCallJava
-            PRIORITY_AUDIO);
+            ANDROID_PRIORITY_FOREGROUND);
 }
 
 void SimpleSoftOMXComponent::prepareForDestruction() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index d2b7378..46f7139 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -130,7 +130,7 @@
         if (window != NULL) {
             native_window_set_buffers_format(window, 0);
             if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
-                LOGE("EGLNativeWindowType %p disconnected failed", window);
+                LOGW("EGLNativeWindowType %p disconnect failed", window);
             }
         }
     }
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index f84cc19..2fe22ff 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -69,6 +69,12 @@
 
     </FrameLayout>
 
+    <include layout="@layout/status_bar_no_recent_apps"
+        android:id="@+id/recents_no_apps"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible" />
+
     <View android:id="@+id/recents_dismiss_button"
         android:layout_width="80px"
         android:layout_height="@*android:dimen/status_bar_height"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index ed9ea7a..4d49077 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -67,6 +67,12 @@
 
     </FrameLayout>
 
+    <include layout="@layout/status_bar_no_recent_apps"
+        android:id="@+id/recents_no_apps"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible" />
+
     <View android:id="@+id/recents_dismiss_button"
         android:layout_width="80px"
         android:layout_height="@*android:dimen/status_bar_height"
diff --git a/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml b/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml
new file mode 100644
index 0000000..47ffb83
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    >
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24dp"
+        android:textColor="#ffffffff"
+        android:text="@string/status_bar_no_recent_apps"
+        android:gravity="center_horizontal"
+        android:layout_gravity="center"
+    />
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bad7e1f..b9e6d78 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -41,6 +41,10 @@
     <!-- Title shown in recents popup for inspecting an application's properties -->
     <string name="status_bar_recent_inspect_item_title">App info</string>
 
+    <!-- Message shown in the middle of the screen after clicking on the recent apps button
+         when there are no recent apps to show [CHAR LIMIT=45]-->
+    <string name="status_bar_no_recent_apps">No recent apps</string>
+
     <!-- The label in the bar at the top of the status bar when there are no notifications
          showing.  [CHAR LIMIT=40]-->
     <string name="status_bar_no_notifications_title">No notifications</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index 9749a1d..fbf00d2 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -38,17 +38,20 @@
     View mRootView;
     View mScrimView;
     View mContentView;
+    View mNoRecentAppsView;
     AnimatorSet mContentAnim;
     Animator.AnimatorListener mListener;
 
     // the panel will start to appear this many px from the end
     final int HYPERSPACE_OFFRAMP = 200;
 
-    public Choreographer(View root, View scrim, View content, Animator.AnimatorListener listener) {
+    public Choreographer(View root, View scrim, View content,
+            View noRecentApps, Animator.AnimatorListener listener) {
         mRootView = root;
         mScrimView = scrim;
         mContentView = content;
         mListener = listener;
+        mNoRecentAppsView = noRecentApps;
     }
 
     void createAnimation(boolean appearing) {
@@ -81,8 +84,24 @@
                 : new android.view.animation.DecelerateInterpolator(1.0f));
         glowAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
 
+        Animator noRecentAppsFadeAnim = null;
+        if (mNoRecentAppsView != null &&  // doesn't exist on large devices
+                mNoRecentAppsView.getVisibility() == View.VISIBLE) {
+            noRecentAppsFadeAnim = ObjectAnimator.ofFloat(mNoRecentAppsView, "alpha",
+                    mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
+            noRecentAppsFadeAnim.setInterpolator(appearing
+                    ? new android.view.animation.AccelerateInterpolator(1.0f)
+                    : new android.view.animation.DecelerateInterpolator(1.0f));
+            noRecentAppsFadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
+        }
+
         mContentAnim = new AnimatorSet();
         final Builder builder = mContentAnim.play(glowAnim).with(posAnim);
+
+        if (noRecentAppsFadeAnim != null) {
+            builder.with(noRecentAppsFadeAnim);
+        }
+
         Drawable background = mScrimView.getBackground();
         if (background != null) {
             Animator bgAnim = ObjectAnimator.ofInt(background,
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 0621b22..6fdc534 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -83,6 +83,7 @@
     private int mIconDpi;
     private View mRecentsScrim;
     private View mRecentsGlowView;
+    private View mRecentsNoApps;
     private ViewGroup mRecentsContainer;
     private Bitmap mDefaultThumbnailBackground;
 
@@ -373,8 +374,9 @@
 
 
         mRecentsGlowView = findViewById(R.id.recents_glow);
-        mRecentsScrim = (View) findViewById(R.id.recents_bg_protect);
-        mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView, this);
+        mRecentsScrim = findViewById(R.id.recents_bg_protect);
+        mRecentsNoApps = findViewById(R.id.recents_no_apps);
+        mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView, mRecentsNoApps, this);
         mRecentsDismissButton = findViewById(R.id.recents_dismiss_button);
         mRecentsDismissButton.setOnClickListener(new OnClickListener() {
             public void onClick(View v) {
@@ -581,6 +583,9 @@
             mThumbnailLoader.cancel(false);
             mThumbnailLoader = null;
         }
+        if (mRecentsNoApps != null) { // doesn't exist on large devices
+            mRecentsNoApps.setVisibility(View.INVISIBLE);
+        }
         mActivityDescriptions = getRecentTasks();
         for (ActivityDescription ad : mActivityDescriptions) {
             ad.setThumbnail(mDefaultThumbnailBackground);
@@ -647,7 +652,11 @@
         } else {
             // Immediately hide this panel
             if (DEBUG) Log.v(TAG, "Nothing to show");
-            hide(false);
+            if (mRecentsNoApps != null) { // doesn't exist on large devices
+                mRecentsNoApps.setVisibility(View.VISIBLE);
+            } else {
+                hide(false);
+            }
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index cbf1c90..91f1527 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -24,6 +24,7 @@
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.graphics.Canvas;
+import android.os.IBinder;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.view.View;
@@ -59,6 +60,10 @@
 
     private boolean mScreenOn = false;
 
+    public interface ShowListener {
+        void onShown(IBinder windowToken);
+    };
+
     /**
      * @param context Used to create views.
      * @param viewManager Keyguard will be attached to this.
@@ -206,7 +211,8 @@
         }
     }
 
-    public synchronized void onScreenTurnedOn() {
+    public synchronized void onScreenTurnedOn(
+            final KeyguardViewManager.ShowListener showListener) {
         if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
         mScreenOn = true;
         if (mKeyguardView != null) {
@@ -214,6 +220,26 @@
 
             // When screen is turned on, need to bind to FaceLock service if we are using FaceLock
             mKeyguardView.bindToFaceLock();
+
+            // Caller should wait for this window to be shown before turning
+            // on the screen.
+            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+                // Keyguard may be in the process of being shown, but not yet
+                // updated with the window manager...  give it a chance to do so.
+                mKeyguardHost.post(new Runnable() {
+                    @Override public void run() {
+                        if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+                            showListener.onShown(mKeyguardHost.getWindowToken());
+                        } else {
+                            showListener.onShown(null);
+                        }
+                    }
+                });
+            } else {
+                showListener.onShown(null);
+            }
+        } else {
+            showListener.onShown(null);
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 64a9677..5d3dee9 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -116,7 +116,6 @@
     private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
     private static final int SET_HIDDEN = 12;
     private static final int KEYGUARD_TIMEOUT = 13;
-    private static final int REPORT_SHOW_DONE = 14;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -239,8 +238,6 @@
 
     private boolean mScreenOn = false;
 
-    private boolean mShowPending = false;
-
     // last known state of the cellular connection
     private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
 
@@ -383,19 +380,7 @@
             } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
                 // Do not enable the keyguard if the prox sensor forced the screen off.
             } else {
-                if (!doKeyguardLocked() && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) {
-                    // The user has explicitly turned off the screen, causing it
-                    // to lock.  We want to block here until the keyguard window
-                    // has shown, so the power manager won't complete the screen
-                    // off flow until that point, so we know it won't turn *on*
-                    // the screen until this is done.
-                    while (mShowPending) {
-                        try {
-                            wait();
-                        } catch (InterruptedException e) {
-                        }
-                    }
-                }
+                doKeyguardLocked();
             }
         }
     }
@@ -403,12 +388,12 @@
     /**
      * Let's us know the screen was turned on.
      */
-    public void onScreenTurnedOn() {
+    public void onScreenTurnedOn(KeyguardViewManager.ShowListener showListener) {
         synchronized (this) {
             mScreenOn = true;
             mDelayedShowingSequence++;
             if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
-            notifyScreenOnLocked();
+            notifyScreenOnLocked(showListener);
         }
     }
 
@@ -573,7 +558,7 @@
      * work that will happen is done; returns false if the caller can wait for
      * the keyguard to be shown.
      */
-    private boolean doKeyguardLocked() {
+    private void doKeyguardLocked() {
         // if another app is disabling us, don't show
         if (!mExternallyEnabled) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
@@ -587,13 +572,13 @@
             // ends (see the broadcast receiver below)
             // TODO: clean this up when we have better support at the window manager level
             // for apps that wish to be on top of the keyguard
-            return true;
+            return;
         }
 
         // if the keyguard is already showing, don't bother
         if (mKeyguardViewManager.isShowing()) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
-            return true;
+            return;
         }
 
         // if the setup wizard hasn't run yet, don't show
@@ -609,18 +594,16 @@
         if (!lockedOrMissing && !provisioned) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
                     + " and the sim is not locked or missing");
-            return true;
+            return;
         }
 
         if (mLockPatternUtils.isLockScreenDisabled()) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
-            return true;
+            return;
         }
 
         if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
-        mShowPending = true;
         showLocked();
-        return false;
     }
 
     /**
@@ -658,9 +641,10 @@
      * @see #onScreenTurnedOn()
      * @see #handleNotifyScreenOn
      */
-    private void notifyScreenOnLocked() {
+    private void notifyScreenOnLocked(KeyguardViewManager.ShowListener showListener) {
         if (DEBUG) Log.d(TAG, "notifyScreenOnLocked");
-        mHandler.sendEmptyMessage(NOTIFY_SCREEN_ON);
+        Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_ON, showListener);
+        mHandler.sendMessage(msg);
     }
 
     /**
@@ -974,7 +958,7 @@
                     handleNotifyScreenOff();
                     return;
                 case NOTIFY_SCREEN_ON:
-                    handleNotifyScreenOn();
+                    handleNotifyScreenOn((KeyguardViewManager.ShowListener)msg.obj);
                     return;
                 case WAKE_WHEN_READY:
                     handleWakeWhenReady(msg.arg1);
@@ -996,12 +980,6 @@
                         doKeyguardLocked();
                     }
                     break;
-                case REPORT_SHOW_DONE:
-                    synchronized (KeyguardViewMediator.this) {
-                        mShowPending = false;
-                        KeyguardViewMediator.this.notifyAll();
-                    }
-                    break;
             }
         }
     };
@@ -1113,12 +1091,6 @@
             playSounds(true);
 
             mShowKeyguardWakeLock.release();
-
-            // We won't say the show is done yet because the view hierarchy
-            // still needs to do the traversal.  Posting this message allows
-            // us to hold off until that is done.
-            Message msg = mHandler.obtainMessage(REPORT_SHOW_DONE);
-            mHandler.sendMessage(msg);
         }
     }
 
@@ -1284,10 +1256,10 @@
      * Handle message sent by {@link #notifyScreenOnLocked()}
      * @see #NOTIFY_SCREEN_ON
      */
-    private void handleNotifyScreenOn() {
+    private void handleNotifyScreenOn(KeyguardViewManager.ShowListener showListener) {
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleNotifyScreenOn");
-            mKeyguardViewManager.onScreenTurnedOn();
+            mKeyguardViewManager.onScreenTurnedOn(showListener);
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index cba1ea1..465fc2c 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -46,6 +46,8 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -74,7 +76,7 @@
  * {@link com.android.internal.policy.impl.KeyguardViewManager}
  * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate.
  */
-public class LockPatternKeyguardView extends KeyguardViewBase {
+public class LockPatternKeyguardView extends KeyguardViewBase implements Handler.Callback {
 
     private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000;
 
@@ -103,9 +105,15 @@
     // The following were added to support FaceLock
     private IFaceLockInterface mFaceLockService;
     private boolean mBoundToFaceLockService = false;
-    private boolean mFaceLockServiceRunning = false;
     private View mFaceLockAreaView;
 
+    private boolean mFaceLockServiceRunning = false;
+    private final Object mFaceLockServiceRunningLock = new Object();
+
+    private Handler mHandler;
+    private final int MSG_SHOW_FACELOCK_AREA_VIEW = 0;
+    private final int MSG_HIDE_FACELOCK_AREA_VIEW = 1;
+
     /**
      * The current {@link KeyguardScreen} will use this to communicate back to us.
      */
@@ -244,6 +252,7 @@
             KeyguardWindowController controller) {
         super(context);
 
+        mHandler = new Handler(this);
         mConfiguration = context.getResources().getConfiguration();
         mEnableFallback = false;
         mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
@@ -700,12 +709,6 @@
                     mKeyguardScreenCallback,
                     mUpdateMonitor.getFailedAttempts());
             view.setEnableFallback(mEnableFallback);
-
-            // TODO(bcolonna): For pattern unlock, it can give us the view where the pattern is
-            // displayed and FaceLock can draw in that area.
-            // For other views it's not so simple and we should probably change how the FaceLock
-            // area is determined.
-            mFaceLockAreaView = view.getUnlockAreaView();
             unlockView = view;
         } else if (unlockMode == UnlockMode.SimPuk) {
             unlockView = new SimPukUnlockScreen(
@@ -755,6 +758,8 @@
             throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
         }
         initializeTransportControlView(unlockView);
+        initializeFaceLockAreaView(unlockView); // Only shows view if FaceLock is enabled
+
         mUnlockScreenMode = unlockMode;
         return unlockView;
     }
@@ -934,6 +939,41 @@
 
     // Everything below pertains to FaceLock - might want to separate this out
 
+    // Only pattern and pin unlock screens actually have a view for the FaceLock area, so it's not
+    // uncommon for it to not exist.  But if it does exist, we need to make sure it's showing if
+    // FaceLock is enabled, and make sure it's not showing if FaceLock is disabled
+    private void initializeFaceLockAreaView(View view) {
+        mFaceLockAreaView = view.findViewById(R.id.faceLockAreaView);
+        if (mFaceLockAreaView == null) {
+            if (DEBUG) Log.d(TAG, "Layout does not have faceLockAreaView");
+        } else {
+            if (mLockPatternUtils.usingBiometricWeak()) {
+                mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW);
+            } else {
+                mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
+            }
+        }
+    }
+
+    // Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops
+    // This needs to be done in a handler because the call could be coming from a callback from the
+    // FaceLock service that is in a thread that can't modify the UI
+    @Override
+    public boolean handleMessage(Message msg) {
+        switch (msg.what) {
+        case MSG_SHOW_FACELOCK_AREA_VIEW:
+            mFaceLockAreaView.setVisibility(View.VISIBLE);
+            break;
+        case MSG_HIDE_FACELOCK_AREA_VIEW:
+            mFaceLockAreaView.setVisibility(View.GONE);
+            break;
+        default:
+            Log.w(TAG, "Unhandled message");
+            return false;
+        }
+        return true;
+    }
+
     // Binds to FaceLock service, but does not tell it to start
     public void bindToFaceLock() {
         if (mLockPatternUtils.usingBiometricWeak()) {
@@ -982,23 +1022,20 @@
                 throw new RuntimeException("Remote exception");
             }
 
-            // TODO(bcolonna): Need to set location properly (only works for pattern view now)
             if (mFaceLockAreaView != null) {
-                int[] unlockLocationOnScreen = new int[2];
-                mFaceLockAreaView.getLocationOnScreen(unlockLocationOnScreen);
-                int x = unlockLocationOnScreen[0];
-                int y = unlockLocationOnScreen[1];
-                int w = mFaceLockAreaView.getWidth();
-                int h = mFaceLockAreaView.getHeight();
-                if (DEBUG) Log.d(TAG, "(x,y) (wxh): (" + x + "," + y + ") (" + w + "x" + h + ")");
-                startFaceLock(mFaceLockAreaView.getWindowToken(), x, y, w, h);
+                startFaceLock(mFaceLockAreaView.getWindowToken(),
+                        mFaceLockAreaView.getLeft(), mFaceLockAreaView.getTop(),
+                        mFaceLockAreaView.getWidth(), mFaceLockAreaView.getHeight());
             }
         }
 
         // Cleans up if FaceLock service unexpectedly disconnects
         @Override
         public void onServiceDisconnected(ComponentName className) {
-            mFaceLockService = null;
+            synchronized(mFaceLockServiceRunningLock) {
+                mFaceLockService = null;
+                mFaceLockServiceRunning = false;
+            }
             if (DEBUG) Log.w(TAG, "Unexpected disconnect from FaceLock service");
         }
     };
@@ -1007,16 +1044,18 @@
     public void startFaceLock(IBinder windowToken, int x, int y, int h, int w)
     {
         if (mLockPatternUtils.usingBiometricWeak()) {
-            if (!mFaceLockServiceRunning) {
-                if (DEBUG) Log.d(TAG, "Starting FaceLock");
-                try {
-                    mFaceLockService.startUi(windowToken, x, y, h, w);
-                } catch (RemoteException e) {
-                    throw new RuntimeException("Remote exception");
+            synchronized (mFaceLockServiceRunningLock) {
+                if (!mFaceLockServiceRunning) {
+                    if (DEBUG) Log.d(TAG, "Starting FaceLock");
+                    try {
+                        mFaceLockService.startUi(windowToken, x, y, h, w);
+                    } catch (RemoteException e) {
+                        throw new RuntimeException("Remote exception");
+                    }
+                    mFaceLockServiceRunning = true;
+                } else {
+                    if (DEBUG) Log.w(TAG, "startFaceLock() attempted while running");
                 }
-                mFaceLockServiceRunning = true;
-            } else {
-                if (DEBUG) Log.w(TAG, "startFaceLock() attempted while running");
             }
         }
     }
@@ -1028,14 +1067,16 @@
             // Note that attempting to stop FaceLock when it's not running is not an issue.
             // FaceLock can return, which stops it and then we try to stop it when the
             // screen is turned off.  That's why we check.
-            if (mFaceLockServiceRunning) {
-                try {
-                    if (DEBUG) Log.d(TAG, "Stopping FaceLock");
-                    mFaceLockService.stopUi();
-                } catch (RemoteException e) {
-                    throw new RuntimeException("Remote exception");
+            synchronized (mFaceLockServiceRunningLock) {
+                if (mFaceLockServiceRunning) {
+                    try {
+                        if (DEBUG) Log.d(TAG, "Stopping FaceLock");
+                        mFaceLockService.stopUi();
+                    } catch (RemoteException e) {
+                        throw new RuntimeException("Remote exception");
+                    }
+                    mFaceLockServiceRunning = false;
                 }
-                mFaceLockServiceRunning = false;
             }
         }
     }
@@ -1047,6 +1088,9 @@
         @Override
         public void unlock() {
             if (DEBUG) Log.d(TAG, "FaceLock unlock");
+            // Note that we don't hide the client FaceLockAreaView because we want to keep the
+            // lock screen covered while the phone is unlocked
+
             stopFaceLock();
             mKeyguardScreenCallback.keyguardDone(true);
             mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
@@ -1057,6 +1101,8 @@
         public void cancel() {
             // In this case, either the user has cancelled out, or FaceLock failed to recognize them
             if (DEBUG) Log.d(TAG, "FaceLock cancel");
+            // Here we hide the client FaceLockViewArea to expose the underlying backup method
+            mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
             stopFaceLock();
         }
 
@@ -1065,8 +1111,12 @@
         public void sleepDevice() {
             // In this case, it appears the phone has been turned on accidentally
             if (DEBUG) Log.d(TAG, "FaceLock accidental turn on");
+            // Here we hide the client FaceLockViewArea to expose the underlying backup method
+            mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
             stopFaceLock();
             // TODO(bcolonna): how do we put the phone back to sleep (i.e., turn off the screen)
+            // TODO(bcolonna): this should be removed once the service is no longer calling it
+            // because we are just going to let the lockscreen timeout
         }
     };
 }
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
index 0cafeb5a..88c42a6 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -199,12 +199,6 @@
         setFocusableInTouchMode(true);
     }
 
-    // TODO(bcolonna): This is to tell FaceLock where to draw...but this covers up the wireless
-    // service text, so we will want to change the way the area is specified
-    public View getUnlockAreaView() {
-        return mLockPatternView;
-    }
-
     public void setEnableFallback(boolean state) {
         if (DEBUG) Log.d(TAG, "setEnableFallback(" + state + ")");
         mEnableFallback = state;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 903b405..1b7271d 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2135,7 +2135,8 @@
                                 com.android.internal.R.attr.actionModePopupWindowStyle);
                         mActionModePopup.setLayoutInScreenEnabled(true);
                         mActionModePopup.setLayoutInsetDecor(true);
-                        mActionModePopup.setClippingEnabled(false);
+                        mActionModePopup.setWindowLayoutType(
+                                WindowManager.LayoutParams.TYPE_APPLICATION);
                         mActionModePopup.setContentView(mActionModeView);
                         mActionModePopup.setWidth(MATCH_PARENT);
 
@@ -2144,10 +2145,12 @@
                                 com.android.internal.R.attr.actionBarSize, heightValue, true);
                         final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
                                 mContext.getResources().getDisplayMetrics());
-                        mActionModePopup.setHeight(height);
+                        mActionModeView.setContentHeight(height);
+                        mActionModePopup.setHeight(WRAP_CONTENT);
                         mShowActionModePopup = new Runnable() {
                             public void run() {
-                                mActionModePopup.showAtLocation(PhoneWindow.DecorView.this,
+                                mActionModePopup.showAtLocation(
+                                        mActionModeView.getApplicationWindowToken(),
                                         Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
                             }
                         };
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 10447ad..b229615 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -40,8 +40,10 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.LocalPowerManager;
 import android.os.Message;
 import android.os.Messenger;
@@ -125,6 +127,7 @@
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
 import android.view.KeyCharacterMap.FallbackAction;
+import android.view.WindowManagerPolicy.ScreenOnListener;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -2814,23 +2817,30 @@
             updateLockScreenTimeout();
             updateScreenSaverTimeoutLocked();
         }
-        try {
-            mWindowManager.waitForAllDrawn();
-        } catch (RemoteException e) {
-        }
-        // Wait for one frame to give surface flinger time to do its
-        // compositing.  Yes this is a hack, but I am really not up right now for
-        // implementing some mechanism to block until SF is done. :p
-        try {
-            Thread.sleep(20);
-        } catch (InterruptedException e) {
-        }
     }
 
     /** {@inheritDoc} */
-    public void screenTurnedOn() {
+    public void screenTurningOn(final ScreenOnListener screenOnListener) {
         EventLog.writeEvent(70000, 1);
-        mKeyguardMediator.onScreenTurnedOn();
+        //Slog.i(TAG, "Screen turning on...");
+        mKeyguardMediator.onScreenTurnedOn(new KeyguardViewManager.ShowListener() {
+            @Override public void onShown(IBinder windowToken) {
+                if (windowToken != null) {
+                    try {
+                        mWindowManager.waitForWindowDrawn(windowToken, new IRemoteCallback.Stub() {
+                            @Override public void sendResult(Bundle data) {
+                                Slog.i(TAG, "Lock screen displayed!");
+                                screenOnListener.onScreenOn();
+                            }
+                        });
+                    } catch (RemoteException e) {
+                    }
+                } else {
+                    Slog.i(TAG, "No lock screen!");
+                    screenOnListener.onScreenOn();
+                }
+            }
+        });
         synchronized (mLock) {
             mScreenOn = true;
             updateOrientationListenerLp();
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index dca795c..9ee5a30 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -434,9 +434,9 @@
 
         // the following loop works on 2 frames
 
-        ".Y4L01:\n"
+        "1:\n"
         "   cmp r8, r2\n"                   // curOut - maxCurOut
-        "   bcs .Y4L02\n"
+        "   bcs 2f\n"
 
 #define MO_ONE_FRAME \
     "   add r0, r1, r7, asl #1\n"       /* in + inputIndex */\
@@ -460,8 +460,8 @@
         MO_ONE_FRAME    // frame 2
 
         "   cmp r7, r3\n"                   // inputIndex - maxInIdx
-        "   bcc .Y4L01\n"
-        ".Y4L02:\n"
+        "   bcc 1b\n"
+        "2:\n"
 
         "   bic r6, r6, #0xC0000000\n"             // phaseFraction & ...
         // save modified values
@@ -541,9 +541,9 @@
         // r13 sp
         // r14
 
-        ".Y5L01:\n"
+        "3:\n"
         "   cmp r8, r2\n"                   // curOut - maxCurOut
-        "   bcs .Y5L02\n"
+        "   bcs 4f\n"
 
 #define ST_ONE_FRAME \
     "   bic r6, r6, #0xC0000000\n"      /* phaseFraction & ... */\
@@ -577,8 +577,8 @@
     ST_ONE_FRAME    // frame 1
 
         "   cmp r7, r3\n"                       // inputIndex - maxInIdx
-        "   bcc .Y5L01\n"
-        ".Y5L02:\n"
+        "   bcc 3b\n"
+        "4:\n"
 
         "   bic r6, r6, #0xC0000000\n"              // phaseFraction & ...
         // save modified values
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 2348d76..e0a2adc 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2394,6 +2394,12 @@
         return mTethering.getTetheredIfaces();
     }
 
+    @Override
+    public String[] getTetheredIfacePairs() {
+        enforceTetherAccessPermission();
+        return mTethering.getTetheredIfacePairs();
+    }
+
     public String[] getTetheringErroredIfaces() {
         enforceTetherAccessPermission();
         return mTethering.getErroredIfaces();
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index c517965..0cffb15 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -123,6 +123,8 @@
         public static final int InterfaceTxCounterResult  = 217;
         public static final int InterfaceRxThrottleResult = 218;
         public static final int InterfaceTxThrottleResult = 219;
+        public static final int QuotaCounterResult        = 220;
+        public static final int TetheringStatsResult      = 221;
 
         public static final int InterfaceChange           = 600;
         public static final int BandwidthControl          = 601;
@@ -1443,6 +1445,73 @@
         return stats;
     }
 
+    @Override
+    public NetworkStats getNetworkStatsTethering(String[] ifacePairs) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        if (ifacePairs.length % 2 != 0) {
+            throw new IllegalArgumentException(
+                    "unexpected ifacePairs; length=" + ifacePairs.length);
+        }
+
+        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
+        for (int i = 0; i < ifacePairs.length; i += 2) {
+            final String ifaceIn = ifacePairs[i];
+            final String ifaceOut = ifacePairs[i + 1];
+            if (ifaceIn != null && ifaceOut != null) {
+                stats.combineValues(getNetworkStatsTethering(ifaceIn, ifaceOut));
+            }
+        }
+        return stats;
+    }
+
+    private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
+        final StringBuilder command = new StringBuilder();
+        command.append("bandwidth gettetherstats ").append(ifaceIn).append(" ").append(ifaceOut);
+
+        final String rsp;
+        try {
+            rsp = mConnector.doCommand(command.toString()).get(0);
+        } catch (NativeDaemonConnectorException e) {
+            throw new IllegalStateException("Error communicating to native daemon", e);
+        }
+
+        final String[] tok = rsp.split(" ");
+        /* Expecting: "code ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets" */
+        if (tok.length != 7) {
+            throw new IllegalStateException("Native daemon returned unexpected result: " + rsp);
+        }
+
+        final int code;
+        try {
+            code = Integer.parseInt(tok[0]);
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException(
+                    "Failed to parse native daemon return code for " + ifaceIn + " " + ifaceOut);
+        }
+        if (code != NetdResponseCode.TetheringStatsResult) {
+            throw new IllegalStateException(
+                    "Unexpected return code from native daemon for " + ifaceIn + " " + ifaceOut);
+        }
+
+        try {
+            final NetworkStats.Entry entry = new NetworkStats.Entry();
+            entry.iface = ifaceIn;
+            entry.uid = UID_ALL;
+            entry.set = SET_DEFAULT;
+            entry.tag = TAG_NONE;
+            entry.rxBytes = Long.parseLong(tok[3]);
+            entry.rxPackets = Long.parseLong(tok[4]);
+            entry.txBytes = Long.parseLong(tok[5]);
+            entry.txPackets = Long.parseLong(tok[6]);
+            return entry;
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException(
+                    "problem parsing tethering stats for " + ifaceIn + " " + ifaceOut + ": " + e);
+        }
+    }
+
     public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index bb21d81..0934cd0 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -161,7 +161,7 @@
     private int mStayOnConditions = 0;
     private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private final int[] mBroadcastWhy = new int[3];
-    private boolean mBroadcastingScreenOff = false;
+    private boolean mPreparingForScreenOn = false;
     private int mPartialCount = 0;
     private int mPowerState;
     // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
@@ -1122,7 +1122,8 @@
             pw.println("  mNextTimeout=" + mNextTimeout + " now=" + now
                     + " " + ((mNextTimeout-now)/1000) + "s from now");
             pw.println("  mDimScreen=" + mDimScreen
-                    + " mStayOnConditions=" + mStayOnConditions);
+                    + " mStayOnConditions=" + mStayOnConditions
+                    + " mPreparingForScreenOn=" + mPreparingForScreenOn);
             pw.println("  mScreenOffReason=" + mScreenOffReason
                     + " mUserState=" + mUserState);
             pw.println("  mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1]
@@ -1341,7 +1342,9 @@
             mBroadcastQueue[0] = on ? 1 : 0;
             mBroadcastQueue[1] = -1;
             mBroadcastQueue[2] = -1;
+            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
             mBroadcastWakeLock.release();
+            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
             mBroadcastWakeLock.release();
             index = 0;
         }
@@ -1371,6 +1374,21 @@
         }
     }
 
+    private WindowManagerPolicy.ScreenOnListener mScreenOnListener =
+            new WindowManagerPolicy.ScreenOnListener() {
+                @Override public void onScreenOn() {
+                    synchronized (mLocks) {
+                        if (mPreparingForScreenOn) {
+                            mPreparingForScreenOn = false;
+                            updateNativePowerStateLocked();
+                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP,
+                                    4, mBroadcastWakeLock.mCount);
+                            mBroadcastWakeLock.release();
+                        }
+                    }
+                }
+    };
+
     private Runnable mNotificationTask = new Runnable()
     {
         public void run()
@@ -1387,14 +1405,17 @@
                         mBroadcastWhy[i] = mBroadcastWhy[i+1];
                     }
                     policy = getPolicyLocked();
-                    if (value == 0) {
-                        mBroadcastingScreenOff = true;
+                    if (value == 1 && !mPreparingForScreenOn) {
+                        mPreparingForScreenOn = true;
+                        mBroadcastWakeLock.acquire();
+                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND,
+                                mBroadcastWakeLock.mCount);
                     }
                 }
                 if (value == 1) {
                     mScreenOnStart = SystemClock.uptimeMillis();
 
-                    policy.screenTurnedOn();
+                    policy.screenTurningOn(mScreenOnListener);
                     try {
                         ActivityManagerNative.getDefault().wakingUp();
                     } catch (RemoteException e) {
@@ -1432,7 +1453,6 @@
                         synchronized (mLocks) {
                             EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3,
                                     mBroadcastWakeLock.mCount);
-                            mBroadcastingScreenOff = false;
                             updateNativePowerStateLocked();
                             mBroadcastWakeLock.release();
                         }
@@ -1464,10 +1484,6 @@
             synchronized (mLocks) {
                 EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
                         SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount);
-                synchronized (mLocks) {
-                    mBroadcastingScreenOff = false;
-                    updateNativePowerStateLocked();
-                }
                 mBroadcastWakeLock.release();
             }
         }
@@ -1795,17 +1811,18 @@
     
     private void updateNativePowerStateLocked() {
         if ((mPowerState & SCREEN_ON_BIT) != 0) {
-            // Don't turn screen on if we are currently reporting a screen off.
+            // Don't turn screen on until we know we are really ready to.
             // This is to avoid letting the screen go on before things like the
-            // lock screen have been displayed due to it going off.
-            if (mBroadcastingScreenOff) {
-                // Currently broadcasting that the screen is off.  Don't
-                // allow screen to go on until that is done.
+            // lock screen have been displayed.
+            if (mPreparingForScreenOn) {
+                // Currently waiting for confirmation from the policy that it
+                // is okay to turn on the screen.  Don't allow the screen to go
+                // on until that is done.
                 return;
             }
             for (int i=0; i<mBroadcastQueue.length; i++) {
-                if (mBroadcastQueue[i] == 0) {
-                    // A screen off is currently enqueued.
+                if (mBroadcastQueue[i] == 1) {
+                    // A screen on is currently enqueued.
                     return;
                 }
             }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index ae8b89d..5286824 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -19,7 +19,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.bluetooth.BluetoothPan;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -28,15 +27,14 @@
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
-import android.net.InterfaceConfiguration;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
+import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkUtils;
 import android.os.Binder;
-import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -51,6 +49,7 @@
 import com.android.internal.util.IState;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.google.android.collect.Lists;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -59,8 +58,8 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.Set;
+
 /**
  * @hide
  *
@@ -68,7 +67,6 @@
  *
  * TODO - look for parent classes and code sharing
  */
-
 public class Tethering extends INetworkManagementEventObserver.Stub {
 
     private Context mContext;
@@ -629,6 +627,19 @@
         return retVal;
     }
 
+    public String[] getTetheredIfacePairs() {
+        final ArrayList<String> list = Lists.newArrayList();
+        synchronized (mIfaces) {
+            for (TetherInterfaceSM sm : mIfaces.values()) {
+                if (sm.isTethered()) {
+                    list.add(sm.mMyUpstreamIfaceName);
+                    list.add(sm.mIfaceName);
+                }
+            }
+        }
+        return list.toArray(new String[list.size()]);
+    }
+
     public String[] getTetherableIfaces() {
         ArrayList<String> list = new ArrayList<String>();
         synchronized (mIfaces) {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 3673eab..947cf9c 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -25,6 +25,7 @@
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
@@ -34,7 +35,7 @@
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.UID_REMOVED;
-import static android.provider.Settings.Secure.NETSTATS_FORCE_COMPLETE_POLL;
+import static android.net.TrafficStats.UID_TETHERING;
 import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
 import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
 import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
@@ -68,6 +69,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
+import android.net.TrafficStats;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -89,6 +91,7 @@
 import com.android.internal.os.AtomicFile;
 import com.android.internal.util.Objects;
 import com.android.server.EventLogTags;
+import com.android.server.connectivity.Tethering;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 import com.google.android.collect.Sets;
@@ -132,14 +135,10 @@
     private static final int MSG_PERFORM_POLL = 0x1;
 
     /** Flags to control detail level of poll event. */
-    private static final int FLAG_POLL_NETWORK = 0x1;
-    private static final int FLAG_POLL_UID = 0x2;
     private static final int FLAG_PERSIST_NETWORK = 0x10;
     private static final int FLAG_PERSIST_UID = 0x20;
-    private static final int FLAG_FORCE_PERSIST = 0x100;
-
-    private static final int FLAG_POLL_ALL = FLAG_POLL_NETWORK | FLAG_POLL_UID;
     private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
+    private static final int FLAG_PERSIST_FORCE = 0x100;
 
     private final Context mContext;
     private final INetworkManagementService mNetworkManager;
@@ -177,7 +176,6 @@
         public long getUidMaxHistory();
         public long getTagMaxHistory();
         public long getTimeCacheMaxAge();
-        public boolean getForceCompletePoll();
     }
 
     private final Object mStatsLock = new Object();
@@ -195,6 +193,7 @@
     private NetworkStats mLastPollNetworkSnapshot;
     private NetworkStats mLastPollUidSnapshot;
     private NetworkStats mLastPollOperationsSnapshot;
+    private NetworkStats mLastPollTetherSnapshot;
 
     private NetworkStats mLastPersistNetworkSnapshot;
     private NetworkStats mLastPersistUidSnapshot;
@@ -258,6 +257,10 @@
         final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
         mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
 
+        // watch for tethering changes
+        final IntentFilter tetherFilter = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
+        mContext.registerReceiver(mTetherReceiver, tetherFilter, CONNECTIVITY_INTERNAL, mHandler);
+
         // listen for periodic polling events
         final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
         mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
@@ -519,7 +522,7 @@
     @Override
     public void forceUpdate() {
         mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
-        performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
+        performPoll(FLAG_PERSIST_ALL);
     }
 
     /**
@@ -543,12 +546,24 @@
         }
     };
 
+    /**
+     * Receiver that watches for {@link Tethering} to claim interface pairs.
+     */
+    private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // on background handler thread, and verified CONNECTIVITY_INTERNAL
+            // permission above.
+            performPoll(FLAG_PERSIST_NETWORK);
+        }
+    };
+
     private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             // on background handler thread, and verified UPDATE_DEVICE_STATS
             // permission above.
-            performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
+            performPoll(FLAG_PERSIST_ALL);
 
             // verify that we're watching global alert
             registerGlobalAlert();
@@ -595,7 +610,7 @@
             if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
                 // kick off background poll to collect network stats; UID stats
                 // are handled during normal polling interval.
-                final int flags = FLAG_POLL_NETWORK | FLAG_PERSIST_NETWORK;
+                final int flags = FLAG_PERSIST_NETWORK;
                 mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
 
                 // re-arm global alert for next update
@@ -617,10 +632,9 @@
         // isn't perfect, since the kernel may already be counting traffic from
         // the updated network.
 
-        // poll both network and UID stats, but only persist network stats,
-        // since this codepath should stay fast. UID stats will be persisted
-        // during next alarm poll event.
-        performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_NETWORK);
+        // poll, but only persist network stats to keep codepath fast. UID stats
+        // will be persisted during next alarm poll event.
+        performPollLocked(FLAG_PERSIST_NETWORK);
 
         final NetworkState[] states;
         try {
@@ -684,19 +698,9 @@
         if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
         final long startRealtime = SystemClock.elapsedRealtime();
 
-        boolean pollNetwork = (flags & FLAG_POLL_NETWORK) != 0;
-        boolean pollUid = (flags & FLAG_POLL_UID) != 0;
-
-        // when complete poll requested, any partial poll enables everything
-        final boolean forceCompletePoll = mSettings.getForceCompletePoll();
-        if (forceCompletePoll && (pollNetwork || pollUid)) {
-            pollNetwork = true;
-            pollUid = true;
-        }
-
         final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
         final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
-        final boolean forcePersist = (flags & FLAG_FORCE_PERSIST) != 0;
+        final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
 
         // try refreshing time source when stale
         if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
@@ -709,32 +713,36 @@
         final long threshold = mSettings.getPersistThreshold();
 
         try {
-            if (pollNetwork) {
-                final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
-                performNetworkPollLocked(networkSnapshot, currentTime);
+            // record network stats
+            final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
+            performNetworkPollLocked(networkSnapshot, currentTime);
 
-                // persist when enough network data has occurred
-                final NetworkStats persistNetworkDelta = computeStatsDelta(
-                        mLastPersistNetworkSnapshot, networkSnapshot, true);
-                final boolean pastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
-                if (forcePersist || (persistNetwork && pastThreshold)) {
-                    writeNetworkStatsLocked();
-                    mLastPersistNetworkSnapshot = networkSnapshot;
-                }
+            // persist when enough network data has occurred
+            final NetworkStats persistNetworkDelta = computeStatsDelta(
+                    mLastPersistNetworkSnapshot, networkSnapshot, true);
+            final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
+            if (persistForce || (persistNetwork && networkPastThreshold)) {
+                writeNetworkStatsLocked();
+                mLastPersistNetworkSnapshot = networkSnapshot;
             }
 
-            if (pollUid) {
-                final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
-                performUidPollLocked(uidSnapshot, currentTime);
+            // record tethering stats; persisted during normal UID cycle below
+            final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
+            final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
+                    ifacePairs);
+            performTetherPollLocked(tetherSnapshot, currentTime);
 
-                // persist when enough network data has occurred
-                final NetworkStats persistUidDelta = computeStatsDelta(
-                        mLastPersistUidSnapshot, uidSnapshot, true);
-                final boolean pastThreshold = persistUidDelta.getTotalBytes() > threshold;
-                if (forcePersist || (persistUid && pastThreshold)) {
-                    writeUidStatsLocked();
-                    mLastPersistUidSnapshot = uidSnapshot;
-                }
+            // record uid stats
+            final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+            performUidPollLocked(uidSnapshot, currentTime);
+
+            // persist when enough network data has occurred
+            final NetworkStats persistUidDelta = computeStatsDelta(
+                    mLastPersistUidSnapshot, uidSnapshot, true);
+            final boolean uidPastThreshold = persistUidDelta.getTotalBytes() > threshold;
+            if (persistForce || (persistUid && uidPastThreshold)) {
+                writeUidStatsLocked();
+                mLastPersistUidSnapshot = uidSnapshot;
             }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem reading network stats", e);
@@ -748,9 +756,7 @@
         }
 
         // sample stats after each full poll
-        if (pollNetwork && pollUid) {
-            performSample();
-        }
+        performSample();
 
         // finally, dispatch updated event to any listeners
         final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
@@ -780,12 +786,6 @@
             history.recordData(timeStart, currentTime, entry);
         }
 
-        // trim any history beyond max
-        final long maxHistory = mSettings.getNetworkMaxHistory();
-        for (NetworkStatsHistory history : mNetworkStats.values()) {
-            history.removeBucketsBefore(currentTime - maxHistory);
-        }
-
         mLastPollNetworkSnapshot = networkSnapshot;
 
         if (LOGD && unknownIface.size() > 0) {
@@ -829,32 +829,54 @@
             history.recordData(timeStart, currentTime, entry);
         }
 
-        // trim any history beyond max
-        final long maxUidHistory = mSettings.getUidMaxHistory();
-        final long maxTagHistory = mSettings.getTagMaxHistory();
-        for (UidStatsKey key : mUidStats.keySet()) {
-            final NetworkStatsHistory history = mUidStats.get(key);
-
-            // detailed tags are trimmed sooner than summary in TAG_NONE
-            if (key.tag == TAG_NONE) {
-                history.removeBucketsBefore(currentTime - maxUidHistory);
-            } else {
-                history.removeBucketsBefore(currentTime - maxTagHistory);
-            }
-        }
-
         mLastPollUidSnapshot = uidSnapshot;
         mLastPollOperationsSnapshot = mOperations;
         mOperations = new NetworkStats(0L, 10);
     }
 
     /**
+     * Update {@link #mUidStats} historical usage for
+     * {@link TrafficStats#UID_TETHERING} based on tethering statistics.
+     */
+    private void performTetherPollLocked(NetworkStats tetherSnapshot, long currentTime) {
+        ensureUidStatsLoadedLocked();
+
+        final NetworkStats delta = computeStatsDelta(
+                mLastPollTetherSnapshot, tetherSnapshot, false);
+        final long timeStart = currentTime - delta.getElapsedRealtime();
+
+        NetworkStats.Entry entry = null;
+        for (int i = 0; i < delta.size(); i++) {
+            entry = delta.getValues(i, entry);
+            final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
+            if (ident == null) {
+                if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
+                        || entry.txPackets > 0) {
+                    Log.w(TAG, "dropping tether delta from unknown iface: " + entry);
+                }
+                continue;
+            }
+
+            final NetworkStatsHistory history = findOrCreateUidStatsLocked(
+                    ident, UID_TETHERING, SET_DEFAULT, TAG_NONE);
+            history.recordData(timeStart, currentTime, entry);
+        }
+
+        // normal UID poll will trim any history beyond max
+        mLastPollTetherSnapshot = tetherSnapshot;
+    }
+
+    /**
      * Sample recent statistics summary into {@link EventLog}.
      */
     private void performSample() {
-        // take sample as total over last 4 hours
-        final long end = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
-        final long start = end - (4 * HOUR_IN_MILLIS);
+        final long largestBucketSize = Math.max(
+                mSettings.getNetworkBucketDuration(), mSettings.getUidBucketDuration());
+
+        // take sample as atomic buckets
+        final long now = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
+        final long end = now - (now % largestBucketSize) + largestBucketSize;
+        final long start = end - largestBucketSize;
 
         NetworkTemplate template = null;
         NetworkStats.Entry ifaceTotal = null;
@@ -864,21 +886,17 @@
         template = buildTemplateMobileAll(getActiveSubscriberId(mContext));
         ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
         uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
-        EventLogTags.writeNetstatsMobileSample(
-                ifaceTotal.rxBytes, ifaceTotal.txBytes,
-                ifaceTotal.rxPackets, ifaceTotal.txPackets,
-                uidTotal.rxBytes, uidTotal.txBytes,
-                uidTotal.rxPackets, uidTotal.txPackets);
+        EventLogTags.writeNetstatsMobileSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
+                ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
+                uidTotal.txBytes, uidTotal.rxPackets);
 
         // collect wifi sample
         template = buildTemplateWifi();
         ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
         uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
-        EventLogTags.writeNetstatsWifiSample(
-                ifaceTotal.rxBytes, ifaceTotal.txBytes,
-                ifaceTotal.rxPackets, ifaceTotal.txPackets,
-                uidTotal.rxBytes, uidTotal.txBytes,
-                uidTotal.rxPackets, uidTotal.txPackets);
+        EventLogTags.writeNetstatsWifiSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
+                ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
+                uidTotal.txBytes, uidTotal.rxPackets);
     }
 
     /**
@@ -1078,6 +1096,15 @@
 
         // TODO: consider duplicating stats and releasing lock while writing
 
+        // trim any history beyond max
+        if (mTime.hasCache()) {
+            final long currentTime = mTime.currentTimeMillis();
+            final long maxHistory = mSettings.getNetworkMaxHistory();
+            for (NetworkStatsHistory history : mNetworkStats.values()) {
+                history.removeBucketsBefore(currentTime - maxHistory);
+            }
+        }
+
         FileOutputStream fos = null;
         try {
             fos = mNetworkFile.startWrite();
@@ -1113,6 +1140,23 @@
 
         // TODO: consider duplicating stats and releasing lock while writing
 
+        // trim any history beyond max
+        if (mTime.hasCache()) {
+            final long currentTime = mTime.currentTimeMillis();
+            final long maxUidHistory = mSettings.getUidMaxHistory();
+            final long maxTagHistory = mSettings.getTagMaxHistory();
+            for (UidStatsKey key : mUidStats.keySet()) {
+                final NetworkStatsHistory history = mUidStats.get(key);
+
+                // detailed tags are trimmed sooner than summary in TAG_NONE
+                if (key.tag == TAG_NONE) {
+                    history.removeBucketsBefore(currentTime - maxUidHistory);
+                } else {
+                    history.removeBucketsBefore(currentTime - maxTagHistory);
+                }
+            }
+        }
+
         // build UidStatsKey lists grouped by ident
         final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
         for (UidStatsKey key : mUidStats.keySet()) {
@@ -1177,7 +1221,7 @@
             }
 
             if (argSet.contains("poll")) {
-                performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_ALL | FLAG_FORCE_PERSIST);
+                performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
                 pw.println("Forced poll");
                 return;
             }
@@ -1405,8 +1449,5 @@
         public long getTimeCacheMaxAge() {
             return DAY_IN_MILLIS;
         }
-        public boolean getForceCompletePoll() {
-            return getSecureBoolean(NETSTATS_FORCE_COMPLETE_POLL, false);
-        }
     }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 15a7ca6..d237953 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -73,6 +73,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.LocalPowerManager;
 import android.os.Looper;
 import android.os.Message;
@@ -91,6 +92,7 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseIntArray;
 import android.util.TypedValue;
@@ -384,6 +386,12 @@
     ArrayList<WindowState> mForceRemoves;
 
     /**
+     * Windows that clients are waiting to have drawn.
+     */
+    ArrayList<Pair<WindowState, IRemoteCallback>> mWaitingForDrawn
+            = new ArrayList<Pair<WindowState, IRemoteCallback>>();
+
+    /**
      * Used when rebuilding window list to keep track of windows that have
      * been removed.
      */
@@ -6295,6 +6303,7 @@
         public static final int DRAG_END_TIMEOUT = 21;
         public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
         public static final int BOOT_TIMEOUT = 23;
+        public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
 
         private Session mLastReportedHold;
 
@@ -6605,11 +6614,6 @@
                     break;
                 }
 
-                case BOOT_TIMEOUT: {
-                    performBootTimeout();
-                    break;
-                }
-
                 case APP_FREEZE_TIMEOUT: {
                     synchronized (mWindowMap) {
                         Slog.w(TAG, "App freeze timeout expired.");
@@ -6678,6 +6682,27 @@
                     notifyHardKeyboardStatusChange();
                     break;
                 }
+
+                case BOOT_TIMEOUT: {
+                    performBootTimeout();
+                    break;
+                }
+
+                case WAITING_FOR_DRAWN_TIMEOUT: {
+                    Pair<WindowState, IRemoteCallback> pair;
+                    synchronized (mWindowMap) {
+                        pair = (Pair<WindowState, IRemoteCallback>)msg.obj;
+                        Slog.w(TAG, "Timeout waiting for drawn: " + pair.first);
+                        if (!mWaitingForDrawn.remove(pair)) {
+                            return;
+                        }
+                    }
+                    try {
+                        pair.second.sendResult(null);
+                    } catch (RemoteException e) {
+                    }
+                    break;
+                }
             }
         }
     }
@@ -8582,39 +8607,54 @@
             }
         }
 
-        mWindowMap.notifyAll();
+        checkDrawnWindowsLocked();
 
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
         enableScreenIfNeededLocked();
     }
 
-    public void waitForAllDrawn() {
+    void checkDrawnWindowsLocked() {
+        if (mWaitingForDrawn.size() > 0) {
+            for (int j=mWaitingForDrawn.size()-1; j>=0; j--) {
+                Pair<WindowState, IRemoteCallback> pair = mWaitingForDrawn.get(j);
+                WindowState win = pair.first;
+                //Slog.i(TAG, "Waiting for drawn " + win + ": removed="
+                //        + win.mRemoved + " visible=" + win.isVisibleLw()
+                //        + " shown=" + win.mSurfaceShown);
+                if (win.mRemoved || !win.isVisibleLw()) {
+                    // Window has been removed or made invisible; no draw
+                    // will now happen, so stop waiting.
+                    Slog.w(TAG, "Aborted waiting for drawn: " + pair.first);
+                    try {
+                        pair.second.sendResult(null);
+                    } catch (RemoteException e) {
+                    }
+                    mWaitingForDrawn.remove(pair);
+                    mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
+                } else if (win.mSurfaceShown) {
+                    // Window is now drawn (and shown).
+                    try {
+                        pair.second.sendResult(null);
+                    } catch (RemoteException e) {
+                    }
+                    mWaitingForDrawn.remove(pair);
+                    mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
+                }
+            }
+        }
+    }
+
+    public void waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
         synchronized (mWindowMap) {
-            while (true) {
-                final int N = mWindows.size();
-                boolean okay = true;
-                for (int i=0; i<N && okay; i++) {
-                    WindowState w = mWindows.get(i);
-                    if (DEBUG_SCREEN_ON) {
-                        Slog.i(TAG, "Window " + w + " vis=" + w.isVisibleLw()
-                                + " obscured=" + w.mObscured + " drawn=" + w.isDrawnLw());
-                    }
-                    if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
-                        if (DEBUG_SCREEN_ON) {
-                            Slog.i(TAG, "Window not yet drawn: " + w);
-                        }
-                        okay = false;
-                        break;
-                    }
-                }
-                if (okay) {
-                    return;
-                }
-                try {
-                    mWindowMap.wait();
-                } catch (InterruptedException e) {
-                }
+            WindowState win = windowForClientLocked(null, token, true);
+            if (win != null) {
+                Pair<WindowState, IRemoteCallback> pair =
+                        new Pair<WindowState, IRemoteCallback>(win, callback);
+                Message m = mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
+                mH.sendMessageDelayed(m, 2000);
+                mWaitingForDrawn.add(pair);
+                checkDrawnWindowsLocked();
             }
         }
     }
@@ -9284,6 +9324,15 @@
                 }
             }
         }
+        if (mWaitingForDrawn.size() > 0) {
+            pw.println();
+            pw.println("  Clients waiting for these windows to be drawn:");
+            for (int i=mWaitingForDrawn.size()-1; i>=0; i--) {
+                Pair<WindowState, IRemoteCallback> pair = mWaitingForDrawn.get(i);
+                pw.print("  Waiting #"); pw.print(i); pw.print(' '); pw.print(pair.first);
+                        pw.print(": "); pw.println(pair.second);
+            }
+        }
         pw.println();
         if (mDisplay != null) {
             pw.print("  Display: init="); pw.print(mInitialDisplayWidth); pw.print("x");
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 0ee3f17..197abde 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -1101,7 +1101,20 @@
             final Matrix tmpMatrix = mTmpMatrix;
 
             // Compute the desired transformation.
-            tmpMatrix.setTranslate(0, 0);
+            if (screenAnimation) {
+                // If we are doing a screen animation, the global rotation
+                // applied to windows can result in windows that are carefully
+                // aligned with each other to slightly separate, allowing you
+                // to see what is behind them.  An unsightly mess.  This...
+                // thing...  magically makes it call good: scale each window
+                // slightly (two pixels larger in each dimension, from the
+                // window's center).
+                final float w = frame.width();
+                final float h = frame.height();
+                tmpMatrix.setScale(1 + 2/w, 1 + 2/h, w/2, h/2);
+            } else {
+                tmpMatrix.reset();
+            }
             tmpMatrix.postScale(mGlobalScale, mGlobalScale);
             if (selfTransformation) {
                 tmpMatrix.postConcat(mTransformation.getMatrix());
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 2b1eea1..99ae027 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -272,7 +272,11 @@
 
         // graceful shutdown system, which should trigger persist of stats, and
         // clear any values in memory.
+        expectCurrentTime();
+        expectDefaultSettings();
+        replay();
         mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
+        verifyAndReset();
 
         // talk with zombie service to assert stats have gone; and assert that
         // we persisted them to file.
@@ -487,6 +491,7 @@
 
         // now pretend two UIDs are uninstalled, which should migrate stats to
         // special "removed" bucket.
+        expectCurrentTime();
         expectDefaultSettings();
         replay();
         final Intent intent = new Intent(ACTION_UID_REMOVED);
@@ -758,7 +763,6 @@
         expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes();
         expect(mSettings.getTagMaxHistory()).andReturn(maxHistory).anyTimes();
         expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
-        expect(mSettings.getForceCompletePoll()).andReturn(false).anyTimes();
     }
 
     private void expectCurrentTime() throws Exception {
diff --git a/telephony/java/com/android/internal/telephony/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
index a52f1ca..8aeee87 100644
--- a/telephony/java/com/android/internal/telephony/ApnContext.java
+++ b/telephony/java/com/android/internal/telephony/ApnContext.java
@@ -154,6 +154,12 @@
         return mState;
     }
 
+    public boolean isDisconnected() {
+        DataConnectionTracker.State currentState = getState();
+        return ((currentState == DataConnectionTracker.State.IDLE) ||
+                    currentState == DataConnectionTracker.State.FAILED);
+    }
+
     public synchronized void setReason(String reason) {
         if (DBG) {
             log("set reason as " + reason + ", for type " + mApnType + ",current state " + mState);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 6d0b696..dd8a60a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -787,9 +787,7 @@
         }
         DataConnectionAc dcac = apnContext.getDataConnectionAc();
         if (tearDown) {
-            boolean isConnected = (apnContext.getState() != State.IDLE
-                                   && apnContext.getState() != State.FAILED);
-            if (!isConnected) {
+            if (apnContext.isDisconnected()) {
                 // The request is tearDown and but ApnContext is not connected.
                 // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
                 apnContext.setState(State.IDLE);
@@ -1019,11 +1017,9 @@
      */
     private void onApnChanged() {
         // TODO: How to handle when multiple APNs are active?
-        boolean isConnected;
 
         ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
-        isConnected = (defaultApnContext.getState() != State.IDLE
-                       && defaultApnContext.getState() != State.FAILED);
+        boolean defaultApnIsDisconnected = defaultApnContext.isDisconnected();
 
         if (mPhone instanceof GSMPhone) {
             // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
@@ -1034,8 +1030,8 @@
         // match the current operator.
         if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
         createAllApnList();
-        cleanUpAllConnections(isConnected, Phone.REASON_APN_CHANGED);
-        if (!isConnected) {
+        cleanUpAllConnections(!defaultApnIsDisconnected, Phone.REASON_APN_CHANGED);
+        if (defaultApnIsDisconnected) {
             setupDataOnReadyApns(Phone.REASON_APN_CHANGED);
         }
     }
@@ -1885,7 +1881,7 @@
 
         // if all data connection are gone, check whether Airplane mode request was
         // pending.
-        if (!isConnected()) {
+        if (isDisconnected()) {
             if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
                 // Radio will be turned off. No need to retry data setup
                 apnContext.setApnSetting(null);
@@ -1957,12 +1953,25 @@
     protected boolean isConnected() {
         for (ApnContext apnContext : mApnContexts.values()) {
             if (apnContext.getState() == State.CONNECTED) {
-            return true;
+                // At least one context is connected, return true
+                return true;
             }
         }
+        // There are not any contexts connected, return false
         return false;
     }
 
+    protected boolean isDisconnected() {
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (!apnContext.isDisconnected()) {
+                // At least one context was not disconnected return false
+                return false;
+            }
+        }
+        // All contexts were disconnected so return true
+        return true;
+    }
+
     @Override
     protected void notifyDataConnection(String reason) {
         if (DBG) log("notifyDataConnection: reason=" + reason);
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index c3d09ff..70cf89e 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -279,6 +279,7 @@
     private static final String LOG_TAG = "InstrumentationTestRunner";
 
     private final Bundle mResults = new Bundle();
+    private Bundle mArguments;
     private AndroidTestRunner mTestRunner;
     private boolean mDebug;
     private boolean mJustCount;
@@ -292,6 +293,7 @@
     @Override
     public void onCreate(Bundle arguments) {
         super.onCreate(arguments);
+        mArguments = arguments;
 
         // Apk paths used to search for test classes when using TestSuiteBuilders.
         String[] apkPaths =
@@ -379,6 +381,16 @@
         start();
     }
 
+    /**
+     * Get the Bundle object that contains the arguments
+     *
+     * @return the Bundle object
+     * @hide
+     */
+    public Bundle getBundle(){
+        return mArguments;
+    }
+
     List<Predicate<TestMethod>> getBuilderRequirements() {
         return new ArrayList<Predicate<TestMethod>>();
     }
diff --git a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
index 54adc249..b803b98 100644
--- a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
+++ b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
@@ -72,6 +72,7 @@
      */
     private void fetchStats(NetworkTemplate template) {
         try {
+            mStatsService.forceUpdate();
             NetworkStats stats = mStatsService.getSummaryForAllUid(template, Long.MIN_VALUE,
                     Long.MAX_VALUE, false);
             reportStats(stats);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index ed29a78..3d1fa7a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -24,6 +24,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.view.Display;
@@ -451,7 +452,7 @@
         return 0;
     }
 
-    public void waitForAllDrawn() {
+    public void waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
         // TODO Auto-generated method stub
     }