Merge "Cache the summary and icons for Security Settings injected tiles."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 26a5e10..07d7ecc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1572,11 +1572,11 @@
         </activity>
 
         <activity android:name="ConfirmLockPattern"
-            android:theme="@style/Theme.ConfirmDeviceCredentials"/>
+            android:theme="@style/SetupWizardTheme.Light"/>
 
         <activity android:name="ConfirmLockPassword"
             android:windowSoftInputMode="stateHidden|adjustResize"
-            android:theme="@style/Theme.ConfirmDeviceCredentials"/>
+            android:theme="@style/SetupWizardTheme.Light"/>
 
         <activity
             android:name=".Settings$FingerprintSuggestionActivity"
@@ -1618,13 +1618,13 @@
         <!-- Note this must not be exported since it returns the password in the intent -->
         <activity android:name="ConfirmLockPattern$InternalActivity"
             android:exported="false"
-            android:theme="@style/Theme.ConfirmDeviceCredentials"/>
+            android:theme="@style/SetupWizardTheme.Light"/>
 
         <!-- Note this must not be exported since it returns the password in the intent -->
         <activity android:name="ConfirmLockPassword$InternalActivity"
             android:exported="false"
             android:windowSoftInputMode="adjustResize"
-            android:theme="@style/Theme.ConfirmDeviceCredentials"/>
+            android:theme="@style/SetupWizardTheme.Light"/>
 
         <activity android:name="SetupChooseLockGeneric"
             android:taskAffinity="com.android.wizard"
diff --git a/res/layout-land/confirm_lock_pattern_internal.xml b/res/layout-land/confirm_lock_pattern_internal.xml
new file mode 100644
index 0000000..4e58f32
--- /dev/null
+++ b/res/layout-land/confirm_lock_pattern_internal.xml
@@ -0,0 +1,113 @@
+<?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.
+-->
+<com.android.setupwizardlib.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:icon="@drawable/ic_lock"
+    android:layout="@layout/suw_glif_blank_template">
+
+    <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+        android:id="@+id/topLayout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal">
+
+        <ScrollView
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:fillViewport="true">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:paddingBottom="24dp">
+
+                <ImageView
+                    android:id="@+id/suw_layout_icon"
+                    style="@style/SuwGlifIcon"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:src="@drawable/ic_lock" />
+
+                <TextView
+                    android:id="@+id/headerText"
+                    style="@style/SuwGlifHeaderTitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    style="@style/SuwDescription.Glif"
+                    android:id="@+id/detailsText"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="?attr/suwMarginSides"
+                    android:layout_marginEnd="?attr/suwMarginSides" />
+
+                <Button
+                    android:id="@+id/cancelButton"
+                    style="@style/SetupWizardButton.Negative"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="?attr/suwMarginSides"
+                    android:layout_marginEnd="?attr/suwMarginSides"
+                    android:text="@string/cancel" />
+
+                <Space
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp"
+                    android:layout_weight="1" />
+
+                <TextView
+                    style="@style/TextAppearance.ConfirmDeviceCredentialsErrorText"
+                    android:accessibilityLiveRegion="polite"
+                    android:id="@+id/errorText"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="?attr/suwMarginSides"
+                    android:layout_marginEnd="?attr/suwMarginSides"
+                    android:layout_marginBottom="24dp"
+                    android:gravity="center_vertical"/>
+
+                <ImageView
+                    android:id="@+id/fingerprintIcon"
+                    android:layout_gravity="center_horizontal"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@string/confirm_fingerprint_icon_content_description"
+                    android:visibility="gone"/>
+            </LinearLayout>
+
+        </ScrollView>
+
+        <FrameLayout
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1">
+
+            <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_gravity="center"
+                android:background="@color/lock_pattern_background" />
+
+        </FrameLayout>
+
+    </com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/battery_header.xml b/res/layout/battery_header.xml
index a2484c2..ef39d8b 100644
--- a/res/layout/battery_header.xml
+++ b/res/layout/battery_header.xml
@@ -24,6 +24,7 @@
     android:orientation="horizontal"
     android:paddingTop="24dp"
     android:paddingBottom="24dp"
+    android:background="@drawable/selectable_card_grey"
     style="@style/EntityHeader">
 
     <com.android.settings.fuelgauge.BatteryMeterView
diff --git a/res/layout/confirm_lock_password_internal.xml b/res/layout/confirm_lock_password_internal.xml
new file mode 100644
index 0000000..e22d764
--- /dev/null
+++ b/res/layout/confirm_lock_password_internal.xml
@@ -0,0 +1,98 @@
+<?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.
+-->
+<com.android.setupwizardlib.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:icon="@drawable/ic_lock"
+    android:layout="@layout/suw_glif_blank_template">
+
+    <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+        android:id="@+id/topLayout"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <ImageView
+            android:id="@+id/suw_layout_icon"
+            style="@style/SuwGlifIcon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_lock" />
+
+        <TextView
+            android:id="@+id/headerText"
+            style="@style/SuwGlifHeaderTitle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/detailsText"
+            style="@style/SuwDescription.Glif"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="?attr/suwMarginSides"
+            android:layout_marginEnd="?attr/suwMarginSides" />
+
+        <Button
+            style="@style/SetupWizardButton.Negative"
+            android:id="@+id/cancelButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/cancel" />
+
+        <Space
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
+
+        <EditText
+            android:id="@+id/password_entry"
+            android:layout_width="208dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="-40dp"
+            android:inputType="textPassword"
+            android:imeOptions="actionNext|flagNoFullscreen"
+            android:gravity="center"
+            android:textSize="16sp"
+            style="@style/TextAppearance.PasswordEntry"/>
+
+        <TextView
+            style="@style/TextAppearance.ConfirmDeviceCredentialsErrorText"
+            android:accessibilityLiveRegion="polite"
+            android:id="@+id/errorText"
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginStart="12dp"
+            android:layout_marginEnd="12dp"
+            android:gravity="center_vertical"/>
+
+        <ImageView
+            android:id="@+id/fingerprintIcon"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="24dp"
+            android:contentDescription="@string/confirm_fingerprint_icon_content_description"
+            android:visibility="gone"/>
+
+    </com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+</com.android.setupwizardlib.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/confirm_lock_pattern_internal_base.xml b/res/layout/confirm_lock_pattern_internal_base.xml
new file mode 100644
index 0000000..359b51b
--- /dev/null
+++ b/res/layout/confirm_lock_pattern_internal_base.xml
@@ -0,0 +1,112 @@
+<?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.
+-->
+<com.android.setupwizardlib.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:icon="@drawable/ic_lock"
+    android:layout="@layout/suw_glif_blank_template">
+
+    <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+        android:id="@+id/topLayout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="2">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="vertical">
+
+                <ImageView
+                    android:id="@+id/suw_layout_icon"
+                    style="@style/SuwGlifIcon"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:src="@drawable/ic_lock" />
+
+                <TextView
+                    android:id="@+id/headerText"
+                    style="@style/SuwGlifHeaderTitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    style="@style/SuwDescription.Glif"
+                    android:id="@+id/detailsText"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="?attr/suwMarginSides"
+                    android:layout_marginEnd="?attr/suwMarginSides" />
+
+                <Button
+                    android:id="@+id/cancelButton"
+                    style="@style/SetupWizardButton.Negative"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="?attr/suwMarginSides"
+                    android:layout_marginEnd="?attr/suwMarginSides"
+                    android:text="@string/cancel" />
+
+            </LinearLayout>
+
+        </ScrollView>
+
+        <com.android.internal.widget.LockPatternView
+            android:id="@+id/lockPattern"
+            android:layout_width="312dp"
+            android:layout_height="0dp"
+            android:layout_weight="3"
+            android:layout_gravity="center_horizontal"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:orientation="vertical">
+
+            <TextView
+                style="@style/TextAppearance.ConfirmDeviceCredentialsErrorText"
+                android:accessibilityLiveRegion="polite"
+                android:id="@+id/errorText"
+                android:layout_width="wrap_content"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="12dp"
+                android:layout_marginStart="12dp"
+                android:layout_marginEnd="12dp"
+                android:gravity="center_vertical"/>
+
+            <ImageView
+                android:id="@+id/fingerprintIcon"
+                android:layout_gravity="center_horizontal"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_marginBottom="24dp"
+                android:contentDescription="@string/confirm_fingerprint_icon_content_description"
+                android:visibility="gone"/>
+        </LinearLayout>
+
+    </com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/search_panel.xml b/res/layout/search_panel.xml
deleted file mode 100644
index d466874..0000000
--- a/res/layout/search_panel.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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:id="@+id/search_panel"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-    <LinearLayout android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="center"
-            android:orientation="vertical">
-
-        <LinearLayout android:id="@+id/layout_suggestions"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:orientation="vertical">
-
-            <!-- Padding is included in the background -->
-            <ListView android:id="@+id/list_suggestions"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:paddingStart="@dimen/dashboard_padding_start"
-                    android:paddingEnd="@dimen/dashboard_padding_end"
-                    android:paddingTop="@dimen/dashboard_padding_top"
-                    android:paddingBottom="@dimen/dashboard_padding_bottom"
-                    android:scrollbarStyle="outsideOverlay"
-                    android:headerDividersEnabled="false"
-                    android:background="@drawable/search_panel_list_background"
-                    android:elevation="@dimen/search_panel_elevation"
-                    />
-
-        </LinearLayout>
-
-        <LinearLayout android:id="@+id/layout_results"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_gravity="center"
-                android:orientation="vertical"
-                android:layout_weight="1">
-
-            <!-- Padding is included in the background -->
-            <ListView android:id="@+id/list_results"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:paddingStart="@dimen/dashboard_padding_start"
-                    android:paddingEnd="@dimen/dashboard_padding_end"
-                    android:paddingTop="@dimen/dashboard_padding_top"
-                    android:paddingBottom="@dimen/dashboard_padding_bottom"
-                    android:scrollbarStyle="outsideOverlay"
-                    android:headerDividersEnabled="false"
-                    android:background="@drawable/search_panel_list_background"
-                    android:elevation="@dimen/search_panel_elevation"
-                    />
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-</FrameLayout>
diff --git a/res/layout/search_panel_results_header.xml b/res/layout/search_panel_results_header.xml
deleted file mode 100644
index 2deb273..0000000
--- a/res/layout/search_panel_results_header.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:layout_width="match_parent"
-          android:layout_height="@dimen/dashboard_category_height"
-          android:paddingStart="@dimen/search_title_padding_start"
-          android:singleLine="true"
-          android:ellipsize="marquee"
-          android:gravity="center_vertical"
-          android:textAppearance="@style/TextAppearance.ResultTitle"
-          android:textAlignment="viewStart"
-          android:text="@string/search_results_label"
-        />
diff --git a/res/layout/search_panel_suggestions_header.xml b/res/layout/search_panel_suggestions_header.xml
deleted file mode 100644
index 98957e4..0000000
--- a/res/layout/search_panel_suggestions_header.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:layout_width="match_parent"
-          android:layout_height="@dimen/dashboard_category_height"
-          android:paddingStart="@dimen/search_title_padding_start"
-          android:singleLine="true"
-          android:ellipsize="marquee"
-          android:gravity="center_vertical"
-          android:textAppearance="@style/TextAppearance.RecentsTitle"
-          android:textAlignment="viewStart"
-          android:text="@string/search_recents_queries_label"
-        />
diff --git a/res/layout/search_result_item.xml b/res/layout/search_result_item.xml
deleted file mode 100644
index b659387..0000000
--- a/res/layout/search_result_item.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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:minHeight="?android:attr/listPreferredItemHeight"
-              android:gravity="center_vertical"
-              android:paddingEnd="?android:attr/scrollbarSize">
-
-    <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:gravity="center"
-            android:minWidth="@*android:dimen/preference_icon_minWidth"
-            android:orientation="horizontal">
-
-        <ImageView
-                android:id="@+id/icon"
-                android:layout_width="@dimen/search_result_item_image_size"
-                android:layout_height="@dimen/search_result_item_image_size"
-                android:scaleType="centerInside"
-                android:layout_marginStart="@dimen/search_result_item_image_margin_start"
-                android:layout_marginEnd="@dimen/search_result_item_image_margin_end"
-                />
-
-    </LinearLayout>
-
-    <RelativeLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1">
-
-        <TextView android:id="@+id/title"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:singleLine="true"
-                  android:textAppearance="?android:attr/textAppearanceMedium"
-                  android:ellipsize="marquee"
-                  android:fadingEdge="horizontal" />
-
-    </RelativeLayout>
-
-</LinearLayout>
diff --git a/res/layout/storage_summary_donut.xml b/res/layout/storage_summary_donut.xml
index 9fd4860..0e63fb1 100644
--- a/res/layout/storage_summary_donut.xml
+++ b/res/layout/storage_summary_donut.xml
@@ -31,8 +31,8 @@
         android:orientation="vertical"
         android:paddingStart="@dimen/preference_no_icon_padding_start"
         android:paddingEnd="@dimen/storage_summary_padding_end"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp" >
+        android:paddingTop="32dp"
+        android:paddingBottom="32dp" >
 
         <TextView
             android:id="@android:id/title"
@@ -53,6 +53,7 @@
             android:layout_marginStart="4dp"
             android:layout_marginEnd="4dp"
             android:maxLines="10"
+            android:paddingBottom="20dp"
             android:textAlignment="viewStart"
             android:textAppearance="@android:style/TextAppearance.Material.Body1" />
 
diff --git a/res/menu/options_menu.xml b/res/menu/options_menu.xml
deleted file mode 100644
index f73ce90..0000000
--- a/res/menu/options_menu.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/search"
-        android:title="@string/search_menu"
-        android:icon="@*android:drawable/ic_search_api_material"
-        android:showAsAction="collapseActionView|ifRoom"
-        android:actionViewClass="android.widget.SearchView" />
-</menu>
\ No newline at end of file
diff --git a/res/values-sw600dp/aliases.xml b/res/values-sw600dp/aliases.xml
index 163e97a..86f2da5 100644
--- a/res/values-sw600dp/aliases.xml
+++ b/res/values-sw600dp/aliases.xml
@@ -20,5 +20,6 @@
      <item name="fingerprint_enroll_enrolling" type="layout">@layout/fingerprint_enroll_enrolling_base</item>
      <item name="fingerprint_enroll_finish" type="layout">@layout/fingerprint_enroll_finish_base</item>
      <item name="choose_lock_pattern" type="layout">@layout/choose_lock_pattern_common</item>
+     <item name="confirm_lock_pattern_internal" type="layout">@layout/confirm_lock_pattern_internal_base</item>
 </resources>
 
diff --git a/res/values/aliases.xml b/res/values/aliases.xml
index 951b1bb..8c4858d 100644
--- a/res/values/aliases.xml
+++ b/res/values/aliases.xml
@@ -17,6 +17,7 @@
 <resources>
      <item name="notification_app_section" type="layout">@*android:layout/preference_category_material</item>
      <item name="confirm_lock_pattern" type="layout">@layout/confirm_lock_pattern_base</item>
+     <item name="confirm_lock_pattern_internal" type="layout">@layout/confirm_lock_pattern_internal_base</item>
      <item name="confirm_lock_password" type="layout">@layout/confirm_lock_password_base</item>
      <item name="fingerprint_enroll_find_sensor" type="layout">@layout/fingerprint_enroll_find_sensor_base</item>
      <item name="fingerprint_enroll_enrolling" type="layout">@layout/fingerprint_enroll_enrolling_base</item>
diff --git a/res/values/config.xml b/res/values/config.xml
index 0fa2b71..f81bf9b 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -67,4 +67,36 @@
     <!-- If the support features are enabled. -->
     <bool name="config_support_enabled">false</bool>
 
+    <!-- List containing the component names of pre-installed screen reader services. -->
+    <string-array name="config_preinstalled_screen_reader_services" translatable="false">
+        <!--
+        <item>com.example.package.first/com.example.class.FirstService</item>
+        <item>com.example.package.second/com.example.class.SecondService</item>
+        -->
+    </string-array>
+
+    <!-- List containing the component names of pre-installed audio and captioning services. -->
+    <string-array name="config_preinstalled_audio_and_caption_services" translatable="false">
+        <!--
+        <item>com.example.package.first/com.example.class.FirstService</item>
+        <item>com.example.package.second/com.example.class.SecondService</item>
+        -->
+    </string-array>
+
+    <!-- List containing the component names of pre-installed display services. -->
+    <string-array name="config_preinstalled_display_services" translatable="false">
+        <!--
+        <item>com.example.package.first/com.example.class.FirstService</item>
+        <item>com.example.package.second/com.example.class.SecondService</item>
+        -->
+    </string-array>
+
+    <!-- List containing the component names of pre-installed interaction control services. -->
+    <string-array name="config_preinstalled_interaction_control_services" translatable="false">
+        <!--
+        <item>com.example.package.first/com.example.class.FirstService</item>
+        <item>com.example.package.second/com.example.class.SecondService</item>
+        -->
+    </string-array>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 79042c4..35d2b87 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1595,18 +1595,21 @@
     <!-- Checkbox title for option to notify user when open networks are nearby -->
     <string name="wifi_notify_open_networks">Open network notification</string>
     <!-- Checkbox summary for option to notify user when open networks are nearby-->
-    <string name="wifi_notify_open_networks_summary">Notify whenever a high quality open network is available</string>
+    <string name="wifi_notify_open_networks_summary">Notify when a high\u2011quality open network is available that may require sign\u2011in</string>
     <!-- Checkbox title for option to enable Wi-Fi when saved networks are nearby -->
     <string name="wifi_wakeup">Turn Wi\u2011Fi back on</string>
-    <!-- Checkbox summary for option to enable Wi-Fi when saved networks are nearby-->
-    <string name="wifi_wakeup_summary">Automatically turn on Wi\u2011Fi near saved networks</string>
+    <!-- Checkbox summary for option to enable Wi-Fi when high quality saved networks are nearby-->
+    <string name="wifi_wakeup_summary">Automatically turn on Wi\u2011Fi near high\u2011quality saved networks</string>
     <!-- Checkbox title for option to toggle poor network detection -->
     <string name="wifi_poor_network_detection">Avoid poor connections</string>
     <!-- Checkbox summary for option to toggle poor network detection -->
     <string name="wifi_poor_network_detection_summary">Don\u2019t use a Wi\u2011Fi network unless it has a good Internet connection</string>
     <!-- Checkbox summary for option to toggle poor network detection [CHAR LIMIT=60] -->
     <string name="wifi_avoid_poor_network_detection_summary">Only use networks that have a good Internet connection</string>
-
+    <!-- Checkbox title for option to connect to open Wi-Fi automatically [CHAR LIMIT=40] -->
+    <string name="use_open_wifi_automatically_title">Connect to open networks</string>
+    <!-- Checkbox summary for option to connect to open Wi-Fi automatically  [CHAR LIMIT=100] -->
+    <string name="use_open_wifi_automatically_summary">Automatically connect to high\u2011quality open networks </string>
     <!-- Preference title for option to install certificates -->
     <string name="wifi_install_credentials">Install certificates</string>
     <!-- Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off. The
@@ -2961,7 +2964,7 @@
     <!-- Tethering controls, item title to go into the tethering settings when USB and Bluetooth tethering are available [CHAR LIMIT=25]-->
     <string name="tether_settings_title_usb_bluetooth">Tethering</string>
     <!-- Tethering controls, item title to go into the tethering settings when USB, Bluetooth and Wifi tethering are available [CHAR LIMIT=50]-->
-    <string name="tether_settings_title_all">Hotspot &amp; Tethering</string>
+    <string name="tether_settings_title_all">Hotspot &amp; tethering</string>
     <!-- Tethering setting summary when both Wi-Fi hotspot and tether are turned on [CHAR LIMIT=NONE]-->
     <string name="tether_settings_summary_hotspot_on_tether_on">Hotspot on, tethering</string>
     <!-- Tethering setting summary when Wi-Fi hotspot is on and tether is off [CHAR LIMIT=NONE]-->
@@ -3019,7 +3022,7 @@
     <string name="tethering_help_button_text">Help</string>
 
     <!-- Wireless controls, item title to go into the network settings -->
-    <string name="network_settings_title">Mobile networks</string>
+    <string name="network_settings_title">Mobile network</string>
 
     <!-- Mobile plan [CHAR LIMIT=35]-->
     <string name="manage_mobile_plan_title" translatable="true">Mobile plan</string>
@@ -3031,7 +3034,7 @@
     <string name="sms_change_default_no_previous_dialog_text" translatable="true">Use <xliff:g id="new_app">%s</xliff:g> as your SMS app?</string>
 
     <!-- Network Scorer Picker title [CHAR LIMIT=40]-->
-    <string name="network_scorer_picker_title">Network Scorer</string>
+    <string name="network_scorer_picker_title">Network Rating Provider</string>
     <string name="network_scorer_picker_none_preference">None</string>
 
     <!-- Wifi Assistant change wi-fi assistant title.  [CHAR LIMIT=40] -->
@@ -3997,22 +4000,30 @@
     <string name="vision_settings_title">Vision Settings</string>
     <!-- Settings description for a brief version of Vision-Related Accessibility Settings. Tells the user that they can adjust these settings now to help them through the remainder of the Setup Wizard and that they can later be changed in Settings. Displayed in Setup Wizard only. [CHAR LIMIT=none] -->
     <string name="vision_settings_description">You can customize this device to fit your needs. These accessibility features can be changed later in Settings.</string>
-    <!-- Title for the accessibility preference category of accessibility services. [CHAR LIMIT=25] -->
-    <string name="accessibility_services_title">Services</string>
+    <!-- Title for the accessibility preference category of screen reader services and settings. [CHAR LIMIT=50] -->
+    <string name="screen_reader_category_title">Screen readers</string>
+    <!-- Title for the accessibility preference category of audio services and settings. [CHAR LIMIT=50] -->
+    <string name="audio_and_captions_category_title">Audio &amp; on-screen text</string>
+    <!-- Title for the accessibility preference category of display services and settings. [CHAR LIMIT=50] -->
+    <string name="display_category_title">Display</string>
+    <!-- Title for the accessibility preference category of interaction control services and settings. [CHAR LIMIT=50] -->
+    <string name="interaction_control_category_title">Interaction controls</string>
+    <!-- Title for the accessibility preference category of services downloaded by the user. [CHAR LIMIT=50] -->
+    <string name="user_installed_services_category_title">Downloaded services</string>
+    <!-- Title for the accessibility preference category of settings considered to be experimental, meaning they might be changed or removed in the future. [CHAR LIMIT=50] -->
+    <string name="experimental_category_title">Experimental</string>
     <!-- Title for the Talkback Accessibility Service. Displayed on the Accessibility Settings screen in Setup Wizard. [CHAR_LIMIT=25] -->
     <string name="talkback_title">Talkback</string>
     <!-- Summary for the Talkback Accessibility Service. Lets the user know that Talkback is a screenreader and that it is usually most helpful to blind and low vision users and whether the service is on. [CHAR_LIMIT=none] -->
     <string name="talkback_summary">Screen reader primarily for people with blindness and low vision</string>
     <!-- Summary for the Select to Speak Accessibility Service. [CHAR_LIMIT=none] -->
     <string name="select_to_speak_summary">Tap items on your screen to hear them read aloud</string>
-    <!-- Title for the accessibility preference category of system related preferences. [CHAR LIMIT=25] -->
-    <string name="accessibility_system_title">System</string>
-    <!-- Title for the accessibility preference category of display related preferences. [CHAR LIMIT=25] -->
-    <string name="accessibility_display_title">Display</string>
     <!-- Title for the accessibility preference screen to enable video captioning. [CHAR LIMIT=35] -->
     <string name="accessibility_captioning_title">Captions</string>
     <!-- Title for the accessibility preference screen to enable screen magnification. [CHAR LIMIT=35] -->
-    <string name="accessibility_screen_magnification_title">Magnification gesture</string>
+    <string name="accessibility_screen_magnification_title">Magnification</string>
+    <!-- Summary for the accessibility preference to enable screen magnification. [CHAR LIMIT=25] -->
+    <string name="accessibility_preference_magnification_summary">Zoom in on screen</string>
     <!-- Short summary for Magnification gesture. Tells the user that this feature allows the user to magnify the screen by tapping 3 times. Appears in accessibility portion of setup wizard -->
     <string name="accessibility_screen_magnification_short_summary">Tap 3 times to zoom</string>
     <!-- Summary for the accessibility preference screen to enable screen magnification. [CHAR LIMIT=none] -->
@@ -4040,7 +4051,7 @@
     <!-- Title for the accessibility preference to configure display color inversion. [CHAR LIMIT=NONE] -->
     <string name="accessibility_display_inversion_preference_title">Color inversion</string>
     <!-- Subtitle for the accessibility preference to configure display color inversion. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_display_inversion_preference_subtitle">(Experimental) May affect performance</string>
+    <string name="accessibility_display_inversion_preference_subtitle">May affect performance</string>
     <!-- Title for accessibility preference for configuring feature that performs click action soon after mouse/trackpad pointer stops moving. [CHAR LIMIT=NONE] -->
     <string name="accessibility_autoclick_preference_title">Click after pointer stops moving</string>
     <!-- Title for accessibility preference for configuring amount of time that has to pass after pointer stops moving before click action can be performed (if automatic click after pointer stops moving feature is enabled). [CHAR LIMIT=NONE] -->
@@ -4199,10 +4210,10 @@
     <string name="disable_service_message">Tapping OK will
         stop <xliff:g id="service" example="TalkBack">%1$s</xliff:g>.</string>
 
-    <!-- Title for the prompt shown as a placeholder if no accessibility serivices are installed. [CHAR LIMIT=50] -->
+    <!-- Title for the prompt shown as a placeholder if no accessibility services are installed. [CHAR LIMIT=50] -->
     <string name="accessibility_no_services_installed">No services installed</string>
 
-    <!-- Default description for an accessibility serivice if the latter doesn't provide one. [CHAR LIMIT=NONE] -->
+    <!-- Default description for an accessibility service if the latter doesn't provide one. [CHAR LIMIT=NONE] -->
     <string name="accessibility_service_default_description">No description provided.</string>
 
     <!-- Accessibility settings: button for lauching settings for an accessibility service -->
@@ -4223,7 +4234,7 @@
     <!-- Title for print service settings screen [CHAR LIMIT=25] -->
     <string name="print_settings_title">Print services</string>
 
-    <!-- Title for the prompt shown as a placeholder if no print serivices are installed. [CHAR LIMIT=50] -->
+    <!-- Title for the prompt shown as a placeholder if no print services are installed. [CHAR LIMIT=50] -->
     <string name="print_no_services_installed">No services installed</string>
 
     <!-- Title for the prompt shown as a placeholder if no printers are found while searching. [CHAR LIMIT=50] -->
@@ -5470,6 +5481,8 @@
     <string name="vpn_no_network">There is no network connection. Please try again later.</string>
     <!-- Toast message when VPN has disconnected automatically due to Clear credentials. [CHAR LIMIT=NONE] -->
     <string name="vpn_disconnected">Disconnected from VPN</string>
+    <!-- Seting subtext indicating the device is not currently connected to any VPN [CHAR LIMIT=40]-->
+    <string name="vpn_disconnected_summary">None</string>
     <!-- Toast message when a certificate is missing. [CHAR LIMIT=100] -->
     <string name="vpn_missing_cert">A certificate is missing. Please edit the profile.</string>
 
@@ -6052,11 +6065,6 @@
         <xliff:g name="first_item">%1$s</xliff:g> > <xliff:g name="second_item">%2$s</xliff:g>
     </string>
 
-    <!-- Text used to identify the search query suggestions / recent searches -->
-    <string name="search_recents_queries_label">Recent searches</string>
-    <!-- Text used to identify the search results -->
-    <string name="search_results_label">Results</string>
-
     <!--Search Keywords [CHAR LIMIT=NONE]-->
     <string name="keywords_wifi">wifi, wi-fi, network connection</string>
     <string name="keywords_more_default_sms_app">text message, texting, messages, messaging</string>
@@ -6333,7 +6341,14 @@
     <string name="lock_screen_notifications_interstitial_title_profile">Profile notifications</string>
 
     <!-- Notification Settings: Title for the option managing notifications per application. [CHAR LIMIT=30] -->
-    <string name="app_notifications_title">App notifications</string>
+    <string name="app_notifications_title">Notifications</string>
+
+    <!-- Notification Settings: Summary for the option managing notifications per application. [CHAR LIMIT=60] -->
+    <plurals name="app_notifications_summary">
+        <item quantity="zero">On for all apps</item>
+        <item quantity="one">Off for 1 app</item>
+        <item quantity="other">Off for <xliff:g id="off_count" example="10">%d</xliff:g> apps</item>
+    </plurals>
 
     <!-- [CHAR LIMIT=100] Notification importance slider title -->
     <string name="notification_importance_title">Importance</string>
@@ -6465,8 +6480,14 @@
     <!-- [CHAR LIMIT=NONE] App notification settings: no channels -->
     <string name="no_channels">This app has not posted any notifications</string>
 
-    <!-- [CHAR LIMIT=60] App notification settings: Text to display for deleted channels -->
-    <string name="deleted_channel_name"><xliff:g id="channel_name" example="Promotions">%1$s</xliff:g> (deleted)</string>
+    <!-- [CHAR LIMIT=NONE] App notification settings: link to app notification settings-->
+    <string name="app_settings_link">Additional settings in the app</string>
+
+    <!-- [CHAR LIMIT=NONE] Footer listing a count of deleted channels. -->
+    <plurals name="deleted_channels">
+        <item quantity="one">%d category deleted</item>
+        <item quantity="other">%d categories deleted</item>
+    </plurals>
 
     <!-- [CHAR LIMIT=NONE] App notification settings: Block option title -->
     <string name="app_notification_block_title">Block all</string>
@@ -6949,11 +6970,8 @@
 
    <!-- Label for list that shows all permissions -->
    <string name="app_permissions">App permissions</string>
-   <!-- Summary of permissions currently granted to apps [CHAR LIMIT=45] -->
-   <string name="app_permissions_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed additional access</string>
-   <!-- Summary of number of apps currently granted a single permission [CHAR
-        LIMIT=45] -->
-   <string name="app_permissions_group_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed</string>
+   <!-- Summary of permissions currently granted to apps [CHAR LIMIT=60] -->
+   <string name="app_permissions_summary">Apps using <xliff:g id="apps" example="location">%1$s</xliff:g></string>
 
    <!-- Label for tap to wake setting [CHAR LIMIT=30] -->
    <string name="tap_to_wake">Tap to wake</string>
@@ -6975,9 +6993,9 @@
 
     <!-- Description of settings item that leads to list of all apps that can open web links [CHAR LIMIT=NONE] -->
     <plurals name="domain_urls_apps_summary">
-        <item quantity="zero">No app can open any supported link</item>
-        <item quantity="one">One app can open its supported links</item>
-        <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps can open their supported links</item>
+        <item quantity="zero">No app opening supported links</item>
+        <item quantity="one">One app opening supported links</item>
+        <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps opening supported links</item>
     </plurals>
 
     <!-- Explanation that the app that will ALWAYS be launched to open web links to domains that it understands -->
@@ -7178,7 +7196,7 @@
     <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring files via MTP. -->
-    <string name="usb_use_file_transfers">Transfer files</string>
+    <string name="usb_use_file_transfers">Transferring files</string>
     <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring files via MTP. -->
@@ -7186,7 +7204,7 @@
     <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring photos via PTP. -->
-    <string name="usb_use_photo_transfers">Transfer photos (PTP)</string>
+    <string name="usb_use_photo_transfers">Transferring photos (PTP)</string>
     <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring photos via PTP. -->
@@ -7194,7 +7212,7 @@
     <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for entering MIDI mode. -->
-    <string name="usb_use_MIDI">Use device as MIDI</string>
+    <string name="usb_use_MIDI">Using device as MIDI</string>
     <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for entering MIDI mode. -->
@@ -7206,8 +7224,6 @@
 
     <!-- Settings item title for USB preference [CHAR LIMIT=35] -->
     <string name="usb_pref">USB</string>
-    <!-- Summary for the USB preference when nothing is connected through the usb port.  [CHAR LIMIT=40] -->
-    <string name="usb_nothing_connected">Nothing connected</string>
 
     <!-- Settings item title for background check prefs [CHAR LIMIT=35] -->
     <string name="background_check_pref">Background check</string>
@@ -7332,38 +7348,38 @@
     <string name="accessibility_lock_screen_progress"><xliff:g id="count" example="1">%1$d</xliff:g> of <xliff:g id="count" example="1">%2$d</xliff:g> characters used</string>
 
     <!-- System Alert Window settings -->
-    <!-- Title of Draw Overlay preference item [CHAR LIMIT=55] -->
-    <string name="draw_overlay_title">Apps that can draw over other apps</string>
-    <!-- Title of draw overlay screen [CHAR LIMIT=30] -->
-    <string name="draw_overlay">Draw over other apps</string>
+    <!-- Title of display overlay preference item [CHAR LIMIT=55] -->
+    <string name="draw_overlay_title">Apps that can display over other apps</string>
+    <!-- Title of display overlay screen [CHAR LIMIT=30] -->
+    <string name="draw_overlay">Display over other apps</string>
     <!-- Settings title in main settings screen for SYSTEM_ALERT_WINDOW [CHAR LIMIT=45] -->
-    <string name="system_alert_window_settings">Draw over other apps</string>
+    <string name="system_alert_window_settings">Display over other apps</string>
     <!-- Title for the apps with SYSTEM_ALERT_WINDOW permission/privilege [CHAR LIMIT=20] -->
     <string name="system_alert_window_apps_title">Apps</string>
-    <!-- Title for the apps that are allowed to draw on top of other apps [CHAR LIMIT=60] -->
-    <string name="system_alert_window_access_title">Draw over other apps</string>
-    <!-- Label for setting which controls whether app can draw overlays [CHAR LIMIT=45] -->
-    <string name="permit_draw_overlay">Permit drawing over other apps</string>
+    <!-- Title for the apps that are allowed to display over other apps [CHAR LIMIT=60] -->
+    <string name="system_alert_window_access_title">Display over other apps</string>
+    <!-- Label for setting which controls whether app can display over other apps [CHAR LIMIT=45] -->
+    <string name="permit_draw_overlay">Allow display over other apps</string>
     <!-- Link to the apps page for SYSTEM_ALERT_WINDOW settings [CHAR LIMIT=45] -->
-    <string name="app_overlay_permission_preference">App draw on top permission</string>
+    <string name="app_overlay_permission_preference">App display on top permission</string>
     <!-- Description of allowing overlay setting [CHAR LIMIT=NONE] -->
-    <string name="allow_overlay_description">This permission allows an app to display on top of other apps you\u2019re using and may interfere with your use of the interface in other applications, or change what you think you are seeing in other applications.</string>
+    <string name="allow_overlay_description">This feature allows an app to display on top of other apps you\u2019re using. It may interfere with your use of those apps or change the way they seem to appear or behave.</string>
 
     <!-- Keyword for VR setting -->
     <string name="keywords_vr_listener">vr virtual reality listener stereo helper service</string>
     <!-- Keyword for SYSTEM_ALERT_WINDOW -->
-    <string name="keywords_system_alert_window">system alert window dialog draw on top other apps</string>
+    <string name="keywords_system_alert_window">system alert window dialog display on top other apps</string>
     <!-- Main settings screen item's title to go into the overlay settings screen [CHAR LIMIT=30] -->
-    <string name="overlay_settings">Draw over other apps</string>
+    <string name="overlay_settings">Display over other apps</string>
 
-    <!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] -->
-    <string name="system_alert_window_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed to draw on top of other apps</string>
+    <!-- Summary of number of apps currently can display over other apps [CHAR LIMIT=60] -->
+    <string name="system_alert_window_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed to display over other apps</string>
 
-    <!-- Label for showing apps that can draw overlays [CHAR LIMIT=45] -->
+    <!-- Label for showing apps that can display over other apps [CHAR LIMIT=45] -->
     <string name="filter_overlay_apps">Apps with permission</string>
-    <!-- Summary of app allowed to draw overlay [CHAR LIMIT=60] -->
+    <!-- Summary of app allowed to display over other apps [CHAR LIMIT=60] -->
     <string name="system_alert_window_on">Yes</string>
-    <!-- Summary of app not allowed to draw overlay [CHAR LIMIT=60] -->
+    <!-- Summary of app not allowed to display over other apps [CHAR LIMIT=60] -->
     <string name="system_alert_window_off">No</string>
 
     <!-- Title for settings screen for controlling apps that can install other apps on device [CHAR LIMIT=50] -->
@@ -7376,7 +7392,7 @@
     <string name="write_settings">Modify system settings</string>
     <!-- Keyword for WRITE_SETTINGS -->
     <string name="keywords_write_settings">write modify system settings</string>
-    <!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] -->
+    <!-- Summary of number of apps currently can write system settings [CHAR LIMIT=60] -->
     <string name="write_settings_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed to modify system settings</string>
 
     <!-- Label for showing apps that can install other apps [CHAR LIMIT=45] -->
@@ -7453,7 +7469,7 @@
     <string name="screen_zoom_conversation_timestamp_4">Tue 6:03PM</string>
 
     <!-- Wi-Fi state - Disconnected [CHAR LIMIT=NONE] -->
-    <string name="disconnected">Disconnected</string>
+    <string name="disconnected">Not connected</string>
 
     <!-- Summary of data usage [CHAR LIMIT=NONE] -->
     <string name="data_usage_summary_format"><xliff:g id="amount" example="50%">%1$s</xliff:g> of data used</string>
@@ -7866,8 +7882,8 @@
     <!-- Title of screen controlling which apps have access to send premium SMS messages [CHAR LIMIT=60] -->
     <string name="premium_sms_access">Premium SMS access</string>
 
-    <!-- [CHAR LIMIT=25] Bluetooth is disabled. -->
-    <string name="bluetooth_disabled">Disabled</string>
+    <!-- Bluetooth is disabled. -->
+    <string name="bluetooth_disabled">Not visible to other devices</string>
 
     <!-- [CHAR LIMIT=60] Name of dev option called "System UI demo mode" -->
     <string name="demo_mode">System UI demo mode</string>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 99e2133..7ca109b 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -77,6 +77,10 @@
         <item name="android:windowAnimationStyle">@null</item>
     </style>
 
+    <style name="SuwSuggestionThemeGlif.Light" parent="SuwThemeGlif.Light">
+        <item name="android:windowAnimationStyle">@android:style/Animation.Activity</item>
+    </style>
+
     <style name="PreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
         <!-- Parent path frameworks/support/v14/preference/res/values/themes.xml -->
         <item name="android:scrollbars">vertical</item>
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index ee21017..5c67d6e 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -18,43 +18,61 @@
         android:title="@string/accessibility_settings"
         android:persistent="true">
 
+    <ListPreference
+            android:key="accessibility_shortcut_preference"
+            android:title="@string/accessibility_global_gesture_preference_title"/>
+
     <PreferenceCategory
-            android:key="services_category"
-            android:title="@string/accessibility_services_title">
+            android:key="user_installed_services_category"
+            android:title="@string/user_installed_services_category_title">
     </PreferenceCategory>
 
     <PreferenceCategory
-            android:key="system_category"
-            android:title="@string/accessibility_system_title">
+            android:key="screen_reader_category"
+            android:title="@string/screen_reader_category_title">
 
         <Preference
-            android:fragment="com.android.settings.accessibility.CaptionPropertiesFragment"
-            android:key="captioning_preference_screen"
-            android:title="@string/accessibility_captioning_title" />
+                android:key="tts_settings_preference"
+                android:fragment="com.android.settings.tts.TextToSpeechSettings"
+                android:title="@string/tts_settings_title"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+            android:key="display_category"
+            android:title="@string/display_category_title">
 
         <Preference
-            android:fragment="com.android.settings.accessibility.ToggleScreenMagnificationPreferenceFragment"
-            android:key="screen_magnification_preference_screen"
-            android:title="@string/accessibility_screen_magnification_title"/>
-
-        <Preference
-            android:fragment="com.android.settings.accessibility.ToggleFontSizePreferenceFragment"
-            android:key="font_size_preference_screen"
-            android:title="@string/title_font_size"/>
+                android:fragment="com.android.settings.accessibility.ToggleFontSizePreferenceFragment"
+                android:key="font_size_preference_screen"
+                android:title="@string/title_font_size"/>
 
         <com.android.settings.display.ScreenZoomPreference
-            android:key="screen_zoom"
-            android:title="@string/screen_zoom_title"/>
+                android:key="screen_zoom"
+                android:title="@string/screen_zoom_title"/>
 
         <Preference
-            android:fragment="com.android.settings.accessibility.ToggleAutoclickPreferenceFragment"
-            android:key="autoclick_preference_screen"
-            android:title="@string/accessibility_autoclick_preference_title"/>
+                android:fragment="com.android.settings.accessibility.ToggleScreenMagnificationPreferenceFragment"
+                android:key="screen_magnification_preference_screen"
+                android:title="@string/accessibility_screen_magnification_title" />
+
+        <Preference
+                android:fragment="com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment"
+                android:key="daltonizer_preference_screen"
+                android:title="@string/accessibility_display_daltonizer_preference_title" />
 
         <SwitchPreference
-                android:key="toggle_high_text_contrast_preference"
-                android:title="@string/accessibility_toggle_high_text_contrast_preference_title"
-                android:summary="@string/experimental_preference"/>
+                android:key="toggle_large_pointer_icon"
+                android:title="@string/accessibility_toggle_large_pointer_icon_title" />
+    </PreferenceCategory>
+
+    <PreferenceCategory
+            android:key="interaction_control_category"
+            android:title="@string/interaction_control_category_title">
+
+        <Preference
+                android:fragment="com.android.settings.accessibility.ToggleAutoclickPreferenceFragment"
+                android:key="autoclick_preference_screen"
+                android:title="@string/accessibility_autoclick_preference_title"/>
 
         <SwitchPreference
                 android:key="toggle_power_button_ends_call_preference"
@@ -66,9 +84,17 @@
                 android:title="@string/accelerometer_title"
                 android:persistent="false"/>
 
-        <SwitchPreference
-                android:key="toggle_large_pointer_icon"
-                android:title="@string/accessibility_toggle_large_pointer_icon_title" />
+        <ListPreference
+                android:key="select_long_press_timeout_preference"
+                android:title="@string/accessibility_long_press_timeout_preference_title"
+                android:entries="@array/long_press_timeout_selector_titles"
+                android:entryValues="@array/long_press_timeout_selector_values"
+                android:persistent="false"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+            android:key="audio_and_captions_category"
+            android:title="@string/audio_and_captions_category_title">
 
         <SwitchPreference
                 android:key="toggle_master_mono"
@@ -76,35 +102,24 @@
                 android:summary="@string/accessibility_toggle_master_mono_summary"
                 android:persistent="false"/>
 
-        <ListPreference
-                android:key="accessibility_shortcut_preference"
-                android:title="@string/accessibility_global_gesture_preference_title"/>
-
         <Preference
-                android:key="tts_settings_preference"
-                android:fragment="com.android.settings.tts.TextToSpeechSettings"
-                android:title="@string/tts_settings_title"/>
-
-        <ListPreference android:key="select_long_press_timeout_preference"
-                android:title="@string/accessibility_long_press_timeout_preference_title"
-                android:entries="@array/long_press_timeout_selector_titles"
-                android:entryValues="@array/long_press_timeout_selector_values"
-                android:persistent="false"/>
-
+                android:fragment="com.android.settings.accessibility.CaptionPropertiesFragment"
+                android:key="captioning_preference_screen"
+                android:title="@string/accessibility_captioning_title" />
     </PreferenceCategory>
 
     <PreferenceCategory
-        android:key="display_category"
-        android:title="@string/accessibility_display_title" >
-        <SwitchPreference
-            android:key="toggle_inversion_preference"
-            android:title="@string/accessibility_display_inversion_preference_title"
-            android:summary="@string/accessibility_display_inversion_preference_subtitle"
-            android:persistent="false" />
-        <Preference
-            android:fragment="com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment"
-            android:key="daltonizer_preference_screen"
-            android:title="@string/accessibility_display_daltonizer_preference_title" />
-    </PreferenceCategory>
+            android:key="experimental_category"
+            android:title="@string/experimental_category_title">
 
+        <SwitchPreference
+                android:key="toggle_high_text_contrast_preference"
+                android:title="@string/accessibility_toggle_high_text_contrast_preference_title" />
+
+        <SwitchPreference
+                android:key="toggle_inversion_preference"
+                android:title="@string/accessibility_display_inversion_preference_title"
+                android:summary="@string/accessibility_display_inversion_preference_subtitle"
+                android:persistent="false" />
+    </PreferenceCategory>
 </PreferenceScreen>
diff --git a/res/xml/channel_notification_settings.xml b/res/xml/channel_notification_settings.xml
index 4af9fe8..fde1bfa 100644
--- a/res/xml/channel_notification_settings.xml
+++ b/res/xml/channel_notification_settings.xml
@@ -23,29 +23,25 @@
         android:key="block"
         android:title="@string/app_notification_block_title"
         android:summary="@string/app_notification_block_summary"
-        android:order="1"
         settings:useAdditionalSummary="true"
         settings:restrictedSwitchSummary="@string/enabled_by_admin" />
 
     <!-- Importance -->
     <com.android.settings.notification.RestrictedDropDownPreference
         android:key="importance"
-        android:title="@string/notification_importance_title"
-        android:order="2"/>
+        android:title="@string/notification_importance_title" />
 
     <!-- Default ringtone -->
     <com.android.settings.notification.DefaultNotificationTonePreference
         android:key="ringtone"
         android:title="@string/notification_ringtone_title"
         android:dialogTitle="@string/notification_ringtone_title"
-        android:order="3"
         android:ringtoneType="notification" />
 
     <!-- Vibration -->
     <com.android.settingslib.RestrictedSwitchPreference
         android:key="vibrate"
         android:title="@string/notification_vibrate_title"
-        android:order="4"
         settings:useAdditionalSummary="true" />
 
     <!-- Show badge -->
@@ -53,7 +49,6 @@
         android:key="badge"
         android:title="@string/notification_badge_title"
         android:summary="@string/notification_badge_summary"
-        android:order="5"
         settings:useAdditionalSummary="true"
         settings:restrictedSwitchSummary="@string/enabled_by_admin" />
 
@@ -61,21 +56,18 @@
     <com.android.settingslib.RestrictedSwitchPreference
         android:key="lights"
         android:title="@string/notification_show_lights_title"
-        android:order="6"
         settings:useAdditionalSummary="true" />
 
     <!-- Visibility Override -->
     <com.android.settings.notification.RestrictedDropDownPreference
             android:key="visibility_override"
-            android:title="@string/app_notification_visibility_override_title"
-            android:order="7" />
+            android:title="@string/app_notification_visibility_override_title" />
 
     <!-- Bypass DND -->
     <com.android.settingslib.RestrictedSwitchPreference
             android:key="bypass_dnd"
             android:title="@string/app_notification_override_dnd_title"
             android:summary="@string/app_notification_override_dnd_summary"
-            android:order="8"
             settings:useAdditionalSummary="true" />
 
 </PreferenceScreen>
diff --git a/res/xml/ia_display_settings.xml b/res/xml/display_settings.xml
similarity index 100%
rename from res/xml/ia_display_settings.xml
rename to res/xml/display_settings.xml
diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml
index adfff63..ea85d53 100644
--- a/res/xml/location_settings.xml
+++ b/res/xml/location_settings.xml
@@ -44,7 +44,7 @@
             </intent>
         </Preference>
 
-        <com.android.settings.DividedCategory
+        <PreferenceCategory
             android:key="recent_location_requests"
             android:title="@string/location_category_recent_location_requests" />
 
diff --git a/res/xml/ia_sound_settings.xml b/res/xml/sound_settings.xml
similarity index 100%
rename from res/xml/ia_sound_settings.xml
rename to res/xml/sound_settings.xml
diff --git a/res/xml/wifi_configure_settings.xml b/res/xml/wifi_configure_settings.xml
index 8e51009..fa5239d 100644
--- a/res/xml/wifi_configure_settings.xml
+++ b/res/xml/wifi_configure_settings.xml
@@ -26,15 +26,21 @@
             android:summary="@string/wifi_wakeup_summary" />
 
     <SwitchPreference
+        android:key="use_open_wifi_automatically"
+        android:icon="@drawable/ic_vpn_key"
+        android:title="@string/use_open_wifi_automatically_title"
+        android:summary="@string/use_open_wifi_automatically_summary" />
+
+    <SwitchPreference
             android:key="notify_open_networks"
             android:title="@string/wifi_notify_open_networks"
             android:icon="@drawable/ic_settings_notifications"
             android:summary="@string/wifi_notify_open_networks_summary" />
 
     <SwitchPreference
-            android:key="wifi_cellular_data_fallback"
-            android:title="@string/wifi_cellular_data_fallback_title"
-            android:summary="@string/wifi_cellular_data_fallback_summary"/>
+        android:key="wifi_cellular_data_fallback"
+        android:title="@string/wifi_cellular_data_fallback_title"
+        android:summary="@string/wifi_cellular_data_fallback_summary"/>
 
     <ListPreference
             android:key="sleep_policy"
diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java b/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java
index e8880ae..489dbbe 100644
--- a/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java
+++ b/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java
@@ -22,16 +22,23 @@
 import android.os.UserManager;
 import android.view.MenuItem;
 import android.view.WindowManager;
+import android.widget.LinearLayout;
 
 public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivity {
 
     private static final String STATE_IS_KEYGUARD_LOCKED = "STATE_IS_KEYGUARD_LOCKED";
 
+    enum ConfirmCredentialTheme {
+        INTERNAL,
+        DARK,
+        WORK
+    }
+
     private boolean mRestoring;
-    private boolean mDark;
     private boolean mEnterAnimationPending;
     private boolean mFirstTimeVisible = true;
     private boolean mIsKeyguardLocked = false;
+    private ConfirmCredentialTheme mConfirmCredentialTheme;
 
     @Override
     protected void onCreate(Bundle savedState) {
@@ -39,12 +46,24 @@
                 Utils.getUserIdFromBundle(this, getIntent().getExtras()));
         if (UserManager.get(this).isManagedProfile(credentialOwnerUserId)) {
             setTheme(R.style.Theme_ConfirmDeviceCredentialsWork);
+            mConfirmCredentialTheme = ConfirmCredentialTheme.WORK;
         } else if (getIntent().getBooleanExtra(
                 ConfirmDeviceCredentialBaseFragment.DARK_THEME, false)) {
             setTheme(R.style.Theme_ConfirmDeviceCredentialsDark);
-            mDark = true;
+            mConfirmCredentialTheme = ConfirmCredentialTheme.DARK;
+        } else {
+            setTheme(R.style.SetupWizardTheme_Light);
+            mConfirmCredentialTheme = ConfirmCredentialTheme.INTERNAL;
         }
         super.onCreate(savedState);
+
+        if (mConfirmCredentialTheme == ConfirmCredentialTheme.INTERNAL) {
+            // Prevent the content parent from consuming the window insets because GlifLayout uses
+            // it to show the status bar background.
+            LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
+            layout.setFitsSystemWindows(false);
+        }
+
         mIsKeyguardLocked = savedState == null
                 ? getSystemService(KeyguardManager.class).isKeyguardLocked()
                 : savedState.getBoolean(STATE_IS_KEYGUARD_LOCKED, false);
@@ -85,7 +104,8 @@
     @Override
     public void onResume() {
         super.onResume();
-        if (!isChangingConfigurations() && !mRestoring && mDark && mFirstTimeVisible) {
+        if (!isChangingConfigurations() && !mRestoring
+                && mConfirmCredentialTheme == ConfirmCredentialTheme.DARK && mFirstTimeVisible) {
             mFirstTimeVisible = false;
             prepareEnterAnimation();
             mEnterAnimationPending = true;
@@ -116,4 +136,8 @@
     public void startEnterAnimation() {
         getFragment().startEnterAnimation();
     }
+
+    public ConfirmCredentialTheme getConfirmCredentialTheme() {
+        return mConfirmCredentialTheme;
+    }
 }
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index 6b55add..5c86dcd 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -122,7 +122,14 @@
                 Bundle savedInstanceState) {
             final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
                     mEffectiveUserId);
-            View view = inflater.inflate(R.layout.confirm_lock_password, null);
+
+            ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
+            View view = inflater.inflate(
+                    activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.INTERNAL
+                            ? R.layout.confirm_lock_password_internal
+                            : R.layout.confirm_lock_password,
+                    container,
+                    false);
 
             mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
             mPasswordEntry.setOnEditorActionListener(this);
@@ -406,25 +413,22 @@
             }
             mDisappearing = true;
 
-            if (getActivity().getThemeResId() == R.style.Theme_ConfirmDeviceCredentialsDark) {
-                mDisappearAnimationUtils.startAnimation(getActiveViews(), new Runnable() {
-                    @Override
-                    public void run() {
-                        // Bail if there is no active activity.
-                        if (getActivity() == null || getActivity().isFinishing()) {
-                            return;
-                        }
-
-                        getActivity().setResult(RESULT_OK, intent);
-                        getActivity().finish();
-                        getActivity().overridePendingTransition(
-                                R.anim.confirm_credential_close_enter,
-                                R.anim.confirm_credential_close_exit);
-                    }
+            final ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
+            // Bail if there is no active activity.
+            if (activity == null || activity.isFinishing()) {
+                return;
+            }
+            if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
+                mDisappearAnimationUtils.startAnimation(getActiveViews(), () -> {
+                    activity.setResult(RESULT_OK, intent);
+                    activity.finish();
+                    activity.overridePendingTransition(
+                            R.anim.confirm_credential_close_enter,
+                            R.anim.confirm_credential_close_exit);
                 });
             } else {
-                getActivity().setResult(RESULT_OK, intent);
-                getActivity().finish();
+                activity.setResult(RESULT_OK, intent);
+                activity.finish();
             }
         }
 
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
index a224ac8..4b65163 100644
--- a/src/com/android/settings/ConfirmLockPattern.java
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -114,7 +114,13 @@
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            View view = inflater.inflate(R.layout.confirm_lock_pattern, null);
+            ConfirmLockPattern activity = (ConfirmLockPattern) getActivity();
+            View view = inflater.inflate(
+                    activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.INTERNAL
+                            ? R.layout.confirm_lock_pattern_internal
+                            : R.layout.confirm_lock_pattern,
+                    container,
+                    false);
             mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
             mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
             mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
@@ -343,27 +349,24 @@
             }
             mDisappearing = true;
 
-            if (getActivity().getThemeResId() == R.style.Theme_ConfirmDeviceCredentialsDark) {
+            final ConfirmLockPattern activity = (ConfirmLockPattern) getActivity();
+            // Bail if there is no active activity.
+            if (activity == null || activity.isFinishing()) {
+                return;
+            }
+            if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
                 mLockPatternView.clearPattern();
                 mDisappearAnimationUtils.startAnimation2d(getActiveViews(),
-                        new Runnable() {
-                            @Override
-                            public void run() {
-                                // Bail if there is no active activity.
-                                if (getActivity() == null || getActivity().isFinishing()) {
-                                    return;
-                                }
-
-                                getActivity().setResult(RESULT_OK, intent);
-                                getActivity().finish();
-                                getActivity().overridePendingTransition(
-                                        R.anim.confirm_credential_close_enter,
-                                        R.anim.confirm_credential_close_exit);
-                            }
+                        () -> {
+                            activity.setResult(RESULT_OK, intent);
+                            activity.finish();
+                            activity.overridePendingTransition(
+                                    R.anim.confirm_credential_close_enter,
+                                    R.anim.confirm_credential_close_exit);
                         }, this);
             } else {
-                getActivity().setResult(RESULT_OK, intent);
-                getActivity().finish();
+                activity.setResult(RESULT_OK, intent);
+                activity.finish();
             }
         }
 
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 6fbffe8..dd4c9a4 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -36,7 +36,6 @@
 import com.android.settings.display.TimeoutPreferenceController;
 import com.android.settings.display.VrDisplayPreferenceController;
 import com.android.settings.display.WallpaperPreferenceController;
-import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 
@@ -64,7 +63,7 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return R.xml.ia_display_settings;
+        return R.xml.display_settings;
     }
 
     @Override
@@ -104,7 +103,7 @@
                     final ArrayList<SearchIndexableResource> result = new ArrayList<>();
 
                     final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.ia_display_settings;
+                    sir.xmlResId = R.xml.display_settings;
                     result.add(sir);
                     return result;
                 }
diff --git a/src/com/android/settings/DividedCategory.java b/src/com/android/settings/DividedCategory.java
deleted file mode 100644
index d1c40c8..0000000
--- a/src/com/android/settings/DividedCategory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.settings;
-
-import android.content.Context;
-import android.support.v7.preference.PreferenceCategory;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.util.AttributeSet;
-
-/**
- * PreferenceCategory that allows a divider above it.
- */
-public class DividedCategory extends PreferenceCategory {
-
-    public DividedCategory(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        super.onBindViewHolder(holder);
-        holder.setDividerAllowedAbove(true);
-    }
-}
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 00a2c7d..73561a6 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -47,13 +47,10 @@
 import android.transition.TransitionManager;
 import android.util.Log;
 import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.Button;
-import android.widget.SearchView;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.settings.Settings.WifiSettingsActivity;
@@ -63,7 +60,6 @@
 import com.android.settings.core.instrumentation.SharedPreferencesLogger;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.DashboardSummary;
-import com.android.settings.dashboard.SearchResultsSummary;
 import com.android.settings.enterprise.EnterprisePrivacySettings;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.DynamicIndexableContentMonitor;
@@ -72,7 +68,6 @@
 import com.android.settings.widget.SwitchBar;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.SettingsDrawerActivity;
-import com.android.settingslib.drawer.Tile;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -81,9 +76,7 @@
 public class SettingsActivity extends SettingsDrawerActivity
         implements PreferenceManager.OnPreferenceTreeClickListener,
         PreferenceFragment.OnPreferenceStartFragmentCallback,
-        ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
-        SearchView.OnQueryTextListener, SearchView.OnCloseListener,
-        MenuItem.OnActionExpandListener {
+        ButtonBarHandler, FragmentManager.OnBackStackChangedListener {
 
     private static final String LOG_TAG = "Settings";
 
@@ -91,8 +84,6 @@
 
     // Constants for state save/restore
     private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
-    private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
-    private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
     private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
     private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
 
@@ -166,8 +157,6 @@
 
     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
 
-    private static final String EMPTY_QUERY = "";
-
     private static final int REQUEST_SUGGESTION = 42;
 
     private String mFragmentClass;
@@ -226,25 +215,15 @@
 
     private ViewGroup mContent;
 
-    private SearchView mSearchView;
-    private MenuItem mSearchMenuItem;
-    private boolean mSearchMenuItemExpanded = false;
-    private SearchResultsSummary mSearchResultsFragment;
     private SearchFeatureProvider mSearchFeatureProvider;
     private MetricsFeatureProvider mMetricsFeatureProvider;
 
     // Categories
     private ArrayList<DashboardCategory> mCategories = new ArrayList<>();
 
-    private boolean mNeedToRevertToInitialFragment = false;
-
     private DashboardFeatureProvider mDashboardFeatureProvider;
-    private Intent mResultIntentData;
     private ComponentName mCurrentSuggestion;
 
-    @VisibleForTesting
-    String mSearchQuery;
-
     public SwitchBar getSwitchBar() {
         return mSwitchBar;
     }
@@ -270,51 +249,12 @@
     }
 
     @Override
-    protected void onStart() {
-        super.onStart();
-
-        if (mNeedToRevertToInitialFragment) {
-            revertToInitialFragment();
-        }
-    }
-
-    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         if (!mDisplaySearch) {
             return false;
         }
 
-        MenuInflater inflater = getMenuInflater();
-        if (mSearchFeatureProvider.isEnabled(this)) {
-            mSearchFeatureProvider.setUpSearchMenu(menu, this);
-            return true;
-        }
-        inflater.inflate(R.menu.options_menu, menu);
-
-
-        // Cache the search query (can be overridden by the OnQueryTextListener)
-        final String query = mSearchQuery;
-
-        mSearchMenuItem = menu.findItem(R.id.search);
-        mSearchView = (SearchView) mSearchMenuItem.getActionView();
-
-        if (mSearchMenuItem == null || mSearchView == null) {
-            return false;
-        }
-
-        if (mSearchResultsFragment != null) {
-            mSearchResultsFragment.setSearchView(mSearchView);
-        }
-
-        mSearchMenuItem.setOnActionExpandListener(this);
-        mSearchView.setMaxWidth(Integer.MAX_VALUE);
-        mSearchView.setOnQueryTextListener(this);
-        mSearchView.setOnCloseListener(this);
-
-        if (mSearchMenuItemExpanded) {
-            mSearchMenuItem.expandActionView();
-        }
-        mSearchView.setQuery(query, true /* submit */);
+        mSearchFeatureProvider.setUpSearchMenu(menu, this);
         return true;
     }
 
@@ -417,8 +357,6 @@
         if (savedState != null) {
             // We are restarting from a previous saved state; used that to initialize, instead
             // of starting fresh.
-            mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
-            mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
             setTitleFromIntent(intent);
 
             ArrayList<DashboardCategory> categories =
@@ -430,7 +368,6 @@
             }
 
             mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
-            mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
 
         } else {
             launchSettingFragment(initialFragmentName, isSubSettings, intent);
@@ -456,21 +393,21 @@
                 Button backButton = (Button)findViewById(R.id.back_button);
                 backButton.setOnClickListener(new OnClickListener() {
                     public void onClick(View v) {
-                        setResult(RESULT_CANCELED, getResultIntentData());
+                        setResult(RESULT_CANCELED, null);
                         finish();
                     }
                 });
                 Button skipButton = (Button)findViewById(R.id.skip_button);
                 skipButton.setOnClickListener(new OnClickListener() {
                     public void onClick(View v) {
-                        setResult(RESULT_OK, getResultIntentData());
+                        setResult(RESULT_OK, null);
                         finish();
                     }
                 });
                 mNextButton = (Button)findViewById(R.id.next_button);
                 mNextButton.setOnClickListener(new OnClickListener() {
                     public void onClick(View v) {
-                        setResult(RESULT_OK, getResultIntentData());
+                        setResult(RESULT_OK, null);
                         finish();
                     }
                 });
@@ -614,20 +551,6 @@
         }
 
         outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
-        outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
-
-        if (mDisplaySearch) {
-            // The option menus are created if the ActionBar is visible and they are also created
-            // asynchronously. If you launch Settings with an Intent action like
-            // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
-            // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
-            // menu item and search view are null.
-            boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
-            outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
-
-            String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
-            outState.putString(SAVE_KEY_SEARCH_QUERY, query);
-        }
     }
 
     @Override
@@ -653,9 +576,6 @@
         }
         mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
 
-        if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
-            onQueryTextSubmit(mSearchQuery);
-        }
         updateTilesList();
     }
 
@@ -958,8 +878,10 @@
             final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
             synchronized (categories) {
                 for (DashboardCategory category : categories) {
-                    for (Tile tile : category.tiles) {
-                        ComponentName component = tile.intent.getComponent();
+                    final int tileCount = category.getTilesCount();
+                    for (int i = 0; i < tileCount; i++) {
+                        final ComponentName component = category.getTile(i).intent.getComponent();
+
                         final String name = component.getClassName();
                         final boolean isEnabledForRestricted = ArrayUtils.contains(
                                 SettingsGateway.SETTINGS_FOR_RESTRICTED, name);
@@ -1011,96 +933,6 @@
         return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
     }
 
-    @Deprecated
-    @Override
-    public boolean onQueryTextSubmit(String query) {
-        if (mSearchFeatureProvider.isEnabled(this)) {
-            return false;
-        }
-        mSearchQuery = query;
-        switchToSearchResultsFragmentIfNeeded();
-        return mSearchResultsFragment.onQueryTextSubmit(query);
-    }
-
-    @Deprecated
-    @Override
-    public boolean onQueryTextChange(String newText) {
-        mSearchQuery = newText;
-        if (mSearchFeatureProvider.isEnabled(this) || mSearchResultsFragment == null) {
-            return false;
-        }
-        return mSearchResultsFragment.onQueryTextChange(newText);
-    }
-
-    @Override
-    public boolean onClose() {
-        return false;
-    }
-
-    @Override
-    public boolean onMenuItemActionExpand(MenuItem item) {
-        if (item.getItemId() == mSearchMenuItem.getItemId()) {
-            switchToSearchResultsFragmentIfNeeded();
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onMenuItemActionCollapse(MenuItem item) {
-        if (item.getItemId() == mSearchMenuItem.getItemId()) {
-            if (mSearchMenuItemExpanded) {
-                revertToInitialFragment();
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public void onProfileTileOpen() {
-        if (!mIsShowingDashboard) {
-            finish();
-        }
-    }
-
-    @Deprecated
-    private void switchToSearchResultsFragmentIfNeeded() {
-        if (mSearchResultsFragment != null) {
-            return;
-        }
-        Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
-        if (current != null && current instanceof SearchResultsSummary) {
-            mSearchResultsFragment = (SearchResultsSummary) current;
-        } else {
-            setContentHeaderView(null);
-            mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
-                    SearchResultsSummary.class.getName(), null, false, true,
-                    R.string.search_results_title, null, true);
-        }
-        mSearchResultsFragment.setSearchView(mSearchView);
-        mSearchMenuItemExpanded = true;
-    }
-
-    @Deprecated
-    public void needToRevertToInitialFragment() {
-        mNeedToRevertToInitialFragment = true;
-    }
-
-    @Deprecated
-    private void revertToInitialFragment() {
-        mNeedToRevertToInitialFragment = false;
-        mSearchResultsFragment = null;
-        mSearchMenuItemExpanded = false;
-        getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
-                FragmentManager.POP_BACK_STACK_INCLUSIVE);
-        if (mSearchMenuItem != null) {
-            mSearchMenuItem.collapseActionView();
-        }
-    }
-
-    public Intent getResultIntentData() {
-        return mResultIntentData;
-    }
-
     public void startSuggestion(Intent intent) {
         if (intent == null || ActivityManager.isUserAMonkey()) {
             return;
@@ -1131,4 +963,4 @@
 
         return bitmap;
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index 807371e..86c340d 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -25,6 +25,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -35,7 +36,9 @@
 import android.support.v7.preference.ListPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceScreen;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -46,18 +49,17 @@
 import com.android.internal.view.RotationPolicy.RotationPolicyListener;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SingleLineSummaryPreference;
 import com.android.settings.Utils;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.accessibility.AccessibilityUtils;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -68,9 +70,20 @@
 public class AccessibilitySettings extends SettingsPreferenceFragment implements
         Preference.OnPreferenceChangeListener, Indexable {
 
+    // Index of the first preference in a preference category.
+    private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
+
     // Preference categories
-    private static final String SERVICES_CATEGORY = "services_category";
-    private static final String SYSTEM_CATEGORY = "system_category";
+    private static final String CATEGORY_SCREEN_READER = "screen_reader_category";
+    private static final String CATEGORY_AUDIO_AND_CAPTIONS = "audio_and_captions_category";
+    private static final String CATEGORY_DISPLAY = "display_category";
+    private static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
+    private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
+
+    private static final String[] CATEGORIES = new String[] {
+        CATEGORY_SCREEN_READER, CATEGORY_AUDIO_AND_CAPTIONS, CATEGORY_DISPLAY,
+        CATEGORY_INTERACTION_CONTROL, CATEGORY_DOWNLOADED_SERVICES
+    };
 
     // Preferences
     private static final String TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE =
@@ -118,10 +131,7 @@
     // ID for dialog that confirms shortcut capabilities
     private static final int DIALOG_ID_ADD_SHORTCUT_WARNING = 1;
 
-    // Auxiliary members.
-    static final Set<ComponentName> sInstalledServices = new HashSet<>();
-
-    private final Map<String, String> mLongPressTimeoutValuetoTitleMap = new HashMap<>();
+    private final Map<String, String> mLongPressTimeoutValueToTitleMap = new HashMap<>();
 
     private final Handler mHandler = new Handler();
 
@@ -129,7 +139,7 @@
         @Override
         public void run() {
             if (getActivity() != null) {
-                updateServicesPreferences();
+                updateServicePreferences();
             }
         }
     };
@@ -164,7 +174,7 @@
             new SettingsContentObserver(mHandler) {
                 @Override
                 public void onChange(boolean selfChange, Uri uri) {
-                    updateServicesPreferences();
+                    updateServicePreferences();
                 }
             };
 
@@ -175,9 +185,12 @@
         }
     };
 
-    // Preference controls.
-    private PreferenceCategory mServicesCategory;
-    private PreferenceCategory mSystemsCategory;
+    private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
+            new ArrayMap<>();
+    private final Map<Preference, PreferenceCategory> mServicePreferenceToPreferenceCategoryMap =
+            new ArrayMap<>();
+    private final Map<ComponentName, PreferenceCategory> mPreBundledServiceComponentToCategoryMap =
+            new ArrayMap<>();
 
     private SwitchPreference mToggleHighTextContrastPreference;
     private SwitchPreference mTogglePowerButtonEndsCallPreference;
@@ -260,7 +273,7 @@
         Settings.Secure.putInt(getContentResolver(),
                 Settings.Secure.LONG_PRESS_TIMEOUT, Integer.parseInt(stringValue));
         mSelectLongPressTimeoutPreference.setSummary(
-                mLongPressTimeoutValuetoTitleMap.get(stringValue));
+                mLongPressTimeoutValueToTitleMap.get(stringValue));
     }
 
     private void handleToggleInversionPreferenceChange(boolean checked) {
@@ -386,8 +399,10 @@
     }
 
     private void initializeAllPreferences() {
-        mServicesCategory = (PreferenceCategory) findPreference(SERVICES_CATEGORY);
-        mSystemsCategory = (PreferenceCategory) findPreference(SYSTEM_CATEGORY);
+        for (int i = 0; i < CATEGORIES.length; i++) {
+            PreferenceCategory prefCategory = (PreferenceCategory) findPreference(CATEGORIES[i]);
+            mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory);
+        }
 
         // Text contrast.
         mToggleHighTextContrastPreference =
@@ -402,14 +417,16 @@
                 (SwitchPreference) findPreference(TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE);
         if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
                 || !Utils.isVoiceCapable(getActivity())) {
-            mSystemsCategory.removePreference(mTogglePowerButtonEndsCallPreference);
+            mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL)
+                    .removePreference(mTogglePowerButtonEndsCallPreference);
         }
 
         // Lock screen rotation.
         mToggleLockScreenRotationPreference =
                 (SwitchPreference) findPreference(TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE);
         if (!RotationPolicy.isRotationSupported(getActivity())) {
-            mSystemsCategory.removePreference(mToggleLockScreenRotationPreference);
+            mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL)
+                    .removePreference(mToggleLockScreenRotationPreference);
         }
 
         // Large pointer icon.
@@ -424,7 +441,7 @@
         mSelectLongPressTimeoutPreference =
                 (ListPreference) findPreference(SELECT_LONG_PRESS_TIMEOUT_PREFERENCE);
         mSelectLongPressTimeoutPreference.setOnPreferenceChangeListener(this);
-        if (mLongPressTimeoutValuetoTitleMap.size() == 0) {
+        if (mLongPressTimeoutValueToTitleMap.size() == 0) {
             String[] timeoutValues = getResources().getStringArray(
                     R.array.long_press_timeout_selector_values);
             mLongPressTimeoutDefault = Integer.parseInt(timeoutValues[0]);
@@ -432,7 +449,7 @@
                     R.array.long_press_timeout_selector_titles);
             final int timeoutValueCount = timeoutValues.length;
             for (int i = 0; i < timeoutValueCount; i++) {
-                mLongPressTimeoutValuetoTitleMap.put(timeoutValues[i], timeoutTitles[i]);
+                mLongPressTimeoutValueToTitleMap.put(timeoutValues[i], timeoutTitles[i]);
             }
         }
 
@@ -459,17 +476,32 @@
     }
 
     private void updateAllPreferences() {
-        updateServicesPreferences();
         updateSystemPreferences();
+        updateServicePreferences();
     }
 
-    private void updateServicesPreferences() {
+    private void updateServicePreferences() {
         // Since services category is auto generated we have to do a pass
         // to generate it since services can come and go and then based on
         // the global accessibility state to decided whether it is enabled.
 
         // Generate.
-        mServicesCategory.removeAll();
+        ArrayList<Preference> servicePreferences =
+                new ArrayList<>(mServicePreferenceToPreferenceCategoryMap.keySet());
+        for (int i = 0; i < servicePreferences.size(); i++) {
+            Preference service = servicePreferences.get(i);
+            PreferenceCategory category = mServicePreferenceToPreferenceCategoryMap.get(service);
+            category.removePreference(service);
+        }
+
+        initializePreBundledServicesMapFromArray(CATEGORY_SCREEN_READER,
+                R.array.config_preinstalled_screen_reader_services);
+        initializePreBundledServicesMapFromArray(CATEGORY_AUDIO_AND_CAPTIONS,
+                R.array.config_preinstalled_audio_and_caption_services);
+        initializePreBundledServicesMapFromArray(CATEGORY_DISPLAY,
+                R.array.config_preinstalled_display_services);
+        initializePreBundledServicesMapFromArray(CATEGORY_INTERACTION_CONTROL,
+                R.array.config_preinstalled_interaction_control_services);
 
         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getActivity());
 
@@ -482,36 +514,49 @@
         final boolean accessibilityEnabled = Settings.Secure.getInt(getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
 
+        PreferenceCategory downloadedServicesCategory =
+                mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
+        // Temporarily add the downloaded services category back if it was previously removed.
+        if (findPreference(CATEGORY_DOWNLOADED_SERVICES) == null) {
+            getPreferenceScreen().addPreference(downloadedServicesCategory);
+        }
+
         for (int i = 0, count = installedServices.size(); i < count; ++i) {
             AccessibilityServiceInfo info = installedServices.get(i);
 
-            RestrictedPreference preference =
-                    new RestrictedPreference(mServicesCategory.getContext());
+            SingleLineSummaryPreference preference =
+                    new SingleLineSummaryPreference(downloadedServicesCategory.getContext(), null);
             String title = info.getResolveInfo().loadLabel(getPackageManager()).toString();
 
+            Drawable icon = info.getResolveInfo().loadIcon(getPackageManager());
+            if (icon == null) {
+                // todo (saigem): add a default
+            }
+
             ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
-            ComponentName componentName = new ComponentName(serviceInfo.packageName,
-                    serviceInfo.name);
+            String packageName = serviceInfo.packageName;
+            ComponentName componentName = new ComponentName(packageName, serviceInfo.name);
+            String componentNameKey = componentName.flattenToString();
 
             preference.setKey(componentName.flattenToString());
 
             preference.setTitle(title);
+            preference.setIcon(icon);
             final boolean serviceEnabled = accessibilityEnabled
                     && enabledServices.contains(componentName);
-            String serviceEnabledString;
-            if (serviceEnabled) {
-                serviceEnabledString = getString(R.string.accessibility_feature_state_on);
-            } else {
-                serviceEnabledString = getString(R.string.accessibility_feature_state_off);
-            }
+            String serviceState = serviceEnabled ?
+                    getString(R.string.accessibility_feature_state_on) :
+                    getString(R.string.accessibility_feature_state_off);
+            String serviceSummary = info.loadSummary(getPackageManager());
+            serviceSummary = (TextUtils.isEmpty(serviceSummary)) ? serviceState :
+                    serviceSummary;
 
             // Disable all accessibility services that are not permitted.
-            String packageName = serviceInfo.packageName;
             boolean serviceAllowed =
                     permittedServices == null || permittedServices.contains(packageName);
             if (!serviceAllowed && !serviceEnabled) {
                 EnforcedAdmin admin = RestrictedLockUtils.checkIfAccessibilityServiceDisallowed(
-                        getActivity(), serviceInfo.packageName, UserHandle.myUserId());
+                        getActivity(), packageName, UserHandle.myUserId());
                 if (admin != null) {
                     preference.setDisabledByAdmin(admin);
                 } else {
@@ -521,9 +566,7 @@
                 preference.setEnabled(true);
             }
 
-            preference.setSummary(serviceEnabledString);
-
-            preference.setOrder(i);
+            preference.setSummary(serviceSummary);
             preference.setFragment(ToggleAccessibilityServicePreferenceFragment.class.getName());
             preference.setPersistent(true);
 
@@ -543,26 +586,33 @@
                 extras.putString(EXTRA_SETTINGS_TITLE,
                         getString(R.string.accessibility_menu_item_settings));
                 extras.putString(EXTRA_SETTINGS_COMPONENT_NAME,
-                        new ComponentName(info.getResolveInfo().serviceInfo.packageName,
-                                settingsClassName).flattenToString());
+                        new ComponentName(packageName, settingsClassName).flattenToString());
             }
-
             extras.putParcelable(EXTRA_COMPONENT_NAME, componentName);
 
-            mServicesCategory.addPreference(preference);
+            PreferenceCategory prefCategory = downloadedServicesCategory;
+            // Set the appropriate category if the service comes pre-installed.
+            if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) {
+                prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName);
+            }
+            preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
+            prefCategory.addPreference(preference);
+            mServicePreferenceToPreferenceCategoryMap.put(preference, prefCategory);
         }
 
-        if (mServicesCategory.getPreferenceCount() == 0) {
-            if (mNoServicesMessagePreference == null) {
-                mNoServicesMessagePreference = new Preference(getPrefContext());
-                mNoServicesMessagePreference.setPersistent(false);
-                mNoServicesMessagePreference.setLayoutResource(
-                        R.layout.text_description_preference);
-                mNoServicesMessagePreference.setSelectable(false);
-                mNoServicesMessagePreference.setSummary(
-                        getString(R.string.accessibility_no_services_installed));
-            }
-            mServicesCategory.addPreference(mNoServicesMessagePreference);
+        // If the user has not installed any additional services, hide the category.
+        if (downloadedServicesCategory.getPreferenceCount() == 0) {
+            PreferenceScreen screen = getPreferenceScreen();
+            screen.removePreference(downloadedServicesCategory);
+        }
+    }
+
+    private void initializePreBundledServicesMapFromArray(String categoryKey, int key) {
+        String[] services = getResources().getStringArray(key);
+        PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
+        for (int i = 0; i < services.length; i++) {
+            ComponentName component = ComponentName.unflattenFromString(services[i]);
+            mPreBundledServiceComponentToCategoryMap.put(component, category);
         }
     }
 
@@ -602,7 +652,7 @@
                 Settings.Secure.LONG_PRESS_TIMEOUT, mLongPressTimeoutDefault);
         String value = String.valueOf(longPressTimeout);
         mSelectLongPressTimeoutPreference.setValue(value);
-        mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValuetoTitleMap.get(value));
+        mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValueToTitleMap.get(value));
 
         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
                 mCaptioningPreferenceScreen);
@@ -706,7 +756,7 @@
                     context.getSystemService(AccessibilityManager.class);
 
             String screenTitle = context.getResources().getString(
-                    R.string.accessibility_services_title);
+                    R.string.accessibility_settings);
 
             // Indexing all services, regardless if enabled.
             List<AccessibilityServiceInfo> services = accessibilityManager
diff --git a/src/com/android/settings/accounts/AccountSyncSettings.java b/src/com/android/settings/accounts/AccountSyncSettings.java
index dc31124..675de0e 100644
--- a/src/com/android/settings/accounts/AccountSyncSettings.java
+++ b/src/com/android/settings/accounts/AccountSyncSettings.java
@@ -18,16 +18,11 @@
 
 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.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.SyncAdapterType;
@@ -55,25 +50,18 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.Utils;
-import com.android.settingslib.RestrictedLockUtils;
 
 import com.google.android.collect.Lists;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
 public class AccountSyncSettings extends AccountPreferenceBase {
 
     public static final String ACCOUNT_KEY = "account";
-    private static final int MENU_SYNC_NOW_ID       = Menu.FIRST;
-    private static final int MENU_SYNC_CANCEL_ID    = Menu.FIRST + 1;
-    private static final int MENU_REMOVE_ACCOUNT_ID = Menu.FIRST + 2;
-    private static final int REALLY_REMOVE_DIALOG = 100;
-    private static final int FAILED_REMOVAL_DIALOG = 101;
+    private static final int MENU_SYNC_NOW_ID = Menu.FIRST;
+    private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 1;
     private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102;
 
     private TextView mUserId;
@@ -81,68 +69,17 @@
     private ImageView mProviderIcon;
     private TextView mErrorInfoView;
     private Account mAccount;
-    private ArrayList<SyncStateSwitchPreference> mSwitches =
-                new ArrayList<SyncStateSwitchPreference>();
     private ArrayList<SyncAdapterType> mInvisibleAdapters = Lists.newArrayList();
 
     @Override
     public Dialog onCreateDialog(final int id) {
         Dialog dialog = null;
-        if (id == REALLY_REMOVE_DIALOG) {
+        if (id == CANT_DO_ONETIME_SYNC_DIALOG) {
             dialog = new AlertDialog.Builder(getActivity())
-                .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,
-                        new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        Activity activity = 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 (!AccountSyncSettings.this.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
-                                }
-                                if (failed && getActivity() != null &&
-                                        !getActivity().isFinishing()) {
-                                    showDialog(FAILED_REMOVAL_DIALOG);
-                                } else {
-                                    finish();
-                                }
-                            }
-                        }, null, mUserHandle);
-                    }
-                })
-                .create();
-        } else if (id == FAILED_REMOVAL_DIALOG) {
-            dialog = new AlertDialog.Builder(getActivity())
-                .setTitle(R.string.really_remove_account_title)
-                .setPositiveButton(android.R.string.ok, null)
-                .setMessage(R.string.remove_account_failed)
-                .create();
-        } else if (id == CANT_DO_ONETIME_SYNC_DIALOG) {
-            dialog = new AlertDialog.Builder(getActivity())
-                .setTitle(R.string.cant_sync_dialog_title)
-                .setMessage(R.string.cant_sync_dialog_message)
-                .setPositiveButton(android.R.string.ok, null)
-                .create();
+                    .setTitle(R.string.cant_sync_dialog_title)
+                    .setMessage(R.string.cant_sync_dialog_message)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
         }
         return dialog;
     }
@@ -155,10 +92,6 @@
     @Override
     public int getDialogMetricsCategory(int dialogId) {
         switch (dialogId) {
-            case REALLY_REMOVE_DIALOG:
-                return MetricsEvent.DIALOG_ACCOUNT_SYNC_REMOVE;
-            case FAILED_REMOVAL_DIALOG:
-                return MetricsEvent.DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL;
             case CANT_DO_ONETIME_SYNC_DIALOG:
                 return MetricsEvent.DIALOG_ACCOUNT_SYNC_CANNOT_ONETIME_SYNC;
             default:
@@ -182,7 +115,7 @@
             Bundle savedInstanceState) {
         final View view = inflater.inflate(R.layout.account_sync_screen, container, false);
 
-        final ViewGroup prefs_container = (ViewGroup) view.findViewById(R.id.prefs_container);
+        final ViewGroup prefs_container = view.findViewById(R.id.prefs_container);
         Utils.prepareCustomPreferencesList(container, view, prefs_container, false);
         View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState);
         prefs_container.addView(prefs);
@@ -218,7 +151,7 @@
             return;
         }
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
-          Log.v(TAG, "Got account: " + mAccount);
+            Log.v(TAG, "Got account: " + mAccount);
         }
         mUserId.setText(mAccount.name);
         mProviderId.setText(mAccount.type);
@@ -287,26 +220,6 @@
                 getString(R.string.sync_menu_sync_cancel))
                 .setIcon(com.android.internal.R.drawable.ic_menu_close_clear_cancel);
 
-        MenuItem removeAccount = menu.add(0, MENU_REMOVE_ACCOUNT_ID, 0,
-                getString(R.string.remove_account_label))
-                .setIcon(R.drawable.ic_menu_delete);
-        removeAccount.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
-                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-        if (RestrictedLockUtils.hasBaseUserRestriction(getPrefContext(),
-                UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle.getIdentifier())) {
-            removeAccount.setEnabled(false);
-        } else {
-            EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(
-                    getPrefContext(), UserManager.DISALLOW_MODIFY_ACCOUNTS,
-                    mUserHandle.getIdentifier());
-            if (admin == null) {
-                admin = RestrictedLockUtils.checkIfAccountManagementDisabled(
-                        getPrefContext(), mAccount.type, mUserHandle.getIdentifier());
-            }
-            RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getPrefContext(),
-                    removeAccount, admin);
-        }
-
         syncNow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
                 MenuItem.SHOW_AS_ACTION_WITH_TEXT);
         syncCancel.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
@@ -334,9 +247,6 @@
             case MENU_SYNC_CANCEL_ID:
                 cancelSyncForEnabledProviders();
                 return true;
-            case MENU_REMOVE_ACCOUNT_ID:
-                showDialog(REALLY_REMOVE_DIALOG);
-                return true;
         }
         return super.onOptionsItemSelected(item);
     }
@@ -459,7 +369,7 @@
         int count = getPreferenceScreen().getPreferenceCount();
         for (int i = 0; i < count; i++) {
             Preference pref = getPreferenceScreen().getPreference(i);
-            if (! (pref instanceof SyncStateSwitchPreference)) {
+            if (!(pref instanceof SyncStateSwitchPreference)) {
                 continue;
             }
             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
@@ -471,7 +381,7 @@
         // plus whatever the system needs to sync, e.g., invisible sync adapters
         if (mAccount != null) {
             for (SyncAdapterType syncAdapter : mInvisibleAdapters) {
-                  requestOrCancelSync(mAccount, syncAdapter.authority, startSync);
+                requestOrCancelSync(mAccount, syncAdapter.authority, startSync);
             }
         }
     }
@@ -518,7 +428,7 @@
 
         for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
             Preference pref = getPreferenceScreen().getPreference(i);
-            if (! (pref instanceof SyncStateSwitchPreference)) {
+            if (!(pref instanceof SyncStateSwitchPreference)) {
                 continue;
             }
             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
@@ -536,14 +446,14 @@
             boolean lastSyncFailed = status != null
                     && status.lastFailureTime != 0
                     && status.getLastFailureMesgAsInt(0)
-                       != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
+                    != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
             if (!syncEnabled) lastSyncFailed = false;
             if (lastSyncFailed && !activelySyncing && !authorityIsPending) {
                 syncIsFailing = true;
             }
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
                 Log.d(TAG, "Update sync status: " + account + " " + authority +
-                        " active = " + activelySyncing + " pend =" +  authorityIsPending);
+                        " active = " + activelySyncing + " pend =" + authorityIsPending);
             }
 
             final long successEndTime = (status == null) ? 0 : status.lastSuccessTime;
@@ -567,7 +477,7 @@
 
             syncPref.setFailed(lastSyncFailed);
             final boolean oneTimeSyncMode = !ContentResolver.getMasterSyncAutomaticallyAsUser(
-                userId);
+                    userId);
             syncPref.setOneTimeSyncMode(oneTimeSyncMode);
             syncPref.setChecked(oneTimeSyncMode || syncEnabled);
         }
@@ -611,7 +521,7 @@
             // Only keep track of sync adapters for this account
             if (!sa.accountType.equals(mAccount.type)) continue;
             if (sa.isUserVisible()) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
                     Log.d(TAG, "updateAccountSwitches: added authority " + sa.authority
                             + " to accountType " + sa.accountType);
                 }
@@ -623,7 +533,7 @@
             }
         }
 
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "looking for sync adapters that match account " + mAccount);
         }
         cacheRemoveAllPrefs(getPreferenceScreen());
@@ -632,7 +542,7 @@
             // We could check services here....
             int syncState = ContentResolver.getIsSyncableAsUser(mAccount, syncAdapter.authority,
                     mUserHandle.getIdentifier());
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
                 Log.d(TAG, "  found authority " + syncAdapter.authority + " " + syncState);
             }
             if (syncState > 0) {
diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
index 3252f4e..7eb7fdd 100644
--- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
@@ -118,13 +118,6 @@
                             context, deviceAddress, deviceName)) {
                 context.startActivity(connectionAccessIntent);
             } else {
-                // Acquire wakelock so that LCD comes up since screen is off
-                PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK |
-                    PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE,
-                        "ConnectionAccessActivity");
-                wakeLock.setReferenceCounted(false);
-                wakeLock.acquire();
-
                 // Put up a notification that leads to the dialog
 
                 // Create an intent triggered by clicking on the
@@ -181,7 +174,6 @@
 
                 notificationManager.notify(getNotificationTag(mRequestType), NOTIFICATION_ID,
                         notification);
-                wakeLock.release();
             }
         } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) {
             // Remove the notification
diff --git a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java b/src/com/android/settings/connecteddevice/UsbModePreferenceController.java
index 5f700bd..6c1a28b 100644
--- a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/UsbModePreferenceController.java
@@ -119,7 +119,7 @@
                 preference.setEnabled(true);
                 preference.setSummary(getTitle(mode));
             } else {
-                preference.setSummary(R.string.usb_nothing_connected);
+                preference.setSummary(R.string.disconnected);
                 preference.setEnabled(false);
             }
         }
diff --git a/src/com/android/settings/core/lifecycle/Lifecycle.java b/src/com/android/settings/core/lifecycle/Lifecycle.java
index 9a42cd9..ef23688 100644
--- a/src/com/android/settings/core/lifecycle/Lifecycle.java
+++ b/src/com/android/settings/core/lifecycle/Lifecycle.java
@@ -18,12 +18,19 @@
 import android.annotation.UiThread;
 import android.content.Context;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
 import android.support.v7.preference.PreferenceScreen;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 import com.android.settings.core.lifecycle.events.OnAttach;
 import com.android.settings.core.lifecycle.events.OnCreate;
+import com.android.settings.core.lifecycle.events.OnCreateOptionsMenu;
 import com.android.settings.core.lifecycle.events.OnDestroy;
+import com.android.settings.core.lifecycle.events.OnOptionsItemSelected;
 import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnPrepareOptionsMenu;
 import com.android.settings.core.lifecycle.events.OnResume;
 import com.android.settings.core.lifecycle.events.OnSaveInstanceState;
 import com.android.settings.core.lifecycle.events.OnStart;
@@ -122,4 +129,31 @@
             }
         }
     }
+
+    public void onCreateOptionsMenu(final Menu menu, final @Nullable MenuInflater inflater) {
+        for (LifecycleObserver observer : mObservers) {
+            if (observer instanceof OnCreateOptionsMenu) {
+                ((OnCreateOptionsMenu) observer).onCreateOptionsMenu(menu, inflater);
+            }
+        }
+    }
+
+    public void onPrepareOptionsMenu(final Menu menu) {
+        for (LifecycleObserver observer : mObservers) {
+            if (observer instanceof OnPrepareOptionsMenu) {
+                ((OnPrepareOptionsMenu) observer).onPrepareOptionsMenu(menu);
+            }
+        }
+    }
+
+    public boolean onOptionsItemSelected(final MenuItem menuItem) {
+        for (LifecycleObserver observer : mObservers) {
+            if (observer instanceof OnOptionsItemSelected) {
+                if (((OnOptionsItemSelected) observer).onOptionsItemSelected(menuItem)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/settings/core/lifecycle/ObservableActivity.java b/src/com/android/settings/core/lifecycle/ObservableActivity.java
index bf64c85..006c9ae 100644
--- a/src/com/android/settings/core/lifecycle/ObservableActivity.java
+++ b/src/com/android/settings/core/lifecycle/ObservableActivity.java
@@ -19,6 +19,8 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.view.Menu;
+import android.view.MenuItem;
 
 /**
  * {@link Activity} that has hooks to observe activity lifecycle events.
@@ -73,4 +75,31 @@
         mLifecycle.onDestroy();
         super.onDestroy();
     }
+
+    @Override
+    public boolean onCreateOptionsMenu(final Menu menu) {
+        if (super.onCreateOptionsMenu(menu)) {
+            mLifecycle.onCreateOptionsMenu(menu, null);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(final Menu menu) {
+        if (super.onPrepareOptionsMenu(menu)) {
+            mLifecycle.onPrepareOptionsMenu(menu);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(final MenuItem menuItem) {
+        boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem);
+        if (!lifecycleHandled) {
+            return super.onOptionsItemSelected(menuItem);
+        }
+        return lifecycleHandled;
+    }
 }
diff --git a/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java b/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java
index 7b35ce5..c3265dd 100644
--- a/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java
+++ b/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java
@@ -17,6 +17,9 @@
 
 import android.app.DialogFragment;
 import android.content.Context;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 /**
  * {@link DialogFragment} that has hooks to observe fragment lifecycle events.
@@ -61,4 +64,24 @@
         super.onDestroy();
     }
 
+    @Override
+    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+        mLifecycle.onCreateOptionsMenu(menu, inflater);
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(final Menu menu) {
+        mLifecycle.onPrepareOptionsMenu(menu);
+        super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(final MenuItem menuItem) {
+        boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem);
+        if (!lifecycleHandled) {
+            return super.onOptionsItemSelected(menuItem);
+        }
+        return lifecycleHandled;
+    }
 }
diff --git a/src/com/android/settings/core/lifecycle/ObservableFragment.java b/src/com/android/settings/core/lifecycle/ObservableFragment.java
index b146325..8dae7ea 100644
--- a/src/com/android/settings/core/lifecycle/ObservableFragment.java
+++ b/src/com/android/settings/core/lifecycle/ObservableFragment.java
@@ -20,6 +20,9 @@
 import android.app.Fragment;
 import android.content.Context;
 import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 public class ObservableFragment extends Fragment {
 
@@ -84,4 +87,28 @@
         mLifecycle.onDestroy();
         super.onDestroy();
     }
+
+    @CallSuper
+    @Override
+    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+        mLifecycle.onCreateOptionsMenu(menu, inflater);
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    @CallSuper
+    @Override
+    public void onPrepareOptionsMenu(final Menu menu) {
+        mLifecycle.onPrepareOptionsMenu(menu);
+        super.onPrepareOptionsMenu(menu);
+    }
+
+    @CallSuper
+    @Override
+    public boolean onOptionsItemSelected(final MenuItem menuItem) {
+        boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem);
+        if (!lifecycleHandled) {
+            return super.onOptionsItemSelected(menuItem);
+        }
+        return lifecycleHandled;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
index abe1427..94a0be1 100644
--- a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
+++ b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
@@ -21,6 +21,9 @@
 import android.os.Bundle;
 import android.support.v14.preference.PreferenceFragment;
 import android.support.v7.preference.PreferenceScreen;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 /**
  * {@link PreferenceFragment} that has hooks to observe fragment lifecycle events.
@@ -95,4 +98,27 @@
         super.onDestroy();
     }
 
+    @CallSuper
+    @Override
+    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+        mLifecycle.onCreateOptionsMenu(menu, inflater);
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    @CallSuper
+    @Override
+    public void onPrepareOptionsMenu(final Menu menu) {
+        mLifecycle.onPrepareOptionsMenu(menu);
+        super.onPrepareOptionsMenu(menu);
+    }
+
+    @CallSuper
+    @Override
+    public boolean onOptionsItemSelected(final MenuItem menuItem) {
+        boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem);
+        if (!lifecycleHandled) {
+            return super.onOptionsItemSelected(menuItem);
+        }
+        return lifecycleHandled;
+    }
 }
diff --git a/src/com/android/settings/core/lifecycle/events/OnCreateOptionsMenu.java b/src/com/android/settings/core/lifecycle/events/OnCreateOptionsMenu.java
new file mode 100644
index 0000000..4c794ba
--- /dev/null
+++ b/src/com/android/settings/core/lifecycle/events/OnCreateOptionsMenu.java
@@ -0,0 +1,24 @@
+/*
+ * 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.core.lifecycle.events;
+
+import android.view.Menu;
+import android.view.MenuInflater;
+
+public interface OnCreateOptionsMenu {
+    void onCreateOptionsMenu(Menu menu, MenuInflater inflater);
+}
diff --git a/src/com/android/settings/core/lifecycle/events/OnOptionsItemSelected.java b/src/com/android/settings/core/lifecycle/events/OnOptionsItemSelected.java
new file mode 100644
index 0000000..b34b407
--- /dev/null
+++ b/src/com/android/settings/core/lifecycle/events/OnOptionsItemSelected.java
@@ -0,0 +1,23 @@
+/*
+ * 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.core.lifecycle.events;
+
+import android.view.MenuItem;
+
+public interface OnOptionsItemSelected {
+    boolean onOptionsItemSelected(MenuItem menuItem);
+}
diff --git a/src/com/android/settings/core/lifecycle/events/OnPrepareOptionsMenu.java b/src/com/android/settings/core/lifecycle/events/OnPrepareOptionsMenu.java
new file mode 100644
index 0000000..d642807
--- /dev/null
+++ b/src/com/android/settings/core/lifecycle/events/OnPrepareOptionsMenu.java
@@ -0,0 +1,24 @@
+/*
+ * 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.core.lifecycle.events;
+
+import android.view.Menu;
+import android.view.MenuInflater;
+
+public interface OnPrepareOptionsMenu {
+    void onPrepareOptionsMenu(Menu menu);
+}
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index f968cd0..e3e95be 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -40,8 +40,8 @@
 import com.android.settings.dashboard.conditional.Condition;
 import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
 import com.android.settings.dashboard.suggestions.SuggestionDismissController;
+import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.SuggestionParser;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.Tile;
 
@@ -60,6 +60,7 @@
     private final Context mContext;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private final DashboardFeatureProvider mDashboardFeatureProvider;
+    private final SuggestionFeatureProvider mSuggestionFeatureProvider;
     private final ArrayList<String> mSuggestionsShownLogged;
     private boolean mFirstFrameDrawn;
 
@@ -97,17 +98,17 @@
         }
     };
 
-    public DashboardAdapter(Context context, SuggestionParser parser,
-            MetricsFeatureProvider metricsFeatureProvider, Bundle savedInstanceState,
+    public DashboardAdapter(Context context, Bundle savedInstanceState,
             List<Condition> conditions) {
         List<Tile> suggestions = null;
         List<DashboardCategory> categories = null;
         int suggestionMode = DashboardData.SUGGESTION_MODE_DEFAULT;
 
         mContext = context;
-        mMetricsFeatureProvider = metricsFeatureProvider;
-        mDashboardFeatureProvider = FeatureFactory.getFactory(context)
-                .getDashboardFeatureProvider(context);
+        final FeatureFactory factory = FeatureFactory.getFactory(context);
+        mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
+        mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
+        mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
         mCache = new IconCache(context);
 
         setHasStableIds(true);
@@ -120,7 +121,7 @@
             mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
                     STATE_SUGGESTIONS_SHOWN_LOGGED);
         } else {
-            mSuggestionsShownLogged = new ArrayList<String>();
+            mSuggestionsShownLogged = new ArrayList<>();
         }
 
         mDashboardData = new DashboardData.Builder()
@@ -173,11 +174,11 @@
         }
         if (shownSuggestions != null) {
             for (Tile suggestion : shownSuggestions) {
-                String suggestionId = getSuggestionIdentifier(mContext, suggestion);
+                final String identifier = mSuggestionFeatureProvider.getSuggestionIdentifier(
+                        mContext, suggestion);
                 mMetricsFeatureProvider.action(
-                        mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
-                        getSuggestionIdentifier(mContext, suggestion));
-                mSuggestionsShownLogged.add(getSuggestionIdentifier(mContext, suggestion));
+                        mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, identifier);
+                mSuggestionsShownLogged.add(identifier);
             }
         }
     }
@@ -237,7 +238,8 @@
                 break;
             case R.layout.suggestion_tile:
                 final Tile suggestion = (Tile) mDashboardData.getItemEntityByPosition(position);
-                String suggestionId = getSuggestionIdentifier(mContext, suggestion);
+                final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
+                        mContext, suggestion);
                 // This is for cases when a suggestion is dismissed and the next one comes to view
                 if (!mSuggestionsShownLogged.contains(suggestionId)) {
                     mMetricsFeatureProvider.action(
@@ -245,14 +247,10 @@
                     mSuggestionsShownLogged.add(suggestionId);
                 }
                 onBindTile(holder, suggestion);
-                holder.itemView.setOnClickListener(new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        mMetricsFeatureProvider.action(mContext,
-                                MetricsEvent.ACTION_SETTINGS_SUGGESTION,
-                                DashboardAdapter.getSuggestionIdentifier(mContext, suggestion));
-                        ((SettingsActivity) mContext).startSuggestion(suggestion.intent);
-                    }
+                holder.itemView.setOnClickListener(v -> {
+                    mMetricsFeatureProvider.action(mContext,
+                            MetricsEvent.ACTION_SETTINGS_SUGGESTION, suggestionId);
+                    ((SettingsActivity) mContext).startSuggestion(suggestion.intent);
                 });
                 break;
             case R.layout.condition_card:
@@ -260,13 +258,7 @@
                         == mDashboardData.getExpandedCondition();
                 ConditionAdapterUtils.bindViews(
                         (Condition) mDashboardData.getItemEntityByPosition(position),
-                        holder, isExpanded, mConditionClickListener,
-                        new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                onExpandClick(v);
-                            }
-                        });
+                        holder, isExpanded, mConditionClickListener, v -> onExpandClick(v));
                 break;
         }
     }
@@ -291,7 +283,8 @@
             return;
         }
         for (Tile suggestion : mDashboardData.getSuggestions()) {
-            String suggestionId = getSuggestionIdentifier(mContext, suggestion);
+            String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
+                    mContext, suggestion);
             if (mSuggestionsShownLogged.contains(suggestionId)) {
                 mMetricsFeatureProvider.action(
                         mContext, MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, suggestionId);
@@ -320,16 +313,6 @@
         return mDashboardData.getItemEntityById(itemId);
     }
 
-    public static String getSuggestionIdentifier(Context context, Tile suggestion) {
-        String packageName = suggestion.intent.getComponent().getPackageName();
-        if (packageName.equals(context.getPackageName())) {
-            // Since Settings provides several suggestions, fill in the class instead of the
-            // package for these.
-            packageName = suggestion.intent.getComponent().getClassName();
-        }
-        return packageName;
-    }
-
     private void notifyDashboardDataChanged(DashboardData prevData) {
         if (mFirstFrameDrawn && prevData != null) {
             final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
@@ -394,33 +377,30 @@
             holder.summary.setText(
                     mContext.getString(R.string.suggestions_summary, undisplayedSuggestionCount));
         }
-        holder.itemView.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                final int suggestionMode;
-                if (moreSuggestions) {
-                    suggestionMode = DashboardData.SUGGESTION_MODE_EXPANDED;
+        holder.itemView.setOnClickListener(v -> {
+            final int suggestionMode;
+            if (moreSuggestions) {
+                suggestionMode = DashboardData.SUGGESTION_MODE_EXPANDED;
 
-                    for (Tile suggestion : mDashboardData.getSuggestions()) {
-                        String suggestionId =
-                                DashboardAdapter.getSuggestionIdentifier(mContext, suggestion);
-                        if (!mSuggestionsShownLogged.contains(suggestionId)) {
-                            mMetricsFeatureProvider.action(
-                                    mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
-                                    suggestionId);
-                            mSuggestionsShownLogged.add(suggestionId);
-                        }
+                for (Tile suggestion : mDashboardData.getSuggestions()) {
+                    final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
+                            mContext, suggestion);
+                    if (!mSuggestionsShownLogged.contains(suggestionId)) {
+                        mMetricsFeatureProvider.action(
+                                mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
+                                suggestionId);
+                        mSuggestionsShownLogged.add(suggestionId);
                     }
-                } else {
-                    suggestionMode = DashboardData.SUGGESTION_MODE_COLLAPSED;
                 }
-
-                DashboardData prevData = mDashboardData;
-                mDashboardData = new DashboardData.Builder(prevData)
-                        .setSuggestionMode(suggestionMode)
-                        .build();
-                notifyDashboardDataChanged(prevData);
+            } else {
+                suggestionMode = DashboardData.SUGGESTION_MODE_COLLAPSED;
             }
+
+            DashboardData prevData = mDashboardData;
+            mDashboardData = new DashboardData.Builder(prevData)
+                    .setSuggestionMode(suggestionMode)
+                    .build();
+            notifyDashboardDataChanged(prevData);
         });
     }
 
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java
index 20bcd3d..1c55bbb 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java
@@ -16,8 +16,8 @@
 package com.android.settings.dashboard;
 
 import android.app.Activity;
-import android.app.Fragment;
 import android.content.Context;
+import android.os.Bundle;
 import android.support.v7.preference.Preference;
 
 import com.android.settingslib.drawer.DashboardCategory;
@@ -78,7 +78,7 @@
      * Returns a {@link ProgressiveDisclosureMixin} for specified fragment.
      */
     ProgressiveDisclosureMixin getProgressiveDisclosureMixin(Context context,
-            DashboardFragment fragment);
+            DashboardFragment fragment, Bundle args);
 
     /**
      * Returns additional intent filter action for dashboard tiles
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index 9c21720..722f9e1 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -168,8 +168,12 @@
 
     @Override
     public ProgressiveDisclosureMixin getProgressiveDisclosureMixin(Context context,
-            DashboardFragment fragment) {
-        return new ProgressiveDisclosureMixin(context, mMetricsFeatureProvider, fragment);
+            DashboardFragment fragment, Bundle args) {
+        boolean keepExpanded = false;
+        if (args != null) {
+            keepExpanded = args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY) != null;
+        }
+        return new ProgressiveDisclosureMixin(context, fragment, keepExpanded);
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index e3845cd..3687929 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -69,7 +69,7 @@
         mDashboardFeatureProvider =
                 FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);
         mProgressiveDisclosureMixin = mDashboardFeatureProvider
-                .getProgressiveDisclosureMixin(context, this);
+                .getProgressiveDisclosureMixin(context, this, getArguments());
         getLifecycle().addObserver(mProgressiveDisclosureMixin);
 
         List<PreferenceController> controllers = getPreferenceControllers(context);
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 4e2baa9..7a5f395 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -190,8 +190,7 @@
         mDashboard.addItemDecoration(new DashboardDecorator(getContext()));
         mDashboard.setListener(this);
         Log.d(TAG, "adapter created");
-        mAdapter = new DashboardAdapter(getContext(), mSuggestionParser, mMetricsFeatureProvider,
-                bundle, mConditionManager.getConditions());
+        mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions());
         mDashboard.setAdapter(mAdapter);
         mSuggestionDismissHandler = new SuggestionDismissController(
                 getContext(), mDashboard, mSuggestionParser, mAdapter);
@@ -245,8 +244,8 @@
             if (isSmartSuggestionEnabled) {
                 List<String> suggestionIds = new ArrayList<>(suggestions.size());
                 for (Tile suggestion : suggestions) {
-                    suggestionIds.add(
-                            DashboardAdapter.getSuggestionIdentifier(context, suggestion));
+                    suggestionIds.add(mSuggestionFeatureProvider.getSuggestionIdentifier(
+                            context, suggestion));
                 }
                 // TODO: create a Suggestion class to maintain the id and other info
                 mSuggestionFeatureProvider.rankSuggestions(suggestions, suggestionIds);
diff --git a/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java b/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java
index be5e21b..07a7293 100644
--- a/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java
+++ b/src/com/android/settings/dashboard/ProgressiveDisclosureMixin.java
@@ -33,6 +33,7 @@
 import com.android.settings.core.lifecycle.LifecycleObserver;
 import com.android.settings.core.lifecycle.events.OnCreate;
 import com.android.settings.core.lifecycle.events.OnSaveInstanceState;
+import com.android.settings.overlay.FeatureFactory;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -56,13 +57,13 @@
     private boolean mUserExpanded;
 
     public ProgressiveDisclosureMixin(Context context,
-            MetricsFeatureProvider metricsFeatureProvider,
-            PreferenceFragment fragment) {
+            PreferenceFragment fragment, boolean keepExpanded) {
         mContext = context;
         mFragment = fragment;
         mExpandButton = new ExpandPreference(context);
         mExpandButton.setOnPreferenceClickListener(this);
-        mMetricsFeatureProvider = metricsFeatureProvider;
+        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+        mUserExpanded = keepExpanded;
     }
 
     @Override
@@ -119,7 +120,7 @@
      * Whether the screen should be collapsed.
      */
     public boolean shouldCollapse(PreferenceScreen screen) {
-        return screen.getPreferenceCount() >= mTileLimit && !mUserExpanded;
+        return !mUserExpanded && screen.getPreferenceCount() >= mTileLimit;
     }
 
     /**
diff --git a/src/com/android/settings/dashboard/SearchResultsSummary.java b/src/com/android/settings/dashboard/SearchResultsSummary.java
deleted file mode 100644
index 06b71d6..0000000
--- a/src/com/android/settings/dashboard/SearchResultsSummary.java
+++ /dev/null
@@ -1,642 +0,0 @@
-/*
- * Copyright (C) 2014 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.dashboard;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.SearchView;
-import android.widget.TextView;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
-import com.android.settings.search.Index;
-
-import java.util.HashMap;
-
-public class SearchResultsSummary extends InstrumentedPreferenceFragment {
-
-    private static final String LOG_TAG = "SearchResultsSummary";
-
-    private static final String EMPTY_QUERY = "";
-    private static char ELLIPSIS = '\u2026';
-
-    private static final String SAVE_KEY_SHOW_RESULTS = ":settings:show_results";
-
-    private SearchView mSearchView;
-
-    private ListView mResultsListView;
-    private SearchResultsAdapter mResultsAdapter;
-    private UpdateSearchResultsTask mUpdateSearchResultsTask;
-
-    private ListView mSuggestionsListView;
-    private SuggestionsAdapter mSuggestionsAdapter;
-    private UpdateSuggestionsTask mUpdateSuggestionsTask;
-
-    private ViewGroup mLayoutSuggestions;
-    private ViewGroup mLayoutResults;
-
-    private String mQuery;
-
-    private boolean mShowResults;
-
-    /**
-     * A basic AsyncTask for updating the query results cursor
-     */
-    private class UpdateSearchResultsTask extends AsyncTask<String, Void, Cursor> {
-        @Override
-        protected Cursor doInBackground(String... params) {
-            return Index.getInstance(getActivity()).search(params[0]);
-        }
-
-        @Override
-        protected void onPostExecute(Cursor cursor) {
-            if (!isCancelled()) {
-                mMetricsFeatureProvider.action(getContext(),
-                        MetricsEvent.ACTION_SEARCH_RESULTS, cursor.getCount());
-                setResultsCursor(cursor);
-                setResultsVisibility(cursor.getCount() > 0);
-            } else if (cursor != null) {
-                cursor.close();
-            }
-        }
-    }
-
-    /**
-     * A basic AsyncTask for updating the suggestions cursor
-     */
-    private class UpdateSuggestionsTask extends AsyncTask<String, Void, Cursor> {
-        @Override
-        protected Cursor doInBackground(String... params) {
-            return Index.getInstance(getActivity()).getSuggestions(params[0]);
-        }
-
-        @Override
-        protected void onPostExecute(Cursor cursor) {
-            if (!isCancelled()) {
-                setSuggestionsCursor(cursor);
-                setSuggestionsVisibility(cursor.getCount() > 0);
-            } else if (cursor != null) {
-                cursor.close();
-            }
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mResultsAdapter = new SearchResultsAdapter(getActivity());
-        mSuggestionsAdapter = new SuggestionsAdapter(getActivity());
-
-        if (savedInstanceState != null) {
-            mShowResults = savedInstanceState.getBoolean(SAVE_KEY_SHOW_RESULTS);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        outState.putBoolean(SAVE_KEY_SHOW_RESULTS, mShowResults);
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-
-        clearSuggestions();
-        clearResults();
-    }
-
-    @Override
-    public void onDestroy() {
-        mResultsListView = null;
-        mResultsAdapter = null;
-        mUpdateSearchResultsTask = null;
-
-        mSuggestionsListView = null;
-        mSuggestionsAdapter = null;
-        mUpdateSuggestionsTask = null;
-
-        mSearchView = null;
-
-        super.onDestroy();
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
-
-        final View view = inflater.inflate(R.layout.search_panel, container, false);
-
-        mLayoutSuggestions = (ViewGroup) view.findViewById(R.id.layout_suggestions);
-        mLayoutResults = (ViewGroup) view.findViewById(R.id.layout_results);
-
-        mResultsListView = (ListView) view.findViewById(R.id.list_results);
-        mResultsListView.setAdapter(mResultsAdapter);
-        mResultsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-            @Override
-            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                // We have a header, so we need to decrement the position by one
-                position--;
-
-                // Some Monkeys could create a case where they were probably clicking on the
-                // List Header and thus the position passed was "0" and then by decrement was "-1"
-                if (position < 0) {
-                    return;
-                }
-
-                final Cursor cursor = mResultsAdapter.mCursor;
-                cursor.moveToPosition(position);
-
-                final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME);
-                final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE);
-                final String action = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION);
-                final String key = cursor.getString(Index.COLUMN_INDEX_KEY);
-
-                final SettingsActivity sa = (SettingsActivity) getActivity();
-                sa.needToRevertToInitialFragment();
-
-                if (TextUtils.isEmpty(action)) {
-                    Bundle args = new Bundle();
-                    args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
-
-                    Utils.startWithFragment(sa, className, args, null, 0, -1, screenTitle,
-                            getMetricsCategory());
-                } else {
-                    final Intent intent = new Intent(action);
-
-                    final String targetPackage = cursor.getString(
-                            Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
-                    final String targetClass = cursor.getString(
-                            Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS);
-                    if (!TextUtils.isEmpty(targetPackage) && !TextUtils.isEmpty(targetClass)) {
-                        final ComponentName component =
-                                new ComponentName(targetPackage, targetClass);
-                        intent.setComponent(component);
-                    }
-                    intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
-
-                    sa.startActivity(intent);
-                }
-
-                saveQueryToDatabase();
-            }
-        });
-        mResultsListView.addHeaderView(
-                LayoutInflater.from(getActivity()).inflate(
-                        R.layout.search_panel_results_header, mResultsListView, false),
-                null, false);
-
-        mSuggestionsListView = (ListView) view.findViewById(R.id.list_suggestions);
-        mSuggestionsListView.setAdapter(mSuggestionsAdapter);
-        mSuggestionsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-            @Override
-            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                // We have a header, so we need to decrement the position by one
-                position--;
-                // Some Monkeys could create a case where they were probably clicking on the
-                // List Header and thus the position passed was "0" and then by decrement was "-1"
-                if (position < 0) {
-                    return;
-                }
-                final Cursor cursor = mSuggestionsAdapter.mCursor;
-                cursor.moveToPosition(position);
-
-                mShowResults = true;
-                mQuery = cursor.getString(0);
-                mSearchView.setQuery(mQuery, false);
-            }
-        });
-        mSuggestionsListView.addHeaderView(
-                LayoutInflater.from(getActivity()).inflate(
-                        R.layout.search_panel_suggestions_header, mSuggestionsListView, false),
-                null, false);
-
-        return view;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.DASHBOARD_SEARCH_RESULTS;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-
-        if (!mShowResults) {
-            showSomeSuggestions();
-        }
-    }
-
-    public void setSearchView(SearchView searchView) {
-        mSearchView = searchView;
-    }
-
-    private void setSuggestionsVisibility(boolean visible) {
-        if (mLayoutSuggestions != null) {
-            mLayoutSuggestions.setVisibility(visible ? View.VISIBLE : View.GONE);
-        }
-    }
-
-    private void setResultsVisibility(boolean visible) {
-        if (mLayoutResults != null) {
-            mLayoutResults.setVisibility(visible ? View.VISIBLE : View.GONE);
-        }
-    }
-
-    private void saveQueryToDatabase() {
-        Index.getInstance(getActivity()).addSavedQuery(mQuery);
-    }
-
-    public boolean onQueryTextSubmit(String query) {
-        mQuery = getFilteredQueryString(query);
-        mShowResults = true;
-        setSuggestionsVisibility(false);
-        updateSearchResults();
-        saveQueryToDatabase();
-
-        return false;
-    }
-
-    public boolean onQueryTextChange(String query) {
-        final String newQuery = getFilteredQueryString(query);
-
-        mQuery = newQuery;
-
-        if (TextUtils.isEmpty(mQuery)) {
-            mShowResults = false;
-            setResultsVisibility(false);
-            updateSuggestions();
-        } else {
-            mShowResults = true;
-            setSuggestionsVisibility(false);
-            updateSearchResults();
-        }
-
-        return true;
-    }
-
-    public void showSomeSuggestions() {
-        setResultsVisibility(false);
-        mQuery = EMPTY_QUERY;
-        updateSuggestions();
-    }
-
-    private void clearSuggestions() {
-        if (mUpdateSuggestionsTask != null) {
-            mUpdateSuggestionsTask.cancel(false);
-            mUpdateSuggestionsTask = null;
-        }
-        setSuggestionsCursor(null);
-        setSuggestionsVisibility(false);
-    }
-
-    private void setSuggestionsCursor(Cursor cursor) {
-        if (mSuggestionsAdapter == null) {
-            return;
-        }
-        Cursor oldCursor = mSuggestionsAdapter.swapCursor(cursor);
-        if (oldCursor != null) {
-            oldCursor.close();
-        }
-    }
-
-    private void clearResults() {
-        if (mUpdateSearchResultsTask != null) {
-            mUpdateSearchResultsTask.cancel(false);
-            mUpdateSearchResultsTask = null;
-        }
-        setResultsCursor(null);
-        setResultsVisibility(false);
-    }
-
-    private void setResultsCursor(Cursor cursor) {
-        if (mResultsAdapter == null) {
-            return;
-        }
-        Cursor oldCursor = mResultsAdapter.swapCursor(cursor);
-        if (oldCursor != null) {
-            oldCursor.close();
-        }
-    }
-
-    private String getFilteredQueryString(CharSequence query) {
-        if (query == null) {
-            return null;
-        }
-        final StringBuilder filtered = new StringBuilder();
-        for (int n = 0; n < query.length(); n++) {
-            char c = query.charAt(n);
-            if (!Character.isLetterOrDigit(c) && !Character.isSpaceChar(c)) {
-                continue;
-            }
-            filtered.append(c);
-        }
-        return filtered.toString();
-    }
-
-    private void clearAllTasks() {
-        if (mUpdateSearchResultsTask != null) {
-            mUpdateSearchResultsTask.cancel(false);
-            mUpdateSearchResultsTask = null;
-        }
-        if (mUpdateSuggestionsTask != null) {
-            mUpdateSuggestionsTask.cancel(false);
-            mUpdateSuggestionsTask = null;
-        }
-    }
-
-    private void updateSuggestions() {
-        clearAllTasks();
-        if (mQuery == null) {
-            setSuggestionsCursor(null);
-        } else {
-            mUpdateSuggestionsTask = new UpdateSuggestionsTask();
-            mUpdateSuggestionsTask.execute(mQuery);
-        }
-    }
-
-    private void updateSearchResults() {
-        clearAllTasks();
-        if (TextUtils.isEmpty(mQuery)) {
-            setResultsVisibility(false);
-            setResultsCursor(null);
-        } else {
-            mUpdateSearchResultsTask = new UpdateSearchResultsTask();
-            mUpdateSearchResultsTask.execute(mQuery);
-        }
-    }
-
-    private static class SuggestionItem {
-        public String query;
-
-        public SuggestionItem(String query) {
-            this.query = query;
-        }
-    }
-
-    private static class SuggestionsAdapter extends BaseAdapter {
-
-        private static final int COLUMN_SUGGESTION_QUERY = 0;
-        private static final int COLUMN_SUGGESTION_TIMESTAMP = 1;
-
-        private Context mContext;
-        private Cursor mCursor;
-        private LayoutInflater mInflater;
-        private boolean mDataValid = false;
-
-        public SuggestionsAdapter(Context context) {
-            mContext = context;
-            mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            mDataValid = false;
-        }
-
-        public Cursor swapCursor(Cursor newCursor) {
-            if (newCursor == mCursor) {
-                return null;
-            }
-            Cursor oldCursor = mCursor;
-            mCursor = newCursor;
-            if (newCursor != null) {
-                mDataValid = true;
-                notifyDataSetChanged();
-            } else {
-                mDataValid = false;
-                notifyDataSetInvalidated();
-            }
-            return oldCursor;
-        }
-
-        @Override
-        public int getCount() {
-            if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0;
-            return mCursor.getCount();
-        }
-
-        @Override
-        public Object getItem(int position) {
-            if (mDataValid && mCursor.moveToPosition(position)) {
-                final String query = mCursor.getString(COLUMN_SUGGESTION_QUERY);
-
-                return new SuggestionItem(query);
-            }
-            return null;
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return 0;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (!mDataValid && convertView == null) {
-                throw new IllegalStateException(
-                        "this should only be called when the cursor is valid");
-            }
-            if (!mCursor.moveToPosition(position)) {
-                throw new IllegalStateException("couldn't move cursor to position " + position);
-            }
-
-            View view;
-
-            if (convertView == null) {
-                view = mInflater.inflate(R.layout.search_suggestion_item, parent, false);
-            } else {
-                view = convertView;
-            }
-
-            TextView query = (TextView) view.findViewById(R.id.title);
-
-            SuggestionItem item = (SuggestionItem) getItem(position);
-            query.setText(item.query);
-
-            return view;
-        }
-    }
-
-    private static class SearchResult {
-        public Context context;
-        public String title;
-        public String summaryOn;
-        public String summaryOff;
-        public String entries;
-        public int iconResId;
-        public String key;
-
-        public SearchResult(Context context, String title, String summaryOn, String summaryOff,
-                            String entries, int iconResId, String key) {
-            this.context = context;
-            this.title = title;
-            this.summaryOn = summaryOn;
-            this.summaryOff = summaryOff;
-            this.entries = entries;
-            this.iconResId = iconResId;
-            this.key = key;
-        }
-    }
-
-    private static class SearchResultsAdapter extends BaseAdapter {
-
-        private Context mContext;
-        private Cursor mCursor;
-        private LayoutInflater mInflater;
-        private boolean mDataValid;
-        private HashMap<String, Context> mContextMap = new HashMap<String, Context>();
-
-        private static final String PERCENT_RECLACE = "%s";
-        private static final String DOLLAR_REPLACE = "$s";
-
-        public SearchResultsAdapter(Context context) {
-            mContext = context;
-            mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            mDataValid = false;
-        }
-
-        public Cursor swapCursor(Cursor newCursor) {
-            if (newCursor == mCursor) {
-                return null;
-            }
-            Cursor oldCursor = mCursor;
-            mCursor = newCursor;
-            if (newCursor != null) {
-                mDataValid = true;
-                notifyDataSetChanged();
-            } else {
-                mDataValid = false;
-                notifyDataSetInvalidated();
-            }
-            return oldCursor;
-        }
-
-        @Override
-        public int getCount() {
-            if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0;
-            return mCursor.getCount();
-        }
-
-        @Override
-        public Object getItem(int position) {
-            if (mDataValid && mCursor.moveToPosition(position)) {
-                final String title = mCursor.getString(Index.COLUMN_INDEX_TITLE);
-                final String summaryOn = mCursor.getString(Index.COLUMN_INDEX_SUMMARY_ON);
-                final String summaryOff = mCursor.getString(Index.COLUMN_INDEX_SUMMARY_OFF);
-                final String entries = mCursor.getString(Index.COLUMN_INDEX_ENTRIES);
-                final String iconResStr = mCursor.getString(Index.COLUMN_INDEX_ICON);
-                final String className = mCursor.getString(
-                        Index.COLUMN_INDEX_CLASS_NAME);
-                final String packageName = mCursor.getString(
-                        Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
-                final String key = mCursor.getString(
-                        Index.COLUMN_INDEX_KEY);
-
-                Context packageContext;
-                if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(packageName)) {
-                    packageContext = mContextMap.get(packageName);
-                    if (packageContext == null) {
-                        try {
-                            packageContext = mContext.createPackageContext(packageName, 0);
-                        } catch (PackageManager.NameNotFoundException e) {
-                            Log.e(LOG_TAG, "Cannot create Context for package: " + packageName);
-                            return null;
-                        }
-                        mContextMap.put(packageName, packageContext);
-                    }
-                } else {
-                    packageContext = mContext;
-                }
-
-                final int iconResId = TextUtils.isEmpty(iconResStr) ?
-                        R.drawable.empty_icon : Integer.parseInt(iconResStr);
-
-                return new SearchResult(packageContext, title, summaryOn, summaryOff,
-                        entries, iconResId, key);
-            }
-            return null;
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return 0;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (!mDataValid && convertView == null) {
-                throw new IllegalStateException(
-                        "this should only be called when the cursor is valid");
-            }
-            if (!mCursor.moveToPosition(position)) {
-                throw new IllegalStateException("couldn't move cursor to position " + position);
-            }
-
-            View view;
-            TextView textTitle;
-            ImageView imageView;
-
-            if (convertView == null) {
-                view = mInflater.inflate(R.layout.search_result_item, parent, false);
-            } else {
-                view = convertView;
-            }
-
-            textTitle = (TextView) view.findViewById(R.id.title);
-            imageView = (ImageView) view.findViewById(R.id.icon);
-
-            final SearchResult result = (SearchResult) getItem(position);
-            textTitle.setText(result.title);
-
-            if (result.iconResId != R.drawable.empty_icon) {
-                final Context packageContext = result.context;
-                final Drawable drawable;
-                try {
-                    drawable = packageContext.getDrawable(result.iconResId);
-                    imageView.setImageDrawable(drawable);
-                } catch (Resources.NotFoundException nfe) {
-                    // Not much we can do except logging
-                    Log.e(LOG_TAG, "Cannot load Drawable for " + result.title);
-                }
-            } else {
-                imageView.setImageDrawable(null);
-                imageView.setBackgroundResource(R.drawable.empty_icon);
-            }
-
-            return view;
-        }
-    }
-}
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java
index 806d6f8..14f5e9c 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java
@@ -49,4 +49,9 @@
      * Dismisses a suggestion.
      */
     void dismissSuggestion(Context context, SuggestionParser parser, Tile suggestion);
+
+    /**
+     * Returns an identifier for the suggestion
+     */
+    String getSuggestionIdentifier(Context context, Tile suggestion);
 }
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
index 9231037..b82c28d 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
@@ -21,7 +21,6 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
-import com.android.settings.dashboard.DashboardAdapter;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.SuggestionParser;
 import com.android.settingslib.drawer.Tile;
@@ -50,9 +49,10 @@
 
 
     public SuggestionFeatureProviderImpl(Context context) {
+        final Context appContext = context.getApplicationContext();
         mSuggestionRanker = new SuggestionRanker(
-                new SuggestionFeaturizer(new EventStore(context.getApplicationContext())));
-        mMetricsFeatureProvider = FeatureFactory.getFactory(context)
+                new SuggestionFeaturizer(new EventStore(appContext)));
+        mMetricsFeatureProvider = FeatureFactory.getFactory(appContext)
                 .getMetricsFeatureProvider();
     }
 
@@ -68,7 +68,7 @@
         }
         mMetricsFeatureProvider.action(
                 context, MetricsProto.MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION,
-                DashboardAdapter.getSuggestionIdentifier(context, suggestion));
+                getSuggestionIdentifier(context, suggestion));
 
         final boolean isSmartSuggestionEnabled = isSmartSuggestionEnabled(context);
         if (!parser.dismissSuggestion(suggestion, isSmartSuggestionEnabled)) {
@@ -81,4 +81,18 @@
         parser.markCategoryDone(suggestion.category);
     }
 
+    @Override
+    public String getSuggestionIdentifier(Context context, Tile suggestion) {
+        if (suggestion.intent == null || suggestion.intent.getComponent() == null) {
+            return "unknown_suggestion";
+        }
+        String packageName = suggestion.intent.getComponent().getPackageName();
+        if (packageName.equals(context.getPackageName())) {
+            // Since Settings provides several suggestions, fill in the class instead of the
+            // package for these.
+            packageName = suggestion.intent.getComponent().getClassName();
+        }
+        return packageName;
+    }
+
 }
diff --git a/src/com/android/settings/datausage/BillingCyclePreference.java b/src/com/android/settings/datausage/BillingCyclePreference.java
index 8e450df..beb0ba1 100644
--- a/src/com/android/settings/datausage/BillingCyclePreference.java
+++ b/src/com/android/settings/datausage/BillingCyclePreference.java
@@ -58,8 +58,9 @@
         mSubId = subId;
         mServices = services;
         mPolicy = services.mPolicyEditor.getPolicy(mTemplate);
-        setSummary(getContext().getString(R.string.billing_cycle_fragment_summary,
-                mPolicy != null ? mPolicy.cycleDay : 1));
+        setSummary(getContext().getString(R.string.billing_cycle_fragment_summary, mPolicy != null
+                ? mPolicy.cycleDay
+                : "1"));
         setIntent(getIntent());
     }
 
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
index a817479..e12f89e 100644
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -46,8 +46,10 @@
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.Spinner;
+
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
 import com.android.settingslib.AppItem;
 import com.android.settingslib.net.ChartData;
 import com.android.settingslib.net.ChartDataLoader;
@@ -56,9 +58,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -73,6 +73,10 @@
  * to inspect based on usage cycle and control through {@link NetworkPolicy}.
  */
 public class DataUsageList extends DataUsageBase {
+
+    public static final String EXTRA_SUB_ID = "sub_id";
+    public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
+
     private static final String TAG = "DataUsage";
     private static final boolean LOGD = false;
 
@@ -82,8 +86,14 @@
 
     private static final int LOADER_CHART_DATA = 2;
     private static final int LOADER_SUMMARY = 3;
-    public static final String EXTRA_SUB_ID = "sub_id";
-    public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
+
+    private final CellDataPreference.DataStateListener mDataStateListener =
+            new CellDataPreference.DataStateListener() {
+                @Override
+                public void onChange(boolean selfChange) {
+                    updatePolicy();
+                }
+            };
 
     private INetworkStatsSession mStatsSession;
 
@@ -93,15 +103,7 @@
     private int mSubId;
     private ChartData mChartData;
 
-    /** Flag used to ignore listeners during binding. */
-    private boolean mBinding;
-
     private UidDetailProvider mUidDetailProvider;
-
-    /**
-     * Local cache of data enabled for subId, used to work around delays.
-     */
-    private final Map<String, Boolean> mMobileDataEnabled = new HashMap<String, Boolean>();
     private CycleAdapter mCycleAdapter;
     private Spinner mCycleSpinner;
     private Preference mUsageAmount;
@@ -146,9 +148,14 @@
         super.onViewCreated(v, savedInstanceState);
 
         mHeader = setPinnedHeaderView(R.layout.apps_filter_spinner);
-        mCycleSpinner = (Spinner) mHeader.findViewById(R.id.filter_spinner);
-        mCycleAdapter = new CycleAdapter(mCycleSpinner.getContext(),
-                new CycleAdapter.SpinnerInterface() {
+        mHeader.findViewById(R.id.filter_settings).setOnClickListener(btn -> {
+            final Bundle args = new Bundle();
+            args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
+            startFragment(DataUsageList.this, BillingCycleSettings.class.getName(),
+                    R.string.billing_cycle, 0, args);
+        });
+        mCycleSpinner = mHeader.findViewById(R.id.filter_spinner);
+        mCycleAdapter = new CycleAdapter(mCycleSpinner.getContext(), new SpinnerInterface() {
             @Override
             public void setAdapter(CycleAdapter cycleAdapter) {
                 mCycleSpinner.setAdapter(cycleAdapter);
@@ -175,7 +182,7 @@
     @Override
     public void onResume() {
         super.onResume();
-
+        mDataStateListener.setListener(true, mSubId, getContext());
         updateBody();
 
         // kick off background task to update stats
@@ -202,6 +209,12 @@
     }
 
     @Override
+    public void onPause() {
+        super.onPause();
+        mDataStateListener.setListener(false, mSubId, getContext());
+    }
+
+    @Override
     public void onDestroy() {
         mUidDetailProvider.clearCache();
         mUidDetailProvider = null;
@@ -217,7 +230,6 @@
      * binds them to visible controls.
      */
     private void updateBody() {
-        mBinding = true;
         if (!isAdded()) return;
 
         final Context context = getActivity();
@@ -231,10 +243,8 @@
         // detail mode can change visible menus, invalidate
         getActivity().invalidateOptionsMenu();
 
-        mBinding = false;
-
         int seriesColor = context.getColor(R.color.sim_noitification);
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID){
+        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             final SubscriptionInfo sir = services.mSubscriptionManager
                     .getActiveSubscriptionInfo(mSubId);
 
@@ -252,33 +262,22 @@
      * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
      * current {@link #mTemplate}.
      */
-    private void updatePolicy(boolean refreshCycle) {
+    private void updatePolicy() {
         final NetworkPolicy policy = services.mPolicyEditor.getPolicy(mTemplate);
+        final View configureButton = mHeader.findViewById(R.id.filter_settings);
         //SUB SELECT
         if (isNetworkPolicyModifiable(policy, mSubId) && isMobileDataAvailable(mSubId)) {
             mChart.setNetworkPolicy(policy);
-            mHeader.findViewById(R.id.filter_settings).setVisibility(View.VISIBLE);
-            mHeader.findViewById(R.id.filter_settings).setOnClickListener(
-                    new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    Bundle args = new Bundle();
-                    args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
-                    startFragment(DataUsageList.this, BillingCycleSettings.class.getName(),
-                            R.string.billing_cycle, 0, args);
-                }
-            });
+            configureButton.setVisibility(View.VISIBLE);
         } else {
             // controls are disabled; don't bind warning/limit sweeps
             mChart.setNetworkPolicy(null);
-            mHeader.findViewById(R.id.filter_settings).setVisibility(View.GONE);
+            configureButton.setVisibility(View.GONE);
         }
 
-        if (refreshCycle) {
-            // generate cycle list based on policy and available history
-            if (mCycleAdapter.updateCycleList(policy, mChartData)) {
-                updateDetailData();
-            }
+        // generate cycle list based on policy and available history
+        if (mCycleAdapter.updateCycleList(policy, mChartData)) {
+            updateDetailData();
         }
     }
 
@@ -413,9 +412,10 @@
     /**
      * Accumulate data usage of a network stats entry for the item mapped by the collapse key.
      * Creates the item if needed.
-     * @param collapseKey the collapse key used to map the item.
-     * @param knownItems collection of known (already existing) items.
-     * @param entry the network stats entry to extract data usage from.
+     *
+     * @param collapseKey  the collapse key used to map the item.
+     * @param knownItems   collection of known (already existing) items.
+     * @param entry        the network stats entry to extract data usage from.
      * @param itemCategory the item is categorized on the list view by this category. Must be
      */
     private static long accumulate(int collapseKey, final SparseArray<AppItem> knownItems,
@@ -461,7 +461,7 @@
         if (LOGD) {
             Log.d(TAG, "hasReadyMobileRadio:"
                     + " conn.isNetworkSupported(TYPE_MOBILE)="
-                                            + conn.isNetworkSupported(TYPE_MOBILE)
+                    + conn.isNetworkSupported(TYPE_MOBILE)
                     + " isReady=" + isReady);
         }
         return retVal;
@@ -480,10 +480,13 @@
         final int slotId = SubscriptionManager.getSlotId(subId);
         final boolean isReady = tele.getSimState(slotId) == SIM_STATE_READY;
 
-        boolean retVal =  conn.isNetworkSupported(TYPE_MOBILE) && isReady;
-        if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subId=" + subId
-                + " conn.isNetworkSupported(TYPE_MOBILE)=" + conn.isNetworkSupported(TYPE_MOBILE)
-                + " isReady=" + isReady);
+        boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
+        if (LOGD) {
+            Log.d(TAG, "hasReadyMobileRadio: subId=" + subId
+                    + " conn.isNetworkSupported(TYPE_MOBILE)="
+                    + conn.isNetworkSupported(TYPE_MOBILE)
+                    + " isReady=" + isReady);
+        }
         return retVal;
     }
 
@@ -524,8 +527,8 @@
             mChartData = data;
             mChart.setNetworkStats(mChartData.network);
 
-            // calcuate policy cycles based on available data
-            updatePolicy(true);
+            // calculate policy cycles based on available data
+            updatePolicy();
         }
 
         @Override
diff --git a/src/com/android/settings/datausage/TemplatePreferenceCategory.java b/src/com/android/settings/datausage/TemplatePreferenceCategory.java
index 1ce5f1c..0be5c73 100644
--- a/src/com/android/settings/datausage/TemplatePreferenceCategory.java
+++ b/src/com/android/settings/datausage/TemplatePreferenceCategory.java
@@ -17,10 +17,10 @@
 import android.content.Context;
 import android.net.NetworkTemplate;
 import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
 import android.util.AttributeSet;
-import com.android.settings.DividedCategory;
 
-public class TemplatePreferenceCategory extends DividedCategory implements TemplatePreference {
+public class TemplatePreferenceCategory extends PreferenceCategory implements TemplatePreference {
 
     private NetworkTemplate mTemplate;
     private int mSubId;
diff --git a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
index 5176e09..ae5e5f1 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
@@ -89,6 +89,8 @@
             // code size.
             if (!app.isSystemApp() || app.isUpdatedSystemApp()) {
                 attributedAppSizeInBytes += stats.getCodeBytes();
+            } else {
+                result.systemSize += stats.getCodeBytes();
             }
             switch (app.category) {
                 case CATEGORY_GAME:
@@ -122,6 +124,7 @@
         public long gamesSize;
         public long musicAppsSize;
         public long otherAppsSize;
+        public long systemSize;
         public StorageStatsSource.ExternalStorageStats externalStats;
     }
 
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 2fa1b18..36cf73e 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -181,7 +181,7 @@
         mGamePreference.setStorageSize(data.gamesSize);
         mAppPreference.setStorageSize(data.otherAppsSize);
         if (mSystemPreference != null) {
-            mSystemPreference.setStorageSize(mSystemSize);
+            mSystemPreference.setStorageSize(mSystemSize + data.systemSize);
         }
 
         long unattributedBytes = data.externalStats.totalBytes - data.externalStats.audioBytes
diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java
index a1fe849..bed2dc5 100644
--- a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java
+++ b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java
@@ -57,7 +57,6 @@
         if (mBatteryInfo == null) {
             return;
         }
-        view.itemView.setClickable(true);
         view.setDividerAllowedAbove(true);
         ((TextView) view.findViewById(R.id.charge)).setText(mBatteryInfo.batteryPercentString);
         ((TextView) view.findViewById(R.id.estimation)).setText(
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index 86ff175..72b361a 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -150,13 +150,13 @@
             uid = userInfo.id;
         }
         VpnConfig vpn = vpns.get(uid);
-        final String vpnName;
+        final String summary;
         if (vpn == null) {
-            vpnName = null;
+            summary = mContext.getString(R.string.vpn_disconnected_summary);
         } else {
-            vpnName = getNameForVpnConfig(vpn, UserHandle.of(uid));
+            summary = getNameForVpnConfig(vpn, UserHandle.of(uid));
         }
-        new Handler(Looper.getMainLooper()).post(() -> mPreference.setSummary(vpnName));
+        new Handler(Looper.getMainLooper()).post(() -> mPreference.setSummary(summary));
     }
 
     private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index d12842c..24405b3 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -34,6 +34,7 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.AppHeader;
+import com.android.settings.DimmableIconPreference;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.applications.AppHeaderController;
@@ -66,14 +67,18 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public void onResume() {
+        super.onResume();
+
         if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
             Log.w(TAG, "Missing package or uid or packageinfo");
-            toastAndFinish();
+            finish();
             return;
         }
-        final Activity activity = getActivity();
+
+        if (getPreferenceScreen() != null) {
+            getPreferenceScreen().removeAll();
+        }
 
         addPreferencesFromResource(R.xml.app_notification_settings);
         getPreferenceScreen().setOrderingAsAdded(true);
@@ -81,44 +86,38 @@
         mBlock = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BLOCK);
         mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
 
-        if (mPkgInfo != null) {
-            setupBlock();
-            setupBadge();
-            // load settings intent
-            ArrayMap<String, AppRow> rows = new ArrayMap<String, AppRow>();
-            rows.put(mAppRow.pkg, mAppRow);
-            collectConfigActivities(rows);
-            new AsyncTask<Void, Void, Void>() {
-                @Override
-                protected Void doInBackground(Void... unused) {
-                    mChannelGroupList = mBackend.getChannelGroups(mPkg, mUid).getList();
-                    Collections.sort(mChannelGroupList, mChannelGroupComparator);
-                    return null;
-                }
+        setupBlock();
+        setupBadge();
+        // load settings intent
+        ArrayMap<String, AppRow> rows = new ArrayMap<String, AppRow>();
+        rows.put(mAppRow.pkg, mAppRow);
+        collectConfigActivities(rows);
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... unused) {
+                mChannelGroupList = mBackend.getChannelGroups(mPkg, mUid).getList();
+                Collections.sort(mChannelGroupList, mChannelGroupComparator);
+                return null;
+            }
 
-                @Override
-                protected void onPostExecute(Void unused) {
-                    populateChannelList();
-                }
-            }.execute();
-        }
-        final Preference pref = FeatureFactory.getFactory(activity)
-            .getApplicationFeatureProvider(activity)
-            .newAppHeaderController(this /* fragment */, null /* appHeader */)
-            .setIcon(mAppRow.icon)
-            .setLabel(mAppRow.label)
-            .setPackageName(mAppRow.pkg)
-            .setUid(mAppRow.uid)
-            .setAppNotifPrefIntent(mAppRow.settingsIntent)
-            .setButtonActions(AppHeaderController.ActionType.ACTION_APP_INFO,
-                AppHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
-            .done(getPrefContext());
+            @Override
+            protected void onPostExecute(Void unused) {
+                populateChannelList();
+            }
+        }.execute();
+
+        final Preference pref = FeatureFactory.getFactory(getActivity())
+                .getApplicationFeatureProvider(getActivity())
+                .newAppHeaderController(this /* fragment */, null /* appHeader */)
+                .setIcon(mAppRow.icon)
+                .setLabel(mAppRow.label)
+                .setPackageName(mAppRow.pkg)
+                .setUid(mAppRow.uid)
+                .setButtonActions(AppHeaderController.ActionType.ACTION_APP_INFO,
+                        AppHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
+                .done(getPrefContext());
         getPreferenceScreen().addPreference(pref);
-    }
 
-    @Override
-    public void onResume() {
-        super.onResume();
         if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
             Log.w(TAG, "Missing package or uid or packageinfo");
             finish();
@@ -145,7 +144,7 @@
                             ? R.string.notification_channels_other
                             : R.string.notification_channels);
                 } else {
-                    groupCategory.setTitle(getNotificationGroupLabel(group));
+                    groupCategory.setTitle(group.getName());
                 }
                 groupCategory.setKey(group.getId());
                 groupCategory.setOrderingAsAdded(true);
@@ -161,45 +160,55 @@
                             getPrefContext());
                     channelPref.setDisabledByAdmin(mSuspendedAppsAdmin);
                     channelPref.setKey(channel.getId());
-                    channelPref.setTitle(getNotificationChannelLabel(channel));
+                    channelPref.setTitle(channel.getName());
                     channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
                     channelPref.setMultiLine(true);
+                    channelPref.setSummary(getImportanceSummary(channel.getImportance()));
+                    Bundle channelArgs = new Bundle();
+                    channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
+                    channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
+                    channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
+                    channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
+                    Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+                            ChannelNotificationSettings.class.getName(),
+                            channelArgs, null, 0, null, false, getMetricsCategory());
+                    channelPref.setIntent(channelIntent);
 
-                    if (channel.isDeleted()) {
-                        channelPref.setTitle(getString(R.string.deleted_channel_name,
-                                getNotificationChannelLabel(channel)));
-                        channelPref.setEnabled(false);
-                    } else {
-                        channelPref.setSummary(getImportanceSummary(channel.getImportance()));
-                        Bundle channelArgs = new Bundle();
-                        channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
-                        channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
-                        channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
-                        channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
-                        Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
-                                ChannelNotificationSettings.class.getName(),
-                                channelArgs, null, 0, null, false, getMetricsCategory());
-                        channelPref.setIntent(channelIntent);
+                    channelPref.setOnPreferenceChangeListener(
+                            new Preference.OnPreferenceChangeListener() {
+                                @Override
+                                public boolean onPreferenceChange(Preference preference,
+                                        Object o) {
+                                    boolean value = (Boolean) o;
+                                    int importance = value ?  IMPORTANCE_LOW : IMPORTANCE_NONE;
+                                    channel.setImportance(importance);
+                                    channel.lockFields(
+                                            NotificationChannel.USER_LOCKED_IMPORTANCE);
+                                    mBackend.updateChannel(mPkg, mUid, channel);
 
-                        channelPref.setOnPreferenceChangeListener(
-                                new Preference.OnPreferenceChangeListener() {
-                                    @Override
-                                    public boolean onPreferenceChange(Preference preference,
-                                            Object o) {
-                                        boolean value = (Boolean) o;
-                                        int importance = value ?  IMPORTANCE_LOW : IMPORTANCE_NONE;
-                                        channel.setImportance(importance);
-                                        channel.lockFields(
-                                                NotificationChannel.USER_LOCKED_IMPORTANCE);
-                                        mBackend.updateChannel(mPkg, mUid, channel);
-
-                                        return true;
-                                    }
-                                });
-                    }
+                                    return true;
+                                }
+                            });
                     groupCategory.addPreference(channelPref);
                 }
             }
+
+            if (mAppRow.settingsIntent != null) {
+                Preference intentPref = new Preference(getPrefContext());
+                intentPref.setIntent(mAppRow.settingsIntent);
+                intentPref.setTitle(mContext.getString(R.string.app_settings_link));
+                getPreferenceScreen().addPreference(intentPref);
+            }
+
+            int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
+            if (deletedChannelCount > 0) {
+                DimmableIconPreference deletedPref = new DimmableIconPreference(getPrefContext());
+                deletedPref.setEnabled(false);
+                deletedPref.setTitle(getResources().getQuantityString(
+                        R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
+                deletedPref.setIcon(R.drawable.ic_info);
+                getPreferenceScreen().addPreference(deletedPref);
+            }
         }
         updateDependents(mAppRow.banned);
     }
@@ -257,8 +266,8 @@
             if (left.isDeleted() != right.isDeleted()) {
                 return Boolean.compare(left.isDeleted(), right.isDeleted());
             }
-            CharSequence leftName = getNotificationChannelLabel(left);
-            CharSequence rightName = getNotificationChannelLabel(right);
+            CharSequence leftName = left.getName();
+            CharSequence rightName = right.getName();
             if (!Objects.equals(leftName, rightName)) {
                 return sCollator.compare(leftName.toString(), rightName.toString());
             }
@@ -278,8 +287,8 @@
                     } else if (right.getId() == null && left.getId() != null) {
                         return -1;
                     }
-                    CharSequence leftName = getNotificationGroupLabel(left);
-                    CharSequence rightName = getNotificationGroupLabel(right);
+                    CharSequence leftName = left.getName();
+                    CharSequence rightName = right.getName();
                     // sort rest of the groups by name
                     if (!Objects.equals(leftName, rightName)) {
                         return sCollator.compare(leftName.toString(), rightName.toString());
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index 18e00f8..7f7aa08 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -73,19 +73,22 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public void onResume() {
+        super.onResume();
         if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
             Log.w(TAG, "Missing package or uid or packageinfo or channel");
-            toastAndFinish();
+            finish();
             return;
         }
-        final Activity activity = getActivity();
+
+        if (getPreferenceScreen() != null) {
+            getPreferenceScreen().removeAll();
+        }
         addPreferencesFromResource(R.xml.channel_notification_settings);
+        getPreferenceScreen().setOrderingAsAdded(true);
 
         // load settings intent
-        ArrayMap<String, NotificationBackend.AppRow>
-                rows = new ArrayMap<String, NotificationBackend.AppRow>();
+        ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<String, NotificationBackend.AppRow>();
         rows.put(mAppRow.pkg, mAppRow);
         collectConfigActivities(rows);
 
@@ -109,36 +112,25 @@
             setupBlockAndImportance();
             updateDependents();
         }
-        final Preference pref = FeatureFactory.getFactory(activity)
-            .getApplicationFeatureProvider(activity)
-            .newAppHeaderController(this /* fragment */, null /* appHeader */)
-            .setIcon(mAppRow.icon)
-            .setLabel(getNotificationChannelLabel(mChannel))
-            .setSummary(mAppRow.label)
-            .setPackageName(mAppRow.pkg)
-            .setUid(mAppRow.uid)
-            .setAppNotifPrefIntent(mAppRow.settingsIntent)
-            .setButtonActions(AppHeaderController.ActionType.ACTION_APP_INFO,
-                AppHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
-            .done(getPrefContext());
+        final Preference pref = FeatureFactory.getFactory(getActivity())
+                .getApplicationFeatureProvider(getActivity())
+                .newAppHeaderController(this /* fragment */, null /* appHeader */)
+                .setIcon(mAppRow.icon)
+                .setLabel(mChannel.getName())
+                .setSummary(mAppRow.label)
+                .setPackageName(mAppRow.pkg)
+                .setUid(mAppRow.uid)
+                .setButtonActions(AppHeaderController.ActionType.ACTION_APP_INFO,
+                        AppHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
+                .done(getPrefContext());
         getPreferenceScreen().addPreference(pref);
-    }
 
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
-            Log.w(TAG, "Missing package or uid or packageinfo or channel");
-            finish();
-            return;
+        if (mAppRow.settingsIntent != null) {
+            Preference intentPref = new Preference(getPrefContext());
+            intentPref.setIntent(mAppRow.settingsIntent);
+            intentPref.setTitle(mContext.getString(R.string.app_settings_link));
+            getPreferenceScreen().addPreference(intentPref);
         }
-        mLights.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
-        if (mImportance.isEnabled()) {
-            mImportance.setDisabledByAdmin(mSuspendedAppsAdmin);
-        }
-        mPriority.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mVisibilityOverride.setDisabledByAdmin(mSuspendedAppsAdmin);
     }
 
     private void setupLights() {
@@ -321,6 +313,7 @@
                         return true;
                     }
                 });
+        mVisibilityOverride.setDisabledByAdmin(mSuspendedAppsAdmin);
     }
 
     private void setRestrictedIfNotificationFeaturesDisabled(CharSequence entry,
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 0881eb7..c1ef018 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -124,7 +124,7 @@
 
     public ParceledListSlice<NotificationChannelGroup> getChannelGroups(String pkg, int uid) {
         try {
-            return sINM.getNotificationChannelGroupsForPackage(pkg, uid, true);
+            return sINM.getNotificationChannelGroupsForPackage(pkg, uid, false);
         } catch (Exception e) {
             Log.w(TAG, "Error calling NoMan", e);
             return ParceledListSlice.emptyList();
@@ -139,6 +139,15 @@
         }
     }
 
+    public int getDeletedChannelCount(String pkg, int uid) {
+        try {
+            return sINM.getDeletedChannelCount(pkg, uid);
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+            return 0;
+        }
+    }
+
     static class Row {
         public String section;
     }
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 6a40ea5..960c3b8 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -127,15 +127,6 @@
         }
 
         mUserId = UserHandle.getUserId(mUid);
-        mAppRow = mBackend.loadAppRow(mContext, mPm, mPkgInfo);
-        mChannel = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_ID)) ?
-                mBackend.getChannel(mPkg, mUid, args.getString(Settings.EXTRA_CHANNEL_ID)) : null;
-
-        mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
-                mContext, mPkg, mUserId);
-        NotificationManager.Policy policy =
-                NotificationManager.from(mContext).getNotificationPolicy();
-        mDndVisualEffectsSuppressed = policy == null ? false : policy.suppressedVisualEffects != 0;
     }
 
     @Override
@@ -146,12 +137,19 @@
             finish();
             return;
         }
+        mAppRow = mBackend.loadAppRow(mContext, mPm, mPkgInfo);
+        Bundle args = getArguments();
+        mChannel = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_ID)) ?
+                mBackend.getChannel(mPkg, mUid, args.getString(Settings.EXTRA_CHANNEL_ID)) : null;
+
         mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
                 mContext, mPkg, mUserId);
-        if (mBlock.isEnabled()) {
-            mBlock.setDisabledByAdmin(mSuspendedAppsAdmin);
-        }
-        mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
+        NotificationManager.Policy policy =
+                NotificationManager.from(mContext).getNotificationPolicy();
+        mDndVisualEffectsSuppressed = policy == null ? false : policy.suppressedVisualEffects != 0;
+
+        mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
+                mContext, mPkg, mUserId);
     }
 
     protected void setVisible(Preference p, boolean visible) {
@@ -251,25 +249,4 @@
                 return getContext().getString(R.string.notification_importance_high);
         }
     }
-
-    protected CharSequence getNotificationGroupLabel(NotificationChannelGroup group) {
-        return getLabel(group.getName(), group.getNameResId());
-    }
-
-    protected CharSequence getNotificationChannelLabel(NotificationChannel channel) {
-        return getLabel(channel.getName(), channel.getNameResId());
-    }
-
-    private CharSequence getLabel(CharSequence name, int nameResId) {
-        if (!TextUtils.isEmpty(name)) {
-            return name;
-        }
-        try {
-            ApplicationInfo info = mPm.getApplicationInfoAsUser(mAppRow.pkg, 0, mAppRow.userId);
-            return mPm.getText(mAppRow.pkg, nameResId, info);
-        } catch (NameNotFoundException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
 }
diff --git a/src/com/android/settings/notification/RedactionInterstitial.java b/src/com/android/settings/notification/RedactionInterstitial.java
index f435257..08474a0 100644
--- a/src/com/android/settings/notification/RedactionInterstitial.java
+++ b/src/com/android/settings/notification/RedactionInterstitial.java
@@ -130,7 +130,7 @@
             if (v.getId() == R.id.redaction_done_button) {
                 final RedactionInterstitial activity = (RedactionInterstitial) getActivity();
                 if (activity != null) {
-                    activity.setResult(RESULT_OK, activity.getResultIntentData());
+                    activity.setResult(RESULT_OK, null);
                     finish();
                 }
             }
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index df60573..79a8970 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -98,7 +98,7 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return R.xml.ia_sound_settings;
+        return R.xml.sound_settings;
     }
 
     @Override
@@ -216,7 +216,7 @@
                 public List<SearchIndexableResource> getXmlResourcesToIndex(
                         Context context, boolean enabled) {
                     final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.ia_sound_settings;
+                    sir.xmlResId = R.xml.sound_settings;
                     return Arrays.asList(sir);
                 }
 
diff --git a/src/com/android/settings/notification/WorkSoundPreferenceController.java b/src/com/android/settings/notification/WorkSoundPreferenceController.java
index a4d7b8d..a80f503 100644
--- a/src/com/android/settings/notification/WorkSoundPreferenceController.java
+++ b/src/com/android/settings/notification/WorkSoundPreferenceController.java
@@ -222,7 +222,7 @@
                     KEY_WORK_ALARM_RINGTONE);
         }
         if (!mVoiceCapable) {
-            mWorkPreferenceCategory.removePreference(mWorkPhoneRingtonePreference);
+            mWorkPhoneRingtonePreference.setVisible(false);
             mWorkPhoneRingtonePreference = null;
         }
 
diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java
index b146a22..879bd77 100644
--- a/src/com/android/settings/search/Index.java
+++ b/src/com/android/settings/search/Index.java
@@ -331,7 +331,7 @@
 
     private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
         try {
-            final int baseRank = Ranking.getBaseRankForAuthority(authority);
+            final int baseRank = 0;
 
             final Context context = mBaseAuthority.equals(authority) ?
                     mContext : mContext.createPackageContext(packageName, 0);
diff --git a/src/com/android/settings/search/Ranking.java b/src/com/android/settings/search/Ranking.java
deleted file mode 100644
index 20f578b..0000000
--- a/src/com/android/settings/search/Ranking.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2014 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.search;
-
-import com.android.settings.ChooseLockGeneric;
-import com.android.settings.DateTimeSettings;
-import com.android.settings.DevelopmentSettings;
-import com.android.settings.DeviceInfoSettings;
-import com.android.settings.DisplaySettings;
-import com.android.settings.LegalSettings;
-import com.android.settings.PrivacySettings;
-import com.android.settings.ScreenPinningSettings;
-import com.android.settings.SecuritySettings;
-import com.android.settings.WallpaperTypeSettings;
-import com.android.settings.accessibility.AccessibilitySettings;
-import com.android.settings.accounts.UserAndAccountDashboardFragment;
-import com.android.settings.applications.AdvancedAppSettings;
-import com.android.settings.applications.SpecialAccessSettings;
-import com.android.settings.bluetooth.BluetoothSettings;
-import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
-import com.android.settings.datausage.DataUsageMeteredSettings;
-import com.android.settings.datausage.DataUsageSummary;
-import com.android.settings.deviceinfo.StorageDashboardFragment;
-import com.android.settings.deviceinfo.StorageSettings;
-import com.android.settings.display.ScreenZoomSettings;
-import com.android.settings.enterprise.EnterprisePrivacySettings;
-import com.android.settings.fuelgauge.BatterySaverSettings;
-import com.android.settings.fuelgauge.PowerUsageSummary;
-import com.android.settings.gestures.AssistGestureSettings;
-import com.android.settings.gestures.DoubleTapPowerSettings;
-import com.android.settings.gestures.DoubleTapScreenSettings;
-import com.android.settings.gestures.DoubleTwistGestureSettings;
-import com.android.settings.gestures.PickupGestureSettings;
-import com.android.settings.gestures.SwipeToNotificationSettings;
-import com.android.settings.language.LanguageAndInputSettings;
-import com.android.settings.location.LocationSettings;
-import com.android.settings.location.ScanningSettings;
-import com.android.settings.network.NetworkDashboardFragment;
-import com.android.settings.notification.ConfigureNotificationSettings;
-import com.android.settings.notification.SoundSettings;
-import com.android.settings.notification.ZenModeAutomationSettings;
-import com.android.settings.notification.ZenModePrioritySettings;
-import com.android.settings.notification.ZenModeSettings;
-import com.android.settings.notification.ZenModeVisualInterruptionSettings;
-import com.android.settings.print.PrintSettingsFragment;
-import com.android.settings.sim.SimSettings;
-import com.android.settings.system.SystemDashboardFragment;
-import com.android.settings.users.UserSettings;
-import com.android.settings.wifi.ConfigureWifiSettings;
-import com.android.settings.wifi.SavedAccessPointsWifiSettings;
-import com.android.settings.wifi.WifiSettings;
-
-import java.util.HashMap;
-
-/**
- * Utility class for dealing with Search Ranking.
- */
-public final class Ranking {
-
-    public static final int RANK_WIFI = 1;
-    public static final int RANK_BT = 2;
-    public static final int RANK_SIM = 3;
-    public static final int RANK_DATA_USAGE = 4;
-    public static final int RANK_WIRELESS = 5;
-    public static final int RANK_DISPLAY = 6;
-    public static final int RANK_WALLPAPER = 7;
-    public static final int RANK_NOTIFICATIONS = 8;
-    public static final int RANK_SOUND = 9;
-    public static final int RANK_APPS = 10;
-    public static final int RANK_STORAGE = 11;
-    public static final int RANK_POWER_USAGE = 12;
-    public static final int RANK_USERS = 13;
-    public static final int RANK_LOCATION = 14;
-    public static final int RANK_SECURITY = 15;
-    public static final int RANK_ACCOUNT = 16;
-    public static final int RANK_IME = 17;
-    public static final int RANK_PRIVACY = 18;
-    public static final int RANK_DATE_TIME = 19;
-    public static final int RANK_ACCESSIBILITY = 20;
-    public static final int RANK_PRINTING = 21;
-    public static final int RANK_DEVELOPEMENT = 22;
-    public static final int RANK_DEVICE_INFO = 23;
-    public static final int RANK_GESTURE = 24;
-
-    public static final int RANK_UNDEFINED = -1;
-    public static final int RANK_OTHERS = 1024;
-    public static final int BASE_RANK_DEFAULT = 2048;
-
-    public static int sCurrentBaseRank = BASE_RANK_DEFAULT;
-
-    private static HashMap<String, Integer> sRankMap = new HashMap<String, Integer>();
-    private static HashMap<String, Integer> sBaseRankMap = new HashMap<String, Integer>();
-
-    static {
-        // Wi-Fi
-        sRankMap.put(WifiSettings.class.getName(), RANK_WIFI);
-        sRankMap.put(ConfigureWifiSettings.class.getName(), RANK_WIFI);
-        sRankMap.put(SavedAccessPointsWifiSettings.class.getName(), RANK_WIFI);
-
-        // BT
-        sRankMap.put(BluetoothSettings.class.getName(), RANK_BT);
-        sRankMap.put(ConnectedDeviceDashboardFragment.class.getName(), RANK_BT);
-
-        // SIM Cards
-        sRankMap.put(SimSettings.class.getName(), RANK_SIM);
-
-        // DataUsage
-        sRankMap.put(DataUsageSummary.class.getName(), RANK_DATA_USAGE);
-        sRankMap.put(DataUsageMeteredSettings.class.getName(), RANK_DATA_USAGE);
-
-        // Other wireless settings
-        sRankMap.put(NetworkDashboardFragment.class.getName(), RANK_WIRELESS);
-
-        // Display
-        sRankMap.put(DisplaySettings.class.getName(), RANK_DISPLAY);
-        sRankMap.put(ScreenZoomSettings.class.getName(), RANK_WIFI);
-
-        // Wallpapers
-        sRankMap.put(WallpaperTypeSettings.class.getName(), RANK_WALLPAPER);
-
-        // Sound
-        sRankMap.put(SoundSettings.class.getName(), RANK_SOUND);
-
-        // Notifications
-        sRankMap.put(ConfigureNotificationSettings.class.getName(), RANK_NOTIFICATIONS);
-        sRankMap.put(ZenModeSettings.class.getName(), RANK_NOTIFICATIONS);
-        sRankMap.put(ZenModePrioritySettings.class.getName(), RANK_NOTIFICATIONS);
-        sRankMap.put(ZenModeAutomationSettings.class.getName(), RANK_NOTIFICATIONS);
-        sRankMap.put(ZenModeVisualInterruptionSettings.class.getName(), RANK_NOTIFICATIONS);
-
-        // Storage
-        sRankMap.put(StorageSettings.class.getName(), RANK_STORAGE);
-
-        // Battery
-        sRankMap.put(PowerUsageSummary.class.getName(), RANK_POWER_USAGE);
-        sRankMap.put(BatterySaverSettings.class.getName(), RANK_POWER_USAGE);
-
-        // Advanced app settings
-        sRankMap.put(AdvancedAppSettings.class.getName(), RANK_APPS);
-        sRankMap.put(SpecialAccessSettings.class.getName(), RANK_APPS);
-
-        // Users
-        sRankMap.put(UserSettings.class.getName(), RANK_USERS);
-
-        // Gestures
-        sRankMap.put(AssistGestureSettings.class.getName(), RANK_GESTURE);
-        sRankMap.put(DoubleTapPowerSettings.class.getName(), RANK_GESTURE);
-        sRankMap.put(DoubleTapScreenSettings.class.getName(), RANK_GESTURE);
-        sRankMap.put(DoubleTwistGestureSettings.class.getName(), RANK_GESTURE);
-        sRankMap.put(PickupGestureSettings.class.getName(), RANK_GESTURE);
-        sRankMap.put(SwipeToNotificationSettings.class.getName(), RANK_GESTURE);
-
-        // Location
-        sRankMap.put(LocationSettings.class.getName(), RANK_LOCATION);
-        sRankMap.put(ScanningSettings.class.getName(), RANK_LOCATION);
-
-        // Security
-        sRankMap.put(SecuritySettings.class.getName(), RANK_SECURITY);
-        sRankMap.put(ChooseLockGeneric.ChooseLockGenericFragment.class.getName(), RANK_SECURITY);
-        sRankMap.put(ScreenPinningSettings.class.getName(), RANK_SECURITY);
-        sRankMap.put(EnterprisePrivacySettings.class.getName(), RANK_SECURITY);
-
-        // Accounts
-        sRankMap.put(UserAndAccountDashboardFragment.class.getName(), RANK_ACCOUNT);
-
-        // IMEs
-        sRankMap.put(LanguageAndInputSettings.class.getName(), RANK_IME);
-
-        // Privacy
-        sRankMap.put(PrivacySettings.class.getName(), RANK_PRIVACY);
-
-        // Date / Time
-        sRankMap.put(DateTimeSettings.class.getName(), RANK_DATE_TIME);
-
-        // Accessibility
-        sRankMap.put(AccessibilitySettings.class.getName(), RANK_ACCESSIBILITY);
-
-        // Print
-        sRankMap.put(PrintSettingsFragment.class.getName(), RANK_PRINTING);
-
-        // Development
-        sRankMap.put(DevelopmentSettings.class.getName(), RANK_DEVELOPEMENT);
-
-        // Device infos
-        sRankMap.put(DeviceInfoSettings.class.getName(), RANK_DEVICE_INFO);
-        sRankMap.put(LegalSettings.class.getName(), RANK_DEVICE_INFO);
-
-        sRankMap.put(StorageDashboardFragment.class.getName(), RANK_STORAGE);
-        sRankMap.put(SystemDashboardFragment.class.getName(), RANK_DEVICE_INFO);
-
-        sBaseRankMap.put("com.android.settings", 0);
-    }
-
-    public static int getRankForClassName(String className) {
-        Integer rank = sRankMap.get(className);
-        return (rank != null) ? (int) rank: RANK_OTHERS;
-    }
-
-    public static int getBaseRankForAuthority(String authority) {
-        synchronized (sBaseRankMap) {
-            Integer base = sBaseRankMap.get(authority);
-            if (base != null) {
-                return base;
-            }
-            sCurrentBaseRank++;
-            sBaseRankMap.put(authority, sCurrentBaseRank);
-            return sCurrentBaseRank;
-        }
-    }
-}
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 561c2c6..a659077 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -95,8 +95,7 @@
     static void addIndex(Class<?> indexClass, @XmlRes int xmlResId,
             @DrawableRes int iconResId) {
         String className = indexClass.getName();
-        int rank = Ranking.getRankForClassName(className);
-        sResMap.put(className, new SearchIndexableResource(rank, xmlResId, className, iconResId));
+        sResMap.put(className, new SearchIndexableResource(0, xmlResId, className, iconResId));
     }
 
     static {
diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java
index d6e6959..0b1d339 100644
--- a/src/com/android/settings/search2/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search2/DatabaseIndexingManager.java
@@ -27,7 +27,6 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteFullException;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.provider.SearchIndexableData;
@@ -42,7 +41,6 @@
 import com.android.settings.core.PreferenceController;
 import com.android.settings.search.IndexDatabaseHelper;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.Ranking;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.search.SearchIndexableResources;
 
@@ -53,10 +51,12 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
@@ -82,9 +82,40 @@
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
 
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.CLASS_NAME;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_ENTRIES;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEY_REF;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_RANK;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ENABLED;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ICON;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_ACTION;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.LOCALE;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.SCREEN_TITLE;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.USER_ID;
+import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
+
+import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_ID;
+import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
+import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_KEY;
+import static com.android.settings.search2.DatabaseResultLoader.SELECT_COLUMNS;
+
 /**
  * Consumes the SearchIndexableProvider content providers.
  * Updates the Resource, Raw Data and non-indexable data for Search.
+ *
+ * TODO this class needs to be refactored by moving most of its methods into controllers
  */
 public class DatabaseIndexingManager {
     private static final String LOG_TAG = "DatabaseIndexingManager";
@@ -93,51 +124,14 @@
     private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
     private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
 
-    private static final List<String> EMPTY_LIST = Collections.<String>emptyList();
+    private static final List<String> EMPTY_LIST = Collections.emptyList();
 
     private final String mBaseAuthority;
 
-    /**
-     * A private class to describe the update data for the Index database
-     */
-    private static class UpdateData {
-        public List<SearchIndexableData> dataToUpdate;
-        public List<SearchIndexableData> dataToDelete;
-        public Map<String, List<String>> nonIndexableKeys;
-
-        public boolean forceUpdate;
-        public boolean fullIndex;
-
-        public UpdateData() {
-            dataToUpdate = new ArrayList<>();
-            dataToDelete = new ArrayList<>();
-            nonIndexableKeys = new HashMap<>();
-        }
-
-        public UpdateData(DatabaseIndexingManager.UpdateData other) {
-            dataToUpdate = new ArrayList<>(other.dataToUpdate);
-            dataToDelete = new ArrayList<>(other.dataToDelete);
-            nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
-            forceUpdate = other.forceUpdate;
-            fullIndex = other.fullIndex;
-        }
-
-        public DatabaseIndexingManager.UpdateData copy() {
-            return new DatabaseIndexingManager.UpdateData(this);
-        }
-
-        public void clear() {
-            dataToUpdate.clear();
-            dataToDelete.clear();
-            nonIndexableKeys.clear();
-            forceUpdate = false;
-            fullIndex = false;
-        }
-    }
-
     private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
-    private final DatabaseIndexingManager.UpdateData mDataToProcess =
-            new DatabaseIndexingManager.UpdateData();
+
+    @VisibleForTesting
+    final UpdateData mDataToProcess = new UpdateData();
     private Context mContext;
 
     public DatabaseIndexingManager(Context context, String baseAuthority) {
@@ -157,43 +151,211 @@
         AsyncTask.execute(new Runnable() {
             @Override
             public void run() {
-                final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
-                List<ResolveInfo> list =
-                        mContext.getPackageManager().queryIntentContentProviders(intent, 0);
-
-                final int size = list.size();
-                for (int n = 0; n < size; n++) {
-                    final ResolveInfo info = list.get(n);
-                    if (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) {
-                        continue;
-                    }
-                    final String authority = info.providerInfo.authority;
-                    final String packageName = info.providerInfo.packageName;
-
-                    addIndexablesFromRemoteProvider(packageName, authority);
-                    addNonIndexablesKeysFromRemoteProvider(packageName, authority);
-                }
-
-                mDataToProcess.fullIndex = true;
-                updateInternal();
+                performIndexing();
             }
         });
     }
 
-    private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
+    /**
+     * Accumulate all data and non-indexable keys from each of the content-providers.
+     * Only the first indexing for the default language gets static search results - subsequent
+     * calls will only gather non-indexable keys.
+     */
+    @VisibleForTesting
+    void performIndexing() {
+        final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
+        final List<ResolveInfo> list =
+                mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+
+        final boolean isLocaleIndexed = isLocaleIndexed();
+
+        for (final ResolveInfo info : list) {
+            if (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) {
+                continue;
+            }
+            final String authority = info.providerInfo.authority;
+            final String packageName = info.providerInfo.packageName;
+
+            if (!isLocaleIndexed) {
+                addIndexablesFromRemoteProvider(packageName, authority);
+            }
+            addNonIndexablesKeysFromRemoteProvider(packageName, authority);
+        }
+
+        final String localeStr = Locale.getDefault().toString();
+        updateDatabase(isLocaleIndexed, localeStr);
+    }
+
+    @VisibleForTesting
+    boolean isLocaleIndexed() {
+        final String locale = Locale.getDefault().toString();
+        return IndexDatabaseHelper.getInstance(mContext).isLocaleAlreadyIndexed(mContext, locale);
+    }
+
+    /**
+     * Adds new data to the database and verifies the correctness of the ENABLED column.
+     * First, the data to be updated and all non-indexable keys are copied locally.
+     * Then all new data to be added is inserted.
+     * Then search results are verified to have the correct value of enabled.
+     * Finally, we record that the locale has been indexed.
+     *
+     * @param isIncrementalUpdate true when the language has already been indexed.
+     * @param localeStr the default locale for the device.
+     */
+    @VisibleForTesting
+    void updateDatabase(boolean isIncrementalUpdate, String localeStr) {
+        mIsAvailable.set(false);
+        final UpdateData copy;
+
+        synchronized (mDataToProcess) {
+            copy = mDataToProcess.copy();
+            mDataToProcess.clear();
+        }
+
+        final List<SearchIndexableData> dataToUpdate = copy.dataToUpdate;
+        final Map<String, Set<String>> nonIndexableKeys = copy.nonIndexableKeys;
+
+        final SQLiteDatabase database = getWritableDatabase();
+        if (database == null) {
+            Log.w(LOG_TAG, "Cannot indexDatabase Index as I cannot get a writable database");
+            return;
+        }
+
         try {
-            final int baseRank = Ranking.getBaseRankForAuthority(authority);
+            database.beginTransaction();
+
+            // Add new data from Providers at initial index time, or inserted later.
+            if (dataToUpdate.size() > 0) {
+                addDataToDatabase(database, localeStr, dataToUpdate, nonIndexableKeys);
+            }
+
+            // Only check for non-indexable key updates after initial index.
+            // Enabled state with non-indexable keys is checked when items are first inserted.
+            if (isIncrementalUpdate) {
+                updateDataInDatabase(database, nonIndexableKeys);
+            }
+
+            database.setTransactionSuccessful();
+        } finally {
+            database.endTransaction();
+        }
+        // TODO Refactor: move the locale out of the helper class
+        IndexDatabaseHelper.setLocaleIndexed(mContext, localeStr);
+
+        mIsAvailable.set(true);
+    }
+
+    /**
+     * Inserts {@link SearchIndexableData} into the database.
+     *
+     * @param database where the data will be inserted.
+     * @param localeStr is the locale of the data to be inserted.
+     * @param dataToUpdate is a {@link List} of the data to be inserted.
+     * @param nonIndexableKeys is a {@link Map} from Package Name to a {@link Set} of keys which
+     *                         identify search results which should not be surfaced.
+     */
+    @VisibleForTesting
+    void addDataToDatabase(SQLiteDatabase database, String localeStr,
+            List<SearchIndexableData> dataToUpdate, Map<String, Set<String>> nonIndexableKeys) {
+        final long current = System.currentTimeMillis();
+
+        for (SearchIndexableData data : dataToUpdate) {
+            try {
+                indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys);
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Cannot index: " + (data != null ? data.className : data)
+                        + " for locale: " + localeStr, e);
+            }
+        }
+
+        final long now = System.currentTimeMillis();
+        Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
+                (now - current) + " millis");
+    }
+
+    /**
+     * Upholds the validity of enabled data for the user.
+     * All rows which are enabled but are now flagged with non-indexable keys will become disabled.
+     * All rows which are disabled but no longer a non-indexable key will become enabled.
+     *
+     * @param database The database to validate.
+     * @param nonIndexableKeys A map between package name and the set of non-indexable keys for it.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void updateDataInDatabase(SQLiteDatabase database,
+            Map<String, Set<String>> nonIndexableKeys) {
+        final String whereEnabled = ENABLED + " = 1";
+        final String whereDisabled = ENABLED + " = 0";
+
+        final Cursor enabledResults = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,
+                whereEnabled, null, null, null, null);
+
+        final ContentValues enabledToDisabledValue = new ContentValues();
+        enabledToDisabledValue.put(ENABLED, 0);
+
+        String packageName;
+        // TODO Refactor: Move these two loops into one method.
+        while (enabledResults.moveToNext()) {
+            // Package name is the key for remote providers.
+            // If package name is null, the provider is Settings.
+            packageName = enabledResults.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
+            if (packageName == null) {
+                packageName = mContext.getPackageName();
+            }
+
+            final String key = enabledResults.getString(COLUMN_INDEX_KEY);
+            final Set<String> packageKeys = nonIndexableKeys.get(packageName);
+
+            // The indexed item is set to Enabled but is now non-indexable
+            if (packageKeys != null && packageKeys.contains(key)) {
+                final String whereClause = DOCID + " = " + enabledResults.getInt(COLUMN_INDEX_ID);
+                database.update(TABLE_PREFS_INDEX, enabledToDisabledValue, whereClause, null);
+            }
+        }
+        enabledResults.close();
+
+        final Cursor disabledResults = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,
+                whereDisabled, null, null, null, null);
+
+        final ContentValues disabledToEnabledValue = new ContentValues();
+        disabledToEnabledValue.put(ENABLED, 1);
+
+        while (disabledResults.moveToNext()) {
+            // Package name is the key for remote providers.
+            // If package name is null, the provider is Settings.
+            packageName = disabledResults.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
+            if (packageName == null) {
+                packageName = mContext.getPackageName();
+            }
+
+            final String key = disabledResults.getString(COLUMN_INDEX_KEY);
+            final Set<String> packageKeys = nonIndexableKeys.get(packageName);
+
+            // The indexed item is set to Disabled but is no longer non-indexable.
+            // We do not enable keys when packageKeys is null because it means the keys came
+            // from an unrecognized package and therefore should not be surfaced as results.
+            if (packageKeys != null && !packageKeys.contains(key)) {
+                String whereClause = DOCID + " = " + disabledResults.getInt(COLUMN_INDEX_ID);
+                database.update(TABLE_PREFS_INDEX, disabledToEnabledValue, whereClause, null);
+            }
+        }
+        disabledResults.close();
+    }
+
+    @VisibleForTesting
+    boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
+        try {
 
             final Context context = mBaseAuthority.equals(authority) ?
                     mContext : mContext.createPackageContext(packageName, 0);
 
             final Uri uriForResources = buildUriForXmlResources(authority);
             addIndexablesForXmlResourceUri(context, packageName, uriForResources,
-                    SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS, baseRank);
+                    SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS);
 
             final Uri uriForRawData = buildUriForRawData(authority);
             addIndexablesForRawDataUri(context, packageName, uriForRawData,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS, baseRank);
+                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS);
             return true;
         } catch (PackageManager.NameNotFoundException e) {
             Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
@@ -202,11 +364,12 @@
         }
     }
 
-    private void addNonIndexablesKeysFromRemoteProvider(String packageName,
+    @VisibleForTesting
+    void addNonIndexablesKeysFromRemoteProvider(String packageName,
             String authority) {
         final List<String> keys =
                 getNonIndexablesKeysFromRemoteProvider(packageName, authority);
-        addNonIndexableKeys(packageName, keys);
+        addNonIndexableKeys(packageName, new HashSet<>(keys));
     }
 
     private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
@@ -235,7 +398,7 @@
             return EMPTY_LIST;
         }
 
-        List<String> result = new ArrayList<String>();
+        final List<String> result = new ArrayList<>();
         try {
             final int count = cursor.getCount();
             if (count > 0) {
@@ -256,31 +419,19 @@
         }
     }
 
-    public void deleteIndexableData(SearchIndexableData data) {
-        synchronized (mDataToProcess) {
-            mDataToProcess.dataToDelete.add(data);
-        }
-    }
-
-    public void addNonIndexableKeys(String authority, List<String> keys) {
+    public void addNonIndexableKeys(String authority, Set<String> keys) {
         synchronized (mDataToProcess) {
             mDataToProcess.nonIndexableKeys.put(authority, keys);
         }
     }
 
-    private void updateFromRemoteProvider(String packageName, String authority) {
-        if (addIndexablesFromRemoteProvider(packageName, authority)) {
-            updateInternal();
-        }
-    }
-
     /**
      * Update the Index for a specific class name resources
      *
-     * @param className the class name (typically a fragment name).
-     * @param rebuild true means that you want to delete the data from the Index first.
+     * @param className              the class name (typically a fragment name).
+     * @param rebuild                true means that you want to delete the data from the Index first.
      * @param includeInSearchResults true means that you want the bit "enabled" set so that the
-     * data will be seen included into the search results
+     *                               data will be seen included into the search results
      */
     public void updateFromClassNameResource(String className, final boolean rebuild,
             boolean includeInSearchResults) {
@@ -297,12 +448,8 @@
         AsyncTask.execute(new Runnable() {
             @Override
             public void run() {
-                if (rebuild) {
-                    deleteIndexableData(res);
-                }
                 addIndexableData(res);
-                mDataToProcess.forceUpdate = true;
-                updateInternal();
+                performIndexing();
                 res.enabled = false;
             }
         });
@@ -313,8 +460,7 @@
             @Override
             public void run() {
                 addIndexableData(data);
-                mDataToProcess.forceUpdate = true;
-                updateInternal();
+                performIndexing();
             }
         });
     }
@@ -347,18 +493,8 @@
                 SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
     }
 
-    private void updateInternal() {
-        synchronized (mDataToProcess) {
-            final DatabaseIndexingManager.UpdateIndexTask task =
-                    new DatabaseIndexingManager.UpdateIndexTask();
-            DatabaseIndexingManager.UpdateData copy = mDataToProcess.copy();
-            task.execute(copy);
-            mDataToProcess.clear();
-        }
-    }
-
     private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,
-            Uri uri, String[] projection, int baseRank) {
+            Uri uri, String[] projection) {
 
         final ContentResolver resolver = packageContext.getContentResolver();
         final Cursor cursor = resolver.query(uri, projection, null, null, null);
@@ -373,7 +509,6 @@
             if (count > 0) {
                 while (cursor.moveToNext()) {
                     final int providerRank = cursor.getInt(COLUMN_INDEX_XML_RES_RANK);
-                    final int rank = (providerRank > 0) ? baseRank + providerRank : baseRank;
 
                     final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);
 
@@ -387,7 +522,6 @@
                             COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS);
 
                     SearchIndexableResource sir = new SearchIndexableResource(packageContext);
-                    sir.rank = rank;
                     sir.xmlResId = xmlResId;
                     sir.className = className;
                     sir.packageName = packageName;
@@ -405,7 +539,7 @@
     }
 
     private void addIndexablesForRawDataUri(Context packageContext, String packageName,
-            Uri uri, String[] projection, int baseRank) {
+            Uri uri, String[] projection) {
 
         final ContentResolver resolver = packageContext.getContentResolver();
         final Cursor cursor = resolver.query(uri, projection, null, null, null);
@@ -420,7 +554,6 @@
             if (count > 0) {
                 while (cursor.moveToNext()) {
                     final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK);
-                    final int rank = (providerRank > 0) ? baseRank + providerRank : baseRank;
 
                     final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE);
                     final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
@@ -443,7 +576,6 @@
                     final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID);
 
                     SearchIndexableRaw data = new SearchIndexableRaw(packageContext);
-                    data.rank = rank;
                     data.title = title;
                     data.summaryOn = summaryOn;
                     data.summaryOff = summaryOff;
@@ -468,7 +600,7 @@
     }
 
     public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
-            SearchIndexableData data, Map<String, List<String>> nonIndexableKeys) {
+            SearchIndexableData data, Map<String, Set<String>> nonIndexableKeys) {
         if (data instanceof SearchIndexableResource) {
             indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
         } else if (data instanceof SearchIndexableRaw) {
@@ -502,7 +634,7 @@
     }
 
     private void indexOneResource(SQLiteDatabase database, String localeStr,
-            SearchIndexableResource sir, Map<String, List<String>> nonIndexableKeysFromResource) {
+            SearchIndexableResource sir, Map<String, Set<String>> nonIndexableKeysFromResource) {
 
         if (sir == null) {
             Log.e(LOG_TAG, "Cannot index a null resource!");
@@ -512,9 +644,9 @@
         final List<String> nonIndexableKeys = new ArrayList<String>();
 
         if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {
-            List<String> resNonIndxableKeys = nonIndexableKeysFromResource.get(sir.packageName);
-            if (resNonIndxableKeys != null && resNonIndxableKeys.size() > 0) {
-                nonIndexableKeys.addAll(resNonIndxableKeys);
+            Set<String> resNonIndexableKeys = nonIndexableKeysFromResource.get(sir.packageName);
+            if (resNonIndexableKeys != null && resNonIndexableKeys.size() > 0) {
+                nonIndexableKeys.addAll(resNonIndexableKeys);
             }
 
             indexFromResource(database, localeStr, sir, nonIndexableKeys);
@@ -605,6 +737,7 @@
             headerKeywords = XmlParserUtils.getDataKeywords(context, attrs);
             enabled = !nonIndexableKeys.contains(key);
 
+            // TODO: Set payload type for header results
             DatabaseRow.Builder headerBuilder = new DatabaseRow.Builder();
             headerBuilder.setLocale(localeStr)
                     .setEntries(null)
@@ -799,31 +932,29 @@
 
         ContentValues values = new ContentValues();
         values.put(IndexDatabaseHelper.IndexColumns.DOCID, row.getDocId());
-        values.put(IndexDatabaseHelper.IndexColumns.LOCALE, row.locale);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, row.rank);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, row.updatedTitle);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, row.normalizedTitle);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, row.updatedSummaryOn);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED,
-                row.normalizedSummaryOn);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, row.updatedSummaryOff);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
-                row.normalizedSummaryOff);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, row.entries);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, row.spaceDelimitedKeywords);
-        values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, row.className);
-        values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, row.screenTitle);
-        values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, row.intentAction);
-        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, row.intentTargetPackage);
-        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, row.intentTargetClass);
-        values.put(IndexDatabaseHelper.IndexColumns.ICON, row.iconResId);
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, row.enabled);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, row.key);
-        values.put(IndexDatabaseHelper.IndexColumns.USER_ID, row.userId);
-        values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, row.payloadType);
-        values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, row.payload);
+        values.put(LOCALE, row.locale);
+        values.put(DATA_RANK, row.rank);
+        values.put(DATA_TITLE, row.updatedTitle);
+        values.put(DATA_TITLE_NORMALIZED, row.normalizedTitle);
+        values.put(DATA_SUMMARY_ON, row.updatedSummaryOn);
+        values.put(DATA_SUMMARY_ON_NORMALIZED, row.normalizedSummaryOn);
+        values.put(DATA_SUMMARY_OFF, row.updatedSummaryOff);
+        values.put(DATA_SUMMARY_OFF_NORMALIZED, row.normalizedSummaryOff);
+        values.put(DATA_ENTRIES, row.entries);
+        values.put(DATA_KEYWORDS, row.spaceDelimitedKeywords);
+        values.put(CLASS_NAME, row.className);
+        values.put(SCREEN_TITLE, row.screenTitle);
+        values.put(INTENT_ACTION, row.intentAction);
+        values.put(INTENT_TARGET_PACKAGE, row.intentTargetPackage);
+        values.put(INTENT_TARGET_CLASS, row.intentTargetClass);
+        values.put(ICON, row.iconResId);
+        values.put(ENABLED, row.enabled);
+        values.put(DATA_KEY_REF, row.key);
+        values.put(USER_ID, row.userId);
+        values.put(PAYLOAD_TYPE, row.payloadType);
+        values.put(PAYLOAD, row.payload);
 
-        database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
+        database.replaceOrThrow(TABLE_PREFS_INDEX, null, values);
 
         if (!TextUtils.isEmpty(row.className) && !TextUtils.isEmpty(row.childClassName)) {
             ContentValues siteMapPair = new ContentValues();
@@ -839,129 +970,34 @@
     }
 
     /**
-     * A private class for updating the Index database
+     * A private class to describe the indexDatabase data for the Index database
      */
-    private class UpdateIndexTask extends AsyncTask<DatabaseIndexingManager.UpdateData, Integer,
-            Void> {
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static class UpdateData {
+        public List<SearchIndexableData> dataToUpdate;
+        public List<SearchIndexableData> dataToDisable;
+        public Map<String, Set<String>> nonIndexableKeys;
 
-        @Override
-        protected void onPreExecute() {
-            super.onPreExecute();
-            mIsAvailable.set(false);
+        public UpdateData() {
+            dataToUpdate = new ArrayList<>();
+            dataToDisable = new ArrayList<>();
+            nonIndexableKeys = new HashMap<>();
         }
 
-        @Override
-        protected void onPostExecute(Void aVoid) {
-            super.onPostExecute(aVoid);
-            mIsAvailable.set(true);
+        public UpdateData(UpdateData other) {
+            dataToUpdate = new ArrayList<>(other.dataToUpdate);
+            dataToDisable = new ArrayList<>(other.dataToDisable);
+            nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
         }
 
-        @Override
-        protected Void doInBackground(DatabaseIndexingManager.UpdateData... params) {
-            try {
-                final List<SearchIndexableData> dataToUpdate = params[0].dataToUpdate;
-                final List<SearchIndexableData> dataToDelete = params[0].dataToDelete;
-                final Map<String, List<String>> nonIndexableKeys = params[0].nonIndexableKeys;
-
-                final boolean forceUpdate = params[0].forceUpdate;
-                final boolean fullIndex = params[0].fullIndex;
-
-                final SQLiteDatabase database = getWritableDatabase();
-                if (database == null) {
-                    Log.e(LOG_TAG, "Cannot update Index as I cannot get a writable database");
-                    return null;
-                }
-                final String localeStr = Locale.getDefault().toString();
-
-                try {
-                    database.beginTransaction();
-                    if (dataToDelete.size() > 0) {
-                        processDataToDelete(database, localeStr, dataToDelete);
-                    }
-                    if (dataToUpdate.size() > 0) {
-                        processDataToUpdate(database, localeStr, dataToUpdate, nonIndexableKeys,
-                                forceUpdate);
-                    }
-                    database.setTransactionSuccessful();
-                } finally {
-                    database.endTransaction();
-                }
-                if (fullIndex) {
-                    IndexDatabaseHelper.setLocaleIndexed(mContext, localeStr);
-                }
-            } catch (SQLiteFullException e) {
-                Log.e(LOG_TAG, "Unable to index search, out of space", e);
-            }
-
-            return null;
+        public UpdateData copy() {
+            return new UpdateData(this);
         }
 
-        private boolean processDataToUpdate(SQLiteDatabase database, String localeStr,
-                List<SearchIndexableData> dataToUpdate, Map<String, List<String>> nonIndexableKeys,
-                boolean forceUpdate) {
-
-            if (!forceUpdate && IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)) {
-                Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");
-                return true;
-            }
-
-            boolean result = false;
-            final long current = System.currentTimeMillis();
-
-            final int count = dataToUpdate.size();
-            for (int n = 0; n < count; n++) {
-                final SearchIndexableData data = dataToUpdate.get(n);
-                try {
-                    indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys);
-                } catch (Exception e) {
-                    Log.e(LOG_TAG, "Cannot index: " + (data != null ? data.className : data)
-                            + " for locale: " + localeStr, e);
-                }
-            }
-
-            final long now = System.currentTimeMillis();
-            Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
-                    (now - current) + " millis");
-            return result;
-        }
-
-        private boolean processDataToDelete(SQLiteDatabase database, String localeStr,
-                List<SearchIndexableData> dataToDelete) {
-
-            boolean result = false;
-            final long current = System.currentTimeMillis();
-
-            final int count = dataToDelete.size();
-            for (int n = 0; n < count; n++) {
-                final SearchIndexableData data = dataToDelete.get(n);
-                if (data == null) {
-                    continue;
-                }
-                if (!TextUtils.isEmpty(data.className)) {
-                    delete(database, IndexDatabaseHelper.IndexColumns.CLASS_NAME, data.className);
-                } else {
-                    if (data instanceof SearchIndexableRaw) {
-                        final SearchIndexableRaw raw = (SearchIndexableRaw) data;
-                        if (!TextUtils.isEmpty(raw.title)) {
-                            delete(database, IndexDatabaseHelper.IndexColumns.DATA_TITLE,
-                                    raw.title);
-                        }
-                    }
-                }
-            }
-
-            final long now = System.currentTimeMillis();
-            Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " +
-                    (now - current) + " millis");
-            return result;
-        }
-
-        private int delete(SQLiteDatabase database, String columName, String value) {
-            final String whereClause = columName + "=?";
-            final String[] whereArgs = new String[]{value};
-
-            return database.delete(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, whereClause,
-                    whereArgs);
+        public void clear() {
+            dataToUpdate.clear();
+            dataToDisable.clear();
+            nonIndexableKeys.clear();
         }
     }
 
diff --git a/src/com/android/settings/search2/SavedQueryController.java b/src/com/android/settings/search2/SavedQueryController.java
new file mode 100644
index 0000000..92ca42a
--- /dev/null
+++ b/src/com/android/settings/search2/SavedQueryController.java
@@ -0,0 +1,96 @@
+/*
+ * 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.search2;
+
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Bundle;
+
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.List;
+
+public class SavedQueryController implements LoaderManager.LoaderCallbacks {
+
+    // TODO: make a generic background task manager to handle one-off tasks like this one.
+
+    private static final int LOADER_ID_SAVE_QUERY_TASK = 0;
+    private static final int LOADER_ID_REMOVE_QUERY_TASK = 1;
+    private static final int LOADER_ID_SAVED_QUERIES = 2;
+    private static final String ARG_QUERY = "remove_query";
+
+    private final Context mContext;
+    private final LoaderManager mLoaderManager;
+    private final SearchFeatureProvider mSearchFeatureProvider;
+    private final SearchResultsAdapter mResultAdapter;
+
+    public SavedQueryController(Context context, LoaderManager loaderManager,
+            SearchResultsAdapter resultsAdapter) {
+        mContext = context;
+        mLoaderManager = loaderManager;
+        mResultAdapter = resultsAdapter;
+        mSearchFeatureProvider = FeatureFactory.getFactory(context)
+                .getSearchFeatureProvider();
+    }
+
+    @Override
+    public Loader onCreateLoader(int id, Bundle args) {
+        switch (id) {
+            case LOADER_ID_SAVE_QUERY_TASK:
+                return new SavedQueryRecorder(mContext, args.getString(ARG_QUERY));
+            case LOADER_ID_REMOVE_QUERY_TASK:
+                return new SavedQueryRemover(mContext, args.getString(ARG_QUERY));
+            case LOADER_ID_SAVED_QUERIES:
+                return mSearchFeatureProvider.getSavedQueryLoader(mContext);
+        }
+        return null;
+    }
+
+    @Override
+    public void onLoadFinished(Loader loader, Object data) {
+        switch (loader.getId()) {
+            case LOADER_ID_REMOVE_QUERY_TASK:
+                mLoaderManager.restartLoader(LOADER_ID_SAVED_QUERIES, null, this);
+                break;
+            case LOADER_ID_SAVED_QUERIES:
+                mResultAdapter.displaySavedQuery((List<SearchResult>) data);
+                break;
+        }
+    }
+
+    @Override
+    public void onLoaderReset(Loader loader) {
+
+    }
+
+    public void saveQuery(String query) {
+        final Bundle args = new Bundle();
+        args.putString(ARG_QUERY, query);
+        mLoaderManager.restartLoader(LOADER_ID_SAVE_QUERY_TASK, args, this);
+    }
+
+    public void removeQuery(String query) {
+        final Bundle args = new Bundle();
+        args.putString(ARG_QUERY, query);
+        mLoaderManager.restartLoader(LOADER_ID_REMOVE_QUERY_TASK, args, this);
+    }
+
+    public void loadSavedQueries() {
+        mLoaderManager.restartLoader(LOADER_ID_SAVED_QUERIES, null, this);
+    }
+}
diff --git a/src/com/android/settings/search2/SavedQueryViewHolder.java b/src/com/android/settings/search2/SavedQueryViewHolder.java
index 6629c89..e468eb9 100644
--- a/src/com/android/settings/search2/SavedQueryViewHolder.java
+++ b/src/com/android/settings/search2/SavedQueryViewHolder.java
@@ -36,4 +36,4 @@
         titleView.setOnClickListener(v -> fragment.onSavedQueryClicked(result.title));
         removeButton.setOnClickListener(v -> fragment.onRemoveSavedQueryClicked(result.title));
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/search2/SearchFragment.java b/src/com/android/settings/search2/SearchFragment.java
index 02ff2c8..eb760ee 100644
--- a/src/com/android/settings/search2/SearchFragment.java
+++ b/src/com/android/settings/search2/SearchFragment.java
@@ -54,11 +54,11 @@
 
     // State values
     private static final String STATE_QUERY = "state_query";
+    private static final String STATE_SHOWING_SAVED_QUERY = "state_showing_saved_query";
     private static final String STATE_NEVER_ENTERED_QUERY = "state_never_entered_query";
     private static final String STATE_RESULT_CLICK_COUNT = "state_result_click_count";
 
     // Loader IDs
-    private static final int LOADER_ID_RECENTS = 0;
     private static final int LOADER_ID_DATABASE = 1;
     private static final int LOADER_ID_INSTALLED_APPS = 2;
 
@@ -74,12 +74,11 @@
     @VisibleForTesting
     String mQuery;
 
-    private final SaveQueryCallback mSaveQueryCallback =
-            new SaveQueryCallback();
-
     private boolean mNeverEnteredQuery = true;
+    private boolean mShowingSavedQuery;
     private int mResultClickCount;
     private MetricsFeatureProvider mMetricsFeatureProvider;
+    private SavedQueryController mSavedQueryController;
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     SearchFeatureProvider mSearchFeatureProvider;
@@ -117,20 +116,26 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setHasOptionsMenu(true);
-        mSearchAdapter = new SearchResultsAdapter(this);
-
-        mSearchFeatureProvider.initFeedbackButton();
-
         final LoaderManager loaderManager = getLoaderManager();
+        mSearchAdapter = new SearchResultsAdapter(this);
+        mSavedQueryController = new SavedQueryController(
+                getContext(), loaderManager, mSearchAdapter);
+        mSearchFeatureProvider.initFeedbackButton();
 
         if (savedInstanceState != null) {
             mQuery = savedInstanceState.getString(STATE_QUERY);
             mNeverEnteredQuery = savedInstanceState.getBoolean(STATE_NEVER_ENTERED_QUERY);
             mResultClickCount = savedInstanceState.getInt(STATE_RESULT_CLICK_COUNT);
-            loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
-            loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
+            mShowingSavedQuery = savedInstanceState.getBoolean(STATE_SHOWING_SAVED_QUERY);
+            if (mShowingSavedQuery) {
+                mSavedQueryController.loadSavedQueries();
+            } else {
+                loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
+                loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
+            }
         } else {
-            loaderManager.initLoader(LOADER_ID_RECENTS, null, this);
+            mShowingSavedQuery = true;
+            mSavedQueryController.loadSavedQueries();
         }
 
         final Activity activity = getActivity();
@@ -180,6 +185,7 @@
         super.onSaveInstanceState(outState);
         outState.putString(STATE_QUERY, mQuery);
         outState.putBoolean(STATE_NEVER_ENTERED_QUERY, mNeverEnteredQuery);
+        outState.putBoolean(STATE_SHOWING_SAVED_QUERY, mShowingSavedQuery);
         outState.putInt(STATE_RESULT_CLICK_COUNT, mResultClickCount);
     }
 
@@ -206,7 +212,8 @@
             final LoaderManager loaderManager = getLoaderManager();
             loaderManager.destroyLoader(LOADER_ID_DATABASE);
             loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
-            loaderManager.restartLoader(LOADER_ID_RECENTS, null /* args */, this /* callback */);
+            mShowingSavedQuery = true;
+            mSavedQueryController.loadSavedQueries();
             mSearchFeatureProvider.hideFeedbackButton();
         } else {
             restartLoaders();
@@ -218,8 +225,7 @@
     @Override
     public boolean onQueryTextSubmit(String query) {
         // Save submitted query.
-        getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_SAVE_QUERY_TASK, null,
-                mSaveQueryCallback);
+        mSavedQueryController.saveQuery(mQuery);
         hideKeyboard();
         return true;
     }
@@ -233,8 +239,6 @@
                 return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
             case LOADER_ID_INSTALLED_APPS:
                 return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
-            case LOADER_ID_RECENTS:
-                return mSearchFeatureProvider.getSavedQueryLoader(activity);
             default:
                 return null;
         }
@@ -243,18 +247,11 @@
     @Override
     public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
             List<? extends SearchResult> data) {
-        final int resultCount;
-        switch (loader.getId()) {
-            case LOADER_ID_RECENTS:
-                resultCount = mSearchAdapter.displaySavedQuery(data);
-                break;
-            default:
-                mSearchAdapter.addSearchResults(data, loader.getClass().getName());
-                if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
-                    return;
-                }
-                resultCount = mSearchAdapter.displaySearchResults();
+        mSearchAdapter.addSearchResults(data, loader.getClass().getName());
+        if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
+            return;
         }
+        final int resultCount = mSearchAdapter.displaySearchResults();
         mNoResultsView.setVisibility(resultCount == 0 ? View.VISIBLE : View.GONE);
         mSearchFeatureProvider.showFeedbackButton(this, getView());
     }
@@ -264,8 +261,7 @@
     }
 
     public void onSearchResultClicked() {
-        getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_SAVE_QUERY_TASK, null,
-                mSaveQueryCallback);
+        mSavedQueryController.saveQuery(mQuery);
         mResultClickCount++;
     }
 
@@ -278,13 +274,11 @@
     }
 
     public void onRemoveSavedQueryClicked(CharSequence title) {
-        final Bundle args = new Bundle();
-        args.putString(SaveQueryCallback.ARG_REMOVE_QUERY, title.toString());
-        getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_REMOVE_QUERY_TASK,
-                args, mSaveQueryCallback);
+        mSavedQueryController.removeQuery(title.toString());
     }
 
     private void restartLoaders() {
+        mShowingSavedQuery = false;
         final LoaderManager loaderManager = getLoaderManager();
         mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
         loaderManager.restartLoader(LOADER_ID_DATABASE, null /* args */, this /* callback */);
@@ -325,37 +319,4 @@
             mResultsRecyclerView.requestFocus();
         }
     }
-
-    private class SaveQueryCallback implements LoaderManager.LoaderCallbacks<Void> {
-        // TODO: make a generic background task manager to handle one-off tasks like this one.
-
-        private static final int LOADER_ID_SAVE_QUERY_TASK = 0;
-        private static final int LOADER_ID_REMOVE_QUERY_TASK = 1;
-        private static final String ARG_REMOVE_QUERY = "remove_query";
-
-        @Override
-        public Loader<Void> onCreateLoader(int id, Bundle args) {
-            switch (id) {
-                case LOADER_ID_SAVE_QUERY_TASK:
-                    return new SavedQueryRecorder(getActivity(), mQuery);
-                case LOADER_ID_REMOVE_QUERY_TASK:
-                    return new SavedQueryRemover(getActivity(), args.getString(ARG_REMOVE_QUERY));
-            }
-            return null;
-        }
-
-        @Override
-        public void onLoadFinished(Loader<Void> loader, Void data) {
-            switch (loader.getId()) {
-                case LOADER_ID_REMOVE_QUERY_TASK:
-                    getLoaderManager().restartLoader(LOADER_ID_RECENTS, null, SearchFragment.this);
-                    break;
-            }
-        }
-
-        @Override
-        public void onLoaderReset(Loader<Void> loader) {
-
-        }
-    }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index dccba13..853f80d 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -59,8 +59,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        final Bundle extras = getIntent().getExtras();
-        final int dialogType = extras.getInt(DIALOG_TYPE_KEY, INVALID_PICK);
+        final int dialogType = getIntent().getIntExtra(DIALOG_TYPE_KEY, INVALID_PICK);
 
         switch (dialogType) {
             case DATA_PICK:
@@ -69,7 +68,7 @@
                 createDialog(this, dialogType).show();
                 break;
             case PREFERRED_PICK:
-                displayPreferredDialog(extras.getInt(PREFERRED_SIM));
+                displayPreferredDialog(getIntent().getIntExtra(PREFERRED_SIM, 0));
                 break;
             default:
                 throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent.");
diff --git a/src/com/android/settings/widget/ExploreByTouchHelper.java b/src/com/android/settings/widget/ExploreByTouchHelper.java
deleted file mode 100644
index b64a74c..0000000
--- a/src/com/android/settings/widget/ExploreByTouchHelper.java
+++ /dev/null
@@ -1,724 +0,0 @@
-/*
- * Copyright (C) 2013 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.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
-
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Copied from setup wizard, which is in turn a modified copy of
- * com.android.internal.ExploreByTouchHelper with the following modifications:
- *
- * - Make accessibility calls to the views, instead of to the accessibility delegate directly to
- *   make sure those methods for View subclasses are called.
- *
- * ExploreByTouchHelper is a utility class for implementing accessibility
- * support in custom {@link android.view.View}s that represent a collection of View-like
- * logical items. It extends {@link android.view.accessibility.AccessibilityNodeProvider} and
- * simplifies many aspects of providing information to accessibility services
- * and managing accessibility focus. This class does not currently support
- * hierarchies of logical items.
- * <p>
- * This should be applied to the parent view using
- * {@link android.view.View#setAccessibilityDelegate}:
- *
- * <pre>
- * mAccessHelper = ExploreByTouchHelper.create(someView, mAccessHelperCallback);
- * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
- * </pre>
- */
-public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
-    /** Virtual node identifier value for invalid nodes. */
-    public static final int INVALID_ID = Integer.MIN_VALUE;
-
-    /** Default class name used for virtual views. */
-    private static final String DEFAULT_CLASS_NAME = View.class.getName();
-
-    // Temporary, reusable data structures.
-    private final Rect mTempScreenRect = new Rect();
-    private final Rect mTempParentRect = new Rect();
-    private final Rect mTempVisibleRect = new Rect();
-    private final int[] mTempGlobalRect = new int[2];
-
-    /** View's context **/
-    private Context mContext;
-
-    /** System accessibility manager, used to check state and send events. */
-    private final AccessibilityManager mManager;
-
-    /** View whose internal structure is exposed through this helper. */
-    private final View mView;
-
-    /** Node provider that handles creating nodes and performing actions. */
-    private ExploreByTouchNodeProvider mNodeProvider;
-
-    /** Virtual view id for the currently focused logical item. */
-    private int mFocusedVirtualViewId = INVALID_ID;
-
-    /** Virtual view id for the currently hovered logical item. */
-    private int mHoveredVirtualViewId = INVALID_ID;
-
-    /**
-     * Factory method to create a new {@link com.google.android.setupwizard.util.ExploreByTouchHelper}.
-     *
-     * @param forView View whose logical children are exposed by this helper.
-     */
-    public ExploreByTouchHelper(View forView) {
-        if (forView == null) {
-            throw new IllegalArgumentException("View may not be null");
-        }
-
-        mView = forView;
-        mContext = forView.getContext();
-        mManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-    }
-
-    /**
-     * Returns the {@link android.view.accessibility.AccessibilityNodeProvider} for this helper.
-     *
-     * @param host View whose logical children are exposed by this helper.
-     * @return The accessibility node provider for this helper.
-     */
-    @Override
-    public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
-        if (mNodeProvider == null) {
-            mNodeProvider = new ExploreByTouchNodeProvider();
-        }
-        return mNodeProvider;
-    }
-
-    /**
-     * Dispatches hover {@link android.view.MotionEvent}s to the virtual view hierarchy when
-     * the Explore by Touch feature is enabled.
-     * <p>
-     * This method should be called by overriding
-     * {@link android.view.View#dispatchHoverEvent}:
-     *
-     * <pre>&#64;Override
-     * public boolean dispatchHoverEvent(MotionEvent event) {
-     *   if (mHelper.dispatchHoverEvent(this, event) {
-     *     return true;
-     *   }
-     *   return super.dispatchHoverEvent(event);
-     * }
-     * </pre>
-     *
-     * @param event The hover event to dispatch to the virtual view hierarchy.
-     * @return Whether the hover event was handled.
-     */
-    public boolean dispatchHoverEvent(MotionEvent event) {
-        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
-            return false;
-        }
-
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_HOVER_MOVE:
-            case MotionEvent.ACTION_HOVER_ENTER:
-                final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
-                updateHoveredVirtualView(virtualViewId);
-                return (virtualViewId != INVALID_ID);
-            case MotionEvent.ACTION_HOVER_EXIT:
-                if (mFocusedVirtualViewId != INVALID_ID) {
-                    updateHoveredVirtualView(INVALID_ID);
-                    return true;
-                }
-                return false;
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Populates an event of the specified type with information about an item
-     * and attempts to send it up through the view hierarchy.
-     * <p>
-     * You should call this method after performing a user action that normally
-     * fires an accessibility event, such as clicking on an item.
-     *
-     * <pre>public void performItemClick(T item) {
-     *   ...
-     *   sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
-     * }
-     * </pre>
-     *
-     * @param virtualViewId The virtual view id for which to send an event.
-     * @param eventType The type of event to send.
-     * @return true if the event was sent successfully.
-     */
-    public boolean sendEventForVirtualView(int virtualViewId, int eventType) {
-        if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
-            return false;
-        }
-
-        final ViewParent parent = mView.getParent();
-        if (parent == null) {
-            return false;
-        }
-
-        final AccessibilityEvent event = createEvent(virtualViewId, eventType);
-        return parent.requestSendAccessibilityEvent(mView, event);
-    }
-
-    /**
-     * Notifies the accessibility framework that the properties of the parent
-     * view have changed.
-     * <p>
-     * You <b>must</b> call this method after adding or removing items from the
-     * parent view.
-     */
-    public void invalidateRoot() {
-        invalidateVirtualView(View.NO_ID);
-    }
-
-    /**
-     * Notifies the accessibility framework that the properties of a particular
-     * item have changed.
-     * <p>
-     * You <b>must</b> call this method after changing any of the properties set
-     * in {@link #onPopulateNodeForVirtualView}.
-     *
-     * @param virtualViewId The virtual view id to invalidate.
-     */
-    public void invalidateVirtualView(int virtualViewId) {
-        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-    }
-
-    /**
-     * Returns the virtual view id for the currently focused item,
-     *
-     * @return A virtual view id, or {@link #INVALID_ID} if no item is
-     *         currently focused.
-     */
-    public int getFocusedVirtualView() {
-        return mFocusedVirtualViewId;
-    }
-
-    /**
-     * Sets the currently hovered item, sending hover accessibility events as
-     * necessary to maintain the correct state.
-     *
-     * @param virtualViewId The virtual view id for the item currently being
-     *            hovered, or {@link #INVALID_ID} if no item is hovered within
-     *            the parent view.
-     */
-    private void updateHoveredVirtualView(int virtualViewId) {
-        if (mHoveredVirtualViewId == virtualViewId) {
-            return;
-        }
-
-        final int previousVirtualViewId = mHoveredVirtualViewId;
-        mHoveredVirtualViewId = virtualViewId;
-
-        // Stay consistent with framework behavior by sending ENTER/EXIT pairs
-        // in reverse order. This is accurate as of API 18.
-        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
-        sendEventForVirtualView(previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
-    }
-
-    /**
-     * Constructs and returns an {@link android.view.accessibility.AccessibilityEvent} for the specified
-     * virtual view id, which includes the host view ({@link android.view.View#NO_ID}).
-     *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            construct an event.
-     * @param eventType The type of event to construct.
-     * @return An {@link android.view.accessibility.AccessibilityEvent} populated with information about
-     *         the specified item.
-     */
-    private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
-        switch (virtualViewId) {
-            case View.NO_ID:
-                return createEventForHost(eventType);
-            default:
-                return createEventForChild(virtualViewId, eventType);
-        }
-    }
-
-    /**
-     * Constructs and returns an {@link android.view.accessibility.AccessibilityEvent} for the host node.
-     *
-     * @param eventType The type of event to construct.
-     * @return An {@link android.view.accessibility.AccessibilityEvent} populated with information about
-     *         the specified item.
-     */
-    private AccessibilityEvent createEventForHost(int eventType) {
-        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        mView.onInitializeAccessibilityEvent(event);
-        return event;
-    }
-
-    /**
-     * Constructs and returns an {@link android.view.accessibility.AccessibilityEvent} populated with
-     * information about the specified item.
-     *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            construct an event.
-     * @param eventType The type of event to construct.
-     * @return An {@link android.view.accessibility.AccessibilityEvent} populated with information about
-     *         the specified item.
-     */
-    private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
-        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        event.setEnabled(true);
-        event.setClassName(DEFAULT_CLASS_NAME);
-
-        // Allow the client to populate the event.
-        onPopulateEventForVirtualView(virtualViewId, event);
-
-        // Make sure the developer is following the rules.
-        if (event.getText().isEmpty() && (event.getContentDescription() == null)) {
-            throw new RuntimeException("Callbacks must add text or a content description in "
-                    + "populateEventForVirtualViewId()");
-        }
-
-        // Don't allow the client to override these properties.
-        event.setPackageName(mView.getContext().getPackageName());
-        event.setSource(mView, virtualViewId);
-
-        return event;
-    }
-
-    /**
-     * Constructs and returns an {@link android.view.accessibility.AccessibilityNodeInfo} for the
-     * specified virtual view id, which includes the host view
-     * ({@link android.view.View#NO_ID}).
-     *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            construct a node.
-     * @return An {@link android.view.accessibility.AccessibilityNodeInfo} populated with information
-     *         about the specified item.
-     */
-    private AccessibilityNodeInfo createNode(int virtualViewId) {
-        switch (virtualViewId) {
-            case View.NO_ID:
-                return createNodeForHost();
-            default:
-                return createNodeForChild(virtualViewId);
-        }
-    }
-
-    /**
-     * Constructs and returns an {@link android.view.accessibility.AccessibilityNodeInfo} for the
-     * host view populated with its virtual descendants.
-     *
-     * @return An {@link android.view.accessibility.AccessibilityNodeInfo} for the parent node.
-     */
-    private AccessibilityNodeInfo createNodeForHost() {
-        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView);
-        mView.onInitializeAccessibilityNodeInfo(node);
-
-        // Add the virtual descendants.
-        final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
-        getVisibleVirtualViews(virtualViewIds);
-
-        for (Integer childVirtualViewId : virtualViewIds) {
-            node.addChild(mView, childVirtualViewId);
-        }
-
-        return node;
-    }
-
-    /**
-     * Constructs and returns an {@link android.view.accessibility.AccessibilityNodeInfo} for the
-     * specified item. Automatically manages accessibility focus actions.
-     * <p>
-     * Allows the implementing class to specify most node properties, but
-     * overrides the following:
-     * <ul>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setPackageName}
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClassName}
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setParent(android.view.View)}
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setSource(android.view.View, int)}
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setVisibleToUser}
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen(android.graphics.Rect)}
-     * </ul>
-     * <p>
-     * Uses the bounds of the parent view and the parent-relative bounding
-     * rectangle specified by
-     * {@link android.view.accessibility.AccessibilityNodeInfo#getBoundsInParent} to automatically
-     * update the following properties:
-     * <ul>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setVisibleToUser}
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent}
-     * </ul>
-     *
-     * @param virtualViewId The virtual view id for item for which to construct
-     *            a node.
-     * @return An {@link android.view.accessibility.AccessibilityNodeInfo} for the specified item.
-     */
-    private AccessibilityNodeInfo createNodeForChild(int virtualViewId) {
-        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
-
-        // Ensure the client has good defaults.
-        node.setEnabled(true);
-        node.setClassName(DEFAULT_CLASS_NAME);
-
-        // Allow the client to populate the node.
-        onPopulateNodeForVirtualView(virtualViewId, node);
-
-        // Make sure the developer is following the rules.
-        if ((node.getText() == null) && (node.getContentDescription() == null)) {
-            throw new RuntimeException("Callbacks must add text or a content description in "
-                    + "populateNodeForVirtualViewId()");
-        }
-
-        node.getBoundsInParent(mTempParentRect);
-        if (mTempParentRect.isEmpty()) {
-            throw new RuntimeException("Callbacks must set parent bounds in "
-                    + "populateNodeForVirtualViewId()");
-        }
-
-        final int actions = node.getActions();
-        if ((actions & AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) != 0) {
-            throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
-                    + "populateNodeForVirtualViewId()");
-        }
-        if ((actions & AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
-            throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
-                    + "populateNodeForVirtualViewId()");
-        }
-
-        // Don't allow the client to override these properties.
-        node.setPackageName(mView.getContext().getPackageName());
-        node.setSource(mView, virtualViewId);
-        node.setParent(mView);
-
-        // Manage internal accessibility focus state.
-        if (mFocusedVirtualViewId == virtualViewId) {
-            node.setAccessibilityFocused(true);
-            node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
-        } else {
-            node.setAccessibilityFocused(false);
-            node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
-        }
-
-        // Set the visibility based on the parent bound.
-        if (intersectVisibleToUser(mTempParentRect)) {
-            node.setVisibleToUser(true);
-            node.setBoundsInParent(mTempParentRect);
-        }
-
-        // Calculate screen-relative bound.
-        mView.getLocationOnScreen(mTempGlobalRect);
-        final int offsetX = mTempGlobalRect[0];
-        final int offsetY = mTempGlobalRect[1];
-        mTempScreenRect.set(mTempParentRect);
-        mTempScreenRect.offset(offsetX, offsetY);
-        node.setBoundsInScreen(mTempScreenRect);
-
-        return node;
-    }
-
-    private boolean performAction(int virtualViewId, int action, Bundle arguments) {
-        switch (virtualViewId) {
-            case View.NO_ID:
-                return performActionForHost(action, arguments);
-            default:
-                return performActionForChild(virtualViewId, action, arguments);
-        }
-    }
-
-    private boolean performActionForHost(int action, Bundle arguments) {
-        return mView.performAccessibilityAction(action, arguments);
-    }
-
-    private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) {
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
-            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
-                return manageFocusForChild(virtualViewId, action, arguments);
-            default:
-                return onPerformActionForVirtualView(virtualViewId, action, arguments);
-        }
-    }
-
-    private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) {
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
-                return requestAccessibilityFocus(virtualViewId);
-            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
-                return clearAccessibilityFocus(virtualViewId);
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Computes whether the specified {@link android.graphics.Rect} intersects with the visible
-     * portion of its parent {@link android.view.View}. Modifies {@code localRect} to contain
-     * only the visible portion.
-     *
-     * @param localRect A rectangle in local (parent) coordinates.
-     * @return Whether the specified {@link android.graphics.Rect} is visible on the screen.
-     */
-    private boolean intersectVisibleToUser(Rect localRect) {
-        // Missing or empty bounds mean this view is not visible.
-        if ((localRect == null) || localRect.isEmpty()) {
-            return false;
-        }
-
-        // Attached to invisible window means this view is not visible.
-        if (mView.getWindowVisibility() != View.VISIBLE) {
-            return false;
-        }
-
-        // An invisible predecessor means that this view is not visible.
-        ViewParent viewParent = mView.getParent();
-        while (viewParent instanceof View) {
-            final View view = (View) viewParent;
-            if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
-                return false;
-            }
-            viewParent = view.getParent();
-        }
-
-        // A null parent implies the view is not visible.
-        if (viewParent == null) {
-            return false;
-        }
-
-        // If no portion of the parent is visible, this view is not visible.
-        if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
-            return false;
-        }
-
-        // Check if the view intersects the visible portion of the parent.
-        return localRect.intersect(mTempVisibleRect);
-    }
-
-    /**
-     * Returns whether this virtual view is accessibility focused.
-     *
-     * @return True if the view is accessibility focused.
-     */
-    private boolean isAccessibilityFocused(int virtualViewId) {
-        return (mFocusedVirtualViewId == virtualViewId);
-    }
-
-    /**
-     * Attempts to give accessibility focus to a virtual view.
-     * <p>
-     * A virtual view will not actually take focus if
-     * {@link android.view.accessibility.AccessibilityManager#isEnabled()} returns false,
-     * {@link android.view.accessibility.AccessibilityManager#isTouchExplorationEnabled()} returns false,
-     * or the view already has accessibility focus.
-     *
-     * @param virtualViewId The id of the virtual view on which to place
-     *            accessibility focus.
-     * @return Whether this virtual view actually took accessibility focus.
-     */
-    private boolean requestAccessibilityFocus(int virtualViewId) {
-        final AccessibilityManager accessibilityManager =
-                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-
-        if (!mManager.isEnabled()
-                || !accessibilityManager.isTouchExplorationEnabled()) {
-            return false;
-        }
-        // TODO: Check virtual view visibility.
-        if (!isAccessibilityFocused(virtualViewId)) {
-            mFocusedVirtualViewId = virtualViewId;
-            // TODO: Only invalidate virtual view bounds.
-            mView.invalidate();
-            sendEventForVirtualView(virtualViewId,
-                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Attempts to clear accessibility focus from a virtual view.
-     *
-     * @param virtualViewId The id of the virtual view from which to clear
-     *            accessibility focus.
-     * @return Whether this virtual view actually cleared accessibility focus.
-     */
-    private boolean clearAccessibilityFocus(int virtualViewId) {
-        if (isAccessibilityFocused(virtualViewId)) {
-            mFocusedVirtualViewId = INVALID_ID;
-            mView.invalidate();
-            sendEventForVirtualView(virtualViewId,
-                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Provides a mapping between view-relative coordinates and logical
-     * items.
-     *
-     * @param x The view-relative x coordinate
-     * @param y The view-relative y coordinate
-     * @return virtual view identifier for the logical item under
-     *         coordinates (x,y)
-     */
-    protected abstract int getVirtualViewAt(float x, float y);
-
-    /**
-     * Populates a list with the view's visible items. The ordering of items
-     * within {@code virtualViewIds} specifies order of accessibility focus
-     * traversal.
-     *
-     * @param virtualViewIds The list to populate with visible items
-     */
-    protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds);
-
-    /**
-     * Populates an {@link android.view.accessibility.AccessibilityEvent} with information about the
-     * specified item.
-     * <p>
-     * Implementations <b>must</b> populate the following required fields:
-     * <ul>
-     * <li>event text, see {@link android.view.accessibility.AccessibilityEvent#getText} or
-     * {@link android.view.accessibility.AccessibilityEvent#setContentDescription}
-     * </ul>
-     * <p>
-     * The helper class automatically populates the following fields with
-     * default values, but implementations may optionally override them:
-     * <ul>
-     * <li>item class name, set to android.view.View, see
-     * {@link android.view.accessibility.AccessibilityEvent#setClassName}
-     * </ul>
-     * <p>
-     * The following required fields are automatically populated by the
-     * helper class and may not be overridden:
-     * <ul>
-     * <li>package name, set to the package of the host view's
-     * {@link android.content.Context}, see {@link android.view.accessibility.AccessibilityEvent#setPackageName}
-     * <li>event source, set to the host view and virtual view identifier,
-     * see {@link android.view.accessibility.AccessibilityRecord#setSource(android.view.View, int)}
-     * </ul>
-     *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            populate the event
-     * @param event The event to populate
-     */
-    protected abstract void onPopulateEventForVirtualView(
-            int virtualViewId, AccessibilityEvent event);
-
-    /**
-     * Populates an {@link android.view.accessibility.AccessibilityNodeInfo} with information
-     * about the specified item.
-     * <p>
-     * Implementations <b>must</b> populate the following required fields:
-     * <ul>
-     * <li>event text, see {@link android.view.accessibility.AccessibilityNodeInfo#setText} or
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setContentDescription}
-     * <li>bounds in parent coordinates, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent}
-     * </ul>
-     * <p>
-     * The helper class automatically populates the following fields with
-     * default values, but implementations may optionally override them:
-     * <ul>
-     * <li>enabled state, set to true, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setEnabled}
-     * <li>item class name, identical to the class name set by
-     * {@link #onPopulateEventForVirtualView}, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setClassName}
-     * </ul>
-     * <p>
-     * The following required fields are automatically populated by the
-     * helper class and may not be overridden:
-     * <ul>
-     * <li>package name, identical to the package name set by
-     * {@link #onPopulateEventForVirtualView}, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setPackageName}
-     * <li>node source, identical to the event source set in
-     * {@link #onPopulateEventForVirtualView}, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setSource(android.view.View, int)}
-     * <li>parent view, set to the host view, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setParent(android.view.View)}
-     * <li>visibility, computed based on parent-relative bounds, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setVisibleToUser}
-     * <li>accessibility focus, computed based on internal helper state, see
-     * {@link android.view.accessibility.AccessibilityNodeInfo#setAccessibilityFocused}
-     * <li>bounds in screen coordinates, computed based on host view bounds,
-     * see {@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen}
-     * </ul>
-     * <p>
-     * Additionally, the helper class automatically handles accessibility
-     * focus management by adding the appropriate
-     * {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS} or
-     * {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
-     * action. Implementations must <b>never</b> manually add these actions.
-     * <p>
-     * The helper class also automatically modifies parent- and
-     * screen-relative bounds to reflect the portion of the item visible
-     * within its parent.
-     *
-     * @param virtualViewId The virtual view identifier of the item for
-     *            which to populate the node
-     * @param node The node to populate
-     */
-    protected abstract void onPopulateNodeForVirtualView(
-            int virtualViewId, AccessibilityNodeInfo node);
-
-    /**
-     * Performs the specified accessibility action on the item associated
-     * with the virtual view identifier. See
-     * {@link android.view.accessibility.AccessibilityNodeInfo#performAction(int, android.os.Bundle)} for
-     * more information.
-     * <p>
-     * Implementations <b>must</b> handle any actions added manually in
-     * {@link #onPopulateNodeForVirtualView}.
-     * <p>
-     * The helper class automatically handles focus management resulting
-     * from {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS}
-     * and
-     * {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
-     * actions.
-     *
-     * @param virtualViewId The virtual view identifier of the item on which
-     *            to perform the action
-     * @param action The accessibility action to perform
-     * @param arguments (Optional) A bundle with additional arguments, or
-     *            null
-     * @return true if the action was performed
-     */
-    protected abstract boolean onPerformActionForVirtualView(
-            int virtualViewId, int action, Bundle arguments);
-
-    /**
-     * Exposes a virtual view hierarchy to the accessibility framework. Only
-     * used in API 16+.
-     */
-    private class ExploreByTouchNodeProvider extends AccessibilityNodeProvider {
-        @Override
-        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
-            return ExploreByTouchHelper.this.createNode(virtualViewId);
-        }
-
-        @Override
-        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
-            return ExploreByTouchHelper.this.performAction(virtualViewId, action, arguments);
-        }
-    }
-}
diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java
index 78e869b..af80fd4 100644
--- a/src/com/android/settings/wifi/ConfigureWifiSettings.java
+++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java
@@ -19,6 +19,7 @@
 import static android.content.Context.WIFI_SERVICE;
 
 import android.content.Context;
+import android.content.Intent;
 import android.net.NetworkScoreManager;
 import android.net.wifi.WifiManager;
 import android.provider.SearchIndexableResource;
@@ -43,7 +44,7 @@
 
     private static final String TAG = "ConfigureWifiSettings";
 
-    private WifiManager mWifiManager;
+    private UseOpenWifiPreferenceController mUseOpenWifiPreferenceController;
 
     @Override
     public int getMetricsCategory() {
@@ -58,7 +59,8 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        mProgressiveDisclosureMixin.setTileLimit(2);
+        mProgressiveDisclosureMixin.setTileLimit(
+            mUseOpenWifiPreferenceController.isAvailable() ? 3 : 2);
         ((SettingsActivity) getActivity()).setDisplaySearchMenu(true);
     }
 
@@ -69,23 +71,35 @@
 
     @Override
     protected List<PreferenceController> getPreferenceControllers(Context context) {
-        mWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
+        final NetworkScoreManagerWrapper networkScoreManagerWrapper =
+                new NetworkScoreManagerWrapper(context.getSystemService(NetworkScoreManager.class));
+        mUseOpenWifiPreferenceController = new UseOpenWifiPreferenceController(context, this,
+                networkScoreManagerWrapper, getLifecycle());
+        final WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
         final List<PreferenceController> controllers = new ArrayList<>();
-        controllers.add(new WifiInfoPreferenceController(context, getLifecycle(), mWifiManager));
-        controllers.add(new CellularFallbackPreferenceController(context));
-        controllers.add(new NotifyOpenNetworksPreferenceController(context, getLifecycle()));
         controllers.add(new WifiWakeupPreferenceController(context, getLifecycle()));
         controllers.add(new NetworkScorerPickerPreferenceController(context,
-                new NetworkScoreManagerWrapper(
-                        (NetworkScoreManager) getSystemService(NETWORK_SCORE_SERVICE))));
+                networkScoreManagerWrapper));
+        controllers.add(new NotifyOpenNetworksPreferenceController(context, getLifecycle()));
+        controllers.add(mUseOpenWifiPreferenceController);
         controllers.add(new WifiSleepPolicyPreferenceController(context));
-        controllers.add(new WifiP2pPreferenceController(context, getLifecycle(), mWifiManager));
+        controllers.add(new WifiInfoPreferenceController(context, getLifecycle(), wifiManager));
+        controllers.add(new CellularFallbackPreferenceController(context));
+        controllers.add(new WifiP2pPreferenceController(context, getLifecycle(), wifiManager));
         controllers.add(new WifiCallingPreferenceController(context));
         controllers.add(new WpsPreferenceController(
-                context, getLifecycle(), mWifiManager, getFragmentManager()));
+                context, getLifecycle(), wifiManager, getFragmentManager()));
         return controllers;
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (mUseOpenWifiPreferenceController == null ||
+                !mUseOpenWifiPreferenceController.onActivityResult(requestCode, resultCode)) {
+            super.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
                 @Override
diff --git a/src/com/android/settings/wifi/UseOpenWifiPreferenceController.java b/src/com/android/settings/wifi/UseOpenWifiPreferenceController.java
new file mode 100644
index 0000000..09f5e92
--- /dev/null
+++ b/src/com/android/settings/wifi/UseOpenWifiPreferenceController.java
@@ -0,0 +1,165 @@
+package com.android.settings.wifi;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.NetworkScoreManager;
+
+import android.net.NetworkScorerAppData;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settings.network.NetworkScoreManagerWrapper;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
+
+/**
+ * {@link PreferenceController} that controls whether a user wants to enable the "use open networks
+ * automatically" feature provider by the current network recommendation provider.
+ */
+public class UseOpenWifiPreferenceController extends PreferenceController
+        implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+    private static final String KEY_USE_OPEN_WIFI_AUTOMATICALLY = "use_open_wifi_automatically";
+    @VisibleForTesting static final int REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY = 400;
+
+    private final ContentResolver mContentResolver;
+    private final Fragment mFragment;
+    private final NetworkScoreManagerWrapper mNetworkScoreManagerWrapper;
+    private final SettingObserver mSettingObserver;
+
+    private Preference mPreference;
+    private ComponentName mEnableUseWifiComponentName;
+
+    public UseOpenWifiPreferenceController(Context context, Fragment fragment,
+            NetworkScoreManagerWrapper networkScoreManagerWrapper, Lifecycle lifecycle) {
+        super(context);
+        mContentResolver = context.getContentResolver();
+        mFragment = fragment;
+        mNetworkScoreManagerWrapper = networkScoreManagerWrapper;
+        mSettingObserver = new SettingObserver();
+        updateEnableUseWifiComponentName();
+        lifecycle.addObserver(this);
+    }
+
+    private void updateEnableUseWifiComponentName() {
+        NetworkScorerAppData appData = mNetworkScoreManagerWrapper.getActiveScorer();
+        mEnableUseWifiComponentName =
+                appData == null ? null : appData.getEnableUseOpenWifiActivity();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(KEY_USE_OPEN_WIFI_AUTOMATICALLY);
+    }
+
+    @Override
+    public void onResume() {
+        mSettingObserver.register(mContentResolver);
+    }
+
+    @Override
+    public void onPause() {
+        mSettingObserver.unregister(mContentResolver);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mEnableUseWifiComponentName != null;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_USE_OPEN_WIFI_AUTOMATICALLY;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        if (!(preference instanceof SwitchPreference)) {
+            return;
+        }
+        final SwitchPreference useOpenWifiPreference = (SwitchPreference) preference;
+        useOpenWifiPreference.setVisible(isAvailable());
+        useOpenWifiPreference.setChecked(isSettingEnabled());
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (!TextUtils.equals(preference.getKey(), KEY_USE_OPEN_WIFI_AUTOMATICALLY)
+                || !isAvailable()) {
+            return false;
+        }
+
+        if (isSettingEnabled()) {
+            Settings.Global.putString(mContentResolver,
+                    Settings.Global.USE_OPEN_WIFI_PACKAGE, "");
+            return true;
+        }
+
+        Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE);
+        intent.setComponent(mEnableUseWifiComponentName);
+        mFragment.startActivityForResult(intent, REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY);
+        return false; // Updating state is done in onActivityResult.
+    }
+
+    private boolean isSettingEnabled() {
+        String enabledUseOpenWifiPackage = Settings.Global.getString(mContentResolver,
+                Settings.Global.USE_OPEN_WIFI_PACKAGE);
+        String currentUseOpenWifiPackage = mEnableUseWifiComponentName == null
+                ? null : mEnableUseWifiComponentName.getPackageName();
+        return TextUtils.equals(enabledUseOpenWifiPackage, currentUseOpenWifiPackage);
+    }
+
+    public boolean onActivityResult(int requestCode, int resultCode) {
+        if (requestCode != REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY) {
+            return false;
+        }
+
+        if (resultCode == Activity.RESULT_OK) {
+            Settings.Global.putString(mContentResolver, Settings.Global.USE_OPEN_WIFI_PACKAGE,
+                    mEnableUseWifiComponentName.getPackageName());
+        }
+        return true;
+    }
+
+    class SettingObserver extends ContentObserver {
+        private final Uri NETWORK_RECOMMENDATIONS_ENABLED_URI =
+                Settings.Global.getUriFor(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED);
+
+        public SettingObserver() {
+            super(new Handler(Looper.getMainLooper()));
+        }
+
+        public void register(ContentResolver cr) {
+            cr.registerContentObserver(NETWORK_RECOMMENDATIONS_ENABLED_URI, false, this);
+            onChange(true /* selfChange */, NETWORK_RECOMMENDATIONS_ENABLED_URI);
+        }
+
+        public void unregister(ContentResolver cr) {
+            cr.unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            if (NETWORK_RECOMMENDATIONS_ENABLED_URI.equals(uri)) {
+                updateEnableUseWifiComponentName();
+                updateState(mPreference);
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index 5f489f2..1ed25f2 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -32,6 +32,7 @@
 import android.support.v7.preference.PreferenceScreen;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.core.lifecycle.Lifecycle;
@@ -53,15 +54,24 @@
         LifecycleObserver, OnResume {
     private static final String TAG = "WifiDetailsPrefCtrl";
 
-    private static final String KEY_CONNECTION_DETAIL_PREF = "connection_detail";
-    private static final String KEY_SIGNAL_STRENGTH_PREF = "signal_strength";
-    private static final String KEY_FREQUENCY_PREF = "frequency";
-    private static final String KEY_SECURITY_PREF = "security";
-    private static final String KEY_IP_ADDRESS_PREF = "ip_address";
-    private static final String KEY_ROUTER_PREF = "router";
-    private static final String KEY_SUBNET_MASK_PREF = "subnet_mask";
-    private static final String KEY_DNS_PREF = "dns";
-    private static final String KEY_IPV6_ADDRESS_CATEGORY = "ipv6_details_category";
+    @VisibleForTesting
+    static final String KEY_CONNECTION_DETAIL_PREF = "connection_detail";
+    @VisibleForTesting
+    static final String KEY_SIGNAL_STRENGTH_PREF = "signal_strength";
+    @VisibleForTesting
+    static final String KEY_FREQUENCY_PREF = "frequency";
+    @VisibleForTesting
+    static final String KEY_SECURITY_PREF = "security";
+    @VisibleForTesting
+    static final String KEY_IP_ADDRESS_PREF = "ip_address";
+    @VisibleForTesting
+    static final String KEY_ROUTER_PREF = "router";
+    @VisibleForTesting
+    static final String KEY_SUBNET_MASK_PREF = "subnet_mask";
+    @VisibleForTesting
+    static final String KEY_DNS_PREF = "dns";
+    @VisibleForTesting
+    static final String KEY_IPV6_ADDRESS_CATEGORY = "ipv6_details_category";
 
     private AccessPoint mAccessPoint;
     private NetworkInfo mNetworkInfo;
@@ -166,7 +176,7 @@
                 R.color.wifi_details_icon_color, mContext.getTheme()));
         mSignalStrengthPref.setIcon(wifiIconDark);
 
-        int summarySignalLevel = WifiManager.calculateSignalLevel(mRssi, mSignalStr.length);
+        int summarySignalLevel = mAccessPoint.getLevel();
         mSignalStrengthPref.setDetailText(mSignalStr[summarySignalLevel]);
 
         // Frequency Pref
diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
index 0a80f21..0073202 100644
--- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
+++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
@@ -23,7 +23,6 @@
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
-import com.android.settings.SettingsActivity;
 import com.android.settings.applications.LayoutPreference;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
diff --git a/tests/app/src/com/android/settings/ConfirmLockPasswordTest.java b/tests/app/src/com/android/settings/ConfirmLockPasswordTest.java
new file mode 100644
index 0000000..05464ad
--- /dev/null
+++ b/tests/app/src/com/android/settings/ConfirmLockPasswordTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.pressKey;
+import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.KeyEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ConfirmLockPasswordTest {
+
+    private Instrumentation mInstrumentation;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
+    }
+
+    @Test
+    public void enterWrongPin_shouldShowErrorMessage() {
+        mInstrumentation.startActivitySync(
+                new Intent(mContext, ConfirmLockPassword.class));
+        onView(withId(R.id.password_entry)).perform(typeText("1234"))
+                .perform(pressKey(KeyEvent.KEYCODE_ENTER));
+        onView(withId(R.id.errorText)).check(matches(withText(R.string.lockpassword_invalid_pin)));
+    }
+
+    @Test
+    public void enterWrongPin_darkTheme_shouldShowErrorMessage() {
+        mInstrumentation.startActivitySync(
+                new Intent(mContext, ConfirmLockPassword.class)
+                        .putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, true));
+        onView(withId(R.id.password_entry)).perform(typeText("1234"))
+                .perform(pressKey(KeyEvent.KEYCODE_ENTER));
+        onView(withId(R.id.errorText)).check(matches(withText(R.string.lockpassword_invalid_pin)));
+    }
+}
diff --git a/tests/robotests/src/android/net/NetworkBadging.java b/tests/robotests/src/android/net/NetworkBadging.java
new file mode 100644
index 0000000..32bb31f
--- /dev/null
+++ b/tests/robotests/src/android/net/NetworkBadging.java
@@ -0,0 +1,34 @@
+package android.net;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Implementation for {@link android.net.NetworkBadging}.
+ *
+ * <p>Can be removed once Robolectric supports Android O.
+ */
+public class NetworkBadging {
+    @IntDef({BADGING_NONE, BADGING_SD, BADGING_HD, BADGING_4K})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Badging {}
+
+    public static final int BADGING_NONE = 0;
+    public static final int BADGING_SD = 10;
+    public static final int BADGING_HD = 20;
+    public static final int BADGING_4K = 30;
+
+    private static Drawable drawable;
+
+    public static Drawable getWifiIcon(
+            int signalLevel, @NetworkBadging.Badging int badging, @Nullable Resources.Theme theme) {
+        return new ColorDrawable(Color.GREEN);
+    }
+}
diff --git a/tests/robotests/src/android/net/wifi/WifiNetworkScoreCache.java b/tests/robotests/src/android/net/wifi/WifiNetworkScoreCache.java
new file mode 100644
index 0000000..08b2971
--- /dev/null
+++ b/tests/robotests/src/android/net/wifi/WifiNetworkScoreCache.java
@@ -0,0 +1,10 @@
+package android.net.wifi;
+
+/**
+ * Empty class def for {@link android.net.wifi.WifiNetworkScoreCache}.
+ *
+ * <p>Can be removed once Robolectric supports Android O.
+ */
+public class WifiNetworkScoreCache {
+
+}
diff --git a/tests/robotests/src/com/android/settings/SettingsActivityTest.java b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
index 7822d83..9d53bc8 100644
--- a/tests/robotests/src/com/android/settings/SettingsActivityTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
@@ -30,7 +30,6 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -60,20 +59,6 @@
     }
 
     @Test
-    public void testQueryTextChange_shouldUpdate() {
-        final String testQuery = "abc";
-
-        assertThat(mActivity.mSearchQuery).isNull();
-        try {
-            mActivity.onQueryTextChange(testQuery);
-        } catch (NullPointerException e) {
-            // Expected, because searchFeatureProvider is not wired up.
-        }
-
-        assertThat(mActivity.mSearchQuery).isEqualTo(testQuery);
-    }
-
-    @Test
     public void launchSettingFragment_nullExtraShowFragment_shouldNotCrash()
             throws ClassNotFoundException {
         mActivity = spy(new SettingsActivity());
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
index fd0ab4b..90728e8 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
@@ -24,7 +24,6 @@
 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.testutils.shadow.ShadowAccountManager;
@@ -44,7 +43,6 @@
 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;
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java
index da25f0f..8b6e900 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java
@@ -113,7 +113,7 @@
         preference.setEnabled(true);
         mController.updateState(preference);
         assertThat(preference.getSummary()).isEqualTo(
-                mContext.getString(R.string.usb_nothing_connected));
+                mContext.getString(R.string.disconnected));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java b/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java
index db83bbb..d863143 100644
--- a/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java
+++ b/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java
@@ -16,12 +16,18 @@
 package com.android.settings.core.lifecycle;
 
 import android.content.Context;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.core.lifecycle.events.OnAttach;
+import com.android.settings.core.lifecycle.events.OnCreateOptionsMenu;
 import com.android.settings.core.lifecycle.events.OnDestroy;
+import com.android.settings.core.lifecycle.events.OnOptionsItemSelected;
 import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnPrepareOptionsMenu;
 import com.android.settings.core.lifecycle.events.OnResume;
 import com.android.settings.core.lifecycle.events.OnStart;
 import com.android.settings.core.lifecycle.events.OnStop;
@@ -71,7 +77,8 @@
     }
 
     public static class TestObserver implements LifecycleObserver, OnAttach, OnStart, OnResume,
-            OnPause, OnStop, OnDestroy {
+            OnPause, OnStop, OnDestroy, OnCreateOptionsMenu, OnPrepareOptionsMenu,
+            OnOptionsItemSelected {
 
         boolean mOnAttachObserved;
         boolean mOnAttachHasContext;
@@ -80,6 +87,9 @@
         boolean mOnPauseObserved;
         boolean mOnStopObserved;
         boolean mOnDestroyObserved;
+        boolean mOnCreateOptionsMenuObserved;
+        boolean mOnPrepareOptionsMenuObserved;
+        boolean mOnOptionsItemSelectedObserved;
 
         @Override
         public void onAttach(Context context) {
@@ -111,6 +121,22 @@
         public void onDestroy() {
             mOnDestroyObserved = true;
         }
+
+        @Override
+        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+            mOnCreateOptionsMenuObserved = true;
+        }
+
+        @Override
+        public boolean onOptionsItemSelected(MenuItem menuItem) {
+            mOnOptionsItemSelectedObserved = true;
+            return true;
+        }
+
+        @Override
+        public void onPrepareOptionsMenu(Menu menu) {
+            mOnPrepareOptionsMenuObserved = true;
+        }
     }
 
     @Test
@@ -122,6 +148,12 @@
         assertThat(activity.mActObserver.mOnStartObserved).isTrue();
         ac.resume();
         assertThat(activity.mActObserver.mOnResumeObserved).isTrue();
+        activity.onCreateOptionsMenu(null);
+        assertThat(activity.mActObserver.mOnCreateOptionsMenuObserved).isTrue();
+        activity.onPrepareOptionsMenu(null);
+        assertThat(activity.mActObserver.mOnPrepareOptionsMenuObserved).isTrue();
+        activity.onOptionsItemSelected(null);
+        assertThat(activity.mActObserver.mOnOptionsItemSelectedObserved).isTrue();
         ac.pause();
         assertThat(activity.mActObserver.mOnPauseObserved).isTrue();
         ac.stop();
@@ -136,7 +168,11 @@
                 Robolectric.buildFragment(TestDialogFragment.class);
         TestDialogFragment fragment = fragmentController.get();
 
-        fragmentController.attach().create().start().resume().pause().stop().destroy();
+        fragmentController.attach().create().start().resume();
+        fragment.onCreateOptionsMenu(null, null);
+        fragment.onPrepareOptionsMenu(null);
+        fragment.onOptionsItemSelected(null);
+        fragmentController.pause().stop().destroy();
 
         assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue();
@@ -145,6 +181,9 @@
         assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
     }
 
     @Test
@@ -153,7 +192,11 @@
                 Robolectric.buildFragment(TestFragment.class);
         TestFragment fragment = fragmentController.get();
 
-        fragmentController.attach().create().start().resume().pause().stop().destroy();
+        fragmentController.attach().create().start().resume();
+        fragment.onCreateOptionsMenu(null, null);
+        fragment.onPrepareOptionsMenu(null);
+        fragment.onOptionsItemSelected(null);
+        fragmentController.pause().stop().destroy();
 
         assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue();
@@ -162,5 +205,35 @@
         assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
+    }
+
+    private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected {
+        public boolean wasCalled = false;
+
+        @Override
+        public boolean onOptionsItemSelected(MenuItem menuItem) {
+            wasCalled = true;
+            return false;
+        }
+    }
+
+    @Test
+    public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() {
+        FragmentController<TestFragment> fragmentController =
+                Robolectric.buildFragment(TestFragment.class);
+        TestFragment fragment = fragmentController.get();
+        OptionItemAccepter accepter = new OptionItemAccepter();
+        fragment.getLifecycle().addObserver(accepter);
+
+        fragmentController.attach().create().start().resume();
+        fragment.onCreateOptionsMenu(null, null);
+        fragment.onPrepareOptionsMenu(null);
+        fragment.onOptionsItemSelected(null);
+        fragmentController.pause().stop().destroy();
+
+        assertThat(accepter.wasCalled).isFalse();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
index 7f0229a..d7daa03 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
@@ -21,35 +21,32 @@
 import android.content.res.Resources;
 import android.view.View;
 import android.widget.FrameLayout;
+
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.conditional.Condition;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
-import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.Tile;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import static com.google.common.truth.Truth.assertThat;
-import org.mockito.Matchers;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -64,20 +61,19 @@
         })
 public class DashboardAdapterTest {
 
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
     @Mock
     private View mView;
     @Mock
     private Condition mCondition;
     @Mock
-    private MetricsFeatureProvider mMetricsFeatureProvider;
-    @Mock
     private Resources mResources;
-    @Mock
-    private DashboardData mDashboardData;
     @Captor
     private ArgumentCaptor<Integer> mActionCategoryCaptor = ArgumentCaptor.forClass(Integer.class);
     @Captor
     private ArgumentCaptor<String> mActionPackageCaptor = ArgumentCaptor.forClass(String.class);
+    private FakeFeatureFactory mFactory;
     private DashboardAdapter mDashboardAdapter;
     private DashboardAdapter.DashboardItemHolder mSuggestionHolder;
     private DashboardData.SuggestionHeaderData mSuggestionHeaderData;
@@ -85,15 +81,20 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        Context context = RuntimeEnvironment.application;
-        context = spy(context);
-        when(context.getResources()).thenReturn(mResources);
-        when(mResources
-                .getQuantityString(any(int.class), any(int.class), Matchers.<Object>anyVararg()))
+        FakeFeatureFactory.setupForTest(mContext);
+        mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+        when(mFactory.suggestionsFeatureProvider
+                .getSuggestionIdentifier(any(Context.class), any(Tile.class)))
+                .thenAnswer(invocation -> {
+                    final Object[] args = invocation.getArguments();
+                    return ((Tile)args[1]).intent.getComponent().getPackageName();
+                });
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getQuantityString(any(int.class), any(int.class), any()))
                 .thenReturn("");
-        FakeFeatureFactory.setupForTest(context);
-        mDashboardAdapter = new DashboardAdapter(context, null, mMetricsFeatureProvider,
-                null, null);
+
+        mDashboardAdapter = new DashboardAdapter(mContext, null, null);
         mSuggestionHeaderData = new DashboardData.SuggestionHeaderData(true, 1, 0);
         when(mView.getTag()).thenReturn(mCondition);
     }
@@ -109,7 +110,7 @@
     @Test
     public void testSuggestionsLogs_NotExpanded() {
         setUpSuggestions(makeSuggestions(new String[]{"pkg1", "pkg2", "pkg3"}));
-        verify(mMetricsFeatureProvider, times(2)).action(
+        verify(mFactory.metricsFeatureProvider, times(2)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1", "pkg2"};
@@ -117,15 +118,15 @@
                 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
                 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION
         };
-        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
         assertThat(mActionCategoryCaptor.getAllValues().toArray()).isEqualTo(expectedActions);
+        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
     }
 
     @Test
     public void testSuggestionsLogs_NotExpandedAndPaused() {
         setUpSuggestions(makeSuggestions(new String[]{"pkg1", "pkg2", "pkg3"}));
         mDashboardAdapter.onPause();
-        verify(mMetricsFeatureProvider, times(4)).action(
+        verify(mFactory.metricsFeatureProvider, times(4)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg1", "pkg2"};
@@ -134,8 +135,8 @@
                 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
                 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
                 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION};
-        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
         assertThat(mActionCategoryCaptor.getAllValues().toArray()).isEqualTo(expectedActions);
+        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
     }
 
     @Test
@@ -144,7 +145,7 @@
         mDashboardAdapter.onBindSuggestionHeader(
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
-        verify(mMetricsFeatureProvider, times(3)).action(
+        verify(mFactory.metricsFeatureProvider, times(3)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg3"};
@@ -164,7 +165,7 @@
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
         mDashboardAdapter.onPause();
-        verify(mMetricsFeatureProvider, times(6)).action(
+        verify(mFactory.metricsFeatureProvider, times(6)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg3", "pkg1", "pkg2", "pkg3"};
@@ -187,7 +188,7 @@
         mDashboardAdapter.onBindSuggestionHeader(
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
-        verify(mMetricsFeatureProvider, times(7)).action(
+        verify(mFactory.metricsFeatureProvider, times(7)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{
@@ -213,7 +214,7 @@
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
         mDashboardAdapter.onPause();
-        verify(mMetricsFeatureProvider, times(10)).action(
+        verify(mFactory.metricsFeatureProvider, times(10)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{
@@ -240,7 +241,7 @@
         mDashboardAdapter.onBindSuggestionHeader(
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
-        verify(mMetricsFeatureProvider, times(1)).action(
+        verify(mFactory.metricsFeatureProvider, times(1)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1"};
@@ -258,7 +259,7 @@
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
         mDashboardAdapter.onPause();
-        verify(mMetricsFeatureProvider, times(2)).action(
+        verify(mFactory.metricsFeatureProvider, times(2)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1", "pkg1"};
@@ -277,7 +278,7 @@
         mDashboardAdapter.onBindSuggestionHeader(
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
-        verify(mMetricsFeatureProvider, times(3)).action(
+        verify(mFactory.metricsFeatureProvider, times(3)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1", "pkg1", "pkg1"};
@@ -298,7 +299,7 @@
                 mSuggestionHolder, mSuggestionHeaderData);
         mSuggestionHolder.itemView.callOnClick();
         mDashboardAdapter.onPause();
-        verify(mMetricsFeatureProvider, times(4)).action(
+        verify(mFactory.metricsFeatureProvider, times(4)).action(
                 any(Context.class), mActionCategoryCaptor.capture(),
                 mActionPackageCaptor.capture());
         String[] expectedPackages = new String[]{"pkg1", "pkg1", "pkg1", "pkg1"};
@@ -313,23 +314,18 @@
     }
 
     private List<Tile> makeSuggestions(String[] pkgNames) {
-        List<Tile> suggestions = new ArrayList<Tile>();
+        final List<Tile> suggestions = new ArrayList<>();
         for (String pkgName : pkgNames) {
-            suggestions.add(makeSuggestion(pkgName, "cls"));
+            Tile suggestion = new Tile();
+            suggestion.intent = new Intent("action");
+            suggestion.intent.setComponent(new ComponentName(pkgName, "cls"));
+            suggestions.add(suggestion);
         }
         return suggestions;
     }
 
-    private Tile makeSuggestion(String pkgName, String className) {
-        Tile suggestion = new Tile();
-        suggestion.intent = new Intent("action");
-        suggestion.intent.setComponent(new ComponentName(pkgName, className));
-        return suggestion;
-    }
-
     private void setUpSuggestions(List<Tile> suggestions) {
-        mDashboardAdapter.setCategoriesAndSuggestions(
-                new ArrayList<DashboardCategory>(), suggestions);
+        mDashboardAdapter.setCategoriesAndSuggestions(new ArrayList<>(), suggestions);
         mSuggestionHolder = mDashboardAdapter.onCreateViewHolder(
                 new FrameLayout(RuntimeEnvironment.application),
                 mDashboardAdapter.getItemViewType(0));
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index 31ffad0..e35fa33 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -74,8 +74,8 @@
         mDashboardCategory.tiles = new ArrayList<>();
         mDashboardCategory.tiles.add(new Tile());
         mTestFragment = new TestFragment(ShadowApplication.getInstance().getApplicationContext());
-        when(mFakeFeatureFactory.dashboardFeatureProvider
-                .getProgressiveDisclosureMixin(any(Context.class), eq(mTestFragment)))
+        when(mFakeFeatureFactory.dashboardFeatureProvider.getProgressiveDisclosureMixin(
+                any(Context.class), eq(mTestFragment), any(Bundle.class)))
                 .thenReturn(mDisclosureMixin);
         when(mFakeFeatureFactory.dashboardFeatureProvider.getTilesForCategory(anyString()))
                 .thenReturn(mDashboardCategory);
diff --git a/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java b/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java
index e872a09..bae6f8f 100644
--- a/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/ProgressiveDisclosureTest.java
@@ -77,8 +77,7 @@
         mAppContext = ShadowApplication.getInstance().getApplicationContext();
         mFakeFeatureFactory = (FakeFeatureFactory) FeatureFactory.getFactory(mContext);
         mMixin = new ProgressiveDisclosureMixin(mAppContext,
-                mFakeFeatureFactory.metricsFeatureProvider,
-                mPreferenceFragment);
+                mPreferenceFragment, false /* keepExpanded */);
         ReflectionHelpers.setField(mMixin, "mExpandButton", mExpandButton);
         mPreference = new Preference(mAppContext);
         mPreference.setKey("test");
@@ -94,6 +93,17 @@
     }
 
     @Test
+    public void shouldNotCollapse_whenStartAsExpanded() {
+        when(mScreen.getPreferenceCount()).thenReturn(5);
+
+        mMixin = new ProgressiveDisclosureMixin(mAppContext,
+                mPreferenceFragment, true /* keepExpanded */);
+        mMixin.setTileLimit(10);
+
+        assertThat(mMixin.shouldCollapse(mScreen)).isFalse();
+    }
+
+    @Test
     public void shouldCollapse_morePreferenceThanLimit() {
         when(mScreen.getPreferenceCount()).thenReturn(5);
         mMixin.setTileLimit(3);
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
index 912947a..b36aef2 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
@@ -36,6 +36,7 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyString;
@@ -81,6 +82,32 @@
     }
 
     @Test
+    public void getSuggestionIdentifier_samePackage_returnClassName() {
+        final Tile suggestion = new Tile();
+        suggestion.intent = new Intent()
+                .setClassName(RuntimeEnvironment.application.getPackageName(), "123");
+        assertThat(mProvider.getSuggestionIdentifier(RuntimeEnvironment.application, suggestion))
+                .isEqualTo("123");
+    }
+
+    @Test
+    public void getSuggestionIdentifier_differentPackage_returnPackageName() {
+        final Tile suggestion = new Tile();
+        suggestion.intent = new Intent()
+                .setClassName(RuntimeEnvironment.application.getPackageName(), "123");
+        assertThat(mProvider.getSuggestionIdentifier(mContext, suggestion))
+                .isEqualTo(RuntimeEnvironment.application.getPackageName());
+    }
+
+    @Test
+    public void getSuggestionIdentifier_nullComponent_shouldNotCrash() {
+        final Tile suggestion = new Tile();
+        suggestion.intent = new Intent();
+        assertThat(mProvider.getSuggestionIdentifier(mContext, suggestion))
+                .isNotEmpty();
+    }
+
+    @Test
     public void dismissSuggestion_hasMoreDismissCount_shouldNotDisableComponent() {
         when(mSuggestionParser.dismissSuggestion(any(Tile.class), anyBoolean()))
                 .thenReturn(false);
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
new file mode 100644
index 0000000..c5570bb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.datausage;
+
+import android.content.Context;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settingslib.NetworkPolicyEditor;
+
+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.util.ReflectionHelpers;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DataUsageListTest {
+
+    @Mock
+    private CellDataPreference.DataStateListener mListener;
+    @Mock
+    private TemplatePreference.NetworkServices mNetworkServices;
+    @Mock
+    private Context mContext;
+    private DataUsageList mDataUsageList;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mNetworkServices.mPolicyEditor = mock(NetworkPolicyEditor.class);
+        mDataUsageList = spy(DataUsageList.class);
+
+        doReturn(mContext).when(mDataUsageList).getContext();
+        ReflectionHelpers.setField(mDataUsageList, "mDataStateListener", mListener);
+        ReflectionHelpers.setField(mDataUsageList, "services", mNetworkServices);
+    }
+
+    @Test
+    public void resumePause_shouldListenUnlistenDataStateChange() {
+        mDataUsageList.onResume();
+
+        verify(mListener).setListener(true, 0, mContext);
+
+        mDataUsageList.onPause();
+
+        verify(mListener).setListener(false, 0, mContext);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index 4622850..961a6c7 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -216,6 +216,7 @@
         result.gamesSize = KILOBYTE * 8;
         result.musicAppsSize = KILOBYTE * 4;
         result.otherAppsSize = KILOBYTE * 9;
+        result.systemSize = KILOBYTE * 10;
         result.externalStats = new StorageStatsSource.ExternalStorageStats(
                 KILOBYTE * 50, // total
                 KILOBYTE * 10, // audio
@@ -230,7 +231,7 @@
         assertThat(image.getSummary().toString()).isEqualTo("35.00KB"); // 15KB video + 20KB images
         assertThat(games.getSummary().toString()).isEqualTo("8.00KB");
         assertThat(apps.getSummary().toString()).isEqualTo("9.00KB");
-        assertThat(system.getSummary().toString()).isEqualTo("6.00KB");
+        assertThat(system.getSummary().toString()).isEqualTo("16.00KB");
         assertThat(files.getSummary().toString()).isEqualTo("5.00KB");
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
index 13abd97..d996750 100644
--- a/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
@@ -134,7 +134,6 @@
         mController.displayPreference(mScreen);
         verify(mWorkCategory).setVisible(false);
 
-
         // However, when a managed profile is added later, the category should appear.
         mController.onResume();
         when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
@@ -204,6 +203,27 @@
     }
 
     @Test
+    public void onResume_noVoiceCapability_shouldHidePhoneRingtone() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
+        mController = new WorkSoundPreferenceController(mContext, mFragment, null, mAudioHelper);
+
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.myUserId());
+        when(mAudioHelper.isUserUnlocked(any(UserManager.class), anyInt())).thenReturn(true);
+        when(mAudioHelper.isSingleVolume()).thenReturn(false);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+        when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
+
+        // Precondition: work profile is available.
+        assertThat(mController.isAvailable()).isTrue();
+
+        mController.displayPreference(mScreen);
+        mController.onResume();
+
+        verify(mWorkCategory.findPreference(KEY_WORK_PHONE_RINGTONE)).setVisible(false);
+    }
+
+    @Test
     public void onResume_availableButLocked_shouldRedactPreferences() {
         final String notAvailable = "(not available)";
         when(mContext.getString(R.string.managed_profile_not_available_label))
diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
index 62bb7fb..012d616 100644
--- a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
@@ -63,7 +63,6 @@
 
         assertThat(index).isNotNull();
         assertThat(index.className).isEqualTo("java.lang.String");
-        assertThat(index.rank).isEqualTo(Ranking.RANK_OTHERS);
         assertThat(index.xmlResId).isEqualTo(XML_RES_ID);
         assertThat(index.iconResId).isEqualTo(ICON_RES_ID);
         final int afterCount = SearchIndexableResources.values().size();
@@ -77,7 +76,6 @@
 
         assertThat(index).isNotNull();
         assertThat(index.className).isEqualTo(WifiSettings.class.getName());
-        assertThat(index.rank).isEqualTo(Ranking.RANK_WIFI);
         assertThat(index.xmlResId).isEqualTo(NO_DATA_RES_ID);
         assertThat(index.iconResId).isEqualTo(R.drawable.ic_settings_wireless);
     }
diff --git a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
index 028cbb0..5b7efcd 100644
--- a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
+++ b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
@@ -56,7 +56,7 @@
 
     @Test
     public void testDataTitleValid_ReturnsPreferenceTitle() {
-        XmlResourceParser parser = getChildByType(R.xml.ia_display_settings,
+        XmlResourceParser parser = getChildByType(R.xml.display_settings,
                 "com.android.settings.TimeoutListPreference");
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String title = XmlParserUtils.getDataTitle(mContext, attrs);
@@ -66,7 +66,7 @@
 
     @Test
     public void testDataKeywordsValid_ReturnsPreferenceKeywords() {
-        XmlResourceParser parser = getParentPrimedParser(R.xml.ia_display_settings);
+        XmlResourceParser parser = getParentPrimedParser(R.xml.display_settings);
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String keywords = XmlParserUtils.getDataKeywords(mContext, attrs);
         String expKeywords = mContext.getString(R.string.keywords_display);
@@ -75,7 +75,7 @@
 
     @Test
     public void testDataKeyValid_ReturnsPreferenceKey() {
-        XmlResourceParser parser = getChildByType(R.xml.ia_display_settings,
+        XmlResourceParser parser = getChildByType(R.xml.display_settings,
                 "com.android.settings.TimeoutListPreference");
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String key = XmlParserUtils.getDataKey(mContext, attrs);
@@ -85,7 +85,7 @@
 
     @Test
     public void testDataSummaryValid_ReturnsPreferenceSummary() {
-        XmlResourceParser parser = getChildByType(R.xml.ia_display_settings,
+        XmlResourceParser parser = getChildByType(R.xml.display_settings,
                 "com.android.settings.TimeoutListPreference");
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String summary = XmlParserUtils.getDataSummary(mContext, attrs);
@@ -128,7 +128,7 @@
 
     @Test
     public void testDataKeyInvalid_ReturnsNull() {
-        XmlResourceParser parser = getParentPrimedParser(R.xml.ia_display_settings);
+        XmlResourceParser parser = getParentPrimedParser(R.xml.display_settings);
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String key = XmlParserUtils.getDataKey(mContext, attrs);
         assertThat(key).isNull();
@@ -136,7 +136,7 @@
 
     @Test
     public void testDataSummaryInvalid_ReturnsNull() {
-        XmlResourceParser parser = getParentPrimedParser(R.xml.ia_display_settings);
+        XmlResourceParser parser = getParentPrimedParser(R.xml.display_settings);
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String summary = XmlParserUtils.getDataSummary(mContext, attrs);
         assertThat(summary).isNull();
@@ -144,7 +144,7 @@
 
     @Test
     public void testDataSummaryOffInvalid_ReturnsNull() {
-        XmlResourceParser parser = getParentPrimedParser(R.xml.ia_display_settings);
+        XmlResourceParser parser = getParentPrimedParser(R.xml.display_settings);
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String summaryOff = XmlParserUtils.getDataSummaryOff(mContext, attrs);
         assertThat(summaryOff).isNull();
@@ -152,7 +152,7 @@
 
     @Test
     public void testDataEntriesInvalid_ReturnsNull() {
-        XmlResourceParser parser = getParentPrimedParser(R.xml.ia_display_settings);
+        XmlResourceParser parser = getParentPrimedParser(R.xml.display_settings);
         final AttributeSet attrs = Xml.asAttributeSet(parser);
         String entries = XmlParserUtils.getDataEntries(mContext, attrs);
         assertThat(entries).isNull();
diff --git a/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java
index fdc1052..7d611c6 100644
--- a/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java
+++ b/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -17,37 +17,64 @@
 
 package com.android.settings.search2;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
 import android.database.Cursor;
+import android.database.MatrixCursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
 import android.provider.SearchIndexableResource;
-
+import android.provider.SearchIndexablesContract;
+import android.util.ArrayMap;
 import com.android.settings.R;
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.search.IndexDatabaseHelper;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.testutils.DatabaseTestUtils;
-
+import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils;
+import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowContentResolver;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+        shadows={ShadowRunnableAsyncTask.class})
 public class DatabaseIndexingManagerTest {
     private final String localeStr = "en_US";
 
@@ -75,15 +102,30 @@
     private final int userId = -1;
     private final boolean enabled = true;
 
+    private final String AUTHORITY_ONE = "authority";
+    private final String PACKAGE_ONE = "com.android.settings";
+
+    private final String TITLE_ONE = "title one";
+    private final String TITLE_TWO = "title two";
+    private final String KEY_ONE = "key one";
+    private final String KEY_TWO = "key two";
+
     private Context mContext;
+
     private DatabaseIndexingManager mManager;
     private SQLiteDatabase mDb;
 
+    @Mock
+    private PackageManager mPackageManager;
+
     @Before
     public void setUp() {
-        mContext = ShadowApplication.getInstance().getApplicationContext();
-        mManager = spy(new DatabaseIndexingManager(mContext, mContext.getPackageName()));
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mManager = spy(new DatabaseIndexingManager(mContext,"com.android.settings"));
         mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
+
+        doReturn(mPackageManager).when(mContext).getPackageManager();
     }
 
     @After
@@ -126,7 +168,7 @@
     // Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow
 
     @Test
-    public void testInsertRawColumn_RowInserted() {
+    public void testInsertRawColumn_rowInserted() {
         SearchIndexableRaw raw = getFakeRaw();
         mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
@@ -134,7 +176,7 @@
     }
 
     @Test
-    public void testInsertRawColumn_RowMatches() {
+    public void testInsertRawColumn_rowMatches() {
         SearchIndexableRaw raw = getFakeRaw();
         mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
@@ -185,7 +227,7 @@
     }
 
     @Test
-    public void testInsertRawColumnMismatchedLocale_NoRowInserted() {
+    public void testInsertRawColumn_mismatchedLocale_noRowInserted() {
         SearchIndexableRaw raw = getFakeRaw("ca-fr");
         mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
@@ -205,19 +247,19 @@
 
     @Test
     public void testAddResource_RowsInserted() {
-        SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        SearchIndexableResource resource = getFakeResource(R.xml.display_settings);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         assertThat(cursor.getCount()).isEqualTo(16);
     }
 
     @Test
-    public void testAddResourceWithNIKs_RowsInsertedDisabled() {
-        SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
+    public void testAddResource_withNIKs_rowsInsertedDisabled() {
+        SearchIndexableResource resource = getFakeResource(R.xml.display_settings);
         // Only add 2 of 16 items to be disabled.
         String[] keys = {"brightness", "wallpaper"};
-        Map<String, List<String>> niks = getNonIndexableKeys(keys);
+        Map<String, Set<String>> niks = getNonIndexableKeys(keys);
+
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
 
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
@@ -227,10 +269,9 @@
     }
 
     @Test
-    public void testAddResourceHeader_RowsMatch() {
+    public void testAddResourceHeader_rowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
 
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
         cursor.moveToPosition(1);
@@ -280,7 +321,7 @@
     }
 
     @Test
-    public void testAddResourceWithChildFragment_shouldUpdateSiteMapDb() {
+    public void testAddResource_withChildFragment_shouldUpdateSiteMapDb() {
         // FIXME: This test was failing. (count = 6 at the end)
 
 //        SearchIndexableResource resource = getFakeResource(R.xml.network_and_internet);
@@ -305,10 +346,9 @@
     }
 
     @Test
-    public void testAddResourceCustomSetting_RowsMatch() {
+    public void testAddResource_customSetting_rowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
         final String prefTitle =
                 mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
         final String prefSummary =
@@ -363,10 +403,9 @@
     }
 
     @Test
-    public void testAddResourceCheckboxPreference_RowsMatch() {
+    public void testAddResource_checkboxPreference_rowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
 
         /* Should return 6 results, with the following titles:
          * Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
@@ -418,10 +457,9 @@
     }
 
     @Test
-    public void testAddResourceListPreference_RowsMatch() {
+    public void testAddResource_listPreference_rowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
 
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
         cursor.moveToPosition(3);
@@ -476,25 +514,23 @@
     //                     UpdateOneRowWithFilteredData -> UpdateOneRow
 
     @Test
-    public void testResourceProvider_RowInserted() {
+    public void testResourceProvider_rowInserted() {
         SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
         resource.xmlResId = 0;
         resource.className = "com.android.settings.display.ScreenZoomSettings";
 
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         assertThat(cursor.getCount()).isEqualTo(1);
     }
 
     @Test
-    public void testResourceProvider_Matches() {
+    public void testResourceProvider_rowMatches() {
         SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
         resource.xmlResId = 0;
         resource.className = "com.android.settings.display.ScreenZoomSettings";
 
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         cursor.moveToPosition(0);
 
@@ -544,23 +580,21 @@
     }
 
     @Test
-    public void testResourceProvider_ResourceRowInserted() {
+    public void testResourceProvider_resourceRowInserted() {
         SearchIndexableResource resource = getFakeResource(0);
         resource.className = "com.android.settings.LegalSettings";
 
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         assertThat(cursor.getCount()).isEqualTo(6);
     }
 
     @Test
-    public void testResourceProvider_ResourceRowMatches() {
+    public void testResourceProvider_resourceRowMatches() {
         SearchIndexableResource resource = getFakeResource(0);
         resource.className = "com.android.settings.display.ScreenZoomSettings";
 
-        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<>());
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
         cursor.moveToPosition(0);
 
@@ -611,12 +645,12 @@
     }
 
     @Test
-    public void testResourceProvider_DisabledResourceRowsInserted() {
+    public void testResourceProvider_disabledResource_rowsInserted() {
         SearchIndexableResource resource = getFakeResource(0);
         resource.className = "com.android.settings.LegalSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<String, Set<String>>());
 
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
         assertThat(cursor.getCount()).isEqualTo(2);
@@ -625,7 +659,7 @@
     }
 
     @Test
-    public void testResourceWithTitleAndSettingName_TitleNotInserted() {
+    public void testResource_withTitleAndSettingName_titleNotInserted() {
         SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
         mManager.indexFromResource(mDb, localeStr, resource, new ArrayList<String>());
 
@@ -634,6 +668,176 @@
         assertThat(cursor.getCount()).isEqualTo(1);
     }
 
+    // Test new public indexing flow
+
+    @Test
+    @Config(shadows= {
+            ShadowDatabaseIndexingUtils.class,
+    })
+    public void testPerformIndexing_fullIndex_getsDataFromProviders() {
+        DummyProvider provider = new DummyProvider();
+        provider.onCreate();
+        ShadowContentResolver.registerProvider(
+                AUTHORITY_ONE, provider
+        );
+
+        // Test that Indexables are added for Full indexing
+        when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
+                .thenReturn(getDummyResolveInfo());
+
+        DatabaseIndexingManager manager =
+                spy(new DatabaseIndexingManager(mContext, "com.android.settings"));
+        doReturn(false).when(manager).isLocaleIndexed();
+
+        manager.performIndexing();
+
+        verify(manager).updateDatabase(false, Locale.getDefault().toString());
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+        cursor.moveToPosition(0);
+
+        // Data Title
+        assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE);
+    }
+
+    @Test
+    @Config(shadows= {
+            ShadowDatabaseIndexingUtils.class,
+    })
+    public void testPerformIndexing_incrementalIndex_noDataAdded() {
+        DummyProvider provider = new DummyProvider();
+        provider.onCreate();
+        ShadowContentResolver.registerProvider(
+                AUTHORITY_ONE, provider
+        );
+
+        // Test that Indexables are added for Full indexing
+        when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
+                .thenReturn(getDummyResolveInfo());
+
+        DatabaseIndexingManager manager =
+                spy(new DatabaseIndexingManager(mContext, "com.android.settings"));
+        doReturn(true).when(manager).isLocaleIndexed();
+
+        manager.performIndexing();
+
+        final Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+
+        assertThat(cursor.getCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() {
+        mManager.updateDatabase(false, localeStr);
+        mManager.mDataToProcess.dataToUpdate.clear();
+        verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
+                anyList(), anyMap());
+    }
+
+    @Test
+    public void testFullUpdatedDatabase_updatedDataInDatabaseNotCalled() {
+        mManager.updateDatabase(false, localeStr);
+        verify(mManager, times(0)).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
+    }
+
+    @Test
+    public void testLocaleUpdated_afterIndexing_localeAdded() {
+        mManager.updateDatabase(false, localeStr);
+        assertThat(IndexDatabaseHelper.getInstance(mContext)
+                .isLocaleAlreadyIndexed(mContext, localeStr)).isTrue();
+    }
+
+    @Test
+    public void testUpdateDatabase_newEligibleData_addedToDatabase() {
+        // Test that addDataToDatabase is called when dataToUpdate is non-empty
+        mManager.mDataToProcess.dataToUpdate.add(getFakeRaw());
+        mManager.updateDatabase(false, localeStr);
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+        cursor.moveToPosition(0);
+
+        // Locale
+        assertThat(cursor.getString(0)).isEqualTo(localeStr);
+        // Data Rank
+        assertThat(cursor.getInt(1)).isEqualTo(rank);
+        // Data Title
+        assertThat(cursor.getString(2)).isEqualTo(updatedTitle);
+        // Normalized Title
+        assertThat(cursor.getString(3)).isEqualTo(normalizedTitle);
+        // Summary On
+        assertThat(cursor.getString(4)).isEqualTo(updatedSummaryOn);
+        // Summary On Normalized
+        assertThat(cursor.getString(5)).isEqualTo(normalizedSummaryOn);
+        // Summary Off
+        assertThat(cursor.getString(6)).isEqualTo(updatedSummaryOff);
+        // Summary off normalized
+        assertThat(cursor.getString(7)).isEqualTo(normalizedSummaryOff);
+        // Entries
+        assertThat(cursor.getString(8)).isEqualTo(entries);
+        // Keywords
+        assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords);
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo(screenTitle);
+        // Class Name
+        assertThat(cursor.getString(11)).isEqualTo(className);
+        // Icon
+        assertThat(cursor.getInt(12)).isEqualTo(iconResId);
+        // Intent Action
+        assertThat(cursor.getString(13)).isEqualTo(action);
+        // Target Package
+        assertThat(cursor.getString(14)).isEqualTo(targetPackage);
+        // Target Class
+        assertThat(cursor.getString(15)).isEqualTo(targetClass);
+        // Enabled
+        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
+        // Data ref key
+        assertThat(cursor.getString(17)).isNotNull();
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    @Test
+    public void testUpdateDataInDatabase_enabledResultsAreNonIndexable_becomeDisabled() {
+        // Both results are enabled, and then TITLE_ONE gets disabled.
+        final boolean enabled = true;
+        insertSpecialCase(TITLE_ONE, enabled, KEY_ONE);
+        insertSpecialCase(TITLE_TWO, enabled, KEY_TWO);
+        Map<String, Set<String>> niks = new ArrayMap<>();
+        Set<String> keys = new HashSet<>();
+        keys.add(KEY_ONE);
+        niks.put(targetPackage, keys);
+
+        mManager.updateDataInDatabase(mDb, niks);
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
+        cursor.moveToPosition(0);
+
+        assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE);
+    }
+
+    @Test
+    public void testUpdateDataInDatabase_DisabledResultsAreIndexable_BecomeEnabled() {
+        // Both results are initially disabled, and then TITLE_TWO gets enabled.
+        final boolean enabled = false;
+        insertSpecialCase(TITLE_ONE, enabled, KEY_ONE);
+        insertSpecialCase(TITLE_TWO, enabled, KEY_TWO);
+        Map<String, Set<String>> niks = new ArrayMap<>();
+        Set<String> keys = new HashSet<>();
+        keys.add(KEY_ONE);
+        niks.put(targetPackage, keys);
+
+        mManager.updateDataInDatabase(mDb, niks);
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
+        cursor.moveToPosition(0);
+
+        assertThat(cursor.getString(2)).isEqualTo(TITLE_TWO);
+    }
+
     // Util functions
 
     private SearchIndexableRaw getFakeRaw() {
@@ -676,10 +880,119 @@
         return sir;
     }
 
-    private Map<String, List<String>> getNonIndexableKeys(String[] keys) {
-        Map<String, List<String>> niks = new HashMap<>();
-        List<String> keysList = new ArrayList<>(Arrays.asList(keys));
+    private Map<String, Set<String>> getNonIndexableKeys(String[] keys) {
+        Map<String, Set<String>> niks = new HashMap<>();
+        Set<String> keysList = new HashSet<>();
+        keysList.addAll(Arrays.asList(keys));
         niks.put(packageName, keysList);
         return niks;
     }
+
+    private List<ResolveInfo> getDummyResolveInfo() {
+        List<ResolveInfo> infoList = new ArrayList<>();
+        ResolveInfo info = new ResolveInfo();
+        info.providerInfo = new ProviderInfo();
+        info.providerInfo.exported = true;
+        info.providerInfo.authority = AUTHORITY_ONE;
+        info.providerInfo.packageName = PACKAGE_ONE;
+        infoList.add(info);
+
+        return infoList;
+    }
+
+    // TODO move this method and its counterpart in CursorToSearchResultConverterTest into
+    // a util class with public fields to assert values.
+    private Cursor getDummyCursor() {
+        MatrixCursor cursor = new MatrixCursor(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS);
+        final String BLANK = "";
+
+        ArrayList<String> item =
+                new ArrayList<>(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS.length);
+        item.add("42"); // Rank
+        item.add(TITLE_ONE); // Title
+        item.add(BLANK); // Summary on
+        item.add(BLANK); // summary off
+        item.add(BLANK); // entries
+        item.add(BLANK); // keywords
+        item.add(BLANK); // screen title
+        item.add(BLANK); // classname
+        item.add("123"); // Icon
+        item.add(BLANK); // Intent action
+        item.add(BLANK); // target package
+        item.add(BLANK); // target class
+        item.add(KEY_ONE); // Key
+        item.add("-1"); // userId
+        cursor.addRow(item);
+
+        return cursor;
+    }
+
+    private void insertSpecialCase(String specialCase, boolean enabled, String key) {
+
+        ContentValues values = new ContentValues();
+        values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode());
+        values.put(IndexDatabaseHelper.IndexColumns.LOCALE, localeStr);
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, specialCase);
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "");
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "");
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "");
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "");
+        values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, "");
+        values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
+        values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
+        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, targetPackage);
+        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, enabled);
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, key);
+        values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
+        values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
+        values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
+
+        mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
+    }
+
+    private class DummyProvider extends ContentProvider {
+
+        @Override
+        public boolean onCreate() {
+            return false;
+        }
+
+        @Override
+        public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+                @Nullable String selection, @Nullable String[] selectionArgs,
+                @Nullable String sortOrder) {
+            if (uri.toString().contains("xml")) {
+                return null;
+            }
+            return getDummyCursor();
+        }
+
+        @Override
+        public String getType(@NonNull Uri uri) {
+            return null;
+        }
+
+        @Override
+        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+            return null;
+        }
+
+        @Override
+        public int delete(@NonNull Uri uri, @Nullable String selection,
+                @Nullable String[] selectionArgs) {
+            return 0;
+        }
+
+        @Override
+        public int update(@NonNull Uri uri, @Nullable ContentValues values,
+                @Nullable String selection, @Nullable String[] selectionArgs) {
+            return 0;
+        }
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
index 3e22d56..2b6ebaf 100644
--- a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
@@ -43,7 +43,6 @@
 import java.util.List;
 
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyList;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -66,7 +65,8 @@
 
     @Mock
     private SavedQueryLoader mSavedQueryLoader;
-
+    @Mock
+    private SavedQueryController mSavedQueryController;
     private FakeFeatureFactory mFeatureFactory;
 
     @Before
@@ -96,6 +96,7 @@
         SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
                 .findFragmentById(R.id.main_content);
 
+        ReflectionHelpers.setField(fragment, "mShowingSavedQuery", false);
         fragment.mQuery = testQuery;
 
         activityController.saveInstanceState(bundle).pause().stop().destroy();
@@ -111,12 +112,6 @@
 
     @Test
     public void screenRotateEmptyString_ShouldNotCrash() {
-        when(mFeatureFactory.searchFeatureProvider
-                .getDatabaseSearchLoader(any(Context.class), anyString()))
-                .thenReturn(mDatabaseResultLoader);
-        when(mFeatureFactory.searchFeatureProvider
-                .getInstalledAppSearchLoader(any(Context.class), anyString()))
-                .thenReturn(mInstalledAppResultLoader);
         when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                 .thenReturn(mSavedQueryLoader);
 
@@ -134,10 +129,12 @@
         activityController = Robolectric.buildActivity(SearchActivity.class);
         activityController.setup(bundle);
 
-        verify(mFeatureFactory.searchFeatureProvider)
+        verify(mFeatureFactory.searchFeatureProvider, never())
                 .getDatabaseSearchLoader(any(Context.class), anyString());
-        verify(mFeatureFactory.searchFeatureProvider)
+        verify(mFeatureFactory.searchFeatureProvider, never())
                 .getInstalledAppSearchLoader(any(Context.class), anyString());
+        verify(mFeatureFactory.searchFeatureProvider, times(2))
+                .getSavedQueryLoader(any(Context.class));
     }
 
     @Test
@@ -160,6 +157,7 @@
 
         fragment.onQueryTextChange(testQuery);
         activityController.get().onBackPressed();
+
         activityController.pause().stop().destroy();
 
         verify(mFeatureFactory.metricsFeatureProvider, never()).action(
@@ -174,7 +172,7 @@
     }
 
     @Test
-    public void queryTextChangeToEmpty_shouldTriggerSavedQueryLoader() {
+    public void queryTextChangeToEmpty_shouldLoadSavedQuery() {
         when(mFeatureFactory.searchFeatureProvider
                 .getDatabaseSearchLoader(any(Context.class), anyString()))
                 .thenReturn(mDatabaseResultLoader);
@@ -190,20 +188,15 @@
 
         SearchFragment fragment = spy((SearchFragment) activityController.get().getFragmentManager()
                 .findFragmentById(R.id.main_content));
-
-        final SearchResultsAdapter adapter = mock(SearchResultsAdapter.class);
-        ReflectionHelpers.setField(fragment, "mSearchAdapter", adapter);
+        ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController);
+        fragment.mQuery = "123";
+        fragment.onQueryTextChange("");
 
         verify(mFeatureFactory.searchFeatureProvider, never())
                 .getDatabaseSearchLoader(any(Context.class), anyString());
         verify(mFeatureFactory.searchFeatureProvider, never())
                 .getInstalledAppSearchLoader(any(Context.class), anyString());
-        verify(mFeatureFactory.searchFeatureProvider)
-                .getSavedQueryLoader(any(Context.class));
-
-        fragment.onLoadFinished(mSavedQueryLoader, null /* data */);
-
-        verify(adapter).displaySavedQuery(anyList());
+        verify(mSavedQueryController).loadSavedQueries();
     }
 
     @Test
@@ -235,6 +228,8 @@
         when(mFeatureFactory.searchFeatureProvider
                 .getInstalledAppSearchLoader(any(Context.class), anyString()))
                 .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
+        when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
+                .thenReturn(mSavedQueryLoader);
 
         ActivityController<SearchActivity> activityController =
                 Robolectric.buildActivity(SearchActivity.class);
@@ -258,6 +253,8 @@
         when(mFeatureFactory.searchFeatureProvider
                 .getInstalledAppSearchLoader(any(Context.class), anyString()))
                 .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
+        when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
+                .thenReturn(mSavedQueryLoader);
 
         ActivityController<SearchActivity> activityController =
                 Robolectric.buildActivity(SearchActivity.class);
@@ -282,12 +279,14 @@
         when(mFeatureFactory.searchFeatureProvider
                 .getInstalledAppSearchLoader(any(Context.class), anyString()))
                 .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
+        when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
+                .thenReturn(mSavedQueryLoader);
 
         ActivityController<SearchActivity> activityController =
                 Robolectric.buildActivity(SearchActivity.class);
         activityController.setup();
-        SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
-                .findFragmentById(R.id.main_content));
+        SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
+                .findFragmentById(R.id.main_content);
 
         fragment.onQueryTextChange("non-empty");
 
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java
index bc43fc3..6ae695b 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java
@@ -29,5 +29,4 @@
     public static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
         return new SyncAdapterType[0];
     }
-
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDatabaseIndexingUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDatabaseIndexingUtils.java
new file mode 100644
index 0000000..724b9c0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDatabaseIndexingUtils.java
@@ -0,0 +1,35 @@
+/*
+ * 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.testutils.shadow;
+
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import com.android.settings.search2.DatabaseIndexingUtils;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/**
+ * Shadow of {@link DatabaseIndexingUtils}
+ */
+@Implements(DatabaseIndexingUtils.class)
+public class ShadowDatabaseIndexingUtils {
+    @Implementation
+    public static boolean isWellKnownProvider(ResolveInfo info, Context context) {
+        return true;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRunnableAsyncTask.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRunnableAsyncTask.java
new file mode 100644
index 0000000..5a71b58
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRunnableAsyncTask.java
@@ -0,0 +1,39 @@
+/*
+ * 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.testutils.shadow;
+
+import android.os.AsyncTask;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowAsyncTask;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Shadow async task to handle runnables in roboletric
+ */
+@Implements(AsyncTask.class)
+public class ShadowRunnableAsyncTask<Params, Progress, Result> extends
+        ShadowAsyncTask<Params, Progress, Result> {
+
+    @Implementation
+    public AsyncTask<Params, Progress, Result> executeOnExecutor(Executor executor,
+            Params... params) {
+        return super.execute(params);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/wifi/UseOpenWifiPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/UseOpenWifiPreferenceControllerTest.java
new file mode 100644
index 0000000..aa89464
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/UseOpenWifiPreferenceControllerTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.wifi;
+
+import static android.provider.Settings.Global.USE_OPEN_WIFI_PACKAGE;
+import static com.android.settings.wifi.UseOpenWifiPreferenceController.REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkScoreManager;
+import android.net.NetworkScorerAppData;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.network.NetworkScoreManagerWrapper;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UseOpenWifiPreferenceControllerTest {
+    private static ComponentName ENABLE_ACTIVITY_COMPONENT = new ComponentName("package", "activityClass");
+    private static NetworkScorerAppData APP_DATA =
+            new NetworkScorerAppData(0, null, null, ENABLE_ACTIVITY_COMPONENT);
+    private static NetworkScorerAppData APP_DATA_NO_ACTIVITY =
+            new NetworkScorerAppData(0, null, null, null);
+
+    @Mock private Lifecycle mLifecycle;
+    @Mock private Fragment mFragment;
+    @Mock private NetworkScoreManagerWrapper mNetworkScoreManagerWrapper;
+    @Captor private ArgumentCaptor<Intent> mIntentCaptor;
+    private Context mContext;
+    private UseOpenWifiPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+    }
+
+    private void createController() {
+        mController = new UseOpenWifiPreferenceController(
+                mContext, mFragment, mNetworkScoreManagerWrapper, mLifecycle);
+    }
+
+    @Test
+    public void testIsAvailable_noScorer() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(null);
+
+        createController();
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void testIsAvailable_noEnableActivity() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(APP_DATA_NO_ACTIVITY);
+
+        createController();
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void testIsAvailable_enableActivityExists() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(APP_DATA);
+
+        createController();
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void onPreferenceChange_nonMatchingKey_shouldDoNothing() {
+        createController();
+
+        final SwitchPreference pref = new SwitchPreference(mContext);
+
+        assertThat(mController.onPreferenceChange(pref, null)).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChange_notAvailable_shouldDoNothing() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(APP_DATA_NO_ACTIVITY);
+
+        createController();
+
+        final Preference pref = new Preference(mContext);
+        pref.setKey(mController.getPreferenceKey());
+
+        assertThat(mController.onPreferenceChange(pref, null)).isFalse();
+    }
+
+    @Test
+    public void onPreferenceChange_matchingKeyAndAvailable_enableShouldStartEnableActivity() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(APP_DATA);
+        createController();
+
+        final SwitchPreference pref = new SwitchPreference(mContext);
+        pref.setKey(mController.getPreferenceKey());
+
+        assertThat(mController.onPreferenceChange(pref, null)).isFalse();
+        verify(mFragment).startActivityForResult(mIntentCaptor.capture(),
+                eq(REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY));
+        Intent activityIntent = mIntentCaptor.getValue();
+        assertThat(activityIntent.getComponent()).isEqualTo(ENABLE_ACTIVITY_COMPONENT);
+        assertThat(activityIntent.getAction()).isEqualTo(NetworkScoreManager.ACTION_CUSTOM_ENABLE);
+    }
+
+    @Test
+    public void onPreferenceChange_matchingKeyAndAvailable_disableShouldUpdateSetting() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(APP_DATA);
+        Settings.Global.putString(mContext.getContentResolver(), USE_OPEN_WIFI_PACKAGE,
+                ENABLE_ACTIVITY_COMPONENT.getPackageName());
+
+        createController();
+
+        final SwitchPreference pref = new SwitchPreference(mContext);
+        pref.setKey(mController.getPreferenceKey());
+
+        assertThat(mController.onPreferenceChange(pref, null)).isTrue();
+        assertThat(Settings.Global.getString(mContext.getContentResolver(), USE_OPEN_WIFI_PACKAGE))
+                .isEqualTo("");
+    }
+
+    @Test
+    public void onActivityResult_nonmatchingRequestCode_shouldDoNothing() {
+        createController();
+
+        assertThat(mController.onActivityResult(234 /* requestCode */ , Activity.RESULT_OK))
+                .isEqualTo(false);
+        assertThat(Settings.Global.getString(mContext.getContentResolver(), USE_OPEN_WIFI_PACKAGE))
+                .isNull();
+    }
+
+    @Test
+    public void onActivityResult_matchingRequestCode_nonOkResult_shouldDoNothing() {
+        createController();
+
+        assertThat(mController
+                .onActivityResult(REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY, Activity.RESULT_CANCELED))
+                .isEqualTo(true);
+        assertThat(Settings.Global.getString(mContext.getContentResolver(), USE_OPEN_WIFI_PACKAGE))
+                .isNull();
+    }
+
+    @Test
+    public void onActivityResult_matchingRequestCode_okResult_updatesSetting() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(APP_DATA);
+        createController();
+
+        assertThat(mController
+                .onActivityResult(REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY, Activity.RESULT_OK))
+                .isEqualTo(true);
+        assertThat(Settings.Global.getString(mContext.getContentResolver(), USE_OPEN_WIFI_PACKAGE))
+                .isEqualTo(ENABLE_ACTIVITY_COMPONENT.getPackageName());
+    }
+
+    @Test
+    public void updateState_preferenceSetCheckedAndSetVisibleWhenSettingsAreEnabled() {
+        when(mNetworkScoreManagerWrapper.getActiveScorer()).thenReturn(APP_DATA);
+        createController();
+
+        final SwitchPreference preference = mock(SwitchPreference.class);
+        Settings.Global.putString(mContext.getContentResolver(), USE_OPEN_WIFI_PACKAGE,
+                ENABLE_ACTIVITY_COMPONENT.getPackageName());
+
+        mController.updateState(preference);
+
+        verify(preference).setVisible(true);
+        verify(preference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_preferenceSetCheckedAndSetVisibleWhenSettingsAreDisabled() {
+        final SwitchPreference preference = mock(SwitchPreference.class);
+        Settings.Global.putString(mContext.getContentResolver(), USE_OPEN_WIFI_PACKAGE, "");
+        createController();
+
+        mController.updateState(preference);
+
+        verify(preference).setVisible(false);
+        verify(preference).setChecked(false);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
new file mode 100644
index 0000000..b2648ac
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.wifi.details;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.NetworkBadging;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+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.core.lifecycle.Lifecycle;
+import com.android.settings.wifi.WifiDetailPreference;
+import com.android.settingslib.wifi.AccessPoint;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiDetailPreferenceControllerTest {
+
+    private static final int LEVEL = 1;
+    private static final int RSSI = -55;
+    private static final String SECURITY = "None";
+
+    private Context mContext = RuntimeEnvironment.application;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Drawable mockWifiIcon;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mockScreen;
+
+    @Mock private AccessPoint mockAccessPoint;
+    @Mock private WifiManager mockWifiManager;
+    @Mock private NetworkInfo mockNetworkInfo;
+    @Mock private WifiConfiguration mockWifiConfig;
+    @Mock private WifiInfo mockWifiInfo;
+
+    @Mock private Preference mockConnectionDetailPref;
+    @Mock private WifiDetailPreference mockSignalStrengthPref;
+    @Mock private WifiDetailPreference mockFrequencyPref;
+    @Mock private WifiDetailPreference mockSecurityPref;
+    @Mock private WifiDetailPreference mockIpAddressPref;
+    @Mock private WifiDetailPreference mockRouterPref;
+    @Mock private WifiDetailPreference mockSubnetPref;
+    @Mock private WifiDetailPreference mockDnsPref;
+    @Mock private PreferenceCategory mockIpv6AddressCategory;
+
+    private Lifecycle mLifecycle;
+    private WifiDetailPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mLifecycle = new Lifecycle();
+        mController = new WifiDetailPreferenceController(
+                mockAccessPoint, mContext, mLifecycle, mockWifiManager);
+
+        when(mockAccessPoint.getConfig()).thenReturn(mockWifiConfig);
+        when(mockAccessPoint.getLevel()).thenReturn(LEVEL);
+        when(mockAccessPoint.getNetworkInfo()).thenReturn(mockNetworkInfo);
+        when(mockAccessPoint.getRssi()).thenReturn(RSSI);
+        when(mockAccessPoint.getSecurityString(false)).thenReturn(SECURITY);
+
+        setupMockedPreferenceScreen();
+
+        when (mockWifiInfo.getRssi()).thenReturn(RSSI);
+        when(mockWifiManager.getConnectionInfo()).thenReturn(mockWifiInfo);
+        when(mockWifiManager.getWifiApConfiguration()).thenReturn(mockWifiConfig);
+    }
+
+    private void setupMockedPreferenceScreen() {
+
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_CONNECTION_DETAIL_PREF))
+                .thenReturn(mockConnectionDetailPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_SIGNAL_STRENGTH_PREF))
+                .thenReturn(mockSignalStrengthPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_FREQUENCY_PREF))
+                .thenReturn(mockFrequencyPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_SECURITY_PREF))
+                .thenReturn(mockSecurityPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IP_ADDRESS_PREF))
+                .thenReturn(mockIpAddressPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_ROUTER_PREF))
+                .thenReturn(mockRouterPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_SUBNET_MASK_PREF))
+                .thenReturn(mockSubnetPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_DNS_PREF))
+                .thenReturn(mockDnsPref);
+        when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_ADDRESS_CATEGORY))
+                .thenReturn(mockIpv6AddressCategory);
+
+        mController.displayPreference(mockScreen);
+    }
+
+    @Test
+    public void isAvailable_shouldAlwaysReturnTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void securityPreference_stringShouldBeSet() {
+        verify(mockSecurityPref).setDetailText(SECURITY);
+    }
+
+    @Test
+    public void latestWifiInfoAndConfig_shouldBeFetchedOnResume() {
+        mController.onResume();
+
+        verify(mockWifiManager).getConnectionInfo();
+        verify(mockWifiManager).getWifiApConfiguration();
+    }
+
+    @Test
+    public void connectionDetailPref_shouldHaveIconSet() {
+        Drawable expectedIcon =
+                NetworkBadging.getWifiIcon(LEVEL, NetworkBadging.BADGING_NONE, mContext.getTheme());
+
+        mController.onResume();
+
+        verify(mockConnectionDetailPref).setIcon(expectedIcon);
+    }
+
+    @Test
+    public void connectionDetailPref_shouldHaveTitleSet() {
+        String summary = "summary";
+        when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
+
+        mController.onResume();
+
+        verify(mockConnectionDetailPref).setTitle(summary);
+    }
+
+    @Test
+    public void signalStrengthPref_shouldHaveIconSet() {
+        mController.onResume();
+
+        verify(mockSignalStrengthPref).setIcon(any(Drawable.class));
+    }
+
+    @Test
+    public void signalStrengthPref_shouldHaveDetailTextSet() {
+        String expectedStrength =
+                mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
+
+        mController.onResume();
+
+        verify(mockSignalStrengthPref).setDetailText(expectedStrength);
+    }
+}
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index f98fccd..f9c0489 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -18,6 +18,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := SettingsUnitTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_INSTRUMENTATION_FOR := Settings
 
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
index c83a594..617e9bb 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
@@ -145,7 +145,7 @@
     }
 
     @Test
-    public void testSystemAppsBaseSizeIsIgnored() throws Exception {
+    public void testSystemAppsBaseSizeIsAddedToSystem() throws Exception {
         ApplicationInfo systemApp =
                 addPackage(PACKAGE_NAME_1, 100, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
         systemApp.flags = ApplicationInfo.FLAG_SYSTEM;
@@ -154,6 +154,7 @@
 
         assertThat(result.size()).isEqualTo(1);
         assertThat(result.get(PRIMARY_USER_ID).otherAppsSize).isEqualTo(10L);
+        assertThat(result.get(PRIMARY_USER_ID).systemSize).isEqualTo(1L);
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java
index 4a62011..bb12efa 100644
--- a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java
+++ b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java
@@ -17,7 +17,6 @@
 package com.android.settings.vpn2;
 
 import static org.mockito.AdditionalMatchers.not;
-import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
 import android.content.Context;