Merge "Remove an O(n^2) algorithm in MPEG4Extractor.cpp to speed up seeks in long streams"
diff --git a/api/14.txt b/api/14.txt
index 0aa94b2..45bb882 100644
--- a/api/14.txt
+++ b/api/14.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/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/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/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2d1eeda..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);
@@ -6531,6 +6535,8 @@
         mLastTouchTime = eventTime;
         mVelocityTracker = VelocityTracker.obtain();
         mSnapScrollMode = SNAP_NONE;
+        mPrivateHandler.sendEmptyMessageDelayed(UPDATE_SELECTION,
+                ViewConfiguration.getTapTimeout());
     }
 
     private void startDrag() {
@@ -7193,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);
@@ -7296,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);
@@ -8186,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/AdapterView.java b/core/java/android/widget/AdapterView.java
index d9c22c4..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;
         }
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/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-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-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/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/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/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/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 cdebba0..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);
     }
@@ -614,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.
@@ -626,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/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/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index e898aac..3e8d255 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
+import android.location.CountryDetector;
 import android.net.Uri;
 import android.os.SystemProperties;
 import android.provider.Contacts;
@@ -1573,6 +1574,32 @@
     }
 
     /**
+     * Checks if a given number is an emergency number for the country that the user is in. The
+     * current country is determined using the CountryDetector.
+     *
+     * @param number the number to look up.
+     * @param context the specific context which the number should be checked against
+     * @return if a phone number is an emergency number for a local country, based on the
+     * CountryDetector.
+     * @see android.location.CountryDetector
+     * @hide
+     */
+    public static boolean isLocalEmergencyNumber(String number, Context context) {
+        String countryIso;
+        CountryDetector detector = (CountryDetector) context.getSystemService(
+                Context.COUNTRY_DETECTOR);
+        if (detector != null) {
+            countryIso = detector.detectCountry().getCountryIso();
+        } else {
+            Locale locale = context.getResources().getConfiguration().locale;
+            countryIso = locale.getCountry();
+            Log.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
+                    + countryIso);
+        }
+        return isEmergencyNumber(number, countryIso);
+    }
+
+    /**
      * isVoiceMailNumber: checks a given number against the voicemail
      *   number provided by the RIL and SIM card. The caller must have
      *   the READ_PHONE_STATE credential.
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 7c37a65..6324550 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -254,9 +254,7 @@
         // Change the callerInfo number ONLY if it is an emergency number
         // or if it is the voicemail number.  If it is either, take a
         // shortcut and skip the query.
-        Locale locale = context.getResources().getConfiguration().locale;
-        String countryIso = getCurrentCountryIso(context, locale);
-        if (PhoneNumberUtils.isEmergencyNumber(number, countryIso)) {
+        if (PhoneNumberUtils.isLocalEmergencyNumber(number, context)) {
             return new CallerInfo().markAsEmergency(context);
         } else if (PhoneNumberUtils.isVoiceMailNumber(number)) {
             return new CallerInfo().markAsVoiceMail();
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 17734ca..4912749 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -403,9 +403,7 @@
         cw.number = number;
 
         // check to see if these are recognized numbers, and use shortcuts if we can.
-        CountryDetector detector = (CountryDetector) context.getSystemService(
-            Context.COUNTRY_DETECTOR);
-        if (PhoneNumberUtils.isEmergencyNumber(number, detector.detectCountry().getCountryIso())) {
+        if (PhoneNumberUtils.isLocalEmergencyNumber(number, context)) {
             cw.event = EVENT_EMERGENCY_NUMBER;
         } else if (PhoneNumberUtils.isVoiceMailNumber(number)) {
             cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index db19321..83efc51 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -190,7 +190,8 @@
 
         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
         boolean isPhoneInEcmMode = inEcm.equals("true");
-        boolean isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(dialString);
+        boolean isEmergencyCall =
+                PhoneNumberUtils.isLocalEmergencyNumber(dialString, phone.getContext());
 
         // Cancel Ecm timer if a second emergency call is originating in Ecm mode
         if (isPhoneInEcmMode && isEmergencyCall) {
@@ -1059,7 +1060,7 @@
      * Disable data call when emergency call is connected
      */
     private void disableDataCallInEmergencyCall(String dialString) {
-        if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
+        if (PhoneNumberUtils.isLocalEmergencyNumber(dialString, phone.getContext())) {
             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
             mIsInEmergencyCall = true;
             phone.mDataConnectionTracker.setInternalDataEnabled(false);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 680b3cd..3799894 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -496,7 +496,7 @@
             return false;
         }
 
-        if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
+        if (PhoneNumberUtils.isLocalEmergencyNumber(dialString, phone.getContext())) {
             return false;
         } else {
             return isShortCodeUSSD(dialString, phone);
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>>();
     }