Merge "Cannot have badge only channels."
diff --git a/res/drawable/ic_arrow_down_24dp.xml b/res/drawable/ic_arrow_down_24dp.xml
new file mode 100644
index 0000000..7c5866d
--- /dev/null
+++ b/res/drawable/ic_arrow_down_24dp.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorAccent">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
+</vector>
diff --git a/res/layout/account_header.xml b/res/layout/account_header.xml
new file mode 100755
index 0000000..7cae95e
--- /dev/null
+++ b/res/layout/account_header.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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"
+        style="@style/EntityHeader"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:gravity="center_vertical"
+        android:paddingTop="24dip"
+        android:paddingBottom="24dip"
+        android:orientation="horizontal">
+
+        <LinearLayout
+          android:id="@+id/icon_container"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:minWidth="60dp"
+          android:orientation="horizontal"
+          android:paddingEnd="12dp"
+          android:paddingTop="12dp"
+          android:paddingBottom="12dp">
+                <ImageView
+                  android:id="@android:id/icon"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:maxWidth="48dp"
+                  android:maxHeight="48dp"/>
+        </LinearLayout>
+
+        <TextView
+          android:id="@android:id/title"
+          style="@style/TextAppearance.EntityHeaderTitle"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:singleLine="true"/>
+
+</LinearLayout>
diff --git a/res/layout/app_details.xml b/res/layout/app_details.xml
index 4e654fd..7994a4c 100644
--- a/res/layout/app_details.xml
+++ b/res/layout/app_details.xml
@@ -18,7 +18,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/app_snippet"
-    style="@style/AppHeader"
+    style="@style/EntityHeader"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal|top"
@@ -37,7 +37,7 @@
 
     <TextView
         android:id="@android:id/title"
-        style="@style/TextAppearance.AppHeaderTitle"
+        style="@style/TextAppearance.EntityHeaderTitle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:singleLine="true"
diff --git a/res/layout/expand_preference.xml b/res/layout/expand_preference.xml
index 640cda7..1392d65 100644
--- a/res/layout/expand_preference.xml
+++ b/res/layout/expand_preference.xml
@@ -15,18 +15,63 @@
   limitations under the License.
   -->
 
+<!-- Based off frameworks/base/core/res/res/layout/preference_material.xml -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/selectable_card_grey"
-    android:gravity="center_vertical"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:orientation="vertical"
-    android:paddingEnd="?android:attr/scrollbarSize">
-    <TextView
-        android:id="@android:id/title"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"/>
-</LinearLayout>
\ No newline at end of file
+        android:minWidth="60dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <com.android.internal.widget.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxWidth="48dp"
+            android:maxHeight="48dp"/>
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:textColor="?android:attr/colorAccent"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:ellipsize="marquee"
+            android:maxLines="1"/>
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/res/layout/remove_account_button.xml b/res/layout/remove_account_button.xml
new file mode 100644
index 0000000..6b47c37
--- /dev/null
+++ b/res/layout/remove_account_button.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <Button
+      android:id="@+id/button"
+      android:text="@string/remove_account_label"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal"
+      android:layout_marginTop="20dip"
+      android:layout_marginBottom="12dip"
+      android:gravity="center" />
+
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 353864e..0f8f762 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6629,7 +6629,7 @@
     <string name="experimental_preference">(Experimental)</string>
 
     <!-- [CHAR LIMIT=45] Auto-rotate setting title -->
-    <string name="display_auto_rotate_title">When device is rotated</string>
+    <string name="display_auto_rotate_title">Device rotation</string>
     <!-- [CHAR LIMIT=70] Rotate when screen is turned option -->
     <string name="display_auto_rotate_rotate">Rotate the contents of the screen</string>
     <!-- [CHAR LIMIT=70] Keep the screen in portrait when rotated -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 35b06eb..a3baa21 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -417,7 +417,7 @@
         <item name="android:progressDrawable">@drawable/ring_progress</item>
     </style>
 
-    <style name="AppHeader">
+    <style name="EntityHeader">
         <item name="android:background">@color/card_background_grey</item>
         <item name="android:gravity">center_horizontal</item>
         <item name="android:paddingTop">16dp</item>
@@ -426,7 +426,7 @@
         <item name="android:paddingBottom">8dp</item>
     </style>
 
-    <style name="TextAppearance.AppHeaderTitle"
+    <style name="TextAppearance.EntityHeaderTitle"
            parent="@android:style/TextAppearance.Material.Subhead">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">24sp</item>
diff --git a/res/xml/account_type_settings.xml b/res/xml/account_type_settings.xml
index d6ba9be..91e90fe 100644
--- a/res/xml/account_type_settings.xml
+++ b/res/xml/account_type_settings.xml
@@ -20,6 +20,12 @@
         settings:keywords="@string/keywords_accounts">
 
     <Preference
+      android:key="account_header"
+      android:layout="@layout/account_header"
+      android:selectable="false"
+      android:order="0"/>
+
+    <Preference
         android:key="account_sync"
         android:title="@string/account_sync_title"
         android:icon="@drawable/ic_sync"
@@ -29,4 +35,9 @@
       android:key="dashboard_tile_placeholder"
       android:order="10"/>
 
+    <com.android.settings.applications.LayoutPreference
+      android:key="remove_account"
+      android:layout="@layout/remove_account_button"
+      android:order="100" />
+
 </PreferenceScreen>
diff --git a/res/xml/development_prefs.xml b/res/xml/development_prefs.xml
index bdbd86f..3c2bab9 100644
--- a/res/xml/development_prefs.xml
+++ b/res/xml/development_prefs.xml
@@ -196,6 +196,42 @@
             android:key="bluetooth_disable_absolute_volume"
             android:title="@string/bluetooth_disable_absolute_volume"
             android:summary="@string/bluetooth_disable_absolute_volume_summary"/>
+
+        <ListPreference
+            android:key="bluetooth_select_a2dp_codec"
+            android:title="@string/bluetooth_select_a2dp_codec_type"
+            android:dialogTitle="@string/bluetooth_select_a2dp_codec_type_dialog_title"
+            android:entries="@array/bluetooth_a2dp_codec_titles"
+            android:entryValues="@array/bluetooth_a2dp_codec_values" />
+
+        <ListPreference
+            android:key="bluetooth_select_a2dp_sample_rate"
+            android:title="@string/bluetooth_select_a2dp_codec_sample_rate"
+            android:dialogTitle="@string/bluetooth_select_a2dp_codec_sample_rate_dialog_title"
+            android:entries="@array/bluetooth_a2dp_codec_sample_rate_titles"
+            android:entryValues="@array/bluetooth_a2dp_codec_sample_rate_values" />
+
+        <ListPreference
+            android:key="bluetooth_select_a2dp_bits_per_sample"
+            android:title="@string/bluetooth_select_a2dp_codec_bits_per_sample"
+            android:dialogTitle="@string/bluetooth_select_a2dp_codec_bits_per_sample_dialog_title"
+            android:entries="@array/bluetooth_a2dp_codec_bits_per_sample_titles"
+            android:entryValues="@array/bluetooth_a2dp_codec_bits_per_sample_values" />
+
+        <ListPreference
+            android:key="bluetooth_select_a2dp_channel_mode"
+            android:title="@string/bluetooth_select_a2dp_codec_channel_mode"
+            android:dialogTitle="@string/bluetooth_select_a2dp_codec_channel_mode_dialog_title"
+            android:entries="@array/bluetooth_a2dp_codec_channel_mode_titles"
+            android:entryValues="@array/bluetooth_a2dp_codec_channel_mode_values" />
+
+        <ListPreference
+            android:key="bluetooth_select_a2dp_ldac_playback_quality"
+            android:title="@string/bluetooth_select_a2dp_codec_ldac_playback_quality"
+            android:dialogTitle="@string/bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title"
+            android:entries="@array/bluetooth_a2dp_codec_ldac_playback_quality_titles"
+            android:entryValues="@array/bluetooth_a2dp_codec_ldac_playback_quality_values" />
+
     </PreferenceCategory>
 
     <PreferenceCategory android:key="debug_input_category"
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java
index 1ae3380..418c1e2 100644
--- a/src/com/android/settings/DevelopmentSettings.java
+++ b/src/com/android/settings/DevelopmentSettings.java
@@ -25,7 +25,10 @@
 import android.app.Dialog;
 import android.app.admin.DevicePolicyManager;
 import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -201,6 +204,12 @@
     private static final String BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_PROPERTY =
             "persist.bluetooth.disableabsvol";
 
+    private static final String BLUETOOTH_SELECT_A2DP_CODEC_KEY = "bluetooth_select_a2dp_codec";
+    private static final String BLUETOOTH_SELECT_A2DP_SAMPLE_RATE_KEY = "bluetooth_select_a2dp_sample_rate";
+    private static final String BLUETOOTH_SELECT_A2DP_BITS_PER_SAMPLE_KEY = "bluetooth_select_a2dp_bits_per_sample";
+    private static final String BLUETOOTH_SELECT_A2DP_CHANNEL_MODE_KEY = "bluetooth_select_a2dp_channel_mode";
+    private static final String BLUETOOTH_SELECT_A2DP_LDAC_PLAYBACK_QUALITY_KEY = "bluetooth_select_a2dp_ldac_playback_quality";
+
     private static final String INACTIVE_APPS_KEY = "inactive_apps";
 
     private static final String IMMEDIATELY_DESTROY_ACTIVITIES_KEY
@@ -268,8 +277,16 @@
     private SwitchPreference mWifiAggressiveHandover;
     private SwitchPreference mMobileDataAlwaysOn;
     private SwitchPreference mBluetoothDisableAbsVolume;
-    private SwitchPreference mOtaDisableAutomaticUpdate;
 
+    private BluetoothA2dp mBluetoothA2dp;
+    private final Object mBluetoothA2dpLock = new Object();
+    private ListPreference mBluetoothSelectA2dpCodec;
+    private ListPreference mBluetoothSelectA2dpSampleRate;
+    private ListPreference mBluetoothSelectA2dpBitsPerSample;
+    private ListPreference mBluetoothSelectA2dpChannelMode;
+    private ListPreference mBluetoothSelectA2dpLdacPlaybackQuality;
+
+    private SwitchPreference mOtaDisableAutomaticUpdate;
     private SwitchPreference mWifiAllowScansWithTraffic;
     private SwitchPreference mStrictMode;
     private SwitchPreference mPointerLocation;
@@ -470,6 +487,12 @@
         mWebViewMultiprocess = findAndInitSwitchPref(WEBVIEW_MULTIPROCESS_KEY);
         mBluetoothDisableAbsVolume = findAndInitSwitchPref(BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_KEY);
 
+        mBluetoothSelectA2dpCodec = addListPreference(BLUETOOTH_SELECT_A2DP_CODEC_KEY);
+        mBluetoothSelectA2dpSampleRate = addListPreference(BLUETOOTH_SELECT_A2DP_SAMPLE_RATE_KEY);
+        mBluetoothSelectA2dpBitsPerSample = addListPreference(BLUETOOTH_SELECT_A2DP_BITS_PER_SAMPLE_KEY);
+        mBluetoothSelectA2dpChannelMode = addListPreference(BLUETOOTH_SELECT_A2DP_CHANNEL_MODE_KEY);
+        mBluetoothSelectA2dpLdacPlaybackQuality = addListPreference(BLUETOOTH_SELECT_A2DP_LDAC_PLAYBACK_QUALITY_KEY);
+
         mWindowAnimationScale = addListPreference(WINDOW_ANIMATION_SCALE_KEY);
         mTransitionAnimationScale = addListPreference(TRANSITION_ANIMATION_SCALE_KEY);
         mAnimatorDurationScale = addListPreference(ANIMATOR_DURATION_SCALE_KEY);
@@ -675,6 +698,20 @@
         if (getActivity().registerReceiver(mUsbReceiver, filter) == null) {
             updateUsbConfigurationValues();
         }
+
+        initBluetoothConfigurationValues();
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(getActivity(),
+                                    mBluetoothA2dpServiceListener,
+                                    BluetoothProfile.A2DP);
+        }
+        filter = new IntentFilter();
+        filter.addAction(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
+        if (getActivity().registerReceiver(mBluetoothA2dpReceiver, filter) == null) {
+            updateBluetoothA2dpConfigurationValues();
+        }
+
         return super.onCreateView(inflater, container, savedInstanceState);
     }
 
@@ -688,6 +725,12 @@
         mSwitchBar.removeOnSwitchChangeListener(this);
         mSwitchBar.hide();
         getActivity().unregisterReceiver(mUsbReceiver);
+        getActivity().unregisterReceiver(mBluetoothA2dpReceiver);
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
+            mBluetoothA2dp = null;
+        }
     }
 
     void updateSwitchPreference(SwitchPreference switchPreference, boolean value) {
@@ -757,6 +800,7 @@
             updateColorTemperature();
         }
         updateBluetoothDisableAbsVolumeOptions();
+        updateBluetoothA2dpConfigurationValues();
     }
 
     private void resetDangerousOptions() {
@@ -1737,6 +1781,361 @@
         }
     }
 
+    private void initBluetoothConfigurationValues() {
+        String[] values;
+        String[] titles;
+        int index;
+
+        // Init the Codec Type - Default
+        values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_values);
+        titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_titles);
+        index = 0;
+        mBluetoothSelectA2dpCodec.setValue(values[index]);
+        mBluetoothSelectA2dpCodec.setSummary(titles[index]);
+
+        // Init the Sample Rate - Default
+        values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_values);
+        titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_titles);
+        index = 0;
+        mBluetoothSelectA2dpSampleRate.setValue(values[index]);
+        mBluetoothSelectA2dpSampleRate.setSummary(titles[index]);
+
+        // Init the Bits Per Sample - Default
+        values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_values);
+        titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_titles);
+        index = 0;
+        mBluetoothSelectA2dpBitsPerSample.setValue(values[index]);
+        mBluetoothSelectA2dpBitsPerSample.setSummary(titles[index]);
+
+        // Init the Channel Mode - Default
+        values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_values);
+        titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_titles);
+        index = 0;
+        mBluetoothSelectA2dpChannelMode.setValue(values[index]);
+        mBluetoothSelectA2dpChannelMode.setSummary(titles[index]);
+
+        // Init the LDAC Playback Quality - High
+        values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_values);
+        titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_titles);
+        index = 0;
+        mBluetoothSelectA2dpLdacPlaybackQuality.setValue(values[index]);
+        mBluetoothSelectA2dpLdacPlaybackQuality.setSummary(titles[index]);
+    }
+
+    private void updateBluetoothA2dpConfigurationValues() {
+        int index;
+        String[] titles;
+        BluetoothCodecConfig codecConfig = null;
+
+        synchronized (mBluetoothA2dpLock) {
+            if (mBluetoothA2dp != null) {
+                codecConfig = mBluetoothA2dp.getCodecConfig();
+            }
+        }
+        if (codecConfig == null)
+            return;
+
+        // Update the Codec Type
+        index = -1;
+        switch (codecConfig.getCodecType()) {
+        case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
+            index = 1;
+            break;
+        case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
+            index = 2;
+            break;
+        case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
+            index = 3;
+            break;
+        case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
+            index = 4;
+            break;
+        case BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID:
+        default:
+            break;
+        }
+        if (index >= 0) {
+            titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_titles);
+            mBluetoothSelectA2dpCodec.setSummary("Streaming: " + titles[index]);
+        }
+
+        // Update the Sample Rate
+        index = -1;
+        switch (codecConfig.getSampleRate()) {
+        case BluetoothCodecConfig.SAMPLE_RATE_44100:
+            index = 1;
+            break;
+        case BluetoothCodecConfig.SAMPLE_RATE_48000:
+            index = 2;
+            break;
+        case BluetoothCodecConfig.SAMPLE_RATE_88200:
+            index = 3;
+            break;
+        case BluetoothCodecConfig.SAMPLE_RATE_96000:
+            index = 4;
+            break;
+        case BluetoothCodecConfig.SAMPLE_RATE_176400:
+        case BluetoothCodecConfig.SAMPLE_RATE_192000:
+        case BluetoothCodecConfig.SAMPLE_RATE_NONE:
+        default:
+            break;
+        }
+        if (index >= 0) {
+            titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_titles);
+            mBluetoothSelectA2dpSampleRate.setSummary("Streaming: " + titles[index]);
+        }
+
+        // Update the Bits Per Sample
+        index = -1;
+        switch (codecConfig.getBitsPerSample()) {
+        case BluetoothCodecConfig.BITS_PER_SAMPLE_16:
+            index = 1;
+            break;
+        case BluetoothCodecConfig.BITS_PER_SAMPLE_24:
+            index = 2;
+            break;
+        case BluetoothCodecConfig.BITS_PER_SAMPLE_32:
+            index = 3;
+            break;
+        case BluetoothCodecConfig.BITS_PER_SAMPLE_NONE:
+        default:
+            break;
+        }
+        if (index >= 0) {
+            titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_titles);
+            mBluetoothSelectA2dpBitsPerSample.setSummary("Streaming: " + titles[index]);
+        }
+
+        // Update the Channel Mode
+        index = -1;
+        switch (codecConfig.getChannelMode()) {
+        case BluetoothCodecConfig.CHANNEL_MODE_MONO:
+            index = 1;
+            break;
+        case BluetoothCodecConfig.CHANNEL_MODE_STEREO:
+            index = 2;
+            break;
+        case BluetoothCodecConfig.CHANNEL_MODE_NONE:
+        default:
+            break;
+        }
+        if (index >= 0) {
+            titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_titles);
+            mBluetoothSelectA2dpChannelMode.setSummary("Streaming: " + titles[index]);
+        }
+
+        // Update the LDAC Playback Quality
+        index = -1;
+        switch ((int)codecConfig.getCodecSpecific1()) {
+        case 1000:
+            index = 0;
+            break;
+        case 1001:
+            index = 1;
+            break;
+        case 1002:
+            index = 2;
+            break;
+        default:
+            break;
+        }
+        if (index >= 0) {
+            titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_titles);
+            mBluetoothSelectA2dpLdacPlaybackQuality.setSummary("Streaming: " + titles[index]);
+        }
+    }
+
+    private void writeBluetoothConfigurationOption(Preference preference,
+                                                   Object newValue) {
+        String[] titles;
+        int index;
+        int codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
+        int codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
+        int sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_NONE;
+        int bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
+        int channelModeValue = BluetoothCodecConfig.CHANNEL_MODE_NONE;
+        long codecSpecific1Value = 0;
+        long codecSpecific2Value = 0;
+        long codecSpecific3Value = 0;
+        long codecSpecific4Value = 0;
+
+        // Codec Type
+        String codecType = mBluetoothSelectA2dpCodec.getValue();
+        if (preference == mBluetoothSelectA2dpCodec) {
+            codecType = newValue.toString();
+            index = mBluetoothSelectA2dpCodec.findIndexOfValue(newValue.toString());
+            if (index >= 0) {
+                titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_titles);
+                mBluetoothSelectA2dpCodec.setSummary(titles[index]);
+            }
+        }
+        index = mBluetoothSelectA2dpCodec.findIndexOfValue(codecType);
+        switch (index) {
+        case 0:
+            // Reset the priority of the current codec to default
+            String oldValue = mBluetoothSelectA2dpCodec.getValue();
+            switch (mBluetoothSelectA2dpCodec.findIndexOfValue(oldValue)) {
+            case 0:
+                break;      // No current codec
+            case 1:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC;
+                break;
+            case 2:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX;
+                break;
+            case 3:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
+                break;
+            case 4:
+                codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
+                break;
+            default:
+                break;
+            }
+            break;
+        case 1:
+            codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC;
+            codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+            break;
+        case 2:
+            codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX;
+            codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+            break;
+        case 3:
+            codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
+            codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+            break;
+        case 4:
+            codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
+            codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+            break;
+        default:
+            break;
+        }
+
+        // Sample Rate
+        String sampleRate = mBluetoothSelectA2dpSampleRate.getValue();
+        if (preference == mBluetoothSelectA2dpSampleRate) {
+            sampleRate = newValue.toString();
+            index = mBluetoothSelectA2dpSampleRate.findIndexOfValue(newValue.toString());
+            if (index >= 0) {
+                titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_titles);
+                mBluetoothSelectA2dpSampleRate.setSummary(titles[index]);
+            }
+        }
+        index = mBluetoothSelectA2dpSampleRate.findIndexOfValue(sampleRate);
+        switch (index) {
+        case 0:
+            // Reset to default
+            break;
+        case 1:
+            sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_44100;
+            break;
+        case 2:
+            sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_48000;
+            break;
+        case 3:
+            sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_88200;
+            break;
+        case 4:
+            sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_96000;
+            break;
+        default:
+            break;
+        }
+
+        // Bits Per Sample
+        String bitsPerSample = mBluetoothSelectA2dpBitsPerSample.getValue();
+        if (preference == mBluetoothSelectA2dpBitsPerSample) {
+            bitsPerSample = newValue.toString();
+            index = mBluetoothSelectA2dpBitsPerSample.findIndexOfValue(newValue.toString());
+            if (index >= 0) {
+                titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_titles);
+                mBluetoothSelectA2dpBitsPerSample.setSummary(titles[index]);
+            }
+        }
+        index = mBluetoothSelectA2dpBitsPerSample.findIndexOfValue(bitsPerSample);
+        switch (index) {
+        case 0:
+            // Reset to default
+            break;
+        case 1:
+            bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_16;
+            break;
+        case 2:
+            bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_24;
+            break;
+        case 3:
+            bitsPerSampleValue = BluetoothCodecConfig.BITS_PER_SAMPLE_32;
+            break;
+        default:
+            break;
+        }
+
+        // Channel Mode
+        String channelMode = mBluetoothSelectA2dpChannelMode.getValue();
+        if (preference == mBluetoothSelectA2dpChannelMode) {
+            channelMode = newValue.toString();
+            index = mBluetoothSelectA2dpChannelMode.findIndexOfValue(newValue.toString());
+            if (index >= 0) {
+                titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_titles);
+                mBluetoothSelectA2dpChannelMode.setSummary(titles[index]);
+            }
+        }
+        index = mBluetoothSelectA2dpChannelMode.findIndexOfValue(channelMode);
+        switch (index) {
+        case 0:
+            // Reset to default
+            break;
+        case 1:
+            channelModeValue = BluetoothCodecConfig.CHANNEL_MODE_MONO;
+            break;
+        case 2:
+            channelModeValue = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+            break;
+        default:
+            break;
+        }
+
+        // LDAC Playback Quality
+        String ldacPlaybackQuality = mBluetoothSelectA2dpLdacPlaybackQuality.getValue();
+        if (preference == mBluetoothSelectA2dpLdacPlaybackQuality) {
+            ldacPlaybackQuality = newValue.toString();
+            index = mBluetoothSelectA2dpLdacPlaybackQuality.findIndexOfValue(newValue.toString());
+            if (index >= 0) {
+                titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_titles);
+                mBluetoothSelectA2dpLdacPlaybackQuality.setSummary(titles[index]);
+            }
+        }
+        index = mBluetoothSelectA2dpLdacPlaybackQuality.findIndexOfValue(ldacPlaybackQuality);
+        switch (index) {
+        case 0:
+            codecSpecific1Value = 1000;
+            break;
+        case 1:
+            codecSpecific1Value = 1001;
+            break;
+        case 2:
+            codecSpecific1Value = 1002;
+            break;
+        default:
+            break;
+        }
+
+        BluetoothCodecConfig codecConfig =
+            new BluetoothCodecConfig(codecTypeValue, codecPriorityValue,
+                                     sampleRateValue, bitsPerSampleValue,
+                                     channelModeValue, codecSpecific1Value,
+                                     codecSpecific2Value, codecSpecific3Value,
+                                     codecSpecific4Value);
+
+        synchronized (mBluetoothA2dpLock) {
+            if (mBluetoothA2dp != null) {
+                mBluetoothA2dp.setCodecConfigPreference(codecConfig);
+            }
+        }
+    }
+
     private void writeImmediatelyDestroyActivitiesOptions() {
         try {
             ActivityManager.getService().setAlwaysFinish(
@@ -2115,6 +2514,13 @@
                 toast.show();
             }
             return false;
+        } else if ((preference == mBluetoothSelectA2dpCodec) ||
+                   (preference == mBluetoothSelectA2dpSampleRate) ||
+                   (preference == mBluetoothSelectA2dpBitsPerSample) ||
+                   (preference == mBluetoothSelectA2dpChannelMode) ||
+                   (preference == mBluetoothSelectA2dpLdacPlaybackQuality)) {
+            writeBluetoothConfigurationOption(preference, newValue);
+            return true;
         } else if (preference == mLogdSize) {
             writeLogdSizeOption(newValue);
             return true;
@@ -2254,6 +2660,31 @@
         }
     };
 
+    private BroadcastReceiver mBluetoothA2dpReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateBluetoothA2dpConfigurationValues();
+        }
+    };
+
+    private BluetoothProfile.ServiceListener mBluetoothA2dpServiceListener =
+        new BluetoothProfile.ServiceListener() {
+            public void onServiceConnected(int profile,
+                                           BluetoothProfile proxy) {
+                synchronized (mBluetoothA2dpLock) {
+                    mBluetoothA2dp = (BluetoothA2dp) proxy;
+                }
+                updateBluetoothA2dpConfigurationValues();
+            }
+
+            public void onServiceDisconnected(int profile) {
+                synchronized (mBluetoothA2dpLock) {
+                    mBluetoothA2dp = null;
+                }
+                updateBluetoothA2dpConfigurationValues();
+            }
+        };
+
     public static class SystemPropPoker extends AsyncTask<Void, Void, Void> {
         @Override
         protected Void doInBackground(Void... params) {
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 356d5c5..91b193d 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -62,6 +62,12 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mProgressiveDisclosureMixin.setTileLimit(4);
+    }
+
+    @Override
     protected String getCategoryKey() {
         return CategoryKey.CATEGORY_DISPLAY;
     }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 179e352..33c8eca 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -63,7 +63,6 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.qstile.DevelopmentModeTile;
 import com.android.settings.search.DynamicIndexableContentMonitor;
-import com.android.settings.search.Index;
 import com.android.settings.search2.SearchFeatureProvider;
 import com.android.settings.widget.SwitchBar;
 import com.android.settingslib.drawer.DashboardCategory;
@@ -462,10 +461,7 @@
         if (mIsShowingDashboard) {
             // Run the Index update only if we have some space
             if (!Utils.isLowStorage(this)) {
-                long indexStartTime = System.currentTimeMillis();
-                Index.getInstance(getApplicationContext()).update();
-                if (DEBUG_TIMING) Log.d(LOG_TAG, "Index.update() took "
-                        + (System.currentTimeMillis() - indexStartTime) + " ms");
+                mSearchFeatureProvider.updateIndex(getApplicationContext());
             } else {
                 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
             }
@@ -942,56 +938,56 @@
 
         String packageName = getPackageName();
         setTileEnabled(new ComponentName(packageName, WifiSettingsActivity.class.getName()),
-                pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin, pm);
+                pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                 Settings.BluetoothSettingsActivity.class.getName()),
-                pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin, pm);
+                pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                 Settings.DataUsageSummaryActivity.class.getName()),
-                Utils.isBandwidthControlEnabled(), isAdmin, pm);
+                Utils.isBandwidthControlEnabled(), isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                 Settings.SimSettingsActivity.class.getName()),
-                Utils.showSimCardTile(this), isAdmin, pm);
+                Utils.showSimCardTile(this), isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                 Settings.PowerUsageSummaryActivity.class.getName()),
-                mBatteryPresent, isAdmin, pm);
+                mBatteryPresent, isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                 Settings.UserSettingsActivity.class.getName()),
                 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
-                && !Utils.isMonkeyRunning(), isAdmin, pm);
+                && !Utils.isMonkeyRunning(), isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                         Settings.WirelessSettingsActivity.class.getName()),
-                !UserManager.isDeviceInDemoMode(this), isAdmin, pm);
+                !UserManager.isDeviceInDemoMode(this), isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                         Settings.DateTimeSettingsActivity.class.getName()),
-                !UserManager.isDeviceInDemoMode(this), isAdmin, pm);
+                !UserManager.isDeviceInDemoMode(this), isAdmin);
         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
         setTileEnabled(new ComponentName(packageName,
                         Settings.PaymentSettingsActivity.class.getName()),
                 pm.hasSystemFeature(PackageManager.FEATURE_NFC)
                         && pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
-                        && adapter != null && adapter.isEnabled(), isAdmin, pm);
+                        && adapter != null && adapter.isEnabled(), isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                 Settings.PrintSettingsActivity.class.getName()),
-                pm.hasSystemFeature(PackageManager.FEATURE_PRINTING), isAdmin, pm);
+                pm.hasSystemFeature(PackageManager.FEATURE_PRINTING), isAdmin);
 
         final boolean showDev = mDevelopmentPreferences.getBoolean(
                     DevelopmentSettings.PREF_SHOW, android.os.Build.TYPE.equals("eng"))
                 && !um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
         setTileEnabled(new ComponentName(packageName,
                         Settings.DevelopmentSettingsActivity.class.getName()),
-                showDev, isAdmin, pm);
+                showDev, isAdmin);
         setTileEnabled(new ComponentName(packageName,
                         Settings.DevelopmentSettingsActivity.DASHBOARD_ALIAS),
-                showDev, isAdmin, pm);
+                showDev, isAdmin);
 
         // Reveal development-only quick settings tiles
         setTileEnabled(new ComponentName(this, DevelopmentModeTile.class), showDev);
@@ -1010,7 +1006,7 @@
                     ComponentName component = tile.intent.getComponent();
                     if (packageName.equals(component.getPackageName()) && !ArrayUtils.contains(
                             SETTINGS_FOR_RESTRICTED, component.getClassName())) {
-                        setTileEnabled(component, false, isAdmin, pm);
+                        setTileEnabled(component, false, isAdmin);
                     }
                 }
             }
@@ -1019,10 +1015,10 @@
         String backupIntent = getResources().getString(R.string.config_backup_settings_intent);
         boolean useDefaultBackup = TextUtils.isEmpty(backupIntent);
         setTileEnabled(new ComponentName(packageName,
-                Settings.PrivacySettingsActivity.class.getName()), useDefaultBackup, isAdmin, pm);
+                Settings.PrivacySettingsActivity.class.getName()), useDefaultBackup, isAdmin);
         setTileEnabled(new ComponentName(packageName,
                         "com.android.settings.PrivacyDashboardAlias"),
-                useDefaultBackup, isAdmin, pm);
+                useDefaultBackup, isAdmin);
 
         boolean hasBackupActivity = false;
         if (!useDefaultBackup) {
@@ -1034,24 +1030,25 @@
             }
         }
 
-        // Enable/disble BackupSettingsActivity and its alias.
+        // Enable/disable BackupSettingsActivity and its alias.
         setTileEnabled(new ComponentName(packageName,
-                BackupSettingsActivity.class.getName()), hasBackupActivity, isAdmin, pm);
+                BackupSettingsActivity.class.getName()), hasBackupActivity, isAdmin);
         setTileEnabled(new ComponentName(packageName,
-                "com.android.settings.BackupResetDashboardAlias"), hasBackupActivity, isAdmin, pm);
+                "com.android.settings.BackupResetDashboardAlias"), hasBackupActivity, isAdmin);
 
         setTileEnabled(new ComponentName(packageName,
                 Settings.EnterprisePrivacySettingsActivity.class.getName()),
                 FeatureFactory.getFactory(this).getEnterprisePrivacyFeatureProvider(this)
-                        .hasDeviceOwner(), isAdmin, pm);
+                        .hasDeviceOwner(), isAdmin);
         setTileEnabled(new ComponentName(packageName,
                         "com.android.settings.EnterprisePrivacyDashboardAlias"),
                 FeatureFactory.getFactory(this).getEnterprisePrivacyFeatureProvider(this)
-                        .hasDeviceOwner(), isAdmin, pm);
+                        .hasDeviceOwner(), isAdmin);
+        // Final step, refresh categories.
+        updateCategories();
     }
 
-    private void setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin,
-            PackageManager pm) {
+    private void setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin) {
         if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName())
                 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, component.getClassName())) {
             enabled = false;
diff --git a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
index cf0a0a8..12f78ff 100644
--- a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
@@ -23,11 +23,13 @@
 import android.os.UserManager;
 import android.support.annotation.VisibleForTesting;
 
+import android.support.v7.preference.Preference;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.accounts.AuthenticatorHelper;
 import com.android.settingslib.drawer.CategoryKey;
 import com.android.settingslib.drawer.Tile;
 
@@ -43,11 +45,16 @@
     public static final String KEY_ACCOUNT_TYPE = "account_type";
     public static final String KEY_ACCOUNT_LABEL = "account_label";
     public static final String KEY_ACCOUNT_TITLE_RES = "account_title_res";
+    public static final String KEY_ACCOUNT_HEADER = "account_header";
+    public static final String KEY_USER_HANDLE = "user_handle";
 
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    Account mAccount;
     private String mAccountLabel;
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     String mAccountType;
     private AccountSyncPreferenceController mAccountSynController;
+    private RemoveAccountPreferenceController mRemoveAccountController;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -57,10 +64,9 @@
         UserHandle userHandle = Utils.getSecureTargetUser(activity.getActivityToken(),
             (UserManager) getSystemService(Context.USER_SERVICE), args,
             activity.getIntent().getExtras());
-        Account account = null;
         if (args != null) {
             if (args.containsKey(KEY_ACCOUNT)) {
-                account = args.getParcelable(KEY_ACCOUNT);
+                mAccount = args.getParcelable(KEY_ACCOUNT);
             }
             if (args.containsKey(KEY_ACCOUNT_LABEL)) {
                 mAccountLabel = args.getString(KEY_ACCOUNT_LABEL);
@@ -69,7 +75,8 @@
                 mAccountType = args.getString(KEY_ACCOUNT_TYPE);
             }
         }
-        mAccountSynController.init(account, userHandle);
+        mAccountSynController.init(mAccount, userHandle);
+        mRemoveAccountController.setAccount(mAccount);
     }
 
     @Override
@@ -78,6 +85,7 @@
         if (mAccountLabel != null) {
             getActivity().setTitle(mAccountLabel);
         }
+        updateAccountHeader();
     }
 
     @Override
@@ -105,6 +113,8 @@
         final List<PreferenceController> controllers = new ArrayList<>();
         mAccountSynController = new AccountSyncPreferenceController(context);
         controllers.add(mAccountSynController);
+        mRemoveAccountController = new RemoveAccountPreferenceController(context, this);
+        controllers.add(mRemoveAccountController);
         return controllers;
     }
 
@@ -120,4 +130,18 @@
         return mAccountType.equals(metadata.getString(METADATA_IA_ACCOUNT));
     }
 
+    @VisibleForTesting
+    void updateAccountHeader() {
+        final Preference headerPreference = findPreference(KEY_ACCOUNT_HEADER);
+        headerPreference.setTitle(mAccount.name);
+        final Context context = getContext();
+        UserHandle userHandle = null;
+        Bundle args = getArguments();
+        if (args != null && args.containsKey(KEY_USER_HANDLE)) {
+            userHandle = args.getParcelable(KEY_USER_HANDLE);
+        }
+        final AuthenticatorHelper helper = new AuthenticatorHelper(context, userHandle, null);
+        headerPreference.setIcon(helper.getDrawableForType(context, mAccountType));
+   }
+
 }
\ No newline at end of file
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index 723a1ec..ad3fac1 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -465,6 +465,8 @@
                     final Bundle fragmentArguments = new Bundle();
                     fragmentArguments.putParcelable(AccountDetailDashboardFragment.KEY_ACCOUNT,
                         account);
+                    fragmentArguments.putParcelable(AccountDetailDashboardFragment.KEY_USER_HANDLE,
+                        userHandle);
                     fragmentArguments.putString(AccountDetailDashboardFragment.KEY_ACCOUNT_TYPE,
                         accountType);
                     fragmentArguments.putString(AccountDetailDashboardFragment.KEY_ACCOUNT_LABEL,
diff --git a/src/com/android/settings/accounts/RemoveAccountPreferenceController.java b/src/com/android/settings/accounts/RemoveAccountPreferenceController.java
new file mode 100644
index 0000000..c8dbe4c
--- /dev/null
+++ b/src/com/android/settings/accounts/RemoveAccountPreferenceController.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.accounts;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Process;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+import java.io.IOException;
+
+public class RemoveAccountPreferenceController extends PreferenceController
+    implements OnClickListener {
+
+    private static final String KEY_REMOVE_ACCOUNT = "remove_account";
+
+    private Account mAccount;
+    private Fragment mParentFragment;
+
+    public RemoveAccountPreferenceController(Context context, Fragment parent) {
+        super(context);
+        mParentFragment = parent;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        final LayoutPreference removeAccountPreference =
+            (LayoutPreference) screen.findPreference(KEY_REMOVE_ACCOUNT);
+        Button removeAccountButton = (Button) removeAccountPreference.findViewById(R.id.button);
+        removeAccountButton.setOnClickListener(this);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_REMOVE_ACCOUNT;
+    }
+
+    @Override
+    public void onClick(View v) {
+        ConfirmRemoveAccountDialog.show(mParentFragment, mAccount);
+    }
+
+    public void setAccount(Account account) {
+        mAccount = account;
+    }
+
+    /**
+     * Dialog to confirm with user about account removal
+     */
+    public static class ConfirmRemoveAccountDialog extends InstrumentedDialogFragment implements
+            DialogInterface.OnClickListener {
+        private static final String SAVE_ACCOUNT = "account";
+        private static final String REMOVE_ACCOUNT_DIALOG = "confirmRemoveAccount";
+        private Account mAccount;
+
+        public static ConfirmRemoveAccountDialog show(Fragment parent, Account account) {
+            if (!parent.isAdded()) {
+                return null;
+            }
+            final ConfirmRemoveAccountDialog dialog = new ConfirmRemoveAccountDialog();
+            dialog.mAccount = account;
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), REMOVE_ACCOUNT_DIALOG);
+            return dialog;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+            if (savedInstanceState != null) {
+                mAccount = (Account) savedInstanceState.getParcelable(SAVE_ACCOUNT);
+            }
+            return new AlertDialog.Builder(context)
+                .setTitle(R.string.really_remove_account_title)
+                .setMessage(R.string.really_remove_account_message)
+                .setNegativeButton(android.R.string.cancel, null)
+                .setPositiveButton(R.string.remove_account_label, this)
+                .create();
+        }
+
+        @Override
+        public void onSaveInstanceState(Bundle outState) {
+            super.onSaveInstanceState(outState);
+            outState.putParcelable(SAVE_ACCOUNT, mAccount);
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsProto.MetricsEvent.DIALOG_ACCOUNT_SYNC_REMOVE;
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            Activity activity = getTargetFragment().getActivity();
+            AccountManager.get(activity).removeAccountAsUser(mAccount, activity,
+                    new AccountManagerCallback<Bundle>() {
+                        @Override
+                        public void run(AccountManagerFuture<Bundle> future) {
+                            // If already out of this screen, don't proceed.
+                            if (!getTargetFragment().isResumed()) {
+                                return;
+                            }
+                            boolean failed = true;
+                            try {
+                                if (future.getResult()
+                                    .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
+                                    failed = false;
+                                }
+                            } catch (OperationCanceledException e) {
+                                // handled below
+                            } catch (IOException e) {
+                                // handled below
+                            } catch (AuthenticatorException e) {
+                                // handled below
+                            }
+                            final Activity activity = getTargetFragment().getActivity();
+                            if (failed && activity != null && !activity.isFinishing()) {
+                                RemoveAccountFailureDialog.show(getTargetFragment());
+                            } else {
+                                activity.finish();
+                            }
+                        }
+                    }, null, Process.myUserHandle());
+        }
+    }
+
+    /**
+     * Dialog to tell user about account removal failure
+     */
+    public static class RemoveAccountFailureDialog extends InstrumentedDialogFragment {
+
+        private static final String FAILED_REMOVAL_DIALOG = "removeAccountFailed";
+
+        public static void show(Fragment parent) {
+            if (!parent.isAdded()) {
+                return;
+            }
+            final RemoveAccountFailureDialog dialog = new RemoveAccountFailureDialog();
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), FAILED_REMOVAL_DIALOG);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+
+            return new AlertDialog.Builder(context)
+                .setTitle(R.string.really_remove_account_title)
+                .setMessage(R.string.remove_account_failed)
+                .setPositiveButton(android.R.string.ok, null)
+                .create();
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsProto.MetricsEvent.DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL;
+        }
+
+    }
+}
diff --git a/src/com/android/settings/applications/AdvancedAppSettings.java b/src/com/android/settings/applications/AdvancedAppSettings.java
index 1a15d6f..5bce24d 100644
--- a/src/com/android/settings/applications/AdvancedAppSettings.java
+++ b/src/com/android/settings/applications/AdvancedAppSettings.java
@@ -16,7 +16,6 @@
 package com.android.settings.applications;
 
 import android.content.Context;
-import android.os.Bundle;
 import android.provider.SearchIndexableResource;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -37,11 +36,6 @@
     static final String TAG = "AdvancedAppSettings";
 
     @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-    }
-
-    @Override
     protected String getCategoryKey() {
         return CategoryKey.CATEGORY_APPS_DEFAULT;
     }
diff --git a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
index 5942897..be70f86 100644
--- a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
+++ b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
@@ -40,6 +40,12 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mProgressiveDisclosureMixin.setTileLimit(3);
+    }
+
+    @Override
     protected String getCategoryKey() {
         return CategoryKey.CATEGORY_APPS;
     }
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index e7d2b58..b5d8a73 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -63,7 +63,7 @@
 
     @Override
     public boolean isEnabled() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 9afe4b2..14d1bdd 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -268,6 +268,7 @@
 
         // Add resource based tiles.
         displayResourceTiles();
+        mProgressiveDisclosureMixin.collapse(getPreferenceScreen());
 
         refreshDashboardTiles(TAG);
     }
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 94e3ac8..6232d2f 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -31,7 +31,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.dashboard.conditional.Condition;
 import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
 import com.android.settings.dashboard.conditional.ConditionManager;
@@ -46,7 +46,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public class DashboardSummary extends InstrumentedPreferenceFragment
+public class DashboardSummary extends InstrumentedFragment
         implements SettingsDrawerActivity.CategoryListener, ConditionManager.ConditionListener,
         FocusRecyclerView.FocusListener {
     public static final boolean DEBUG = false;
@@ -93,10 +93,9 @@
                     ((SettingsActivity) getActivity()).getDashboardCategories());
         }
 
-        Context context = getContext();
-        mConditionManager = ConditionManager.get(context, false);
-        mSuggestionParser = new SuggestionParser(context,
-                context.getSharedPreferences(SUGGESTIONS, 0), R.xml.suggestion_ordering);
+        mConditionManager = ConditionManager.get(activity, false);
+        mSuggestionParser = new SuggestionParser(activity,
+                activity.getSharedPreferences(SUGGESTIONS, 0), R.xml.suggestion_ordering);
         mSuggestionsChecks = new SuggestionsChecks(getContext());
         if (savedInstanceState == null) {
             mSuggestionsShownLogged = new ArrayList<>();
diff --git a/src/com/android/settings/dashboard/ExpandPreference.java b/src/com/android/settings/dashboard/ExpandPreference.java
index 215bfc5..cfa1836 100644
--- a/src/com/android/settings/dashboard/ExpandPreference.java
+++ b/src/com/android/settings/dashboard/ExpandPreference.java
@@ -47,6 +47,7 @@
 
     private void init() {
         setLayoutResource(R.layout.expand_preference);
+        setIcon(R.drawable.ic_arrow_down_24dp);
         setTitle(R.string.wifi_more);
         setOrder(999);
     }
diff --git a/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java b/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java
index 8110f32..2907028 100644
--- a/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java
+++ b/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java
@@ -25,6 +25,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.settings.R;
 import com.android.settings.core.lifecycle.LifecycleObserver;
 import com.android.settings.core.lifecycle.events.OnCreate;
 import com.android.settings.core.lifecycle.events.OnSaveInstanceState;
@@ -40,18 +41,19 @@
     private static final String STATE_USER_EXPANDED = "state_user_expanded";
     private static final int DEFAULT_TILE_LIMIT = 300;
 
-    private int mTileLimit = DEFAULT_TILE_LIMIT;
-
+    private final Context mContext;
     private final DashboardFeatureProvider mDashboardFeatureProvider;
     // Collapsed preference sorted by order.
     private final List<Preference> mCollapsedPrefs = new ArrayList<>();
-    private final ExpandPreference mExpandButton;
+    private /* final */ ExpandPreference mExpandButton;
     private final PreferenceFragment mFragment;
 
+    private int mTileLimit = DEFAULT_TILE_LIMIT;
     private boolean mUserExpanded;
 
     public ProgressiveDisclosureMixin(Context context,
             DashboardFeatureProvider dashboardFeatureProvider, PreferenceFragment fragment) {
+        mContext = context;
         mFragment = fragment;
         mExpandButton = new ExpandPreference(context);
         mExpandButton.setOnPreferenceClickListener(this);
@@ -181,6 +183,8 @@
                 if (mCollapsedPrefs.isEmpty()) {
                     // Removed last element, remove expand button too.
                     screen.removePreference(mExpandButton);
+                } else {
+                    updateExpandButtonSummary();
                 }
                 return;
             }
@@ -216,10 +220,28 @@
             insertionIndex = insertionIndex * -1 - 1;
         }
         mCollapsedPrefs.add(insertionIndex, preference);
+        updateExpandButtonSummary();
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     List<Preference> getCollapsedPrefs() {
         return mCollapsedPrefs;
     }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void updateExpandButtonSummary() {
+        final int size = mCollapsedPrefs.size();
+        if (size == 0) {
+            mExpandButton.setSummary(null);
+        } else if (size == 1) {
+            mExpandButton.setSummary(mCollapsedPrefs.get(0).getTitle());
+        } else {
+            CharSequence summary = mCollapsedPrefs.get(0).getTitle();
+            for (int i = 1; i < size; i++) {
+                summary = mContext.getString(R.string.join_many_items_middle, summary,
+                        mCollapsedPrefs.get(i).getTitle());
+            }
+            mExpandButton.setSummary(summary);
+        }
+    }
 }
diff --git a/src/com/android/settings/search2/SearchFeatureProviderImpl.java b/src/com/android/settings/search2/SearchFeatureProviderImpl.java
index 5aa21df..97b7bfe 100644
--- a/src/com/android/settings/search2/SearchFeatureProviderImpl.java
+++ b/src/com/android/settings/search2/SearchFeatureProviderImpl.java
@@ -19,19 +19,21 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 
 import com.android.settings.R;
-import com.android.settings.search.Index;
-
 import com.android.settings.applications.PackageManagerWrapperImpl;
+import com.android.settings.search.Index;
 
 /**
  * FeatureProvider for the refactored search code.
  */
 public class SearchFeatureProviderImpl implements SearchFeatureProvider {
 
+    private static final String TAG = "SearchFeatureProvider";
+
     private DatabaseIndexingManager mDatabaseIndexingManager;
 
     @Override
@@ -81,10 +83,12 @@
 
     @Override
     public void updateIndex(Context context) {
+        long indexStartTime = System.currentTimeMillis();
         if (isEnabled(context)) {
             getIndexingManager(context).update();
         } else {
             Index.getInstance(context).update();
         }
+        Log.d(TAG, "Index.update() took " + (System.currentTimeMillis() - indexStartTime) + " ms");
     }
 }
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
index 7006dfb..3ba4d02 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
@@ -15,35 +15,69 @@
  */
 package com.android.settings.accounts;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.content.Context;
 import android.os.Bundle;
+import android.os.UserHandle;
+import android.support.v7.preference.Preference;
 
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.shadow.ShadowAccountManager;
+import com.android.settings.testutils.shadow.ShadowContentResolver;
 import com.android.settingslib.drawer.CategoryKey;
 import com.android.settingslib.drawer.Tile;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class AccountDetailDashboardFragmentTest {
 
     private static final String METADATA_CATEGORY = "com.android.settings.category";
     private static final String METADATA_ACCOUNT_TYPE = "com.android.settings.ia.account";
+    private static final String METADATA_USER_HANDLE = "user_handle";
+    private static final String PREF_ACCOUNT_HEADER = "account_header";
+
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private AccountManager mAccountManager;
+    @Mock
+    private Preference mPreference;
 
     private AccountDetailDashboardFragment mFragment;
+    private Context mContext;
 
     @Before
     public void setUp() {
-        mFragment = new AccountDetailDashboardFragment();
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowContext = ShadowApplication.getInstance();
+        shadowContext.setSystemService(Context.ACCOUNT_SERVICE, mAccountManager);
+        mContext = spy(shadowContext.getApplicationContext());
+
+        mFragment = spy(new AccountDetailDashboardFragment());
         final Bundle args = new Bundle();
-        args.putString(METADATA_ACCOUNT_TYPE, "com.abc");
+        args.putParcelable(METADATA_USER_HANDLE, UserHandle.CURRENT);
+        mFragment.setArguments(args);
         mFragment.mAccountType = "com.abc";
+        mFragment.mAccount = new Account("name1@abc.com", "com.abc");
     }
 
     @Test
@@ -83,4 +117,18 @@
         assertThat(mFragment.displayTile(tile)).isFalse();
     }
 
+    @Test
+    @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class})
+    public void updateAccountHeader_shouldShowAccountName() throws Exception {
+        when(mAccountManager.getAuthenticatorTypesAsUser(anyInt())).thenReturn(
+            new AuthenticatorDescription[0]);
+        when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]);
+        when(mFragment.getContext()).thenReturn(mContext);
+        doReturn(mPreference).when(mFragment).findPreference(PREF_ACCOUNT_HEADER);
+
+        mFragment.updateAccountHeader();
+
+        verify(mPreference).setTitle("name1@abc.com");
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java
new file mode 100644
index 0000000..50f3ac6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.accounts;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AuthenticatorDescription;
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v14.preference.PreferenceFragment;
+import android.widget.Button;
+
+import com.android.settings.AccessiblePreferenceCategory;
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.search.SearchIndexableRaw;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowAccountManager;
+import com.android.settings.testutils.shadow.ShadowContentResolver;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class RemoveAccountPreferenceControllerTest {
+
+    private static final String KEY_REMOVE_ACCOUNT = "remove_account";
+    private static final String TAG_REMOVE_ACCOUNT_DIALOG = "confirmRemoveAccount";
+
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private AccountManager mAccountManager;
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceFragment mFragment;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private FragmentManager mFragmentManager;
+    @Mock
+    private FragmentTransaction mFragmentTransaction;
+    @Mock
+    private LayoutPreference mPreference;
+
+    private Context mContext;
+    private RemoveAccountPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowContext = ShadowApplication.getInstance();
+        shadowContext.setSystemService(Context.ACCOUNT_SERVICE, mAccountManager);
+        mContext = spy(shadowContext.getApplicationContext());
+
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+        when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
+        when(mFragment.getFragmentManager()).thenReturn(mFragmentManager);
+        when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
+        when(mAccountManager.getAuthenticatorTypesAsUser(anyInt())).thenReturn(
+            new AuthenticatorDescription[0]);
+        when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]);
+        mController = new RemoveAccountPreferenceController(mContext, mFragment);
+    }
+
+    @Test
+    public void displayPreference_shouldAddClickListener() {
+        when(mScreen.findPreference(KEY_REMOVE_ACCOUNT)).thenReturn(mPreference);
+        final Button button = mock(Button.class);
+        when(mPreference.findViewById(R.id.button)).thenReturn(button);
+
+        mController.displayPreference(mScreen);
+
+        verify(button).setOnClickListener(mController);
+    }
+
+    @Test
+    public void onClick_shouldStartConfirmDialog() {
+        when(mFragment.isAdded()).thenReturn(true);
+        mController.onClick(null);
+
+        verify(mFragmentTransaction).add(
+            any(RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.class),
+            eq(TAG_REMOVE_ACCOUNT_DIALOG));
+    }
+
+    @Test
+    @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class})
+    public void confirmRemove_shouldRemoveAccount() {
+        when(mFragment.isAdded()).thenReturn(true);
+        Activity activity = mock(Activity.class);
+        when(activity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
+        when(mFragment.getActivity()).thenReturn(activity);
+
+        Account account = new Account("Account11", "com.acct1");
+        RemoveAccountPreferenceController.ConfirmRemoveAccountDialog dialog =
+            RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.show(mFragment, account);
+
+        dialog.onClick(null, 0);
+        verify(mAccountManager).removeAccountAsUser(eq(account), any(Activity.class),
+            any(AccountManagerCallback.class), any(Handler.class), any(UserHandle.class));
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java b/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java
index ffc6874..bb00cb8 100644
--- a/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java
@@ -21,6 +21,7 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
+import com.android.settings.R;
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.overlay.FeatureFactory;
@@ -34,6 +35,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
 
 import java.util.List;
 
@@ -41,6 +43,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -56,10 +59,11 @@
     private FakeFeatureFactory mFakeFeatureFactory;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private PreferenceFragment mPreferenceFragment;
+    @Mock
+    private ExpandPreference mExpandButton;
     private PreferenceScreen mScreen;
     private Context mAppContext;
     private Preference mPreference;
-
     private ProgressiveDisclosureMixin mMixin;
 
     @Before
@@ -71,6 +75,7 @@
         mFakeFeatureFactory = (FakeFeatureFactory) FeatureFactory.getFactory(mContext);
         mMixin = new ProgressiveDisclosureMixin(mAppContext,
                 mFakeFeatureFactory.dashboardFeatureProvider, mPreferenceFragment);
+        ReflectionHelpers.setField(mMixin, "mExpandButton", mExpandButton);
         mPreference = new Preference(mAppContext);
         mPreference.setKey("test");
         when(mFakeFeatureFactory.dashboardFeatureProvider.isEnabled()).thenReturn(true);
@@ -166,6 +171,7 @@
 
         mMixin.collapse(screen);
         assertThat(mMixin.isCollapsed()).isFalse();
+        verify(mExpandButton, never()).setSummary(anyString());
         verify(screen, never()).addPreference(any(Preference.class));
         verify(screen, never()).removePreference(any(Preference.class));
     }
@@ -180,6 +186,7 @@
         mMixin.collapse(screen);
 
         assertThat(mMixin.isCollapsed()).isTrue();
+        verify(mExpandButton, atLeastOnce()).setSummary(anyString());
         verify(screen).addPreference(any(ExpandPreference.class));
         verify(screen, times(3)).removePreference(any(Preference.class));
     }
@@ -224,7 +231,9 @@
         lastPref.setOrder(100);
         // Add something to collapsed list so we are in collapsed state.
         mMixin.addToCollapsedList(new Preference(mAppContext));
+        verify(mExpandButton).setSummary(anyString());
         assertThat(mMixin.getCollapsedPrefs().size()).isEqualTo(1);
+
         // 3 prefs on screen, 2 are real and the last one is more button.
         when(mScreen.getPreferenceCount()).thenReturn(3);
         when(mScreen.getPreference(1)).thenReturn(lastPref);
@@ -244,7 +253,9 @@
         lastPref.setOrder(100);
         // Add something to collapsed list so we are in collapsed state.
         mMixin.addToCollapsedList(new Preference(mAppContext));
+        verify(mExpandButton).setSummary(anyString());
         assertThat(mMixin.getCollapsedPrefs().size()).isEqualTo(1);
+
         // 3 prefs on screen, 2 are real and the last one is more button.
         when(mScreen.getPreferenceCount()).thenReturn(3);
         when(mScreen.getPreference(1)).thenReturn(lastPref);
@@ -255,6 +266,40 @@
 
         verify(mScreen, never()).removePreference(any(Preference.class));
         verify(mScreen, never()).addPreference(any(Preference.class));
+        verify(mExpandButton, times(2)).setSummary(anyString());
         assertThat(mMixin.getCollapsedPrefs().get(0)).isSameAs(toBeAdded);
     }
+
+    @Test
+    public void updateExpandSummary_noPref_noSummary() {
+        mMixin.updateExpandButtonSummary();
+
+        verify(mExpandButton).setSummary(null);
+    }
+
+    @Test
+    public void updateExapndSummary_singlePref_expandSummarySameAsPrefTitle() {
+        final String TEST = "test";
+        final Preference pref = new Preference(mAppContext);
+        pref.setTitle(TEST);
+
+        mMixin.addToCollapsedList(pref);
+        verify(mExpandButton).setSummary(TEST);
+    }
+
+    @Test
+    public void updateExapndSummary_multiPrefs_useCombinedPrefTitleAsSummary() {
+        final String TEST1 = "test1";
+        final String TEST2 = "test2";
+        final Preference pref1 = new Preference(mAppContext);
+        pref1.setTitle(TEST1);
+        final Preference pref2 = new Preference(mAppContext);
+        pref2.setTitle(TEST2);
+
+        mMixin.addToCollapsedList(pref1);
+        mMixin.addToCollapsedList(pref2);
+
+        verify(mExpandButton)
+                .setSummary(mAppContext.getString(R.string.join_many_items_middle, TEST1, TEST2));
+    }
 }