Merge "Point to the new checkstyle location for packages/apps/Settings PREUPLOAD hook."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3c35428..87b6372 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -276,21 +276,6 @@
                 android:value="true" />
         </activity>
 
-        <activity android:name=".wifi.WifiSetupActivity"
-                android:taskAffinity="com.android.wizard"
-                android:theme="@style/SetupWizardDisableAppStartingTheme"
-                android:label="@string/wifi_setup_wizard_title"
-                android:icon="@drawable/empty_icon"
-                android:clearTaskOnLaunch="true"
-                android:windowSoftInputMode="adjustNothing">
-            <intent-filter android:priority="1">
-                <action android:name="com.android.net.wifi.SETUP_WIFI_NETWORK" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
-                android:value="true" />
-        </activity>
-
         <activity android:name="Settings$AdvancedWifiSettingsActivity"
                 android:taskAffinity="com.android.settings"
                 android:label="@string/wifi_advanced_settings_label"
@@ -2565,16 +2550,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".ActiveNetworkScorerDialog"
-                  android:label="@string/wifi_assistant_title"
-                  android:excludeFromRecents="true"
-                  android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
-            <intent-filter android:priority="1">
-                <action android:name="android.net.scoring.CHANGE_ACTIVE" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
         <activity android:name="Settings$NotificationAccessSettingsActivity"
                   android:label="@string/manage_notification_access_title"
                   android:taskAffinity="">
@@ -3083,10 +3058,22 @@
                        android:value="com.android.settings.datausage.DataUsageSummary"/>
         </activity-alias>
 
+        <activity android:name=".Settings$ConnectedDeviceDashboardActivity"
+                  android:label="@string/connected_devices_dashboard_title"
+                  android:icon="@drawable/ic_bt_laptop">
+            <intent-filter android:priority="10">
+                <action android:name="com.android.settings.action.SETTINGS"/>
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"/>
+            <meta-data android:name="com.android.settings.category"
+                       android:value="com.android.settings.category.ia.homepage"/>
+        </activity>
+
         <activity android:name=".Settings$AppAndNotificationDashboardActivity"
                   android:label="@string/app_and_notification_dashboard_title"
                   android:icon="@drawable/ic_settings_applications">
-            <intent-filter android:priority="8">
+            <intent-filter android:priority="9">
                 <action android:name="com.android.settings.action.SETTINGS"/>
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
@@ -3097,7 +3084,7 @@
 
         <activity-alias android:name="BatteryDashboardAlias"
                         android:targetActivity="Settings$PowerUsageSummaryActivity">
-            <intent-filter android:priority="7">
+            <intent-filter android:priority="8">
                 <action android:name="com.android.settings.action.SETTINGS"/>
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
@@ -3108,7 +3095,7 @@
 
         <activity-alias android:name="DisplayDashboardAlias"
                         android:targetActivity="Settings$DisplaySettingsActivity">
-            <intent-filter android:priority="6">
+            <intent-filter android:priority="7">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
             <meta-data android:name="com.android.settings.category"
@@ -3121,7 +3108,7 @@
 
         <activity-alias android:name="SoundDashboardAlias"
                         android:targetActivity="Settings$SoundSettingsActivity">
-            <intent-filter android:priority="5">
+            <intent-filter android:priority="6">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
             <meta-data android:name="com.android.settings.category"
@@ -3135,7 +3122,7 @@
         <activity android:name=".Settings$StorageDashboardActivity"
                   android:label="@string/storage_settings"
                   android:icon="@drawable/ic_settings_storage">
-            <intent-filter android:priority="4">
+            <intent-filter android:priority="5">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
             <meta-data android:name="com.android.settings.category"
@@ -3162,7 +3149,7 @@
 
         <activity-alias android:name="SecurityDashboardAlias"
                 android:targetActivity="Settings$SecuritySettingsActivity">
-            <intent-filter android:priority="3">
+            <intent-filter android:priority="4">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
             <meta-data android:name="com.android.settings.category"
@@ -3173,6 +3160,18 @@
                        android:value="true" />
         </activity-alias>
 
+        <activity android:name=".Settings$UserAndAccountDashboardActivity"
+                  android:label="@string/account_dashboard_title"
+                  android:icon="@drawable/ic_settings_accounts">
+            <intent-filter android:priority="3">
+                <action android:name="com.android.settings.action.SETTINGS"/>
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.accounts.UserAndAccountDashboardFragment"/>
+            <meta-data android:name="com.android.settings.category"
+                       android:value="com.android.settings.category.ia.homepage"/>
+        </activity>
+
         <activity-alias android:name="LocationDashboardAlias"
                         android:targetActivity="Settings$LocationSettingsActivity">
             <intent-filter>
@@ -3186,6 +3185,19 @@
                        android:value="true" />
         </activity-alias>
 
+        <activity-alias android:name="AccessibilityDashboardAlias"
+                        android:targetActivity="Settings$AccessibilitySettingsActivity">
+            <intent-filter android:priority="2">
+                <action android:name="com.android.settings.action.SETTINGS" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.category"
+                       android:value="com.android.settings.category.ia.homepage" />
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.accessibility.AccessibilitySettings" />
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                       android:value="true" />
+        </activity-alias>
+
         <activity android:name=".Settings$SystemDashboardActivity"
                   android:label="@string/header_category_system"
                   android:icon="@drawable/ic_settings_about">
@@ -3257,19 +3269,6 @@
                        android:value="true" />
         </activity-alias>
 
-        <activity-alias android:name="AccessibilityDashboardAlias"
-                    android:targetActivity="Settings$AccessibilitySettingsActivity">
-            <intent-filter android:priority="150">
-                <action android:name="com.android.settings.action.SETTINGS" />
-            </intent-filter>
-            <meta-data android:name="com.android.settings.category"
-                       android:value="com.android.settings.category.ia.system" />
-            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                       android:value="com.android.settings.accessibility.AccessibilitySettings" />
-            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
-                       android:value="true" />
-        </activity-alias>
-
         <activity-alias android:name="PrivacyDashboardAlias"
             android:targetActivity="Settings$PrivacySettingsActivity">
             <intent-filter android:priority="60">
@@ -3334,18 +3333,6 @@
                        android:value="com.android.settings.applications.ProcessStatsSummary" />
         </activity-alias>
 
-        <activity android:name=".Settings$ConnectedDeviceDashboardActivity"
-                  android:label="@string/connected_devices_dashboard_title"
-                  android:icon="@drawable/ic_bt_laptop">
-            <intent-filter android:priority="9">
-                <action android:name="com.android.settings.action.SETTINGS"/>
-            </intent-filter>
-            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                       android:value="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"/>
-            <meta-data android:name="com.android.settings.category"
-                       android:value="com.android.settings.category.ia.homepage"/>
-        </activity>
-
         <activity-alias android:name="BluetoothDashboardAlias"
                         android:targetActivity="Settings$BluetoothSettingsActivity">
             <intent-filter android:priority="7">
@@ -3416,18 +3403,6 @@
                        android:value="true" />
         </activity-alias>
 
-        <activity android:name=".Settings$UserAndAccountDashboardActivity"
-                  android:label="@string/account_dashboard_title"
-                  android:icon="@drawable/ic_settings_accounts">
-            <intent-filter android:priority="2">
-                <action android:name="com.android.settings.action.SETTINGS"/>
-            </intent-filter>
-            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                       android:value="com.android.settings.accounts.UserAndAccountDashboardFragment"/>
-            <meta-data android:name="com.android.settings.category"
-                       android:value="com.android.settings.category.ia.homepage"/>
-        </activity>
-
         <activity-alias android:name="UserDashboardAlias"
                         android:targetActivity="Settings$UserSettingsActivity">
             <intent-filter android:priority="8">
diff --git a/res/drawable-hdpi/setup_illustration_horizontal_tile.jpg b/res/drawable-hdpi/setup_illustration_horizontal_tile.jpg
deleted file mode 100644
index 8d330ce..0000000
--- a/res/drawable-hdpi/setup_illustration_horizontal_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/setup_illustration_lock_screen.jpg b/res/drawable-hdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index 263d33e..0000000
--- a/res/drawable-hdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/setup_illustration_tile.jpg b/res/drawable-hdpi/setup_illustration_tile.jpg
deleted file mode 100644
index d518abd..0000000
--- a/res/drawable-hdpi/setup_illustration_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/setup_illustration_wifi.jpg b/res/drawable-hdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index 2e7241d..0000000
--- a/res/drawable-hdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/setup_illustration_horizontal_tile.jpg b/res/drawable-mdpi/setup_illustration_horizontal_tile.jpg
deleted file mode 100644
index 2b9b04b..0000000
--- a/res/drawable-mdpi/setup_illustration_horizontal_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/setup_illustration_lock_screen.jpg b/res/drawable-mdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index 004fa9e..0000000
--- a/res/drawable-mdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/setup_illustration_tile.jpg b/res/drawable-mdpi/setup_illustration_tile.jpg
deleted file mode 100644
index f2e1bcb..0000000
--- a/res/drawable-mdpi/setup_illustration_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/setup_illustration_wifi.jpg b/res/drawable-mdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index 4a9b8a5..0000000
--- a/res/drawable-mdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/setup_illustration_lock_screen.jpg b/res/drawable-sw600dp-hdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index 933e986..0000000
--- a/res/drawable-sw600dp-hdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/setup_illustration_wifi.jpg b/res/drawable-sw600dp-hdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index 7359f56..0000000
--- a/res/drawable-sw600dp-hdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/setup_illustration_lock_screen.jpg b/res/drawable-sw600dp-mdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index 57e0385..0000000
--- a/res/drawable-sw600dp-mdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/setup_illustration_wifi.jpg b/res/drawable-sw600dp-mdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index 9b13e51..0000000
--- a/res/drawable-sw600dp-mdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/setup_illustration_lock_screen.jpg b/res/drawable-sw600dp-xhdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index e329a96..0000000
--- a/res/drawable-sw600dp-xhdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/setup_illustration_wifi.jpg b/res/drawable-sw600dp-xhdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index dc9e385..0000000
--- a/res/drawable-sw600dp-xhdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/setup_illustration_lock_screen.jpg b/res/drawable-sw600dp-xxhdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index 7934e8a..0000000
--- a/res/drawable-sw600dp-xxhdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/setup_illustration_wifi.jpg b/res/drawable-sw600dp-xxhdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index b57bc17..0000000
--- a/res/drawable-sw600dp-xxhdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxxhdpi/setup_illustration_lock_screen.jpg b/res/drawable-sw600dp-xxxhdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index ab61328..0000000
--- a/res/drawable-sw600dp-xxxhdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxxhdpi/setup_illustration_wifi.jpg b/res/drawable-sw600dp-xxxhdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index b940a9b..0000000
--- a/res/drawable-sw600dp-xxxhdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/setup_illustration_horizontal_tile.jpg b/res/drawable-xhdpi/setup_illustration_horizontal_tile.jpg
deleted file mode 100644
index ae67f38..0000000
--- a/res/drawable-xhdpi/setup_illustration_horizontal_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/setup_illustration_lock_screen.jpg b/res/drawable-xhdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index b78a59d..0000000
--- a/res/drawable-xhdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/setup_illustration_tile.jpg b/res/drawable-xhdpi/setup_illustration_tile.jpg
deleted file mode 100644
index 8beeda8..0000000
--- a/res/drawable-xhdpi/setup_illustration_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/setup_illustration_wifi.jpg b/res/drawable-xhdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index c03f4dd..0000000
--- a/res/drawable-xhdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/setup_illustration_horizontal_tile.jpg b/res/drawable-xxhdpi/setup_illustration_horizontal_tile.jpg
deleted file mode 100644
index 18b885d..0000000
--- a/res/drawable-xxhdpi/setup_illustration_horizontal_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/setup_illustration_lock_screen.jpg b/res/drawable-xxhdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index 875e55e..0000000
--- a/res/drawable-xxhdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/setup_illustration_tile.jpg b/res/drawable-xxhdpi/setup_illustration_tile.jpg
deleted file mode 100644
index 05d4804..0000000
--- a/res/drawable-xxhdpi/setup_illustration_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/setup_illustration_wifi.jpg b/res/drawable-xxhdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index e0791cd..0000000
--- a/res/drawable-xxhdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/setup_illustration_horizontal_tile.jpg b/res/drawable-xxxhdpi/setup_illustration_horizontal_tile.jpg
deleted file mode 100644
index 6d97626..0000000
--- a/res/drawable-xxxhdpi/setup_illustration_horizontal_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/setup_illustration_lock_screen.jpg b/res/drawable-xxxhdpi/setup_illustration_lock_screen.jpg
deleted file mode 100644
index 8ac85a4..0000000
--- a/res/drawable-xxxhdpi/setup_illustration_lock_screen.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/setup_illustration_tile.jpg b/res/drawable-xxxhdpi/setup_illustration_tile.jpg
deleted file mode 100644
index 9d5c4c9..0000000
--- a/res/drawable-xxxhdpi/setup_illustration_tile.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/setup_illustration_wifi.jpg b/res/drawable-xxxhdpi/setup_illustration_wifi.jpg
deleted file mode 100644
index 62352d3..0000000
--- a/res/drawable-xxxhdpi/setup_illustration_wifi.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_info_outline_24dp.xml b/res/drawable/ic_info_outline_24dp.xml
new file mode 100644
index 0000000..3fe1e9e
--- /dev/null
+++ b/res/drawable/ic_info_outline_24dp.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorSecondary">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/res/drawable/setup_ic_add_another_network.xml b/res/drawable/setup_ic_add_another_network.xml
deleted file mode 100644
index fd716d1..0000000
--- a/res/drawable/setup_ic_add_another_network.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-    Copyright (C) 2015 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="25.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="38.0"
-        android:viewportHeight="48.0">
-
-    <group android:translateX="5.0" android:translateY="10.0">
-
-        <path
-            android:fillColor="?android:attr/colorControlNormal"
-            android:pathData="M28.0,16.0L16.0,16.0l0.0,12.0l-4.0,0.0L12.0,16.0L0.0,16.0l0.0,-4.0l12.0,0.0L12.0,0.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
-
-    </group>
-
-</vector>
diff --git a/res/layout/choose_lock_pattern_common.xml b/res/layout/choose_lock_pattern_common.xml
index 6557e6a..c50aa5b 100644
--- a/res/layout/choose_lock_pattern_common.xml
+++ b/res/layout/choose_lock_pattern_common.xml
@@ -22,7 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:icon="@drawable/ic_lock"
-    settings:suwHeaderText="@string/wifi_setup_wizard_title">
+    settings:suwHeaderText="@string/lockpassword_choose_your_pattern_header">
 
     <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
         android:id="@+id/topLayout"
diff --git a/res/layout/fingerprint_enroll_introduction.xml b/res/layout/fingerprint_enroll_introduction.xml
index 2348b6a..ccd1f62 100644
--- a/res/layout/fingerprint_enroll_introduction.xml
+++ b/res/layout/fingerprint_enroll_introduction.xml
@@ -35,6 +35,12 @@
             android:layout_height="wrap_content"
             android:text="@string/security_settings_fingerprint_enroll_introduction_message" />
 
+        <com.android.setupwizardlib.view.RichTextView
+            android:id="@+id/error_text"
+            style="@style/SuwDescription.Glif"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/res/layout/fingerprint_settings_footer.xml b/res/layout/fingerprint_settings_footer.xml
deleted file mode 100644
index 8d17052..0000000
--- a/res/layout/fingerprint_settings_footer.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 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.settings.widget.LinkTextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@android:style/TextAppearance.Material.Body1"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:textColor="?android:attr/textColorSecondary"
-    android:paddingTop="32dp"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:selectable="false" />
diff --git a/res/layout/preference_footer.xml b/res/layout/preference_footer.xml
new file mode 100644
index 0000000..64fad74
--- /dev/null
+++ b/res/layout/preference_footer.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2016 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="60dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <com.android.internal.widget.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    </LinearLayout>
+
+    <com.android.settings.widget.LinkTextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingBottom="16dp"
+        android:paddingTop="16dp"
+        android:maxLines="10"
+        android:textColor="?android:attr/textColorSecondary"
+        android:ellipsize="marquee"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/setup_wifi_add_network.xml b/res/layout/setup_wifi_add_network.xml
deleted file mode 100644
index 23577c5..0000000
--- a/res/layout/setup_wifi_add_network.xml
+++ /dev/null
@@ -1,47 +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:id="@+id/other_network"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/setup_add_network_item_height"
-    android:background="?android:attr/selectableItemBackground"
-    android:gravity="center_vertical"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart">
-
-    <LinearLayout
-        android:layout_width="@dimen/setup_preference_icon_frame_width"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/setup_preference_icon_frame_margin_start"
-        android:gravity="start|center_vertical"
-        android:paddingEnd="@dimen/setup_preference_icon_frame_padding_end">
-
-        <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/setup_ic_add_another_network" />
-
-    </LinearLayout>
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/wifi_other_network"
-        android:textAppearance="@style/TextAppearance.SetupWizardDescription" />
-
-</LinearLayout>
diff --git a/res/layout/setup_wifi_empty.xml b/res/layout/setup_wifi_empty.xml
deleted file mode 100644
index b1659d7..0000000
--- a/res/layout/setup_wifi_empty.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 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="wrap_content"
-    android:paddingTop="@dimen/setup_wizard_margin_top"
-    android:paddingStart="@dimen/suw_layout_margin_sides"
-    android:paddingEnd="@dimen/suw_layout_margin_sides"
-    android:textAppearance="@style/TextAppearance.SetupWizardDescription" />
diff --git a/res/layout/setup_wifi_layout.xml b/res/layout/setup_wifi_layout.xml
deleted file mode 100644
index 62bb6a1..0000000
--- a/res/layout/setup_wifi_layout.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright 2015, 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.SetupWizardListLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:settings="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    settings:suwBackgroundTile="@drawable/setup_illustration_tile"
-    settings:suwHeaderText="@string/wifi_setup_wizard_title"
-    settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
-    settings:suwIllustrationImage="@drawable/setup_illustration_wifi" />
diff --git a/res/layout/setup_wifi_mac_address.xml b/res/layout/setup_wifi_mac_address.xml
deleted file mode 100644
index 60f3212..0000000
--- a/res/layout/setup_wifi_mac_address.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2015 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="@dimen/setup_add_network_item_height"
-    android:gravity="center_vertical"
-    android:orientation="vertical"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:textDirection="locale">
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="@dimen/setup_list_no_icon_padding"
-        android:text="@string/wifi_advanced_mac_address_title" />
-
-    <TextView
-        android:id="@+id/mac_address"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="@dimen/setup_list_no_icon_padding" />
-
-</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2931a98..4b3dba9 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -40,14 +40,6 @@
 
     <dimen name="crypt_clock_size">100sp</dimen>
 
-    <dimen name="setup_add_network_item_height">56dp</dimen>
-    <!-- Size of padding to give in the wifi list when there is no icon -->
-    <dimen name="setup_list_no_icon_padding">56dp</dimen>
-    <dimen name="setup_preference_icon_frame_margin_start">-4dp</dimen>
-    <dimen name="setup_preference_icon_frame_padding_end">12dp</dimen>
-    <dimen name="setup_preference_icon_frame_width">60dp</dimen>
-    <dimen name="setup_wizard_margin_top">24dp</dimen>
-
     <dimen name="divider_height">3dip</dimen>
     <dimen name="divider_margin_top">6dip</dimen>
     <dimen name="divider_margin_bottom">7dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4d872b1..3d7a777 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -866,6 +866,10 @@
 
     <!-- Text shown when "Add fingerprint" button is disabled -->
     <string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
+    <!-- Text shown when users has enrolled a maximum number of fingerprints [CHAR LIMIT=NONE] -->
+    <string name="fingerprint_intro_error_max">You\u2019ve added the maximum number of fingerprints</string>
+    <!-- Text shown when an unknown error caused the device to be unable to add fingerprints [CHAR LIMIT=NONE] -->
+    <string name="fingerprint_intro_error_unknown">Can\u2019t add more fingerprints</string>
 
     <!-- Title shown in a dialog which asks the user to confirm when the last fingerprint gets deleted by him. [CHAR LIMIT=50]-->
     <string name="fingerprint_last_delete_title">Remove all fingerprints?</string>
@@ -1471,9 +1475,6 @@
     <!-- Bluetooth settings.  Dock Setting Dialog - Remember setting and don't ask user again -->
     <string name="bluetooth_dock_settings_remember">Remember settings</string>
 
-    <!-- Wifi Assistant title.  [CHAR LIMIT=40] -->
-    <string name="wifi_assistant_title">Wi\u2011Fi Assistant</string>
-
     <!-- Wifi Display settings. The title of the screen. [CHAR LIMIT=40] -->
     <string name="wifi_display_settings_title">Cast</string>
     <!-- Wifi Display settings. The title of a menu item to enable wireless display [CHAR LIMIT=40] -->
@@ -1568,8 +1569,6 @@
     <string name="wifi_settings_title">Wi\u2011Fi</string>
     <!-- Summary text of the Wi-fi settings screen -->
     <string name="wifi_settings_summary">Set up &amp; manage wireless access points</string>
-    <!-- Used in the 1st-level settings screen to turn on Wi-Fi  [CHAR LIMIT=60] -->
-    <string name="wifi_setup_wizard_title">Select Wi\u2011Fi network</string>
     <!-- Used by Account creation for turning on Wi-Fi  [CHAR LIMIT=60] -->
     <string name="wifi_select_network">Select Wi\u2011Fi</string>
     <!-- Summary text when turning Wi-Fi or bluetooth on -->
@@ -1592,12 +1591,12 @@
     <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="wifi_automatically_connect_title">Use open Wi\u2011Fi automatically</string>
-    <!-- Checkbox summary for option to connect to open Wi-Fi automatically  [CHAR LIMIT=100] -->
-    <string name="wifi_automatically_connect_summary">Let a Wi\u2011Fi assistant automatically connect to open networks determined to be high quality</string>
-    <!-- Dialog title for option to select an app which connects to open Wi-Fi automatically [CHAR LIMIT=40] -->
-    <string name="wifi_select_assistant_dialog_title">Choose assistant</string>
+
+    <!-- Network recommendations toggle -->
+    <!--TODO(jjoslin): Provide final strings and mark for translation. BUG: 32913919 -->
+    <string translatable="false" name="networking_allow_recommendations_title">Use network recommendations</string>
+    <string translatable="false" name="networking_allow_recommendations_summary">When enabled, use network recommendations when selecting the Wi\u2011Fi network to connect to</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
@@ -1663,8 +1662,6 @@
     <string name="wifi_empty_list_wifi_on">Searching for Wi\u2011Fi networks\u2026</string>
     <!-- Wifi Settings. text displayed when user has restriction DISALLOW_CONFIG_WIFI [CHAR LIMIT=NONE]-->
     <string name="wifi_empty_list_user_restricted">You don\u2019t have permission to change the Wi\u2011Fi network.</string>
-    <!-- Wi-Fi settings. title for setup other network button [CHAR LIMIT=35]-->
-    <string name="wifi_other_network">Add another network</string>
     <!-- Wi-Fi settings. content description for more button [CHAR LIMIT=50]-->
     <string name="wifi_more">More</string>
     <!-- Wi-Fi settings. wps menu title [CHAR LIMIT=25]-->
@@ -1813,30 +1810,6 @@
     <string name="wifi_failed_save_message">Failed to save network</string>
     <!-- Button label to dismiss the dialog -->
     <string name="wifi_cancel">Cancel</string>
-    <!-- Button for skipping a step after having been warned of a potential concern [CHAR LIMIT=30] -->
-    <string name="wifi_skip_anyway">Skip anyway</string>
-    <!-- Button for going to the previous screen or step [CHAR LIMIT=20] -->
-    <string name="wifi_dont_skip">Go back</string>
-
-    <!-- Text warning about skipping Wi-Fi with a mobile connect (tablet) [CHAR LIMIT=NONE]-->
-    <string name="wifi_skipped_message" product="tablet">WARNING: if you skip Wi\u2011Fi, your tablet will only use cellular data for initial downloads and updates. To avoid possible data charges, connect to Wi\u2011Fi.</string>
-    <!-- Text warning about skipping Wi-Fi with a mobile connect (device) [CHAR LIMIT=NONE]-->
-    <string name="wifi_skipped_message" product="device">WARNING: if you skip Wi\u2011Fi, your device will only use cellular data for initial downloads and updates. To avoid possible data charges, connect to Wi\u2011Fi.</string>
-    <!-- Text warning about skipping Wi-Fi with a mobile connect (phone) [CHAR LIMIT=NONE]-->
-    <string name="wifi_skipped_message" product="default">WARNING: if you skip Wi\u2011Fi, your phone will only use cellular data for initial downloads and updates. To avoid possible data charges, connect to Wi\u2011Fi.</string>
-
-    <!-- Text warning about skipping Wi-Fi without a mobile connect (tablet) [CHAR LIMIT=NONE] -->
-    <string name="wifi_and_mobile_skipped_message" product="tablet">If you skip Wi\u2011Fi:\n\n<li>Your tablet won\u2019t have an Internet connection.</li>\n\n<li>You won\u2019t get software updates until you connect to the Internet.</li>\n\n<li>You can\u2019t activate device protection features at this time.</li></string>
-    <!-- Text warning about skipping Wi-Fi without a mobile connect (device) [CHAR LIMIT=NONE] -->
-    <string name="wifi_and_mobile_skipped_message" product="device">If you skip Wi\u2011Fi:\n\n<li>Your device won\u2019t have an Internet connection.</li>\n\n<li>You won\u2019t get software updates until you connect to the Internet.</li>\n\n<li>You can\u2019t activate device protection features at this time.</li></string>
-    <!-- Text warning about skipping Wi-Fi without a mobile connect (phone) [CHAR LIMIT=NONE] -->
-    <string name="wifi_and_mobile_skipped_message" product="default">If you skip Wi\u2011Fi:\n\n<li>Your phone won\u2019t have an Internet connection.</li>\n\n<li>You won\u2019t get software updates until you connect to the Internet.</li>\n\n<li>You can\u2019t activate device protection features at this time.</li></string>
-    <!-- Text alerting that Wi-Fi couldn't connect (tablet) [CHAR LIMIT=NONE] -->
-    <string name="wifi_connect_failed_message" product="tablet">The tablet was unable to connect to this Wi\u2011Fi network.</string>
-    <!-- Text alerting that Wi-Fi couldn't connect (device) [CHAR LIMIT=NONE] -->
-    <string name="wifi_connect_failed_message" product="device">The device was unable to connect to this Wi\u2011Fi network.</string>
-    <!-- Text alerting that Wi-Fi couldn't connect (phone) [CHAR LIMIT=NONE] -->
-    <string name="wifi_connect_failed_message" product="default">The phone was unable to connect to this Wi\u2011Fi network.</string>
 
     <!-- Wi-Fi Advanced Settings --> <skip />
     <!-- Wi-Fi settings screen, Saved networks, settings section.  This is a header shown above Saved networks wifi settings. [CHAR LIMIT=30] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index f4b39d4..ce7ddaa 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -140,9 +140,6 @@
         <item name="android:scrollbarStyle">outsideOverlay</item>
     </style>
 
-    <style name="TextAppearance.SetupWizardDescription" parent="@android:style/TextAppearance.Material.Subhead">
-    </style>
-
     <style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
         <item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
         <item name="android:minHeight">3dip</item>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index edc1dbf..02bf523 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -22,13 +22,6 @@
     <attr name="side_margin" format="reference|dimension" />
     <attr name="wifi_signal_color" format="reference" />
 
-    <style name="SetupWizardDisableAppStartingTheme">
-        <!-- Theme to disable the app starting window. The actual theme of the activity needs to
-             be then set in code via setTheme or onApplyThemeResource. -->
-        <item name="preferenceTheme">@style/PreferenceTheme.SetupWizard</item>
-        <item name="android:windowBackground">@null</item>
-    </style>
-
     <style name="SetupWizardTheme" parent="SuwThemeGlif">
         <!-- For all Alert Dialogs -->
         <item name="android:alertDialogTheme">@style/ThemeOverlay.AlertDialog</item>
diff --git a/res/xml/wifi_configure_settings.xml b/res/xml/wifi_configure_settings.xml
index 863007c..53fda71 100644
--- a/res/xml/wifi_configure_settings.xml
+++ b/res/xml/wifi_configure_settings.xml
@@ -35,11 +35,10 @@
         android:entries="@array/wifi_sleep_policy_entries"
         android:entryValues="@array/wifi_sleep_policy_values" />
 
-    <com.android.settings.AppListSwitchPreference
-        android:key="wifi_assistant"
-        android:title="@string/wifi_automatically_connect_title"
-        android:summary="@string/wifi_automatically_connect_summary"
-        android:dialogTitle="@string/wifi_select_assistant_dialog_title" />
+    <SwitchPreference
+      android:key="allow_recommendations"
+      android:title="@string/networking_allow_recommendations_title"
+      android:summary="@string/networking_allow_recommendations_summary"/>
 
     <SwitchPreference
         android:key="wifi_cellular_data_fallback"
diff --git a/src/com/android/settings/ActiveNetworkScorerDialog.java b/src/com/android/settings/ActiveNetworkScorerDialog.java
deleted file mode 100644
index ae04b58..0000000
--- a/src/com/android/settings/ActiveNetworkScorerDialog.java
+++ /dev/null
@@ -1,112 +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;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.NetworkScoreManager;
-import android.net.NetworkScorerAppManager;
-import android.net.NetworkScorerAppManager.NetworkScorerAppData;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-
-/**
- * Dialog to allow a user to select a new network scorer.
- *
- * <p>Finishes with {@link #RESULT_CANCELED} in all circumstances unless the scorer is successfully
- * changed or was already set to the new value (in which case it finishes with {@link #RESULT_OK}).
- */
-public final class ActiveNetworkScorerDialog extends AlertActivity implements
-        DialogInterface.OnClickListener {
-    private static final String TAG = "ActiveNetScorerDlg";
-
-    private String mNewPackageName;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        Intent intent = getIntent();
-        mNewPackageName = intent.getStringExtra(NetworkScoreManager.EXTRA_PACKAGE_NAME);
-
-        if (!buildDialog()) {
-            finish();
-        }
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        switch (which) {
-            case BUTTON_POSITIVE:
-                NetworkScoreManager nsm =
-                    (NetworkScoreManager) getSystemService(Context.NETWORK_SCORE_SERVICE);
-                if (nsm.setActiveScorer(mNewPackageName)) {
-                    setResult(RESULT_OK);
-                }
-                break;
-            case BUTTON_NEGATIVE:
-                break;
-        }
-    }
-
-    private boolean buildDialog() {
-        // TOOD: http://b/23422763
-        if (UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
-            Log.i(TAG, "Can only set scorer for owner/system user.");
-            return false;
-        }
-        NetworkScorerAppManager networkScorerAppManager = new NetworkScorerAppManager(this);
-        NetworkScorerAppData newScorer = networkScorerAppManager.getScorer(mNewPackageName);
-        if (newScorer == null) {
-            Log.e(TAG, "New package " + mNewPackageName + " is not a valid scorer.");
-            return false;
-        }
-
-        NetworkScorerAppData oldScorer = networkScorerAppManager.getActiveScorer();
-        if (oldScorer != null && TextUtils.equals(oldScorer.mPackageName, mNewPackageName)) {
-            Log.i(TAG, "New package " + mNewPackageName + " is already the active scorer.");
-            // Set RESULT_OK to indicate to the caller that the "switch" was successful.
-            setResult(RESULT_OK);
-            return false;
-        }
-
-        // Compose dialog.
-        CharSequence newName = newScorer.mScorerName;
-        final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.network_scorer_change_active_dialog_title);
-        if (oldScorer != null) {
-            p.mMessage = getString(R.string.network_scorer_change_active_dialog_text, newName,
-                    oldScorer.mScorerName);
-        } else {
-            p.mMessage = getString(R.string.network_scorer_change_active_no_previous_dialog_text,
-                    newName);
-        }
-        p.mPositiveButtonText = getString(R.string.yes);
-        p.mNegativeButtonText = getString(R.string.no);
-        p.mPositiveButtonListener = this;
-        p.mNegativeButtonListener = this;
-        setupAlert();
-
-        return true;
-    }
-}
diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java
index eeb4779..ddf0dec 100644
--- a/src/com/android/settings/RadioInfo.java
+++ b/src/com/android/settings/RadioInfo.java
@@ -1164,7 +1164,7 @@
 
     void setImsConfigProvisionedState(int configItem, boolean state) {
         if (phone != null && mImsManager != null) {
-            QueuedWork.singleThreadExecutor().submit(new Runnable() {
+            QueuedWork.queue(new Runnable() {
                 public void run() {
                     try {
                         mImsManager.getConfigInterface().setProvisionedValue(
@@ -1174,7 +1174,7 @@
                         Log.e(TAG, "setImsConfigProvisioned() exception:", e);
                     }
                 }
-            });
+            }, false);
         }
     }
 
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 5f42579..ee500a9 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -48,6 +48,7 @@
 import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.core.instrumentation.Instrumentable;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.widget.FooterPreferenceMixin;
 import com.android.settingslib.HelpUtils;
 
 import java.util.UUID;
@@ -70,6 +71,9 @@
 
     private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
 
+    protected final FooterPreferenceMixin mFooterPreferenceMixin =
+            new FooterPreferenceMixin(this, getLifecycle());
+
     private SettingsDialogFragment mDialogFragment;
 
     private String mHelpUri;
@@ -123,7 +127,6 @@
 
     private LayoutPreference mHeader;
 
-    private LayoutPreference mFooter;
     private View mEmptyView;
     private LinearLayoutManager mLayoutManager;
     private HighlightablePreferenceGroupAdapter mAdapter;
@@ -277,10 +280,6 @@
         return mHeader;
     }
 
-    public LayoutPreference getFooterView() {
-        return mFooter;
-    }
-
     protected void setHeaderView(int resource) {
         mHeader = new LayoutPreference(getPrefContext(), resource);
         addPreferenceToTop(mHeader);
@@ -298,29 +297,6 @@
         }
     }
 
-    protected void setFooterView(int resource) {
-        setFooterView(resource != 0 ? new LayoutPreference(getPrefContext(), resource) : null);
-    }
-
-    protected void setFooterView(View v) {
-        setFooterView(v != null ? new LayoutPreference(getPrefContext(), v) : null);
-    }
-
-    private void setFooterView(LayoutPreference footer) {
-        if (getPreferenceScreen() != null && mFooter != null) {
-            getPreferenceScreen().removePreference(mFooter);
-        }
-        if (footer != null) {
-            mFooter = footer;
-            mFooter.setOrder(ORDER_LAST);
-            if (getPreferenceScreen() != null) {
-                getPreferenceScreen().addPreference(mFooter);
-            }
-        } else {
-            mFooter = null;
-        }
-    }
-
     @Override
     public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
         if (preferenceScreen != null && !preferenceScreen.isAttached()) {
@@ -332,9 +308,6 @@
             if (mHeader != null) {
                 preferenceScreen.addPreference(mHeader);
             }
-            if (mFooter != null) {
-                preferenceScreen.addPreference(mFooter);
-            }
         }
     }
 
@@ -343,7 +316,7 @@
         if (getPreferenceScreen() != null) {
             boolean show = (getPreferenceScreen().getPreferenceCount()
                     - (mHeader != null ? 1 : 0)
-                    - (mFooter != null ? 1 : 0)) <= 0;
+                    - (mFooterPreferenceMixin.hasFooter() ? 1 : 0)) <= 0;
             mEmptyView.setVisibility(show ? View.VISIBLE : View.GONE);
         } else {
             mEmptyView.setVisibility(View.VISIBLE);
diff --git a/src/com/android/settings/SetupWizardUtils.java b/src/com/android/settings/SetupWizardUtils.java
index 5563c3b..a0a3719 100644
--- a/src/com/android/settings/SetupWizardUtils.java
+++ b/src/com/android/settings/SetupWizardUtils.java
@@ -16,11 +16,8 @@
 
 package com.android.settings;
 
-import android.app.Activity;
-import android.app.Dialog;
 import android.content.Intent;
 
-import com.android.setupwizardlib.util.SystemBarHelper;
 import com.android.setupwizardlib.util.WizardManagerHelper;
 
 public class SetupWizardUtils {
@@ -41,22 +38,6 @@
         }
     }
 
-    /**
-     * Sets the immersive mode related flags based on the extra in the intent which started the
-     * activity.
-     */
-    public static void setImmersiveMode(Activity activity) {
-        final boolean useImmersiveMode = activity.getIntent().getBooleanExtra(
-                WizardManagerHelper.EXTRA_USE_IMMERSIVE_MODE, false);
-        if (useImmersiveMode) {
-            SystemBarHelper.hideSystemBars(activity.getWindow());
-        }
-    }
-
-    public static void applyImmersiveFlags(final Dialog dialog) {
-        SystemBarHelper.hideSystemBars(dialog);
-    }
-
     public static void copySetupExtras(Intent fromIntent, Intent toIntent) {
         toIntent.putExtra(WizardManagerHelper.EXTRA_THEME,
                 fromIntent.getStringExtra(WizardManagerHelper.EXTRA_THEME));
diff --git a/src/com/android/settings/applications/ApplicationFeatureProvider.java b/src/com/android/settings/applications/ApplicationFeatureProvider.java
index 8c7b257..bb0fd4c 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProvider.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java
@@ -35,7 +35,7 @@
     /**
      * Callback that receives the total number of packages installed on the device.
      */
-    public interface NumberOfInstalledAppsCallback {
+    interface NumberOfInstalledAppsCallback {
         void onNumberOfInstalledAppsResult(int num);
     }
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index a967abf..1dd0471 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -50,6 +50,7 @@
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
+import com.android.settings.widget.FooterPreference;
 import com.android.settings.widget.SwitchBar;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
@@ -97,7 +98,7 @@
 
 
     // accessed from inner class (not private to avoid thunks)
-    Preference mMyDevicePreference;
+    FooterPreference mMyDevicePreference;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -120,7 +121,7 @@
                 final Resources res = context.getResources();
                 final Locale locale = res.getConfiguration().getLocales().get(0);
                 final BidiFormatter bidiFormatter = BidiFormatter.getInstance(locale);
-                mMyDevicePreference.setSummary(res.getString(
+                mMyDevicePreference.setTitle(res.getString(
                             R.string.bluetooth_is_visible_message,
                             bidiFormatter.unicodeWrap(mLocalAdapter.getName())));
             }
@@ -171,10 +172,8 @@
         mAvailableDevicesCategory.setOrder(2);
         getPreferenceScreen().addPreference(mAvailableDevicesCategory);
 
-        mMyDevicePreference = new Preference(getPrefContext());
+        mMyDevicePreference = mFooterPreferenceMixin.createFooterPreference();
         mMyDevicePreference.setSelectable(false);
-        mMyDevicePreference.setOrder(3);
-        getPreferenceScreen().addPreference(mMyDevicePreference);
 
         setHasOptionsMenu(true);
     }
@@ -356,7 +355,7 @@
                 final Resources res = getResources();
                 final Locale locale = res.getConfiguration().getLocales().get(0);
                 final BidiFormatter bidiFormatter = BidiFormatter.getInstance(locale);
-                mMyDevicePreference.setSummary(res.getString(
+                mMyDevicePreference.setTitle(res.getString(
                             R.string.bluetooth_is_visible_message,
                             bidiFormatter.unicodeWrap(mLocalAdapter.getName())));
 
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
index 401b13c..6150920 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
@@ -150,7 +150,7 @@
     static void persistDiscoveringTimestamp(final Context context) {
         // Load the shared preferences and edit it on a background
         // thread (but serialized!).
-        QueuedWork.singleThreadExecutor().submit(new Runnable() {
+        QueuedWork.queue(new Runnable() {
                 public void run() {
                     SharedPreferences.Editor editor = getSharedPreferences(context).edit();
                     editor.putLong(
@@ -158,7 +158,7 @@
                         System.currentTimeMillis());
                     editor.apply();
                 }
-            });
+            }, false);
     }
 
     static boolean hasDockAutoConnectSetting(Context context, String addr) {
diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
index 1724d0c..3a4d0c2 100644
--- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java
+++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
@@ -17,13 +17,21 @@
 package com.android.settings.core;
 
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 
 import com.android.settings.core.instrumentation.Instrumentable;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settings.core.lifecycle.ObservablePreferenceFragment;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.PreferenceDividerDecoration;
+import com.android.settings.survey.SurveyMixin;
 
 /**
  * Instrumented fragment that logs visibility state.
@@ -48,9 +56,13 @@
     protected final int GESTURE_DOUBLE_TAP_SCREEN = PLACEHOLDER_METRIC + 11;
     protected final int GESTURE_DOUBLE_TWIST = PLACEHOLDER_METRIC + 12;
 
+    private final PreferenceDividerDecoration mDividerDecoration =
+            new PreferenceDividerDecoration();
+
     public InstrumentedPreferenceFragment() {
         // Mixin that logs visibility change for activity.
         getLifecycle().addObserver(new VisibilityLoggerMixin(getMetricsCategory()));
+        getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName()));
     }
 
     @Override
@@ -60,6 +72,20 @@
     }
 
     @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = super.onCreateView(inflater, container, savedInstanceState);
+        getListView().addItemDecoration(mDividerDecoration);
+        return view;
+    }
+
+    @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
     }
+
+    @Override
+    public void setDivider(Drawable divider) {
+        mDividerDecoration.setDivider(divider);
+        super.setDivider(new ColorDrawable(Color.TRANSPARENT));
+    }
 }
diff --git a/src/com/android/settings/core/PreferenceController.java b/src/com/android/settings/core/PreferenceController.java
index 8f551de..01c998a 100644
--- a/src/com/android/settings/core/PreferenceController.java
+++ b/src/com/android/settings/core/PreferenceController.java
@@ -20,6 +20,7 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.search.SearchIndexableRaw;
+import com.android.settings.search2.ResultPayload;
 
 import java.util.List;
 
@@ -104,4 +105,11 @@
             screen.removePreference(pref);
         }
     }
+
+    /**
+     * @return the {@link ResultPayload} corresponding to the search result type for the preference.
+     */
+    public ResultPayload getResultPayload() {
+        return null;
+    }
 }
diff --git a/src/com/android/settings/core/lifecycle/Lifecycle.java b/src/com/android/settings/core/lifecycle/Lifecycle.java
index c47f97e..9a42cd9 100644
--- a/src/com/android/settings/core/lifecycle/Lifecycle.java
+++ b/src/com/android/settings/core/lifecycle/Lifecycle.java
@@ -18,6 +18,7 @@
 import android.annotation.UiThread;
 import android.content.Context;
 import android.os.Bundle;
+import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.core.lifecycle.events.OnAttach;
 import com.android.settings.core.lifecycle.events.OnCreate;
@@ -27,6 +28,7 @@
 import com.android.settings.core.lifecycle.events.OnSaveInstanceState;
 import com.android.settings.core.lifecycle.events.OnStart;
 import com.android.settings.core.lifecycle.events.OnStop;
+import com.android.settings.core.lifecycle.events.SetPreferenceScreen;
 import com.android.settings.utils.ThreadUtils;
 
 import java.util.ArrayList;
@@ -73,6 +75,14 @@
         }
     }
 
+    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+        for (LifecycleObserver observer : mObservers) {
+            if (observer instanceof SetPreferenceScreen) {
+                ((SetPreferenceScreen) observer).setPreferenceScreen(preferenceScreen);
+            }
+        }
+    }
+
     public void onResume() {
         for (LifecycleObserver observer : mObservers) {
             if (observer instanceof OnResume) {
diff --git a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
index f55b183..0a1a628 100644
--- a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
+++ b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceScreen;
 
 /**
  * {@link PreferenceFragment} that has hooks to observe fragment lifecycle events.
@@ -46,6 +47,12 @@
         super.onCreate(savedInstanceState);
     }
 
+    @Override
+    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+        mLifecycle.setPreferenceScreen(preferenceScreen);
+        super.setPreferenceScreen(preferenceScreen);
+    }
+
     @CallSuper
     @Override
     public void onSaveInstanceState(Bundle outState) {
diff --git a/src/com/android/settings/core/lifecycle/events/SetPreferenceScreen.java b/src/com/android/settings/core/lifecycle/events/SetPreferenceScreen.java
new file mode 100644
index 0000000..d206ed3
--- /dev/null
+++ b/src/com/android/settings/core/lifecycle/events/SetPreferenceScreen.java
@@ -0,0 +1,24 @@
+/*
+ * 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.core.lifecycle.events;
+
+import android.support.v7.preference.PreferenceScreen;
+
+public interface SetPreferenceScreen {
+
+    void setPreferenceScreen(PreferenceScreen preferenceScreen);
+}
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index e59b7ba..ab1ec13 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -17,9 +17,6 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
@@ -118,16 +115,6 @@
     }
 
     @Override
-    public void setDivider(Drawable divider) {
-        if (mDashboardFeatureProvider.isEnabled()) {
-            // Intercept divider and set it transparent so system divider decoration is disabled.
-            super.setDivider(new ColorDrawable(Color.TRANSPARENT));
-        } else {
-            super.setDivider(divider);
-        }
-    }
-
-    @Override
     public void onStart() {
         super.onStart();
         final DashboardCategory category =
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
index bbaf54c..551564d 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
@@ -52,6 +52,7 @@
     private UserManager mUserManager;
     private boolean mHasPassword;
     private boolean mFingerprintUnlockDisabledByAdmin;
+    private TextView mErrorText;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -67,13 +68,41 @@
             setHeaderText(R.string.security_settings_fingerprint_enroll_introduction_title);
         }
 
-        final Button cancelButton = (Button) findViewById(R.id.fingerprint_cancel_button);
+        Button cancelButton = (Button) findViewById(R.id.fingerprint_cancel_button);
         cancelButton.setOnClickListener(this);
 
+        mErrorText = (TextView) findViewById(R.id.error_text);
+
         mUserManager = UserManager.get(this);
         updatePasswordQuality();
     }
 
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        final FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(this);
+        int errorMsg = 0;
+        if (fingerprintManager != null) {
+            final int max = getResources().getInteger(
+                    com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+            final int numEnrolledFingerprints =
+                    fingerprintManager.getEnrolledFingerprints(mUserId).size();
+            if (numEnrolledFingerprints >= max) {
+                errorMsg = R.string.fingerprint_intro_error_max;
+            }
+        } else {
+            errorMsg = R.string.fingerprint_intro_error_unknown;
+        }
+        if (errorMsg == 0) {
+            mErrorText.setText(null);
+            getNextButton().setVisibility(View.VISIBLE);
+        } else {
+            mErrorText.setText(errorMsg);
+            getNextButton().setVisibility(View.GONE);
+        }
+    }
+
     private void updatePasswordQuality() {
         final int passwordQuality = new ChooseLockSettingsHelper(this).utils()
                 .getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId));
diff --git a/src/com/android/settings/fingerprint/FingerprintSettings.java b/src/com/android/settings/fingerprint/FingerprintSettings.java
index 58060f3..0f48f8c 100644
--- a/src/com/android/settings/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/fingerprint/FingerprintSettings.java
@@ -17,7 +17,6 @@
 package com.android.settings.fingerprint;
 
 
-import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -26,7 +25,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.hardware.fingerprint.Fingerprint;
@@ -49,15 +47,12 @@
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
-import android.text.method.LinkMovementMethod;
 import android.text.style.URLSpan;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.EditText;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -68,6 +63,8 @@
 import com.android.settings.SubSettings;
 import com.android.settings.Utils;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.widget.FooterPreference;
+import com.android.settings.widget.FooterPreferenceMixin;
 import com.android.settingslib.HelpUtils;
 import com.android.settingslib.RestrictedLockUtils;
 
@@ -298,21 +295,14 @@
                 mLaunchedConfirm = true;
                 launchChooseOrConfirmLock();
             }
-        }
 
-        @Override
-        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-            super.onViewCreated(view, savedInstanceState);
-            TextView v = (TextView) LayoutInflater.from(view.getContext()).inflate(
-                    R.layout.fingerprint_settings_footer, null);
-            EnforcedAdmin admin = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
-                    getActivity(), DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId);
-            v.setText(LearnMoreSpan.linkify(getText(admin != null
+            final FooterPreference pref = mFooterPreferenceMixin.createFooterPreference();
+            final EnforcedAdmin admin = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+                    activity, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId);
+            pref.setTitle(LearnMoreSpan.linkify(getText(admin != null
                             ? R.string.security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled
                             : R.string.security_settings_fingerprint_enroll_disclaimer),
                     getString(getHelpResource()), admin));
-            v.setMovementMethod(new LinkMovementMethod());
-            setFooterView(v);
         }
 
         protected void removeFingerprintPreference(int fingerprintId) {
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollEnrolling.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollEnrolling.java
index 0e4501c..2c44f6d 100644
--- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollEnrolling.java
@@ -19,7 +19,6 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.app.DialogFragment;
 import android.app.FragmentManager;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -93,7 +92,7 @@
                     .setTitle(R.string.setup_fingerprint_enroll_enrolling_skip_title)
                     .setMessage(R.string.setup_fingerprint_enroll_enrolling_skip_message)
                     .setCancelable(false)
-                    .setPositiveButton(R.string.wifi_skip_anyway,
+                    .setPositiveButton(R.string.skip_anyway_button_label,
                             new DialogInterface.OnClickListener() {
                                 @Override
                                 public void onClick(DialogInterface dialog, int id) {
@@ -104,7 +103,7 @@
                                     }
                                 }
                             })
-                    .setNegativeButton(R.string.wifi_dont_skip,
+                    .setNegativeButton(R.string.go_back_button_label,
                             new DialogInterface.OnClickListener() {
                                 @Override
                                 public void onClick(DialogInterface dialog, int id) {
diff --git a/src/com/android/settings/notification/AudioHelper.java b/src/com/android/settings/notification/AudioHelper.java
index ea5bc4a..07cd912 100644
--- a/src/com/android/settings/notification/AudioHelper.java
+++ b/src/com/android/settings/notification/AudioHelper.java
@@ -16,8 +16,12 @@
 
 package com.android.settings.notification;
 
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.media.AudioSystem;
+import android.os.UserHandle;
+import android.os.UserManager;
+import com.android.settings.Utils;
 
 /**
  * Helper class to wrap API for testing
@@ -34,4 +38,11 @@
         return AudioSystem.isSingleVolume(mContext);
     }
 
+    public int getManagedProfileId(UserManager um) {
+        return Utils.getManagedProfileId(um, UserHandle.myUserId());
+    }
+
+    public Context createPackageContextAsUser(@UserIdInt int profileId) {
+        return Utils.createPackageContextAsUser(mContext, profileId);
+    }
 }
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index 9166377..5f53d9a 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -16,45 +16,26 @@
 
 package com.android.settings.notification;
 
-import android.annotation.UserIdInt;
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.FragmentManager;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioManager;
-import android.media.AudioSystem;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.preference.SeekBarVolumizer;
 import android.provider.SearchIndexableResource;
-import android.provider.Settings;
 import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.support.v7.preference.TwoStatePreference;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.RingtonePreference;
-import com.android.settings.DefaultRingtonePreference;
-import com.android.settings.Utils;
 import com.android.settings.core.PreferenceController;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settings.core.lifecycle.Lifecycle;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.SummaryLoader;
@@ -66,16 +47,9 @@
 import java.util.Arrays;
 import java.util.List;
 
-public class SoundSettings extends DashboardFragment
-        implements OnPreferenceChangeListener {
+public class SoundSettings extends DashboardFragment {
     private static final String TAG = "SoundSettings";
 
-    private static final String KEY_WORK_CATEGORY = "sound_work_settings_section";
-    private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
-    private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
-    private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
-    private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
-
     private static final String SELECTED_PREFERENCE_KEY = "selected_preference";
     private static final int REQUEST_CODE = 200;
 
@@ -84,20 +58,9 @@
     private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
     private final H mHandler = new H();
 
-    private Context mContext;
-    private boolean mVoiceCapable;
-
-    private PreferenceGroup mWorkPreferenceCategory;
-    private TwoStatePreference mWorkUsePersonalSounds;
-    private Preference mWorkPhoneRingtonePreference;
-    private Preference mWorkNotificationRingtonePreference;
-    private Preference mWorkAlarmRingtonePreference;
-
-    private UserManager mUserManager;
+    private WorkSoundPreferenceController mWorkSoundController;
     private RingtonePreference mRequestPreference;
 
-    private @UserIdInt int mManagedProfileId;
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.SOUND;
@@ -106,10 +69,6 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mContext = getActivity();
-        mUserManager = UserManager.get(getContext());
-        mVoiceCapable = Utils.isVoiceCapable(mContext);
-
         if (savedInstanceState != null) {
             String selectedPreference = savedInstanceState.getString(SELECTED_PREFERENCE_KEY, null);
             if (!TextUtils.isEmpty(selectedPreference)) {
@@ -119,25 +78,6 @@
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-
-        mManagedProfileId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
-        if (mManagedProfileId != UserHandle.USER_NULL && shouldShowRingtoneSettings()) {
-            if ((mWorkPreferenceCategory == null)) {
-                // Work preferences not yet set
-                addPreferencesFromResource(R.xml.sound_work_settings);
-                initWorkPreferences();
-            }
-            if (!mWorkUsePersonalSounds.isChecked()) {
-                updateWorkRingtoneSummaries();
-            }
-        } else {
-            maybeRemoveWorkPreferences();
-        }
-    }
-
-    @Override
     public void onPause() {
         super.onPause();
         mVolumeCallback.stopSample();
@@ -190,6 +130,10 @@
         controllers.add(new AlarmRingtonePreferenceController(context));
         controllers.add(new NotificationRingtonePreferenceController(context));
 
+        // === Work Sound Settings ===
+        mWorkSoundController = new WorkSoundPreferenceController(context, this, getLifecycle());
+        controllers.add(mWorkSoundController);
+
         return controllers;
     }
 
@@ -209,33 +153,6 @@
         }
     }
 
-    /**
-     * Updates the summary of work preferences
-     *
-     * This fragment only listens to changes on the work ringtone preferences, identified by keys
-     * "work_ringtone", "work_notification_ringtone" and "work_alarm_ringtone".
-     *
-     * Note: Changes to the personal ringtones aren't listened to this way because they were already
-     * handled using a {@link #SettingsObserver} ContentObserver. This wouldn't be appropriate for
-     * work settings since the Settings app runs on the personal user.
-     */
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        int ringtoneType;
-        if (KEY_WORK_PHONE_RINGTONE.equals(preference.getKey())) {
-            ringtoneType = RingtoneManager.TYPE_RINGTONE;
-        } else if (KEY_WORK_NOTIFICATION_RINGTONE.equals(preference.getKey())) {
-            ringtoneType = RingtoneManager.TYPE_NOTIFICATION;
-        } else if (KEY_WORK_ALARM_RINGTONE.equals(preference.getKey())) {
-            ringtoneType = RingtoneManager.TYPE_ALARM;
-        } else {
-            return true;
-        }
-
-        preference.setSummary(updateRingtoneName(getManagedProfileContext(), ringtoneType));
-        return true;
-    }
-
     // === Volumes ===
 
     final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
@@ -265,23 +182,6 @@
         }
     };
 
-
-    // === Phone & notification ringtone ===
-
-    private boolean shouldShowRingtoneSettings() {
-        return !AudioSystem.isSingleVolume(mContext);
-    }
-
-    private static CharSequence updateRingtoneName(Context context, int type) {
-        if (context == null) {
-            Log.e(TAG, "Unable to update ringtone name, no context provided");
-            return null;
-        }
-        Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
-        return Ringtone.getTitle(context, ringtoneUri, false /* followSettingsUri */,
-                true /* allowRemote */);
-    }
-
     // === Callbacks ===
 
 
@@ -393,149 +293,9 @@
 
     // === Work Sound Settings ===
 
-    private Context getManagedProfileContext() {
-        if (mManagedProfileId == UserHandle.USER_NULL) {
-            return null;
-        }
-        return Utils.createPackageContextAsUser(mContext, mManagedProfileId);
-    }
-
-    private DefaultRingtonePreference initWorkPreference(String key) {
-        DefaultRingtonePreference pref =
-                (DefaultRingtonePreference) getPreferenceScreen().findPreference(key);
-        pref.setOnPreferenceChangeListener(this);
-
-        // Required so that RingtonePickerActivity lists the work profile ringtones
-        pref.setUserId(mManagedProfileId);
-        return pref;
-    }
-
-    private void initWorkPreferences() {
-        mWorkPreferenceCategory = (PreferenceGroup) getPreferenceScreen()
-                .findPreference(KEY_WORK_CATEGORY);
-        mWorkUsePersonalSounds = (TwoStatePreference) getPreferenceScreen()
-                .findPreference(KEY_WORK_USE_PERSONAL_SOUNDS);
-        mWorkPhoneRingtonePreference = initWorkPreference(KEY_WORK_PHONE_RINGTONE);
-        mWorkNotificationRingtonePreference = initWorkPreference(KEY_WORK_NOTIFICATION_RINGTONE);
-        mWorkAlarmRingtonePreference = initWorkPreference(KEY_WORK_ALARM_RINGTONE);
-
-        if (!mVoiceCapable) {
-            mWorkPreferenceCategory.removePreference(mWorkPhoneRingtonePreference);
-            mWorkPhoneRingtonePreference = null;
-        }
-
-        Context managedProfileContext = getManagedProfileContext();
-        if (Settings.Secure.getIntForUser(managedProfileContext.getContentResolver(),
-                Settings.Secure.SYNC_PARENT_SOUNDS, 0, mManagedProfileId) == 1) {
-            enableWorkSyncSettings();
-        } else {
-            disableWorkSyncSettings();
-        }
-
-        mWorkUsePersonalSounds.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if ((boolean) newValue) {
-                    UnifyWorkDialogFragment.show(SoundSettings.this);
-                    return false;
-                } else {
-                    disableWorkSync();
-                    return true;
-                }
-            }
-        });
-    }
-
-    private void enableWorkSync() {
-        RingtoneManager.enableSyncFromParent(getManagedProfileContext());
-        enableWorkSyncSettings();
-    }
-
-    private void enableWorkSyncSettings() {
-        mWorkUsePersonalSounds.setChecked(true);
-
-        if (mWorkPhoneRingtonePreference != null) {
-            mWorkPhoneRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
-        }
-        mWorkNotificationRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
-        mWorkAlarmRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
-    }
-
-    private void disableWorkSync() {
-        RingtoneManager.disableSyncFromParent(getManagedProfileContext());
-        disableWorkSyncSettings();
-    }
-
-    private void disableWorkSyncSettings() {
-        if (mWorkPhoneRingtonePreference != null) {
-            mWorkPhoneRingtonePreference.setEnabled(true);
-        }
-        mWorkNotificationRingtonePreference.setEnabled(true);
-        mWorkAlarmRingtonePreference.setEnabled(true);
-
-        updateWorkRingtoneSummaries();
-    }
-
-    private void updateWorkRingtoneSummaries() {
-        Context managedProfileContext = getManagedProfileContext();
-
-        if (mWorkPhoneRingtonePreference != null) {
-            mWorkPhoneRingtonePreference.setSummary(
-                    updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_RINGTONE));
-        }
-        mWorkNotificationRingtonePreference.setSummary(
-                updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_NOTIFICATION));
-        mWorkAlarmRingtonePreference.setSummary(
-                updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_ALARM));
-    }
-
-    private void maybeRemoveWorkPreferences() {
-        if (mWorkPreferenceCategory == null) {
-            // No work preferences to remove
-            return;
-        }
-        getPreferenceScreen().removePreference(mWorkPreferenceCategory);
-        mWorkPreferenceCategory = null;
-        mWorkPhoneRingtonePreference = null;
-        mWorkNotificationRingtonePreference = null;
-        mWorkAlarmRingtonePreference = null;
-    }
-
-    public static class UnifyWorkDialogFragment extends InstrumentedDialogFragment
-            implements DialogInterface.OnClickListener {
-        private static final String TAG = "UnifyWorkDialogFragment";
-        private static final int REQUEST_CODE = 200;
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.DIALOG_UNIFY_SOUND_SETTINGS;
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            return new AlertDialog.Builder(getActivity())
-                    .setTitle(R.string.work_sync_dialog_title)
-                    .setMessage(R.string.work_sync_dialog_message)
-                    .setPositiveButton(R.string.work_sync_dialog_yes, UnifyWorkDialogFragment.this)
-                    .setNegativeButton(android.R.string.no, null)
-                    .create();
-        }
-
-        public static void show(SoundSettings parent) {
-            FragmentManager fm = parent.getFragmentManager();
-            if (fm.findFragmentByTag(TAG) == null) {
-                UnifyWorkDialogFragment fragment = new UnifyWorkDialogFragment();
-                fragment.setTargetFragment(parent, REQUEST_CODE);
-                fragment.show(fm, TAG);
-            }
-        }
-
-        @Override
-        public void onClick(DialogInterface dialog, int which) {
-            SoundSettings soundSettings = (SoundSettings) getTargetFragment();
-            if (soundSettings.isAdded()) {
-                soundSettings.enableWorkSync();
-            }
+    void enableWorkSync() {
+        if (mWorkSoundController != null) {
+            mWorkSoundController.enableWorkSync();
         }
     }
 }
diff --git a/src/com/android/settings/notification/WorkSoundPreferenceController.java b/src/com/android/settings/notification/WorkSoundPreferenceController.java
new file mode 100644
index 0000000..cc671ad
--- /dev/null
+++ b/src/com/android/settings/notification/WorkSoundPreferenceController.java
@@ -0,0 +1,319 @@
+/*
+ * 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.notification;
+
+import android.annotation.UserIdInt;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.media.AudioSystem;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.Preference.OnPreferenceChangeListener;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.TwoStatePreference;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.DefaultRingtonePreference;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnResume;
+
+
+public class WorkSoundPreferenceController extends PreferenceController implements
+    OnPreferenceChangeListener, LifecycleObserver, OnResume {
+
+    private static final String TAG = "WorkSoundPrefController";
+    private static final String KEY_WORK_CATEGORY = "sound_work_settings_section";
+    private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
+    private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
+    private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
+    private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
+
+    private PreferenceGroup mWorkPreferenceCategory;
+    private TwoStatePreference mWorkUsePersonalSounds;
+    private Preference mWorkPhoneRingtonePreference;
+    private Preference mWorkNotificationRingtonePreference;
+    private Preference mWorkAlarmRingtonePreference;
+    private boolean mVoiceCapable;
+    private UserManager mUserManager;
+    private SoundSettings mParent;
+    private AudioHelper mHelper;
+
+    private @UserIdInt
+    int mManagedProfileId;
+
+    public WorkSoundPreferenceController(Context context, SoundSettings parent,
+        Lifecycle lifecycle) {
+        this(context, parent, lifecycle, new AudioHelper(context));
+    }
+
+    @VisibleForTesting
+    WorkSoundPreferenceController(Context context, SoundSettings parent, Lifecycle lifecycle,
+        AudioHelper helper) {
+        super(context);
+        mUserManager = UserManager.get(context);
+        mVoiceCapable = Utils.isVoiceCapable(mContext);
+        mParent = parent;
+        mHelper = helper;
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        // do nothing
+    }
+
+    @Override
+    public void onResume() {
+        if (isAvailable()) {
+            if ((mWorkPreferenceCategory == null)) {
+                // Work preferences not yet set
+                mParent.addPreferencesFromResource(R.xml.sound_work_settings);
+                initWorkPreferences();
+            }
+            if (!mWorkUsePersonalSounds.isChecked()) {
+                updateWorkRingtoneSummaries();
+            }
+        } else {
+            maybeRemoveWorkPreferences();
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_WORK_CATEGORY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
+        return mManagedProfileId != UserHandle.USER_NULL && shouldShowRingtoneSettings();
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        return false;
+    }
+
+    /**
+     * Updates the summary of work preferences
+     *
+     * This controller listens to changes on the work ringtone preferences, identified by keys
+     * "work_ringtone", "work_notification_ringtone" and "work_alarm_ringtone".
+     */
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        int ringtoneType;
+        if (KEY_WORK_PHONE_RINGTONE.equals(preference.getKey())) {
+            ringtoneType = RingtoneManager.TYPE_RINGTONE;
+        } else if (KEY_WORK_NOTIFICATION_RINGTONE.equals(preference.getKey())) {
+            ringtoneType = RingtoneManager.TYPE_NOTIFICATION;
+        } else if (KEY_WORK_ALARM_RINGTONE.equals(preference.getKey())) {
+            ringtoneType = RingtoneManager.TYPE_ALARM;
+        } else {
+            return true;
+        }
+
+        preference.setSummary(updateRingtoneName(getManagedProfileContext(), ringtoneType));
+        return true;
+    }
+
+    // === Phone & notification ringtone ===
+
+    private boolean shouldShowRingtoneSettings() {
+        return !mHelper.isSingleVolume();
+    }
+
+    private CharSequence updateRingtoneName(Context context, int type) {
+        if (context == null) {
+            Log.e(TAG, "Unable to update ringtone name, no context provided");
+            return null;
+        }
+        Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
+        return Ringtone.getTitle(context, ringtoneUri, false /* followSettingsUri */,
+            true /* allowRemote */);
+    }
+
+    private Context getManagedProfileContext() {
+        if (mManagedProfileId == UserHandle.USER_NULL) {
+            return null;
+        }
+        return mHelper.createPackageContextAsUser(mManagedProfileId);
+    }
+
+    private DefaultRingtonePreference initWorkPreference(String key) {
+        DefaultRingtonePreference pref =
+            (DefaultRingtonePreference) mParent.getPreferenceScreen().findPreference(key);
+        pref.setOnPreferenceChangeListener(this);
+
+        // Required so that RingtonePickerActivity lists the work profile ringtones
+        pref.setUserId(mManagedProfileId);
+        return pref;
+    }
+
+    private void initWorkPreferences() {
+        mWorkPreferenceCategory = (PreferenceGroup) mParent.getPreferenceScreen()
+            .findPreference(KEY_WORK_CATEGORY);
+        mWorkUsePersonalSounds = (TwoStatePreference) mParent.getPreferenceScreen()
+            .findPreference(KEY_WORK_USE_PERSONAL_SOUNDS);
+        mWorkPhoneRingtonePreference = initWorkPreference(KEY_WORK_PHONE_RINGTONE);
+        mWorkNotificationRingtonePreference = initWorkPreference(KEY_WORK_NOTIFICATION_RINGTONE);
+        mWorkAlarmRingtonePreference = initWorkPreference(KEY_WORK_ALARM_RINGTONE);
+
+        if (!mVoiceCapable) {
+            mWorkPreferenceCategory.removePreference(mWorkPhoneRingtonePreference);
+            mWorkPhoneRingtonePreference = null;
+        }
+
+        Context managedProfileContext = getManagedProfileContext();
+        if (Settings.Secure.getIntForUser(managedProfileContext.getContentResolver(),
+            Settings.Secure.SYNC_PARENT_SOUNDS, 0, mManagedProfileId) == 1) {
+            enableWorkSyncSettings();
+        } else {
+            disableWorkSyncSettings();
+        }
+
+        mWorkUsePersonalSounds.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+            @Override
+            public boolean onPreferenceChange(Preference preference, Object newValue) {
+                if ((boolean) newValue) {
+                    UnifyWorkDialogFragment.show(mParent);
+                    return false;
+                } else {
+                    disableWorkSync();
+                    return true;
+                }
+            }
+        });
+    }
+
+    void enableWorkSync() {
+        RingtoneManager.enableSyncFromParent(getManagedProfileContext());
+        enableWorkSyncSettings();
+    }
+
+    private void enableWorkSyncSettings() {
+        mWorkUsePersonalSounds.setChecked(true);
+
+        if (mWorkPhoneRingtonePreference != null) {
+            mWorkPhoneRingtonePreference.setSummary(
+                com.android.settings.R.string.work_sound_same_as_personal);
+        }
+        mWorkNotificationRingtonePreference.setSummary(
+            com.android.settings.R.string.work_sound_same_as_personal);
+        mWorkAlarmRingtonePreference.setSummary(
+            com.android.settings.R.string.work_sound_same_as_personal);
+    }
+
+    private void disableWorkSync() {
+        RingtoneManager.disableSyncFromParent(getManagedProfileContext());
+        disableWorkSyncSettings();
+    }
+
+    private void disableWorkSyncSettings() {
+        if (mWorkPhoneRingtonePreference != null) {
+            mWorkPhoneRingtonePreference.setEnabled(true);
+        }
+        mWorkNotificationRingtonePreference.setEnabled(true);
+        mWorkAlarmRingtonePreference.setEnabled(true);
+
+        updateWorkRingtoneSummaries();
+    }
+
+    private void updateWorkRingtoneSummaries() {
+        Context managedProfileContext = getManagedProfileContext();
+
+        if (mWorkPhoneRingtonePreference != null) {
+            mWorkPhoneRingtonePreference.setSummary(
+                updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_RINGTONE));
+        }
+        mWorkNotificationRingtonePreference.setSummary(
+            updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_NOTIFICATION));
+        mWorkAlarmRingtonePreference.setSummary(
+            updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_ALARM));
+    }
+
+    private void maybeRemoveWorkPreferences() {
+        if (mWorkPreferenceCategory == null) {
+            // No work preferences to remove
+            return;
+        }
+        mParent.getPreferenceScreen().removePreference(mWorkPreferenceCategory);
+        mWorkPreferenceCategory = null;
+        mWorkPhoneRingtonePreference = null;
+        mWorkNotificationRingtonePreference = null;
+        mWorkAlarmRingtonePreference = null;
+    }
+
+    public static class UnifyWorkDialogFragment extends InstrumentedDialogFragment
+        implements DialogInterface.OnClickListener {
+        private static final String TAG = "UnifyWorkDialogFragment";
+        private static final int REQUEST_CODE = 200;
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsEvent.DIALOG_UNIFY_SOUND_SETTINGS;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return new AlertDialog.Builder(getActivity())
+                .setTitle(com.android.settings.R.string.work_sync_dialog_title)
+                .setMessage(com.android.settings.R.string.work_sync_dialog_message)
+                .setPositiveButton(com.android.settings.R.string.work_sync_dialog_yes,
+                    UnifyWorkDialogFragment.this)
+                .setNegativeButton(android.R.string.no, null)
+                .create();
+        }
+
+        public static void show(SoundSettings parent) {
+            FragmentManager fm = parent.getFragmentManager();
+            if (fm.findFragmentByTag(TAG) == null) {
+                UnifyWorkDialogFragment fragment = new UnifyWorkDialogFragment();
+                fragment.setTargetFragment(parent, REQUEST_CODE);
+                fragment.show(fm, TAG);
+            }
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            SoundSettings soundSettings = (SoundSettings) getTargetFragment();
+            if (soundSettings.isAdded()) {
+                soundSettings.enableWorkSync();
+            }
+        }
+    }
+
+}
diff --git a/src/com/android/settings/overlay/SurveyFeatureProvider.java b/src/com/android/settings/overlay/SurveyFeatureProvider.java
index 79e907f..907bed8 100644
--- a/src/com/android/settings/overlay/SurveyFeatureProvider.java
+++ b/src/com/android/settings/overlay/SurveyFeatureProvider.java
@@ -40,16 +40,46 @@
      *
      * @param activity The host activity to show the survey in.
      * @param surveyId A unique Id representing a survey to download.
+     * @return A boolean indicating if a survey was shown or not.
      */
-    void showSurveyIfAvailable(Activity activity, String surveyId);
+    boolean showSurveyIfAvailable(Activity activity, String surveyId);
 
     /**
      * A helper method to get the surveyId. Implementers should create a mapping of
      * keys to surveyIds and provide them via this function.
      *
      * @param context A valid context.
-     * @param key The key to get the surveyId for.
+     * @param simpleKey The simple name of the key to get the surveyId for.
      * @return The unique Id as a string or null on error.
      */
-    String getSurveyId(Context context, String key);
+    String getSurveyId(Context context, String simpleKey);
+
+    /**
+     * Removes the survey for {@code siteId} if it expired, then returns the expiration date (as a
+     * unix timestamp) for the remaining survey should it exist and be ready to show. Returns -1 if
+     * no valid survey exists after removing the potentially expired one.
+     *
+     * @param context the calling context.
+     * @param surveyId the site ID.
+     * @return the unix timestamp for the available survey for the given {@coe siteId} or -1 if
+     * there is none available.
+     */
+    long getSurveyExpirationDate(Context context, String surveyId);
+
+    /**
+     * Registers an activity to show surveys/prompts as soon as they are downloaded. The receiver
+     * should be unregistered prior to destroying the activity to avoid undefined behavior by
+     * calling {@link #unregisterReceiver(Activity, BroadcastReceiver)}.
+     * @param activity The activity that should show surveys once they are downloaded.
+     * @return the broadcast receiver listening for survey downloads. Must be unregistered before
+     * leaving the activity.
+     */
+    BroadcastReceiver createAndRegisterReceiver(Activity activity);
+
+    /**
+     * Unregisters the broadcast receiver for this activity. Should only be called once per activity
+     * after a call to {@link #createAndRegisterReceiver(Activity)}.
+     * @param activity The activity that was used to register the BroadcastReceiver.
+     */
+    void unregisterReceiver(Activity activity, BroadcastReceiver receiver);
 }
diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java
index 585f4f4..4692609 100644
--- a/src/com/android/settings/password/SetNewPasswordActivity.java
+++ b/src/com/android/settings/password/SetNewPasswordActivity.java
@@ -50,7 +50,8 @@
             finish();
             return;
         }
-        mSetNewPasswordController = SetNewPasswordController.create(this, this, getIntent());
+        mSetNewPasswordController = SetNewPasswordController.create(
+                this, this, getIntent(), getActivityToken());
         mSetNewPasswordController.dispatchSetNewPasswordIntent();
     }
 
diff --git a/src/com/android/settings/password/SetNewPasswordController.java b/src/com/android/settings/password/SetNewPasswordController.java
index 00cf33e..9443541 100644
--- a/src/com/android/settings/password/SetNewPasswordController.java
+++ b/src/com/android/settings/password/SetNewPasswordController.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.UserManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -60,14 +61,15 @@
     private final DevicePolicyManager mDevicePolicyManager;
     private final Ui mUi;
 
-    public static SetNewPasswordController create(Context context, Ui ui, Intent intent) {
+    public static SetNewPasswordController create(Context context, Ui ui, Intent intent,
+            IBinder activityToken) {
         // Trying to figure out which user is setting new password. If it is
         // ACTION_SET_NEW_PARENT_PROFILE_PASSWORD or the calling user is not allowed to set
         // separate profile challenge, it is the current user to set new password. Otherwise,
         // it is the user who starts this activity setting new password.
         int userId = ActivityManager.getCurrentUser();
         if (ACTION_SET_NEW_PASSWORD.equals(intent.getAction())) {
-            final int callingUserId = Utils.getSecureTargetUser(context.getActivityToken(),
+            final int callingUserId = Utils.getSecureTargetUser(activityToken,
                     UserManager.get(context), null, intent.getExtras()).getIdentifier();
             final LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
             if (lockPatternUtils.isSeparateProfileChallengeAllowed(callingUserId)) {
diff --git a/src/com/android/settings/search/BaseSearchIndexProvider.java b/src/com/android/settings/search/BaseSearchIndexProvider.java
index 0fe1944..bb518ba 100644
--- a/src/com/android/settings/search/BaseSearchIndexProvider.java
+++ b/src/com/android/settings/search/BaseSearchIndexProvider.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.provider.SearchIndexableResource;
+import com.android.settings.core.PreferenceController;
 
 import java.util.Collections;
 import java.util.List;
@@ -46,4 +47,9 @@
     public List<String> getNonIndexableKeys(Context context) {
         return EMPTY_LIST;
     }
+
+    @Override
+    public List<PreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
 }
diff --git a/src/com/android/settings/search/IndexDatabaseHelper.java b/src/com/android/settings/search/IndexDatabaseHelper.java
index cddee68..fcf5e7c 100644
--- a/src/com/android/settings/search/IndexDatabaseHelper.java
+++ b/src/com/android/settings/search/IndexDatabaseHelper.java
@@ -28,46 +28,48 @@
     private static final String TAG = "IndexDatabaseHelper";
 
     private static final String DATABASE_NAME = "search_index.db";
-    private static final int DATABASE_VERSION = 115;
+    private static final int DATABASE_VERSION = 116;
 
     private static final String INDEX = "index";
 
     public interface Tables {
-        public static final String TABLE_PREFS_INDEX = "prefs_index";
-        public static final String TABLE_META_INDEX = "meta_index";
-        public static final String TABLE_SAVED_QUERIES = "saved_queries";
+        String TABLE_PREFS_INDEX = "prefs_index";
+        String TABLE_META_INDEX = "meta_index";
+        String TABLE_SAVED_QUERIES = "saved_queries";
     }
 
     public interface IndexColumns {
-        public static final String DOCID = "docid";
-        public static final String LOCALE = "locale";
-        public static final String DATA_RANK = "data_rank";
-        public static final String DATA_TITLE = "data_title";
-        public static final String DATA_TITLE_NORMALIZED = "data_title_normalized";
-        public static final String DATA_SUMMARY_ON = "data_summary_on";
-        public static final String DATA_SUMMARY_ON_NORMALIZED = "data_summary_on_normalized";
-        public static final String DATA_SUMMARY_OFF = "data_summary_off";
-        public static final String DATA_SUMMARY_OFF_NORMALIZED = "data_summary_off_normalized";
-        public static final String DATA_ENTRIES = "data_entries";
-        public static final String DATA_KEYWORDS = "data_keywords";
-        public static final String CLASS_NAME = "class_name";
-        public static final String SCREEN_TITLE = "screen_title";
-        public static final String INTENT_ACTION = "intent_action";
-        public static final String INTENT_TARGET_PACKAGE = "intent_target_package";
-        public static final String INTENT_TARGET_CLASS = "intent_target_class";
-        public static final String ICON = "icon";
-        public static final String ENABLED = "enabled";
-        public static final String DATA_KEY_REF = "data_key_reference";
-        public static final String USER_ID = "user_id";
+        String DOCID = "docid";
+        String LOCALE = "locale";
+        String DATA_RANK = "data_rank";
+        String DATA_TITLE = "data_title";
+        String DATA_TITLE_NORMALIZED = "data_title_normalized";
+        String DATA_SUMMARY_ON = "data_summary_on";
+        String DATA_SUMMARY_ON_NORMALIZED = "data_summary_on_normalized";
+        String DATA_SUMMARY_OFF = "data_summary_off";
+        String DATA_SUMMARY_OFF_NORMALIZED = "data_summary_off_normalized";
+        String DATA_ENTRIES = "data_entries";
+        String DATA_KEYWORDS = "data_keywords";
+        String CLASS_NAME = "class_name";
+        String SCREEN_TITLE = "screen_title";
+        String INTENT_ACTION = "intent_action";
+        String INTENT_TARGET_PACKAGE = "intent_target_package";
+        String INTENT_TARGET_CLASS = "intent_target_class";
+        String ICON = "icon";
+        String ENABLED = "enabled";
+        String DATA_KEY_REF = "data_key_reference";
+        String USER_ID = "user_id";
+        String PAYLOAD_TYPE = "payload_type";
+        String PAYLOAD = "payload";
     }
 
     public interface MetaColumns {
-        public static final String BUILD = "build";
+        String BUILD = "build";
     }
 
     public interface SavedQueriesColums {
-        public static final String QUERY = "query";
-        public static final String TIME_STAMP = "timestamp";
+        String QUERY = "query";
+        String TIME_STAMP = "timestamp";
     }
 
     private static final String CREATE_INDEX_TABLE =
@@ -110,6 +112,10 @@
                     IndexColumns.DATA_KEY_REF +
                     ", " +
                     IndexColumns.USER_ID +
+                    ", " +
+                    IndexColumns.PAYLOAD_TYPE +
+                    ", " +
+                    IndexColumns.PAYLOAD +
                     ");";
 
     private static final String CREATE_META_TABLE =
diff --git a/src/com/android/settings/search/Indexable.java b/src/com/android/settings/search/Indexable.java
index e87d9dc..2ec4ac0 100644
--- a/src/com/android/settings/search/Indexable.java
+++ b/src/com/android/settings/search/Indexable.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.provider.SearchIndexableResource;
+import com.android.settings.core.PreferenceController;
 
 import java.util.List;
 
@@ -65,5 +66,11 @@
          * @return a list of {@link SearchIndexableRaw} references. Can be null.
          */
         List<String> getNonIndexableKeys(Context context);
+
+        /**
+         * @param context
+         * @return a list of {@link PreferenceController} for ResultPayload data during Indexing.
+         */
+        List<PreferenceController> getPreferenceControllers(Context context);
     }
 }
diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java
index 9bb4538..fa482ef 100644
--- a/src/com/android/settings/search2/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search2/DatabaseIndexingManager.java
@@ -34,10 +34,12 @@
 import android.provider.SearchIndexableResource;
 import android.provider.SearchIndexablesContract;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.settings.core.PreferenceController;
 import com.android.settings.search.IndexDatabaseHelper;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.Ranking;
@@ -86,56 +88,6 @@
 public class DatabaseIndexingManager {
     private static final String LOG_TAG = "DatabaseIndexingManager";
 
-    // Those indices should match the indices of SELECT_COLUMNS !
-    public static final int COLUMN_INDEX_RANK = 0;
-    public static final int COLUMN_INDEX_TITLE = 1;
-    public static final int COLUMN_INDEX_SUMMARY_ON = 2;
-    public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
-    public static final int COLUMN_INDEX_ENTRIES = 4;
-    public static final int COLUMN_INDEX_KEYWORDS = 5;
-    public static final int COLUMN_INDEX_CLASS_NAME = 6;
-    public static final int COLUMN_INDEX_SCREEN_TITLE = 7;
-    public static final int COLUMN_INDEX_ICON = 8;
-    public static final int COLUMN_INDEX_INTENT_ACTION = 9;
-    public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 10;
-    public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 11;
-    public static final int COLUMN_INDEX_ENABLED = 12;
-    public static final int COLUMN_INDEX_KEY = 13;
-    public static final int COLUMN_INDEX_USER_ID = 14;
-
-    // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values
-    private static final String[] SELECT_COLUMNS = new String[] {
-            IndexDatabaseHelper.IndexColumns.DATA_RANK,               // 0
-            IndexDatabaseHelper.IndexColumns.DATA_TITLE,              // 1
-            IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON,         // 2
-            IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF,        // 3
-            IndexDatabaseHelper.IndexColumns.DATA_ENTRIES,            // 4
-            IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS,           // 5
-            IndexDatabaseHelper.IndexColumns.CLASS_NAME,              // 6
-            IndexDatabaseHelper.IndexColumns.SCREEN_TITLE,            // 7
-            IndexDatabaseHelper.IndexColumns.ICON,                    // 8
-            IndexDatabaseHelper.IndexColumns.INTENT_ACTION,           // 9
-            IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE,   // 10
-            IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS,     // 11
-            IndexDatabaseHelper.IndexColumns.ENABLED,                 // 12
-            IndexDatabaseHelper.IndexColumns.DATA_KEY_REF             // 13
-    };
-
-    private static final String[] MATCH_COLUMNS_PRIMARY = {
-            IndexDatabaseHelper.IndexColumns.DATA_TITLE,
-            IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED,
-            IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS
-    };
-
-    private static final String[] MATCH_COLUMNS_SECONDARY = {
-            IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON,
-            IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED,
-            IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF,
-            IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
-            IndexDatabaseHelper.IndexColumns.DATA_ENTRIES
-    };
-
-
     private static final String NODE_NAME_PREFERENCE_SCREEN = "PreferenceScreen";
     private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
     private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
@@ -514,7 +466,7 @@
         }
     }
 
-    private void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
+    public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
             SearchIndexableData data, Map<String, List<String>> nonIndexableKeys) {
         if (data instanceof SearchIndexableResource) {
             indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
@@ -530,22 +482,22 @@
             return;
         }
 
-        updateOneRowWithFilteredData(database, localeStr,
-                raw.title,
-                raw.summaryOn,
-                raw.summaryOff,
-                raw.entries,
-                raw.className,
-                raw.screenTitle,
-                raw.iconResId,
-                raw.rank,
-                raw.keywords,
-                raw.intentAction,
-                raw.intentTargetPackage,
-                raw.intentTargetClass,
-                raw.enabled,
-                raw.key,
-                raw.userId);
+        DatabaseRow.Builder builder = new DatabaseRow.Builder();
+        builder.setLocale(localeStr)
+                .setEntries(raw.entries)
+                .setClassName(raw.className)
+                .setScreenTitle(raw.screenTitle)
+                .setIconResId(raw.iconResId)
+                .setRank(raw.rank)
+                .setIntentAction(raw.intentAction)
+                .setIntentTargetPackage(raw.intentTargetPackage)
+                .setIntentTargetClass(raw.intentTargetClass)
+                .setEnabled(raw.enabled)
+                .setKey(raw.key)
+                .setUserId(raw.userId);
+
+        updateOneRowWithFilteredData(database, builder, raw.title, raw.summaryOn, raw.summaryOff,
+                raw.keywords);
     }
 
     private void indexOneResource(SQLiteDatabase database, String localeStr,
@@ -622,25 +574,45 @@
             final int outerDepth = parser.getDepth();
             final AttributeSet attrs = Xml.asAttributeSet(parser);
 
-            final String screenTitle = XMLParserUtil.getDataTitle(context, attrs);
+            final String screenTitle = XmlParserUtils.getDataTitle(context, attrs);
 
-            String key = XMLParserUtil.getDataKey(context, attrs);
+            String key = XmlParserUtils.getDataKey(context, attrs);
 
             String title;
             String summary;
             String keywords;
+            ResultPayload payload;
+
+            ArrayMap<String, PreferenceController> controllerUriMap = null;
+
+            if (fragmentName != null) {
+                controllerUriMap = (ArrayMap) DatabaseIndexingUtils
+                        .getPreferenceControllerUriMap(fragmentName, context);
+            }
 
             // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
             // hyphens.
             if (!nonIndexableKeys.contains(key)) {
-                title = XMLParserUtil.getDataTitle(context, attrs);
-                summary = XMLParserUtil.getDataSummary(context, attrs);
-                keywords = XMLParserUtil.getDataKeywords(context, attrs);
+                title = XmlParserUtils.getDataTitle(context, attrs);
+                summary = XmlParserUtils.getDataSummary(context, attrs);
+                keywords = XmlParserUtils.getDataKeywords(context, attrs);
 
-                updateOneRowWithFilteredData(database, localeStr, title, summary, null, null,
-                        fragmentName, screenTitle, iconResId, rank,
-                        keywords, intentAction, intentTargetPackage, intentTargetClass, true,
-                        key, -1 /* default user id */);
+                DatabaseRow.Builder builder = new DatabaseRow.Builder();
+                builder.setLocale(localeStr)
+                        .setEntries(null)
+                        .setClassName(fragmentName)
+                        .setScreenTitle(screenTitle)
+                        .setIconResId(iconResId)
+                        .setRank(rank)
+                        .setIntentAction(intentAction)
+                        .setIntentTargetPackage(intentTargetPackage)
+                        .setIntentTargetClass(intentTargetClass)
+                        .setEnabled(true)
+                        .setKey(key)
+                        .setUserId(-1 /* default user id */);
+
+                updateOneRowWithFilteredData(database, builder, title, summary,
+                        null /* summary off */, keywords);
             }
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -651,43 +623,56 @@
 
                 nodeName = parser.getName();
 
-                key = XMLParserUtil.getDataKey(context, attrs);
+                key = XmlParserUtils.getDataKey(context, attrs);
                 if (nonIndexableKeys.contains(key)) {
                     continue;
                 }
 
-                title = XMLParserUtil.getDataTitle(context, attrs);
-                keywords = XMLParserUtil.getDataKeywords(context, attrs);
+                title = XmlParserUtils.getDataTitle(context, attrs);
+                keywords = XmlParserUtils.getDataKeywords(context, attrs);
+
+                DatabaseRow.Builder builder = new DatabaseRow.Builder();
+                builder.setLocale(localeStr)
+                        .setClassName(fragmentName)
+                        .setScreenTitle(screenTitle)
+                        .setIconResId(iconResId)
+                        .setRank(rank)
+                        .setIntentAction(intentAction)
+                        .setIntentTargetPackage(intentTargetPackage)
+                        .setIntentTargetClass(intentTargetClass)
+                        .setEnabled(true)
+                        .setKey(key)
+                        .setUserId(-1 /* default user id */);
 
                 if (!nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) {
-                    summary = XMLParserUtil.getDataSummary(context, attrs);
+                    summary = XmlParserUtils.getDataSummary(context, attrs);
 
                     String entries = null;
 
                     if (nodeName.endsWith(NODE_NAME_LIST_PREFERENCE)) {
-                        entries = XMLParserUtil.getDataEntries(context, attrs);
+                        entries = XmlParserUtils.getDataEntries(context, attrs);
                     }
 
+                    payload = DatabaseIndexingUtils.getPayloadFromUriMap(controllerUriMap, key);
+
+                    builder.setEntries(entries)
+                            .setPayload(payload);
+
                     // Insert rows for the child nodes of PreferenceScreen
-                    updateOneRowWithFilteredData(database, localeStr, title, summary, null, entries,
-                            fragmentName, screenTitle, iconResId, rank,
-                            keywords, intentAction, intentTargetPackage, intentTargetClass,
-                            true, key, -1 /* default user id */);
+                    updateOneRowWithFilteredData(database, builder, title, summary,
+                            null /* summary off */, keywords);
                 } else {
-                    String summaryOn = XMLParserUtil.getDataSummaryOn(context, attrs);
-                    String summaryOff = XMLParserUtil.getDataSummaryOff(context, attrs);
+                    String summaryOn = XmlParserUtils.getDataSummaryOn(context, attrs);
+                    String summaryOff = XmlParserUtils.getDataSummaryOff(context, attrs);
 
                     if (TextUtils.isEmpty(summaryOn) && TextUtils.isEmpty(summaryOff)) {
-                        summaryOn = XMLParserUtil.getDataSummary(context, attrs);
+                        summaryOn = XmlParserUtils.getDataSummary(context, attrs);
                     }
 
-                    updateOneRowWithFilteredData(database, localeStr, title, summaryOn, summaryOff,
-                            null, fragmentName, screenTitle, iconResId, rank,
-                            keywords, intentAction, intentTargetPackage, intentTargetClass,
-                            true, key, -1 /* default user id */);
+                    updateOneRowWithFilteredData(database, builder, title, summaryOn, summaryOff,
+                            keywords);
                 }
             }
-
         } catch (XmlPullParserException e) {
             throw new RuntimeException("Error parsing PreferenceScreen", e);
         } catch (IOException e) {
@@ -709,6 +694,7 @@
         final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(context, enabled);
 
         if (rawList != null) {
+
             final int rawSize = rawList.size();
             for (int i = 0; i < rawSize; i++) {
                 SearchIndexableRaw raw = rawList.get(i);
@@ -722,22 +708,22 @@
                     continue;
                 }
 
-                updateOneRowWithFilteredData(database, localeStr,
-                        raw.title,
-                        raw.summaryOn,
-                        raw.summaryOff,
-                        raw.entries,
-                        className,
-                        raw.screenTitle,
-                        iconResId,
-                        rank,
-                        raw.keywords,
-                        raw.intentAction,
-                        raw.intentTargetPackage,
-                        raw.intentTargetClass,
-                        raw.enabled,
-                        raw.key,
-                        raw.userId);
+                DatabaseRow.Builder builder = new DatabaseRow.Builder();
+                builder.setLocale(localeStr)
+                        .setEntries(raw.entries)
+                        .setClassName(className)
+                        .setScreenTitle(raw.screenTitle)
+                        .setIconResId(iconResId)
+                        .setRank(rank)
+                        .setIntentAction(raw.intentAction)
+                        .setIntentTargetPackage(raw.intentTargetPackage)
+                        .setIntentTargetClass(raw.intentTargetClass)
+                        .setEnabled(raw.enabled)
+                        .setKey(raw.key)
+                        .setUserId(raw.userId);
+
+                updateOneRowWithFilteredData(database, builder, raw.title, raw.summaryOn,
+                        raw.summaryOff, raw.keywords);
             }
         }
 
@@ -766,70 +752,68 @@
         }
     }
 
-    private void updateOneRowWithFilteredData(SQLiteDatabase database, String locale,
-            String title, String summaryOn, String summaryOff, String entries,
-            String className,
-            String screenTitle, int iconResId, int rank, String keywords,
-            String intentAction, String intentTargetPackage, String intentTargetClass,
-            boolean enabled, String key, int userId) {
+    private void updateOneRowWithFilteredData(SQLiteDatabase database, DatabaseRow.Builder builder,
+            String title, String summaryOn, String summaryOff,String keywords) {
 
-        final String updatedTitle = XMLParserUtil.normalizeHyphen(title);
-        final String updatedSummaryOn = XMLParserUtil.normalizeHyphen(summaryOn);
-        final String updatedSummaryOff = XMLParserUtil.normalizeHyphen(summaryOff);
+        final String updatedTitle = DatabaseIndexingUtils.normalizeHyphen(title);
+        final String updatedSummaryOn = DatabaseIndexingUtils.normalizeHyphen(summaryOn);
+        final String updatedSummaryOff = DatabaseIndexingUtils.normalizeHyphen(summaryOff);
 
-        final String normalizedTitle = XMLParserUtil.normalizeString(updatedTitle);
-        final String normalizedSummaryOn = XMLParserUtil.normalizeString(updatedSummaryOn);
-        final String normalizedSummaryOff = XMLParserUtil.normalizeString(updatedSummaryOff);
+        final String normalizedTitle = DatabaseIndexingUtils.normalizeString(updatedTitle);
+        final String normalizedSummaryOn = DatabaseIndexingUtils.normalizeString(updatedSummaryOn);
+        final String normalizedSummaryOff = DatabaseIndexingUtils
+                .normalizeString(updatedSummaryOff);
 
-        final String spaceDelimitedKeywords = XMLParserUtil.normalizeKeywords(keywords);
+        final String spaceDelimitedKeywords = DatabaseIndexingUtils.normalizeKeywords(keywords);
 
-        updateOneRow(database, locale,
-                updatedTitle, normalizedTitle, updatedSummaryOn, normalizedSummaryOn,
-                updatedSummaryOff, normalizedSummaryOff, entries, className, screenTitle, iconResId,
-                rank, spaceDelimitedKeywords, intentAction, intentTargetPackage, intentTargetClass,
-                enabled, key, userId);
+        builder.setUpdatedTitle(updatedTitle)
+                .setUpdatedSummaryOn(updatedSummaryOn)
+                .setUpdatedSummaryOff(updatedSummaryOff)
+                .setNormalizedTitle(normalizedTitle)
+                .setNormalizedSummaryOn(normalizedSummaryOn)
+                .setNormalizedSummaryOff(normalizedSummaryOff)
+                .setSpaceDelimitedKeywords(spaceDelimitedKeywords);
+
+        updateOneRow(database, builder.build());
     }
 
-    private void updateOneRow(SQLiteDatabase database, String locale, String updatedTitle,
-            String normalizedTitle, String updatedSummaryOn, String normalizedSummaryOn,
-            String updatedSummaryOff, String normalizedSummaryOff, String entries, String className,
-            String screenTitle, int iconResId, int rank, String spaceDelimitedKeywords,
-            String intentAction, String intentTargetPackage, String intentTargetClass,
-            boolean enabled, String key, int userId) {
+    private void updateOneRow(SQLiteDatabase database, DatabaseRow row) {
 
-        if (TextUtils.isEmpty(updatedTitle)) {
+        if (TextUtils.isEmpty(row.updatedTitle)) {
             return;
         }
 
         // The DocID should contains more than the title string itself (you may have two settings
         // with the same title). So we need to use a combination of the title and the screenTitle.
-        StringBuilder sb = new StringBuilder(updatedTitle);
-        sb.append(screenTitle);
+        StringBuilder sb = new StringBuilder(row.updatedTitle);
+        sb.append(row.screenTitle);
         int docId = sb.toString().hashCode();
 
         ContentValues values = new ContentValues();
         values.put(IndexDatabaseHelper.IndexColumns.DOCID, docId);
-        values.put(IndexDatabaseHelper.IndexColumns.LOCALE, locale);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, rank);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, updatedTitle);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, normalizedTitle);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, updatedSummaryOn);
+        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,
-                normalizedSummaryOn);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, updatedSummaryOff);
+                row.normalizedSummaryOn);
+        values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, row.updatedSummaryOff);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
-                normalizedSummaryOff);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, entries);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, spaceDelimitedKeywords);
-        values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, className);
-        values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, screenTitle);
-        values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, intentAction);
-        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, intentTargetPackage);
-        values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, intentTargetClass);
-        values.put(IndexDatabaseHelper.IndexColumns.ICON, iconResId);
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, enabled);
-        values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, key);
-        values.put(IndexDatabaseHelper.IndexColumns.USER_ID, userId);
+                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);
 
         database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
     }
@@ -960,4 +944,195 @@
                     whereArgs);
         }
     }
+
+    public static class DatabaseRow {
+        public final String locale;
+        public final String updatedTitle;
+        public final String normalizedTitle;
+        public final String updatedSummaryOn;
+        public final String normalizedSummaryOn;
+        public final String updatedSummaryOff;
+        public final String normalizedSummaryOff;
+        public final String entries;
+        public final String className;
+        public final String screenTitle;
+        public final int iconResId;
+        public final int rank;
+        public final String spaceDelimitedKeywords;
+        public final String intentAction;
+        public final String intentTargetPackage;
+        public final String intentTargetClass;
+        public final boolean enabled;
+        public final String key;
+        public final int userId;
+        public final int payloadType;
+        public final byte[] payload;
+
+        private DatabaseRow(Builder builder) {
+            locale = builder.mLocale;
+            updatedTitle = builder.mUpdatedTitle;
+            normalizedTitle = builder.mNormalizedTitle;
+            updatedSummaryOn = builder.mUpdatedSummaryOn;
+            normalizedSummaryOn = builder.mNormalizedSummaryOn;
+            updatedSummaryOff = builder.mUpdatedSummaryOff;
+            normalizedSummaryOff = builder.mNormalizedSummaryOff;
+            entries = builder.mEntries;
+            className = builder.mClassName;
+            screenTitle = builder.mScreenTitle;
+            iconResId = builder.mIconResId;
+            rank = builder.mRank;
+            spaceDelimitedKeywords = builder.mSpaceDelimitedKeywords;
+            intentAction = builder.mIntentAction;
+            intentTargetPackage = builder.mIntentTargetPackage;
+            intentTargetClass = builder.mIntentTargetClass;
+            enabled = builder.mEnabled;
+            key = builder.mKey;
+            userId = builder.mUserId;
+            payloadType = builder.mPayloadType;
+            payload = builder.mPayload != null ? ResultPayloadUtils.marshall(builder.mPayload)
+                    : null;
+        }
+
+        public static class Builder {
+            private String mLocale;
+            private String mUpdatedTitle;
+            private String mNormalizedTitle;
+            private String mUpdatedSummaryOn;
+            private String mNormalizedSummaryOn;
+            private String mUpdatedSummaryOff;
+            private String mNormalizedSummaryOff;
+            private String mEntries;
+            private String mClassName;
+            private String mScreenTitle;
+            private int mIconResId;
+            private int mRank;
+            private String mSpaceDelimitedKeywords;
+            private String mIntentAction;
+            private String mIntentTargetPackage;
+            private String mIntentTargetClass;
+            private boolean mEnabled;
+            private String mKey;
+            private int mUserId;
+            @ResultPayload.PayloadType private int mPayloadType;
+            private ResultPayload mPayload;
+
+            public Builder setLocale(String locale) {
+                mLocale = locale;
+                return this;
+            }
+
+            public Builder setUpdatedTitle(String updatedTitle) {
+                mUpdatedTitle = updatedTitle;
+                return this;
+            }
+
+            public Builder setNormalizedTitle(String normalizedTitle) {
+                mNormalizedTitle = normalizedTitle;
+                return this;
+            }
+
+            public Builder setUpdatedSummaryOn(String updatedSummaryOn) {
+                mUpdatedSummaryOn = updatedSummaryOn;
+                return this;
+            }
+
+            public Builder setNormalizedSummaryOn(String normalizedSummaryOn) {
+                mNormalizedSummaryOn = normalizedSummaryOn;
+                return this;
+            }
+
+            public Builder setUpdatedSummaryOff(String updatedSummaryOff) {
+                mUpdatedSummaryOff = updatedSummaryOff;
+                return this;
+            }
+
+            public Builder setNormalizedSummaryOff(String normalizedSummaryOff) {
+                this.mNormalizedSummaryOff = normalizedSummaryOff;
+                return this;
+            }
+
+            public Builder setEntries(String entries) {
+                mEntries = entries;
+                return this;
+            }
+
+            public Builder setClassName(String className) {
+                mClassName = className;
+                return this;
+            }
+
+            public Builder setScreenTitle(String screenTitle) {
+                mScreenTitle = screenTitle;
+                return this;
+            }
+
+            public Builder setIconResId(int iconResId) {
+                mIconResId = iconResId;
+                return this;
+            }
+
+            public Builder setRank(int rank) {
+                mRank = rank;
+                return this;
+            }
+
+            public Builder setSpaceDelimitedKeywords(String spaceDelimitedKeywords) {
+                mSpaceDelimitedKeywords = spaceDelimitedKeywords;
+                return this;
+            }
+
+            public Builder setIntentAction(String intentAction) {
+                mIntentAction = intentAction;
+                return this;
+            }
+
+            public Builder setIntentTargetPackage(String intentTargetPackage) {
+                mIntentTargetPackage = intentTargetPackage;
+                return this;
+            }
+
+            public Builder setIntentTargetClass(String intentTargetClass) {
+                mIntentTargetClass = intentTargetClass;
+                return this;
+            }
+
+            public Builder setEnabled(boolean enabled) {
+                mEnabled = enabled;
+                return this;
+            }
+
+            public Builder setKey(String key) {
+                mKey = key;
+                return this;
+            }
+
+            public Builder setUserId(int userId) {
+                mUserId = userId;
+                return this;
+            }
+
+            public Builder setPayload(ResultPayload payload) {
+                mPayload = payload;
+
+                if(mPayload != null) {
+                    setPayloadType(mPayload.getType());
+                }
+                return this;
+            }
+
+            /**
+             * Payload type is added when a Payload is added to the Builder in {setPayload}
+             * @param payloadType PayloadType
+             * @return The Builder
+             */
+            private Builder setPayloadType(@ResultPayload.PayloadType int payloadType) {
+                mPayloadType = payloadType;
+                return this;
+            }
+
+            public DatabaseRow build() {
+                return new DatabaseRow(this);
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/search2/DatabaseIndexingUtils.java b/src/com/android/settings/search2/DatabaseIndexingUtils.java
index a8f64df..c7a7f92 100644
--- a/src/com/android/settings/search2/DatabaseIndexingUtils.java
+++ b/src/com/android/settings/search2/DatabaseIndexingUtils.java
@@ -23,12 +23,19 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.settings.core.PreferenceController;
 import com.android.settings.search.Indexable;
 
 import java.lang.reflect.Field;
+import java.text.Normalizer;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * Utility class for {@like DatabaseIndexingManager} to handle the mapping between Payloads
@@ -36,17 +43,82 @@
  */
 public class DatabaseIndexingUtils {
 
-    private static final String LOG_TAG = "IndexingUtil";
+    private static final String TAG = "IndexingUtil";
 
     private static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =
             "SEARCH_INDEX_DATA_PROVIDER";
 
+    private static final String NON_BREAKING_HYPHEN = "\u2011";
+    private static final String EMPTY = "";
+    private static final String LIST_DELIMITERS = "[,]\\s*";
+    private static final String HYPHEN = "-";
+    private static final String SPACE = " ";
+
+    private static final Pattern REMOVE_DIACRITICALS_PATTERN
+            = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
+
+    /**
+     * @param className which wil provide the map between from {@link Uri}s to
+     * {@link PreferenceController}
+     * @param context
+     * @return A map between {@link Uri}s and {@link PreferenceController}s to get the payload
+     * types for Settings.
+     */
+    public static Map<String, PreferenceController> getPreferenceControllerUriMap(
+            String className, Context context) {
+        final Class<?> clazz = getIndexableClass(className);
+
+        if (clazz == null) {
+            Log.d(TAG, "SearchIndexableResource '" + className +
+                    "' should implement the " + Indexable.class.getName() + " interface!");
+            return null;
+        }
+
+        // Will be non null only for a Local provider implementing a
+        // SEARCH_INDEX_DATA_PROVIDER field
+        final Indexable.SearchIndexProvider provider = getSearchIndexProvider(clazz);
+        List<PreferenceController> controllers =
+                provider.getPreferenceControllers(context);
+
+        if (controllers == null ) {
+            return null;
+        }
+
+        ArrayMap<String, PreferenceController> map = new ArrayMap<>();
+
+        for (PreferenceController controller : controllers) {
+            map.put(controller.getPreferenceKey(), controller);
+        }
+
+        return map;
+    }
+
+    /**
+     * @param uriMap Map between the {@link PreferenceController} keys and the controllers themselves.
+     * @param key The look up key
+     * @return The Payload from the {@link PreferenceController} specified by the key, if it exists.
+     * Otherwise null.
+     */
+    public static ResultPayload getPayloadFromUriMap(ArrayMap<String, PreferenceController> uriMap,
+            String key) {
+        if (uriMap == null) {
+            return null;
+        }
+
+        PreferenceController controller = uriMap.get(key);
+        if (controller == null) {
+            return null;
+        }
+
+        return controller.getResultPayload();
+    }
+
     public static Class<?> getIndexableClass(String className) {
         final Class<?> clazz;
         try {
             clazz = Class.forName(className);
         } catch (ClassNotFoundException e) {
-            Log.d(LOG_TAG, "Cannot find class: " + className);
+            Log.d(TAG, "Cannot find class: " + className);
             return null;
         }
         return isIndexableClass(clazz) ? clazz : null;
@@ -61,15 +133,15 @@
             final Field f = clazz.getField(FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER);
             return (Indexable.SearchIndexProvider) f.get(null);
         } catch (NoSuchFieldException e) {
-            Log.d(LOG_TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
+            Log.d(TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
         } catch (SecurityException se) {
-            Log.d(LOG_TAG,
+            Log.d(TAG,
                     "Security exception for field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
         } catch (IllegalAccessException e) {
-            Log.d(LOG_TAG,
+            Log.d(TAG,
                     "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
         } catch (IllegalArgumentException e) {
-            Log.d(LOG_TAG,
+            Log.d(TAG,
                     "Illegal argument when accessing field '" +
                             FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
         }
@@ -115,4 +187,19 @@
             return false;
         }
     }
+
+    public static String normalizeHyphen(String input) {
+        return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
+    }
+
+    public static String normalizeString(String input) {
+        final String nohyphen = (input != null) ? input.replaceAll(HYPHEN, EMPTY) : EMPTY;
+        final String normalized = Normalizer.normalize(nohyphen, Normalizer.Form.NFD);
+
+        return REMOVE_DIACRITICALS_PATTERN.matcher(normalized).replaceAll("").toLowerCase();
+    }
+
+    public static String normalizeKeywords(String input) {
+        return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
+    }
 }
diff --git a/src/com/android/settings/search2/IntentPayload.java b/src/com/android/settings/search2/IntentPayload.java
index 1ef3797..3e1049e 100644
--- a/src/com/android/settings/search2/IntentPayload.java
+++ b/src/com/android/settings/search2/IntentPayload.java
@@ -18,7 +18,6 @@
 
 import android.content.Intent;
 import android.os.Parcel;
-import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Encapsulates the standard intent based results as seen in first party apps and Settings results.
diff --git a/src/com/android/settings/search2/ResultPayloadUtils.java b/src/com/android/settings/search2/ResultPayloadUtils.java
new file mode 100644
index 0000000..41d15e2
--- /dev/null
+++ b/src/com/android/settings/search2/ResultPayloadUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.search2;
+
+import android.os.BadParcelableException;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.io.StreamCorruptedException;
+
+/**
+ * Utility class to Marshall and Unmarshall the payloads stored in the SQLite Database
+ */
+public class ResultPayloadUtils {
+
+    private static final String TAG = "PayloadUtil";
+
+    public static byte[] marshall(ResultPayload payload) {
+        Parcel parcel = Parcel.obtain();
+        payload.writeToParcel(parcel, 0);
+        byte[] bytes = parcel.marshall();
+        parcel.recycle();
+        return bytes;
+    }
+
+    public static <T> T unmarshall(byte[] bytes, Parcelable.Creator<T> creator) {
+        T result;
+        Parcel parcel = unmarshall(bytes);
+        result = creator.createFromParcel(parcel);
+        parcel.recycle();
+        return result;
+    }
+
+    private static Parcel unmarshall(byte[] bytes) {
+        Parcel parcel = Parcel.obtain();
+        parcel.unmarshall(bytes, 0, bytes.length);
+        parcel.setDataPosition(0);
+        return parcel;
+    }
+}
diff --git a/src/com/android/settings/search2/XMLParserUtil.java b/src/com/android/settings/search2/XmlParserUtils.java
similarity index 75%
rename from src/com/android/settings/search2/XMLParserUtil.java
rename to src/com/android/settings/search2/XmlParserUtils.java
index dcb3cbb..748d4b0 100644
--- a/src/com/android/settings/search2/XMLParserUtil.java
+++ b/src/com/android/settings/search2/XmlParserUtils.java
@@ -30,19 +30,10 @@
 /**
  * Utility class to parse elements of XML preferences
  */
-public class XMLParserUtil {
-
-    private static final String NON_BREAKING_HYPHEN = "\u2011";
-    private static final String EMPTY = "";
-    private static final String LIST_DELIMITERS = "[,]\\s*";
-    private static final String HYPHEN = "-";
-    private static final String SPACE = " ";
+public class XmlParserUtils {
 
     private static final String ENTRIES_SEPARATOR = "|";
 
-    private static final Pattern REMOVE_DIACRITICALS_PATTERN
-            = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
-
     public static String getDataKey(Context context, AttributeSet attrs) {
         return getData(context, attrs,
                 com.android.internal.R.styleable.Preference,
@@ -83,7 +74,7 @@
         return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords);
     }
 
-    public static String getData(Context context, AttributeSet set, int[] attrs, int resId) {
+    private static String getData(Context context, AttributeSet set, int[] attrs, int resId) {
         final TypedArray sa = context.obtainStyledAttributes(set, attrs);
         final TypedValue tv = sa.peekValue(resId);
 
@@ -98,7 +89,7 @@
         return (data != null) ? data.toString() : null;
     }
 
-    public static String getDataEntries(Context context, AttributeSet set, int[] attrs, int resId) {
+    private static String getDataEntries(Context context, AttributeSet set, int[] attrs, int resId) {
         final TypedArray sa = context.obtainStyledAttributes(set, attrs);
         final TypedValue tv = sa.peekValue(resId);
 
@@ -119,19 +110,4 @@
         }
         return result.toString();
     }
-
-    public static String normalizeHyphen(String input) {
-        return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
-    }
-
-    public static String normalizeString(String input) {
-        final String nohyphen = (input != null) ? input.replaceAll(HYPHEN, EMPTY) : EMPTY;
-        final String normalized = Normalizer.normalize(nohyphen, Normalizer.Form.NFD);
-
-        return REMOVE_DIACRITICALS_PATTERN.matcher(normalized).replaceAll("").toLowerCase();
-    }
-
-    public static String normalizeKeywords(String input) {
-        return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
-    }
 }
diff --git a/src/com/android/settings/survey/SurveyMixin.java b/src/com/android/settings/survey/SurveyMixin.java
new file mode 100644
index 0000000..5e0754a
--- /dev/null
+++ b/src/com/android/settings/survey/SurveyMixin.java
@@ -0,0 +1,82 @@
+/*
+ * 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.survey;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.overlay.SurveyFeatureProvider;
+
+/**
+ * attaches extra, survey related work to the onResume method of registered observable classes
+ * in settings. This allows new classes to automatically support settings provided the extend
+ * one of the relevant classes in com.android.settings.lifecycle.
+ */
+public class SurveyMixin implements LifecycleObserver, OnResume, OnPause {
+
+    private String mName;
+    private InstrumentedPreferenceFragment mFragment;
+    private BroadcastReceiver mReceiver;
+
+    /**
+     * A mixin that attempts to perform survey related tasks right before onResume is called
+     * in a Settings PreferenceFragment. This will allow for remote updating and creation of
+     * surveys.
+     * @param fragment The fragment that this mixin will be attached to.
+     * @param fragmentName The simple name of the fragment.
+     */
+    public SurveyMixin(InstrumentedPreferenceFragment fragment, String fragmentName) {
+        mName = fragmentName;
+        mFragment = fragment;
+    }
+
+    @Override
+    public void onResume() {
+        Activity activity = mFragment.getActivity();
+
+        // guard against the activity not existing yet or the feature being disabled
+        if (activity != null) {
+            SurveyFeatureProvider provider =
+                    FeatureFactory.getFactory(activity).getSurveyFeatureProvider(activity);
+            if (provider != null) {
+
+                // Try to download a survey if there is none available, show the survey otherwise
+                String id = provider.getSurveyId(activity, mName);
+                if (provider.getSurveyExpirationDate(activity, id) <= -1) {
+                    // register the receiver to show the survey on completion.
+                    mReceiver = provider.createAndRegisterReceiver(activity);
+                    provider.downloadSurvey(activity, id, "fakeData");
+                } else {
+                    provider.showSurveyIfAvailable(activity, id);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onPause() {
+        Activity activity = mFragment.getActivity();
+        if (mReceiver != null && activity != null) {
+            SurveyFeatureProvider provider =
+                    FeatureFactory.getFactory(activity).getSurveyFeatureProvider(activity);
+            provider.unregisterReceiver(activity, mReceiver);
+        }
+    }
+}
diff --git a/src/com/android/settings/widget/FooterPreference.java b/src/com/android/settings/widget/FooterPreference.java
new file mode 100644
index 0000000..4a0d128
--- /dev/null
+++ b/src/com/android/settings/widget/FooterPreference.java
@@ -0,0 +1,71 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.method.LinkMovementMethod;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * A custom preference acting as "footer" of a page. It has a field for icon and text. It is added
+ * to screen as the last preference.
+ */
+public class FooterPreference extends Preference {
+
+    static final int ORDER_FOOTER = Integer.MAX_VALUE - 1;
+    static final String KEY_FOOTER = "footer_preference";
+
+    public FooterPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    public FooterPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    public FooterPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public FooterPreference(Context context) {
+        super(context);
+        init();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        TextView title = (TextView) holder.itemView.findViewById(android.R.id.title);
+        title.setMovementMethod(new LinkMovementMethod());
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.preference_footer);
+        setIcon(R.drawable.ic_info_outline_24dp);
+        setKey(KEY_FOOTER);
+        setOrder(ORDER_FOOTER);
+    }
+}
diff --git a/src/com/android/settings/widget/FooterPreferenceMixin.java b/src/com/android/settings/widget/FooterPreferenceMixin.java
new file mode 100644
index 0000000..53e3d75
--- /dev/null
+++ b/src/com/android/settings/widget/FooterPreferenceMixin.java
@@ -0,0 +1,71 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.SetPreferenceScreen;
+
+public class FooterPreferenceMixin implements LifecycleObserver, SetPreferenceScreen {
+
+    private final PreferenceFragment mFragment;
+    private FooterPreference mFooterPreference;
+
+    public FooterPreferenceMixin(PreferenceFragment fragment, Lifecycle lifecycle) {
+        mFragment = fragment;
+        lifecycle.addObserver(this);
+    }
+
+    @Override
+    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+        if (mFooterPreference != null) {
+            preferenceScreen.addPreference(mFooterPreference);
+        }
+    }
+
+    /**
+     * Creates a new {@link FooterPreference}.
+     */
+    public FooterPreference createFooterPreference() {
+        final PreferenceScreen screen = mFragment.getPreferenceScreen();
+        if (mFooterPreference != null && screen != null) {
+            screen.removePreference(mFooterPreference);
+        }
+        mFooterPreference = new FooterPreference(getPrefContext());
+
+        if (screen != null) {
+            screen.addPreference(mFooterPreference);
+        }
+        return mFooterPreference;
+    }
+
+    /**
+     * Returns an UI context with theme properly set for new Preference objects.
+     */
+    private Context getPrefContext() {
+        return mFragment.getPreferenceManager().getContext();
+    }
+
+    public boolean hasFooter() {
+        return mFooterPreference != null;
+    }
+}
+
diff --git a/src/com/android/settings/widget/PreferenceDividerDecoration.java b/src/com/android/settings/widget/PreferenceDividerDecoration.java
new file mode 100644
index 0000000..4466a3d
--- /dev/null
+++ b/src/com/android/settings/widget/PreferenceDividerDecoration.java
@@ -0,0 +1,79 @@
+/*
+ * 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.widget;
+
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceGroupAdapter;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+public class PreferenceDividerDecoration extends RecyclerView.ItemDecoration {
+
+    private Drawable mDivider;
+    private int mDividerHeight;
+
+    public void setDivider(Drawable divider) {
+        if (divider != null) {
+            mDividerHeight = divider.getIntrinsicHeight();
+        } else {
+            mDividerHeight = 0;
+        }
+        mDivider = divider;
+    }
+
+    public void setDividerHeight(int dividerHeight) {
+        mDividerHeight = dividerHeight;
+    }
+
+    @Override
+    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
+        if (mDivider == null) {
+            return;
+        }
+        final int childCount = parent.getChildCount();
+        final int width = parent.getWidth();
+        for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
+            final View view = parent.getChildAt(childViewIndex);
+            if (shouldDrawDividerAbove(view, parent)) {
+                int top = (int) ViewCompat.getY(view);
+                mDivider.setBounds(0, top, width, top + mDividerHeight);
+                mDivider.draw(c);
+            }
+        }
+    }
+
+    private boolean shouldDrawDividerAbove(View view, RecyclerView parent) {
+        final RecyclerView.Adapter adapter = parent.getAdapter();
+        if (adapter == null || !(adapter instanceof PreferenceGroupAdapter)) {
+            return false;
+        }
+        final PreferenceGroupAdapter prefAdapter = (PreferenceGroupAdapter) adapter;
+        final int adapterPosition = parent.getChildAdapterPosition(view);
+        if (adapterPosition == RecyclerView.NO_POSITION) {
+            return false;
+        }
+        final Preference pref = prefAdapter.getItem(adapterPosition);
+        if (pref instanceof PreferenceCategory) {
+            return adapterPosition != 0;
+        }
+        return pref instanceof FooterPreference;
+    }
+}
diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java
index 9739ef9..01ae99c 100644
--- a/src/com/android/settings/wifi/ConfigureWifiSettings.java
+++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java
@@ -19,14 +19,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.net.NetworkScoreManager;
-import android.net.NetworkScorerAppManager;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.ListPreference;
@@ -35,12 +31,9 @@
 import android.util.Log;
 import android.widget.Toast;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.AppListSwitchPreference;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
-
-import java.util.Collection;
 import java.util.List;
 
 public class ConfigureWifiSettings extends SettingsPreferenceFragment
@@ -53,12 +46,9 @@
     private static final String KEY_NOTIFY_OPEN_NETWORKS = "notify_open_networks";
     private static final String KEY_SLEEP_POLICY = "sleep_policy";
     private static final String KEY_CELLULAR_FALLBACK = "wifi_cellular_data_fallback";
-    private static final String KEY_WIFI_ASSISTANT = "wifi_assistant";
+    private static final String KEY_ALLOW_RECOMMENDATIONS = "allow_recommendations";
 
     private WifiManager mWifiManager;
-    private NetworkScoreManager mNetworkScoreManager;
-    private AppListSwitchPreference mWifiAssistantPreference;
-
     private IntentFilter mFilter;
 
     @Override
@@ -74,8 +64,6 @@
         mFilter = new IntentFilter();
         mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        mNetworkScoreManager =
-                (NetworkScoreManager) getSystemService(Context.NETWORK_SCORE_SERVICE);
     }
 
     @Override
@@ -119,15 +107,10 @@
             }
         }
 
-        mWifiAssistantPreference = (AppListSwitchPreference) findPreference(KEY_WIFI_ASSISTANT);
-        Collection<NetworkScorerAppManager.NetworkScorerAppData> scorers =
-                new NetworkScorerAppManager(context).getAllValidScorers();
-        if (UserManager.get(context).isAdminUser() && !scorers.isEmpty()) {
-            mWifiAssistantPreference.setOnPreferenceChangeListener(this);
-            initWifiAssistantPreference(scorers);
-        } else if (mWifiAssistantPreference != null) {
-            getPreferenceScreen().removePreference(mWifiAssistantPreference);
-        }
+        SwitchPreference allowRecommendations =
+            (SwitchPreference) findPreference(KEY_ALLOW_RECOMMENDATIONS);
+        allowRecommendations.setChecked(Settings.Global.getInt(getContentResolver(),
+            Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1);
 
         ListPreference sleepPolicyPref = (ListPreference) findPreference(KEY_SLEEP_POLICY);
         if (sleepPolicyPref != null) {
@@ -187,6 +170,10 @@
             String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI;
             Settings.Global.putString(getContentResolver(), settingName,
                     ((SwitchPreference) preference).isChecked() ? "1" : null);
+        } else if (KEY_ALLOW_RECOMMENDATIONS.equals(key)) {
+            Settings.Global.putInt(getActivity().getContentResolver(),
+                Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+                ((SwitchPreference) preference).isChecked() ? 1 : 0);
         } else {
             return super.onPreferenceTreeClick(preference);
         }
@@ -198,34 +185,6 @@
         final Context context = getActivity();
         String key = preference.getKey();
 
-        if (KEY_WIFI_ASSISTANT.equals(key)) {
-            NetworkScorerAppManager.NetworkScorerAppData wifiAssistant =
-                    new NetworkScorerAppManager(context).getScorer((String) newValue);
-            if (wifiAssistant == null) {
-                mNetworkScoreManager.setActiveScorer(null);
-                return true;
-            }
-
-            Intent intent = new Intent();
-            if (wifiAssistant.mConfigurationActivityClassName != null) {
-                // App has a custom configuration activity; launch that.
-                // This custom activity will be responsible for launching the system
-                // dialog.
-                intent.setClassName(wifiAssistant.mPackageName,
-                        wifiAssistant.mConfigurationActivityClassName);
-            } else {
-                // Fall back on the system dialog.
-                intent.setAction(NetworkScoreManager.ACTION_CHANGE_ACTIVE);
-                intent.putExtra(NetworkScoreManager.EXTRA_PACKAGE_NAME,
-                        wifiAssistant.mPackageName);
-            }
-
-            startActivity(intent);
-            // Don't update the preference widget state until the child activity returns.
-            // It will be updated in onResume after the activity finishes.
-            return false;
-        }
-
         if (KEY_SLEEP_POLICY.equals(key)) {
             try {
                 String stringValue = (String) newValue;
@@ -259,19 +218,6 @@
         wifiIpAddressPref.setSelectable(false);
     }
 
-    private void initWifiAssistantPreference(
-            Collection<NetworkScorerAppManager.NetworkScorerAppData> scorers) {
-        int count = scorers.size();
-        String[] packageNames = new String[count];
-        int i = 0;
-        for (NetworkScorerAppManager.NetworkScorerAppData scorer : scorers) {
-            packageNames[i] = scorer.mPackageName;
-            i++;
-        }
-        mWifiAssistantPreference.setPackageNames(packageNames,
-                mNetworkScoreManager.getActiveScorerPackage());
-    }
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.CONFIGURE_WIFI;
diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
deleted file mode 100644
index 68f28ee..0000000
--- a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
+++ /dev/null
@@ -1,205 +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.wifi;
-
-import android.app.Dialog;
-import android.net.wifi.WifiConfiguration;
-import android.os.Bundle;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.android.settings.R;
-import com.android.settings.SetupWizardUtils;
-import com.android.setupwizardlib.SetupWizardListLayout;
-import com.android.setupwizardlib.view.NavigationBar;
-
-/**
- * This customized version of WifiSettings is shown to the user only during Setup Wizard. Menu
- * is not shown, clicking on an access point will auto-advance to the next screen (once connected),
- * and, if the user opts to skip ahead without a wifi connection, a warning message alerts of
- * possible carrier data charges or missing software updates.
- */
-public class WifiSettingsForSetupWizard extends WifiSettings {
-
-    private static final String TAG = "WifiSettingsForSetupWizard";
-
-    private SetupWizardListLayout mLayout;
-    private View mAddOtherNetworkItem;
-    private TextView mEmptyFooter;
-    private View mMacAddressFooter;
-    private boolean mListLastEmpty = false;
-
-    @Override
-    public View onCreateView(final LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        mLayout = (SetupWizardListLayout)
-                inflater.inflate(R.layout.setup_wifi_layout, container, false);
-        final ListView list = mLayout.getListView();
-
-        mAddOtherNetworkItem = inflater.inflate(R.layout.setup_wifi_add_network, list, false);
-        list.addFooterView(mAddOtherNetworkItem, null, true);
-        mAddOtherNetworkItem.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mWifiManager.isWifiEnabled()) {
-                    onAddNetworkPressed();
-                }
-            }
-        });
-
-        mMacAddressFooter = inflater.inflate(R.layout.setup_wifi_mac_address, list, false);
-        list.addFooterView(mMacAddressFooter, null, false);
-
-        final NavigationBar navigationBar = mLayout.getNavigationBar();
-        if (navigationBar != null) {
-            WifiSetupActivity activity = (WifiSetupActivity) getActivity();
-            activity.onNavigationBarCreated(navigationBar);
-        }
-
-        return mLayout;
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        if (hasNextButton()) {
-            getNextButton().setVisibility(View.GONE);
-        }
-
-        updateMacAddress();
-    }
-
-    @Override
-    public void onAccessPointsChanged() {
-        super.onAccessPointsChanged();
-        PreferenceScreen preferenceScreen = getPreferenceScreen();
-        updateFooter(preferenceScreen == null || preferenceScreen.getPreferenceCount() == 0);
-    }
-
-    @Override
-    public void onWifiStateChanged(int state) {
-        super.onWifiStateChanged(state);
-        updateMacAddress();
-    }
-
-    @Override
-    public void registerForContextMenu(View view) {
-        // Suppressed during setup wizard
-    }
-
-    @Override
-    /* package */ WifiEnabler createWifiEnabler() {
-        // Not shown during setup wizard
-        return null;
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        // Do not show menu during setup wizard
-    }
-
-    @Override
-    public Dialog onCreateDialog(int dialogId) {
-        final Dialog dialog = super.onCreateDialog(dialogId);
-        SetupWizardUtils.applyImmersiveFlags(dialog);
-        return dialog;
-    }
-
-    @Override
-    protected void connect(final WifiConfiguration config, boolean isSavedNetwork) {
-        WifiSetupActivity activity = (WifiSetupActivity) getActivity();
-        activity.networkSelected();
-        super.connect(config, isSavedNetwork);
-    }
-
-    @Override
-    protected void connect(final int networkId, boolean isSavedNetwork) {
-        WifiSetupActivity activity = (WifiSetupActivity) getActivity();
-        activity.networkSelected();
-        super.connect(networkId, isSavedNetwork);
-    }
-
-    @Override
-    protected TextView initEmptyTextView() {
-        final LayoutInflater inflater = LayoutInflater.from(getActivity());
-        mEmptyFooter = (TextView) inflater.inflate(R.layout.setup_wifi_empty, getListView(), false);
-        return mEmptyFooter;
-    }
-
-    protected void updateFooter(boolean isEmpty) {
-        if (isEmpty != mListLastEmpty) {
-            if (isEmpty) {
-                setFooterView(mEmptyFooter);
-            } else {
-                LinearLayout layout = new LinearLayout(getContext());
-                layout.setOrientation(LinearLayout.VERTICAL);
-                layout.addView(mAddOtherNetworkItem);
-                layout.addView(mMacAddressFooter);
-                setFooterView(layout);
-            }
-            mListLastEmpty = isEmpty;
-        }
-    }
-
-    @Override
-    public View setPinnedHeaderView(int layoutResId) {
-        // Pinned header is not supported in setup wizard
-        return null;
-    }
-
-    @Override
-    public void setPinnedHeaderView(View pinnedHeader) {
-        // Pinned header is not supported in setup wizard
-    }
-
-    @Override
-    protected void setProgressBarVisible(boolean visible) {
-        if (mLayout != null) {
-            if (visible) {
-                mLayout.showProgressBar();
-            } else {
-                mLayout.hideProgressBar();
-            }
-        }
-    }
-
-    private void updateMacAddress() {
-        if (mMacAddressFooter != null) {
-            String macAddress = null;
-            if (mWifiManager != null) {
-                android.net.wifi.WifiInfo connectionInfo = mWifiManager.getConnectionInfo();
-                if (connectionInfo != null) {
-                    macAddress = connectionInfo.getMacAddress();
-                }
-            }
-            final TextView macAddressTextView =
-                    (TextView) mMacAddressFooter.findViewById(R.id.mac_address);
-            macAddressTextView.setText(!TextUtils.isEmpty(macAddress) ?
-                    macAddress : getString(R.string.status_unavailable));
-        }
-    }
-}
diff --git a/src/com/android/settings/wifi/WifiSetupActivity.java b/src/com/android/settings/wifi/WifiSetupActivity.java
deleted file mode 100644
index f640b6e..0000000
--- a/src/com/android/settings/wifi/WifiSetupActivity.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.support.v14.preference.PreferenceFragment;
-import android.util.Log;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.ButtonBarHandler;
-import com.android.settings.R;
-import com.android.settings.SetupWizardUtils;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.setupwizardlib.view.NavigationBar;
-
-public class WifiSetupActivity extends WifiPickerActivity
-        implements ButtonBarHandler, NavigationBar.NavigationBarListener {
-    private static final String TAG = "WifiSetupActivity";
-
-    // this boolean extra specifies whether to auto finish when connection is established
-    private static final String EXTRA_AUTO_FINISH_ON_CONNECT = "wifi_auto_finish_on_connect";
-
-    // This boolean extra specifies whether network is required
-    private static final String EXTRA_IS_NETWORK_REQUIRED = "is_network_required";
-
-    // This boolean extra specifies whether wifi is required
-    private static final String EXTRA_IS_WIFI_REQUIRED = "is_wifi_required";
-
-    // Whether auto finish is suspended until user connects to an access point
-    private static final String EXTRA_REQUIRE_USER_NETWORK_SELECTION =
-            "wifi_require_user_network_selection";
-
-    // Key for whether the user selected network in saved instance state bundle
-    private static final String PARAM_USER_SELECTED_NETWORK = "userSelectedNetwork";
-
-    // Activity result when pressing the Skip button
-    private static final int RESULT_SKIP = Activity.RESULT_FIRST_USER;
-
-    // Whether to auto finish when the user selected a network and successfully connected
-    private boolean mAutoFinishOnConnection;
-    // Whether network is required to proceed. This is decided in SUW and passed in as an extra.
-    private boolean mIsNetworkRequired;
-    // Whether wifi is required to proceed. This is decided in SUW and passed in as an extra.
-    private boolean mIsWifiRequired;
-    // Whether the user connected to a network. This excludes the auto-connecting by the system.
-    private boolean mUserSelectedNetwork;
-    // Whether the device is connected to WiFi
-    private boolean mWifiConnected;
-
-    private NavigationBar mNavigationBar;
-
-    private IntentFilter mFilter = new IntentFilter();
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            // Refresh the connection state with the latest connection info. Use the connection info
-            // from ConnectivityManager instead of the one attached in the intent to make sure
-            // we have the most up-to-date connection state. b/17511772
-            refreshConnectionState();
-        }
-    };
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final Intent intent = getIntent();
-        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-
-        mAutoFinishOnConnection = intent.getBooleanExtra(EXTRA_AUTO_FINISH_ON_CONNECT, false);
-        mIsNetworkRequired = intent.getBooleanExtra(EXTRA_IS_NETWORK_REQUIRED, false);
-        mIsWifiRequired = intent.getBooleanExtra(EXTRA_IS_WIFI_REQUIRED, false);
-        // Behave like the user already selected a network if we do not require selection
-        mUserSelectedNetwork = !intent.getBooleanExtra(EXTRA_REQUIRE_USER_NETWORK_SELECTION, false);
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putBoolean(PARAM_USER_SELECTED_NETWORK, mUserSelectedNetwork);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle savedInstanceState) {
-        super.onRestoreInstanceState(savedInstanceState);
-        mUserSelectedNetwork = savedInstanceState.getBoolean(PARAM_USER_SELECTED_NETWORK, true);
-    }
-
-    private boolean isWifiConnected() {
-        final ConnectivityManager connectivity = (ConnectivityManager)
-                getSystemService(Context.CONNECTIVITY_SERVICE);
-        boolean wifiConnected = connectivity != null &&
-                connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
-        mWifiConnected = wifiConnected;
-        return wifiConnected;
-    }
-
-    private void refreshConnectionState() {
-        if (isWifiConnected()) {
-            if (mAutoFinishOnConnection && mUserSelectedNetwork) {
-                Log.d(TAG, "Auto-finishing with connection");
-                finish(Activity.RESULT_OK);
-                // Require a user selection before auto-finishing next time we are here. The user
-                // can either connect to a different network or press "next" to proceed.
-                mUserSelectedNetwork = false;
-            }
-            setNextButtonText(R.string.suw_next_button_label);
-            setNextButtonEnabled(true);
-        } else if (mIsWifiRequired || (mIsNetworkRequired && !isNetworkConnected())) {
-            // We do not want the user to skip wifi setting if
-            // - wifi is required, but wifi connection hasn't been established yet;
-            // - or network is required, but no valid connection has been established.
-            setNextButtonText(R.string.skip_label);
-            setNextButtonEnabled(false);
-        } else {
-            // In other cases, user can choose to skip. Specifically these cases are
-            // - wifi is not required;
-            // - and network is not required;
-            // -     or network is required and a valid connection has been established.
-            setNextButtonText(R.string.skip_label);
-            setNextButtonEnabled(true);
-        }
-    }
-
-    private void setNextButtonEnabled(boolean enabled) {
-        if (mNavigationBar != null) {
-            mNavigationBar.getNextButton().setEnabled(enabled);
-        }
-    }
-
-    private void setNextButtonText(int resId) {
-        if (mNavigationBar != null) {
-            mNavigationBar.getNextButton().setText(resId);
-        }
-    }
-
-    /* package */ void networkSelected() {
-        Log.d(TAG, "Network selected by user");
-        mUserSelectedNetwork = true;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        registerReceiver(mReceiver, mFilter);
-        refreshConnectionState();
-    }
-
-    @Override
-    public void onPause() {
-        unregisterReceiver(mReceiver);
-        super.onPause();
-    }
-
-    @Override
-    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
-        resid = SetupWizardUtils.getTheme(getIntent());
-        super.onApplyThemeResource(theme, resid, first);
-    }
-
-    @Override
-    protected boolean isValidFragment(String fragmentName) {
-        return WifiSettingsForSetupWizard.class.getName().equals(fragmentName);
-    }
-
-    @Override
-    /* package */ Class<? extends PreferenceFragment> getWifiSettingsClass() {
-        return WifiSettingsForSetupWizard.class;
-    }
-
-    /**
-     * Complete this activity and return the results to the caller.
-     */
-    public void finish(int resultCode) {
-        Log.d(TAG, "finishing, resultCode=" + resultCode);
-        setResult(resultCode);
-        finish();
-    }
-
-    public void onNavigationBarCreated(final NavigationBar bar) {
-        mNavigationBar = bar;
-        bar.setNavigationBarListener(this);
-        SetupWizardUtils.setImmersiveMode(this);
-    }
-
-    @Override
-    public void onNavigateBack() {
-        onBackPressed();
-    }
-
-    @Override
-    public void onNavigateNext() {
-        if (mWifiConnected) {
-            finish(RESULT_OK);
-        } else {
-            // Warn of possible data charges if there is a network connection, or lack of updates
-            // if there is none.
-            final int message = isNetworkConnected() ? R.string.wifi_skipped_message :
-                    R.string.wifi_and_mobile_skipped_message;
-            WifiSkipDialog.newInstance(message).show(getFragmentManager(), "dialog");
-        }
-    }
-
-    /**
-     * @return True if there is a valid network connection, whether it is via WiFi, mobile data or
-     *         other means.
-     */
-    private boolean isNetworkConnected() {
-        final ConnectivityManager connectivity = (ConnectivityManager)
-                getSystemService(Context.CONNECTIVITY_SERVICE);
-        if (connectivity == null) {
-            return false;
-        }
-        final NetworkInfo info = connectivity.getActiveNetworkInfo();
-        return info != null && info.isConnected();
-    }
-
-    public static class WifiSkipDialog extends InstrumentedDialogFragment {
-        public static WifiSkipDialog newInstance(int messageRes) {
-            final Bundle args = new Bundle();
-            args.putInt("messageRes", messageRes);
-            final WifiSkipDialog dialog = new WifiSkipDialog();
-            dialog.setArguments(args);
-            return dialog;
-        }
-
-        public WifiSkipDialog() {
-            // no-arg constructor for fragment
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsProto.MetricsEvent.DIALOG_WIFI_SKIP;
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            int messageRes = getArguments().getInt("messageRes");
-            final AlertDialog dialog = new AlertDialog.Builder(getActivity())
-                    .setMessage(messageRes)
-                    .setCancelable(false)
-                    .setPositiveButton(R.string.wifi_skip_anyway,
-                            new DialogInterface.OnClickListener() {
-                                @Override
-                                public void onClick(DialogInterface dialog, int id) {
-                                    WifiSetupActivity activity = (WifiSetupActivity) getActivity();
-                                    activity.finish(RESULT_SKIP);
-                                }
-                            })
-                    .setNegativeButton(R.string.wifi_dont_skip,
-                            new DialogInterface.OnClickListener() {
-                                @Override
-                                public void onClick(DialogInterface dialog, int id) {
-                                }
-                            })
-                    .create();
-            SetupWizardUtils.applyImmersiveFlags(dialog);
-            return dialog;
-        }
-    }
-}
diff --git a/tests/robotests/assets/grandfather_not_in_search_index_provider_registry b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry
index 29f3816..a95fc0b 100644
--- a/tests/robotests/assets/grandfather_not_in_search_index_provider_registry
+++ b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry
@@ -1,3 +1,2 @@
 com.android.settings.display.ScreenZoomPreferenceFragmentForSetupWizard
-com.android.settings.wifi.WifiSettingsForSetupWizard
 com.android.settings.print.PrintServiceSettingsFragment
diff --git a/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
new file mode 100644
index 0000000..4d8101f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/WorkSoundPreferenceControllerTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.notification;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.TwoStatePreference;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.DefaultRingtonePreference;
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+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.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.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WorkSoundPreferenceControllerTest {
+
+    private static final String KEY_WORK_CATEGORY = "sound_work_settings_section";
+    private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
+    private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
+    private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
+    private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private AudioHelper mAudioHelper;
+    @Mock
+    private SoundSettings mFragment;
+
+    private WorkSoundPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        mController = new WorkSoundPreferenceController(mContext, mFragment, null, mAudioHelper);
+    }
+
+    @Test
+    public void isAvailable_managedProfileAndNotSingleVolume_shouldReturnTrue() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.myUserId());
+        when(mAudioHelper.isSingleVolume()).thenReturn(false);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noManagedProfile_shouldReturnFalse() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.USER_NULL);
+        when(mAudioHelper.isSingleVolume()).thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_singleVolume_shouldReturnFalse() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.myUserId());
+        when(mAudioHelper.isSingleVolume()).thenReturn(true);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void onResume_available_shouldAddPreferenceCategory() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+                .thenReturn(UserHandle.myUserId());
+        when(mAudioHelper.isSingleVolume()).thenReturn(false);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+        when(mScreen.findPreference(KEY_WORK_CATEGORY))
+            .thenReturn(mock(PreferenceGroup.class));
+        when(mScreen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS))
+            .thenReturn(mock(TwoStatePreference.class));
+        when(mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
+            .thenReturn(mock(DefaultRingtonePreference.class));
+        when(mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
+            .thenReturn(mock(DefaultRingtonePreference.class));
+        when(mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
+            .thenReturn(mock(DefaultRingtonePreference.class));
+        when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
+
+        mController.onResume();
+
+        verify(mFragment).addPreferencesFromResource(R.xml.sound_work_settings);
+    }
+
+    @Test
+    public void onResume_notAvailable_shouldNotAddPreferenceCategory() {
+        when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+        when(mAudioHelper.getManagedProfileId(any(UserManager.class)))
+            .thenReturn(UserHandle.USER_NULL);
+        when(mAudioHelper.isSingleVolume()).thenReturn(true);
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+        mController.onResume();
+
+        verify(mFragment, never()).addPreferencesFromResource(anyInt());
+    }
+
+    @Test
+    public void onPreferenceChange_shouldUpdateSummary() {
+        final Preference preference = mock(Preference.class);
+        when(preference.getKey()).thenReturn(KEY_WORK_PHONE_RINGTONE);
+
+        mController.onPreferenceChange(preference, "hello");
+
+        verify(preference).setSummary(anyString());
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
new file mode 100644
index 0000000..8b363b0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
@@ -0,0 +1,616 @@
+/*
+ * 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.search;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.provider.SearchIndexableResource;
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.search2.DatabaseIndexingManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DatabaseIndexingManagerTest {
+    private final String localeStr = "en_US";
+
+    private final int rank = 42;
+    private final String title = "title\u2011title";
+    private final String updatedTitle = "title-title";
+    private final String normalizedTitle = "titletitle";
+    private final String summaryOn = "summary\u2011on";
+    private final String updatedSummaryOn = "summary-on";
+    private final String normalizedSummaryOn = "summaryon";
+    private final String summaryOff = "summary\u2011off";
+    private final String updatedSummaryOff ="summary-off";
+    private final String normalizedSummaryOff = "summaryoff";
+    private final String entries = "entries";
+    private final String keywords = "keywords, keywordss, keywordsss";
+    private final String spaceDelimittedKeywords = "keywords keywordss keywordsss";
+    private final String screenTitle = "screen title";
+    private final String className = "class name";
+    private final int iconResId = 0xff;
+    private final String action = "action";
+    private final String targetPackage = "target package";
+    private final String targetClass = "target class";
+    private final String packageName = "package name";
+    private final String key = "key";
+    private final int userId = -1;
+    private final boolean enabled = true;
+
+    private Context mContext;
+    private DatabaseIndexingManager mManager;
+    private SQLiteDatabase mDb;
+
+    @Before
+    public void setUp() {
+        mContext = ShadowApplication.getInstance().getApplicationContext();
+        mManager = spy(new DatabaseIndexingManager(mContext, mContext.getPackageName()));
+        mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
+    }
+
+    @After
+    public void cleanUp() {
+        Field instance;
+        Class clazz = IndexDatabaseHelper.class;
+        try {
+            instance = clazz.getDeclaredField("sSingleton");
+            instance.setAccessible(true);
+            instance.set(null, null);
+        } catch (Exception e) {
+            throw new RuntimeException();
+        }
+    }
+
+    @Test
+    public void testDatabaseSchema() {
+        Cursor dbCursor = mDb.query("prefs_index", null, null, null, null, null, null);
+        List<String> columnNames = new ArrayList<>(Arrays.asList(dbCursor.getColumnNames()));
+        // Note that docid is not included.
+        List<String> expColumnNames = new ArrayList<>(Arrays.asList(new String[ ]{
+                "locale",
+                "data_rank",
+                "data_title",
+                "data_title_normalized",
+                "data_summary_on",
+                "data_summary_on_normalized",
+                "data_summary_off",
+                "data_summary_off_normalized",
+                "data_entries",
+                "data_keywords",
+                "class_name",
+                "screen_title",
+                "intent_action",
+                "intent_target_package",
+                "intent_target_class",
+                "icon",
+                "enabled",
+                "data_key_reference",
+                "user_id",
+                "payload_type",
+                "payload"
+        }));
+        // Prevent database schema regressions
+        assertThat(columnNames).containsAllIn(expColumnNames);
+    }
+
+    // Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow
+
+    @Test
+    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);
+        assertThat(cursor.getCount()).isEqualTo(1);
+    }
+
+    @Test
+    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);
+        cursor.moveToPosition(0);
+
+        // Locale
+        assertThat(cursor.getString(0)).isEqualTo(localeStr);
+        // Data Rank
+        assertThat(cursor.getInt(1)).isEqualTo(raw.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(raw.entries);
+        // Keywords
+        assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords);
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo(raw.screenTitle);
+        // Class Name
+        assertThat(cursor.getString(11)).isEqualTo(raw.className);
+        // Icon
+        assertThat(cursor.getInt(12)).isEqualTo(raw.iconResId);
+        // Intent Action
+        assertThat(cursor.getString(13)).isEqualTo(raw.intentAction);
+        // Target Package
+        assertThat(cursor.getString(14)).isEqualTo(raw.intentTargetPackage);
+        // Target Class
+        assertThat(cursor.getString(15)).isEqualTo(raw.intentTargetClass);
+        // Enabled
+        assertThat(cursor.getInt(16) == 1).isEqualTo(raw.enabled);
+        // Data ref key
+        assertThat(cursor.getString(17)).isNotNull();
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(raw.userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    @Test
+    public void testInsertRawColumnMismatchedLocale_NoRowInserted() {
+        SearchIndexableRaw raw = getFakeRaw("ca-fr");
+        mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+        assertThat(cursor.getCount()).isEqualTo(0);
+    }
+
+    // Tests for the flow: IndexOneResource -> IndexFromResource ->
+    //                     UpdateOneRowWithFilteredData -> UpdateOneRow
+
+    @Test
+    public void testNullResource_NothingInserted() {
+        mManager.indexOneSearchIndexableData(mDb, localeStr, null /* searchIndexableResource */,
+                new HashMap<String, List<String>>());
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+        assertThat(cursor.getCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void testAddResource_RowsInserted() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+        assertThat(cursor.getCount()).isEqualTo(6);
+    }
+
+    @Test
+    public void testAddResourceHeader_RowsMatch() {
+        SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
+        cursor.moveToPosition(1);
+
+        // Locale
+        assertThat(cursor.getString(0)).isEqualTo(localeStr);
+        // Data Rank
+        assertThat(cursor.getInt(1)).isEqualTo(rank);
+        // Data Title
+        assertThat(cursor.getString(2)).isEqualTo("Apps");
+        // Normalized Title
+        assertThat(cursor.getString(3)).isEqualTo("apps");
+        // Summary On
+        assertThat(cursor.getString(4)).isEqualTo("Manage apps, set up quick launch shortcuts");
+        // Summary On Normalized
+        assertThat(cursor.getString(5)).isEqualTo("manage apps, set up quick launch shortcuts");
+        // Summary Off - only on for checkbox preferences
+        assertThat(cursor.getString(6)).isEmpty();
+        // Summary off normalized - only on for checkbox preferences
+        assertThat(cursor.getString(7)).isEmpty();
+        // Entries - only on for list preferences
+        assertThat(cursor.getString(8)).isNull();
+        // Keywords
+        assertThat(cursor.getString(9)).isEmpty();
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo("Apps");
+        // 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)).isEqualTo("applications_settings");
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload - should be updated to real payloads as controllers are added
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    @Test
+    public void testAddResourceCustomSetting_RowsMatch() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+
+        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("Swipe for notifications");
+        // Normalized Title
+        assertThat(cursor.getString(3)).isEqualTo("swipe for notifications");
+        // Summary On
+        assertThat(cursor.getString(4)).isEqualTo("To check your notifications, " +
+                "swipe down on the fingerprint sensor on the back of your phone.");
+        // Summary On Normalized
+        assertThat(cursor.getString(5)).isEqualTo("to check your notifications, " +
+                "swipe down on the fingerprint sensor on the back of your phone.");
+        // Summary Off - only on for checkbox preferences
+        assertThat(cursor.getString(6)).isEmpty();
+        // Summary off normalized - only on for checkbox preferences
+        assertThat(cursor.getString(7)).isEmpty();
+        // Entries - only on for list preferences
+        assertThat(cursor.getString(8)).isNull();
+        // Keywords
+        assertThat(cursor.getString(9)).isEmpty();
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo("Gestures");
+        // 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)).isEqualTo("gesture_swipe_down_fingerprint");
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload - should be updated to real payloads as controllers are added
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    @Test
+    public void testAddResourceCheckboxPreference_RowsMatch() {
+        SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+
+        /* Should return 6 results, with the following titles:
+         * Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
+         */
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", 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("Advanced settings");
+        // Normalized Title
+        assertThat(cursor.getString(3)).isEqualTo("advanced settings");
+        // Summary On
+        assertThat(cursor.getString(4)).isEqualTo("Enable more settings options");
+        // Summary On Normalized
+        assertThat(cursor.getString(5)).isEqualTo("enable more settings options");
+        // Summary Off
+        assertThat(cursor.getString(6)).isEqualTo("Enable more settings options");
+        // Summary Off
+        assertThat(cursor.getString(7)).isEqualTo("enable more settings options");
+        // Entries - only on for list preferences
+        assertThat(cursor.getString(8)).isNull();
+        // Keywords
+        assertThat(cursor.getString(9)).isEmpty();
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo("Apps");
+        // 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)).isEqualTo("toggle_advanced_settings");
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload - should be updated to real payloads as controllers are added
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    @Test
+    public void testAddResourceListPreference_RowsMatch() {
+        SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
+        cursor.moveToPosition(3);
+        // Locale
+        assertThat(cursor.getString(0)).isEqualTo(localeStr);
+        // Data Rank
+        assertThat(cursor.getInt(1)).isEqualTo(rank);
+        // Data Title
+        assertThat(cursor.getString(2)).isEqualTo("Preferred install location");
+        // Normalized Title
+        assertThat(cursor.getString(3)).isEqualTo("preferred install location");
+        // Summary On
+        assertThat(cursor.getString(4)).isEqualTo("Change the preferred installation location for new apps");
+        // Summary On Normalized
+        assertThat(cursor.getString(5)).isEqualTo("change the preferred installation location for new apps");
+        // Summary Off - only on for checkbox preferences
+        assertThat(cursor.getString(6)).isEmpty();
+        // Summary off normalized - only on for checkbox preferences
+        assertThat(cursor.getString(7)).isEmpty();
+        // Entries - only on for list preferences
+        assertThat(cursor.getString(8)).isEqualTo("Internal device storage|Removable SD card|" +
+                        "Let the system decide|");
+        // Keywords
+        assertThat(cursor.getString(9)).isEmpty();
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo("Apps");
+        // 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)).isEqualTo("app_install_location");
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload - should be updated to real payloads as controllers are added
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    // Tests for the flow: IndexOneResource -> IndexFromProvider -> IndexFromResource ->
+    //                     UpdateOneRowWithFilteredData -> UpdateOneRow
+
+    @Test
+    public void testResourceProvider_RowInserted() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        resource.xmlResId = 0;
+        resource.className = "com.android.settings.display.ScreenZoomSettings";
+
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+        assertThat(cursor.getCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void testResourceProvider_Matches() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        resource.xmlResId = 0;
+        resource.className = "com.android.settings.display.ScreenZoomSettings";
+
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+        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("Display size");
+        // Normalized Title
+        assertThat(cursor.getString(3)).isEqualTo("display size");
+        // Summary On
+        assertThat(cursor.getString(4)).isEmpty();
+        // Summary On Normalized
+        assertThat(cursor.getString(5)).isEmpty();
+        // Summary Off - only on for checkbox preferences
+        assertThat(cursor.getString(6)).isEmpty();
+        // Summary off normalized - only on for checkbox preferences
+        assertThat(cursor.getString(7)).isEmpty();
+        // Entries - only on for list preferences
+        assertThat(cursor.getString(8)).isNull();
+        // Keywords
+        assertThat(cursor.getString(9)).isEqualTo("display density screen zoom scale scaling");
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo("Display size");
+        // Class Name
+        assertThat(cursor.getString(11))
+                .isEqualTo("com.android.settings.display.ScreenZoomSettings");
+        // Icon
+        assertThat(cursor.getInt(12)).isEqualTo(iconResId);
+        // Intent Action
+        assertThat(cursor.getString(13)).isNull();
+        // Target Package
+        assertThat(cursor.getString(14)).isNull();
+        // Target Class
+        assertThat(cursor.getString(15)).isNull();
+        // Enabled
+        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
+        // Data ref key
+        assertThat(cursor.getString(17)).isNull();
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload - should be updated to real payloads as controllers are added
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    @Test
+    public void testResourceProvider_ResourceRowInserted() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        resource.xmlResId = 0;
+        resource.className = "com.android.settings.LegalSettings";
+
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
+        assertThat(cursor.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testResourceProvider_ResourceRowMatches() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        resource.xmlResId = 0;
+        resource.className = "com.android.settings.LegalSettings";
+
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", 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("Legal information");
+        // Normalized Title
+        assertThat(cursor.getString(3)).isEqualTo("legal information");
+        // Summary On
+        assertThat(cursor.getString(4)).isEmpty();
+        // Summary On Normalized
+        assertThat(cursor.getString(5)).isEmpty();
+        // Summary Off - only on for checkbox preferences
+        assertThat(cursor.getString(6)).isEmpty();
+        // Summary off normalized - only on for checkbox preferences
+        assertThat(cursor.getString(7)).isEmpty();
+        // Entries - only on for list preferences
+        assertThat(cursor.getString(8)).isNull();
+        // Keywords
+        assertThat(cursor.getString(9)).isEmpty();
+        // Screen Title
+        assertThat(cursor.getString(10)).isEqualTo("Legal information");
+        // Class Name
+        assertThat(cursor.getString(11))
+                .isEqualTo("com.android.settings.LegalSettings");
+        // Icon
+        assertThat(cursor.getInt(12)).isEqualTo(iconResId);
+        // Intent Action
+        assertThat(cursor.getString(13)).isNull();
+        // Target Package
+        assertThat(cursor.getString(14)).isNull();
+        // Target Class
+        assertThat(cursor.getString(15)).isNull();
+        // Enabled
+        assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
+        // Data ref key
+        assertThat(cursor.getString(17)).isNull();
+        // User Id
+        assertThat(cursor.getInt(18)).isEqualTo(userId);
+        // Payload Type - default is 0
+        assertThat(cursor.getInt(19)).isEqualTo(0);
+        // Payload - should be updated to real payloads as controllers are added
+        assertThat(cursor.getBlob(20)).isNull();
+    }
+
+    // Util functions
+
+    private SearchIndexableRaw getFakeRaw() {
+        return getFakeRaw(localeStr);
+    }
+
+    private SearchIndexableRaw getFakeRaw(String localeStr) {
+        SearchIndexableRaw data = new SearchIndexableRaw(mContext);
+        data.locale = new Locale(localeStr);
+        data.rank = rank;
+        data.title = title;
+        data.summaryOn = summaryOn;
+        data.summaryOff = summaryOff;
+        data.entries = entries;
+        data.keywords = keywords;
+        data.screenTitle = screenTitle;
+        data.className = className;
+        data.packageName = packageName;
+        data.iconResId = iconResId;
+        data.intentAction = action;
+        data.intentTargetPackage = targetPackage;
+        data.intentTargetClass = targetClass;
+        data.key = key;
+        data.userId = userId;
+        data.enabled = enabled;
+        return data;
+    }
+
+    private SearchIndexableResource getFakeResource(int xml) {
+        SearchIndexableResource sir = new SearchIndexableResource(mContext);
+        sir.rank = rank;
+        sir.xmlResId = xml;
+        sir.className = className;
+        sir.packageName = packageName;
+        sir.iconResId = iconResId;
+        sir.intentAction = action;
+        sir.intentTargetPackage = targetPackage;
+        sir.intentTargetClass = targetClass;
+        sir.enabled = enabled;
+        return sir;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseRowTest.java b/tests/robotests/src/com/android/settings/search/DatabaseRowTest.java
new file mode 100644
index 0000000..34626bd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/DatabaseRowTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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 static for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.settings.search;
+
+import android.content.Intent;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.search2.DatabaseIndexingManager.DatabaseRow;
+import com.android.settings.search2.DatabaseIndexingManager.DatabaseRow.Builder;
+import com.android.settings.search2.IntentPayload;
+import com.android.settings.search2.ResultPayload;
+import com.android.settings.search2.ResultPayloadUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DatabaseRowTest {
+    private Builder builder;
+
+    private static final String LOCALE = "locale";
+    private static final String UPDATED_TITLE = "updated title";
+    private static final String NORMALIZED_TITLE = "normal title";
+    private static final String UPDATED_SUMMARY_ON = "updated summary on";
+    private static final String NORMALIZED_SUMMARY_ON = "normalized summary on";
+    private static final String UPDATED_SUMMARY_OFF = "updated summary off";
+    private static final String NORMALIZED_SUMMARY_OFF = "normalized summary off";
+    private static final String ENTRIES = "entries";
+    private static final String CLASS_NAME = "class name";
+    private static final String SCREEN_TITLE = "sceen title";
+    private static final int ICON_RES_ID = 0xff;
+    private static final int RANK = 1;
+    private static final String SPACE_DELIMITED_KEYWORDS = "keywords";
+    private static final String INTENT_ACTION = "intent action";
+    private static final String INTENT_TARGET_PACKAGE = "target package";
+    private static final String INTENT_TARGET_CLASS = "target class";
+    private static final boolean ENABLED = true;
+    private static final String KEY = "key";
+    private static final int USER_ID = 1;
+    private static IntentPayload intentPayload;
+
+    private final String EXTRA_KEY = "key";
+    private final String EXTRA_VALUE = "value";
+
+    @Before
+    public void setUp() {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_KEY, EXTRA_VALUE);
+        intentPayload = new IntentPayload(intent);
+
+        builder = new DatabaseRow.Builder();
+    }
+
+    @Test
+    public void testFullRowBuild_NonNull() {
+        DatabaseRow row = generateRow();
+        assertThat(row).isNotNull();
+    }
+
+    @Test
+    public void testPrimativesBuild_NoDataLoss() {
+        DatabaseRow row = generateRow();
+
+        assertThat(row.locale).isEqualTo(LOCALE);
+        assertThat(row.updatedTitle).isEqualTo(UPDATED_TITLE);
+        assertThat(row.normalizedTitle).isEqualTo(NORMALIZED_TITLE);
+        assertThat(row.updatedSummaryOn).isEqualTo(UPDATED_SUMMARY_ON);
+        assertThat(row.normalizedSummaryOn).isEqualTo(NORMALIZED_SUMMARY_ON);
+        assertThat(row.updatedSummaryOff).isEqualTo(UPDATED_SUMMARY_OFF);
+        assertThat(row.normalizedSummaryOff).isEqualTo(NORMALIZED_SUMMARY_OFF);
+        assertThat(row.entries).isEqualTo(ENTRIES);
+        assertThat(row.className).isEqualTo(CLASS_NAME);
+        assertThat(row.screenTitle).isEqualTo(SCREEN_TITLE);
+        assertThat(row.iconResId).isEqualTo(ICON_RES_ID);
+        assertThat(row.rank).isEqualTo(RANK);
+        assertThat(row.spaceDelimitedKeywords).isEqualTo(SPACE_DELIMITED_KEYWORDS);
+        assertThat(row.intentAction).isEqualTo(INTENT_ACTION);
+        assertThat(row.intentTargetClass).isEqualTo(INTENT_TARGET_CLASS);
+        assertThat(row.intentTargetPackage).isEqualTo(INTENT_TARGET_PACKAGE);
+        assertThat(row.enabled).isEqualTo(ENABLED);
+        assertThat(row.userId).isEqualTo(USER_ID);
+        assertThat(row.key).isEqualTo(KEY);
+        assertThat(row.payloadType).isEqualTo(ResultPayload.PayloadType.INTENT);
+    }
+
+    @Test
+    public void testPayload_PayloadTypeAdded() {
+        DatabaseRow row = generateRow();
+        byte[] marshalledPayload = row.payload;
+        IntentPayload payload = ResultPayloadUtils.unmarshall(marshalledPayload,
+                IntentPayload.CREATOR);
+
+        Intent intent = payload.intent;
+        assertThat(intent.getExtra(EXTRA_KEY)).isEqualTo(EXTRA_VALUE);
+    }
+
+    @Test
+    public void TestNullPayload_NoCrash() {
+        Builder builder = new Builder();
+        builder.setPayload(null);
+        DatabaseRow row = builder.build();
+
+        assertThat(row.payload).isNull();
+    }
+
+    private DatabaseRow generateRow() {
+        builder.setLocale(LOCALE)
+                .setUpdatedTitle(UPDATED_TITLE)
+                .setNormalizedTitle(NORMALIZED_TITLE)
+                .setUpdatedSummaryOn(UPDATED_SUMMARY_ON)
+                .setNormalizedSummaryOn(NORMALIZED_SUMMARY_ON)
+                .setUpdatedSummaryOff(UPDATED_SUMMARY_OFF)
+                .setNormalizedSummaryOff(NORMALIZED_SUMMARY_OFF)
+                .setEntries(ENTRIES)
+                .setClassName(CLASS_NAME)
+                .setScreenTitle(SCREEN_TITLE)
+                .setIconResId(ICON_RES_ID)
+                .setRank(RANK)
+                .setSpaceDelimitedKeywords(SPACE_DELIMITED_KEYWORDS)
+                .setIntentAction(INTENT_ACTION)
+                .setIntentTargetPackage(INTENT_TARGET_PACKAGE)
+                .setIntentTargetClass(INTENT_TARGET_CLASS)
+                .setEnabled(ENABLED)
+                .setKey(KEY)
+                .setUserId(USER_ID)
+                .setPayload(intentPayload);
+
+        return(builder.build());
+    }
+}
+;
diff --git a/tests/robotests/src/com/android/settings/search/ResultPayloadUtilsTest.java b/tests/robotests/src/com/android/settings/search/ResultPayloadUtilsTest.java
new file mode 100644
index 0000000..3875d86
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/ResultPayloadUtilsTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.search;
+
+import android.content.Intent;
+import android.os.BadParcelableException;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.search2.IntentPayload;
+import com.android.settings.search2.ResultPayloadUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.StreamCorruptedException;
+
+import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.fail;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ResultPayloadUtilsTest {
+    private IntentPayload payload;
+
+    private final String EXTRA_KEY = "key";
+    private final String EXTRA_VALUE = "value";
+
+    @Before
+    public void setUp() {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_KEY, EXTRA_VALUE);
+        payload = new IntentPayload(intent);
+    }
+
+    @Test
+    public void testUnmarshallBadData_ExceptionThrown() {
+        byte[] badData = "I'm going to fail :)".getBytes();
+        try {
+            ResultPayloadUtils.unmarshall(badData, IntentPayload.CREATOR);
+            fail("unmarshall should throw exception");
+        } catch ( RuntimeException e) {
+            assertThat(e).isNotNull();
+        }
+    }
+
+    @Test
+    public void testMarshallIntentPayload_NonEmptyArray() {
+        byte[] marshalledPayload = ResultPayloadUtils.marshall(payload);
+        assertThat(marshalledPayload).isNotNull();
+        assertThat(marshalledPayload).isNotEmpty();
+    }
+
+    @Test
+    public void testUnmarshall_PreservedData() {
+        byte[] marshalledPayload = ResultPayloadUtils.marshall(payload);
+        IntentPayload newPayload = ResultPayloadUtils.unmarshall(marshalledPayload,
+                IntentPayload.CREATOR);
+
+        String originalIntentExtra = payload.intent.getStringExtra(EXTRA_KEY);
+        String copiedIntentExtra = newPayload.intent.getStringExtra(EXTRA_KEY);
+        assertThat(originalIntentExtra).isEqualTo(copiedIntentExtra);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultBuilderTest.java b/tests/robotests/src/com/android/settings/search/SearchResultBuilderTest.java
index 5649a95..f83b595 100644
--- a/tests/robotests/src/com/android/settings/search/SearchResultBuilderTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchResultBuilderTest.java
@@ -100,24 +100,18 @@
     }
 
     @Test
-    public void testNoRank_BuildSearchResultException() {
+    public void testNoRank_BuildSearchResult_pass() {
         mBuilder.addTitle(mTitle)
                 .addSummary(mSummary)
                 .addBreadcrumbs(mBreadcrumbs)
                 .addIcon(mIcon)
                 .addPayload(mResultPayload);
 
-        SearchResult result = null;
-        try {
-            result = mBuilder.build();
-        } catch (IllegalArgumentException e) {
-            // passes.
-        }
-        assertThat(result).isNull();
+        assertThat(mBuilder.build()).isNotNull();
     }
 
     @Test
-    public void testNoIcon_BuildSearchResultException() {
+    public void testNoIcon_BuildSearchResult_pass() {
         mBuilder.addTitle(mTitle)
                 .addSummary(mSummary)
                 .addRank(mRank)
diff --git a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
new file mode 100644
index 0000000..d0c691c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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.search;
+
+import android.content.res.XmlResourceParser;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.R;
+
+import com.android.settings.search2.XmlParserUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.xmlpull.v1.XmlPullParser;
+
+import static com.google.common.truth.Truth.assertThat;
+
+/**
+ * These tests use a series of preferences that have specific attributes which are sometimes
+ * uncommon (such as summaryOn).
+ *
+ * If changing a preference file breaks a test in this test file, please replace its reference
+ * with another preference with a matchin replacement attribute.
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class XmlParserUtilTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = ShadowApplication.getInstance().getApplicationContext();
+    }
+
+    @Test
+    public void testDataTitleValid_ReturnsPreferenceTitle() {
+        XmlResourceParser parser = getChildByType(R.xml.gesture_settings,
+                "com.android.settings.gestures.GesturePreference");
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String title = XmlParserUtils.getDataTitle(mContext, attrs);
+        String expTitle = mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
+        assertThat(title).isEqualTo(expTitle);
+    }
+
+    @Test
+    public void testDataKeywordsValid_ReturnsPreferenceKeywords() {
+        XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings);
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String keywords = XmlParserUtils.getDataKeywords(mContext, attrs);
+        String expKeywords = mContext.getString(R.string.keywords_gesture);
+        assertThat(keywords).isEqualTo(expKeywords);
+    }
+
+    @Test
+    public void testDataKeyValid_ReturnsPreferenceKey() {
+        XmlResourceParser parser = getChildByType(R.xml.gesture_settings,
+                "com.android.settings.gestures.GesturePreference");
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String key = XmlParserUtils.getDataKey(mContext, attrs);
+        String expKey = "gesture_swipe_down_fingerprint";
+        assertThat(key).isEqualTo(expKey);
+    }
+
+    @Test
+    public void testDataSummaryValid_ReturnsPreferenceSummary() {
+        XmlResourceParser parser = getChildByType(R.xml.gesture_settings,
+                "com.android.settings.gestures.GesturePreference");
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String summary = XmlParserUtils.getDataSummary(mContext, attrs);
+        String expSummary = mContext.getString(R.string.fingerprint_swipe_for_notifications_summary);
+        assertThat(summary).isEqualTo(expSummary);
+
+    }
+
+    @Test
+    public void testDataSummaryOnValid_ReturnsPreferenceSummaryOn() {
+        XmlResourceParser parser = getChildByType(R.xml.application_settings, "CheckBoxPreference");
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String summary = XmlParserUtils.getDataSummaryOn(mContext, attrs);
+        String expSummary = mContext.getString(R.string.advanced_settings_summary);
+        assertThat(summary).isEqualTo(expSummary);
+    }
+
+    @Test
+    public void testDataSummaryOffValid_ReturnsPreferenceSummaryOff() {
+        XmlResourceParser parser = getChildByType(R.xml.application_settings, "CheckBoxPreference");
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String summary = XmlParserUtils.getDataSummaryOff(mContext, attrs);
+        String expSummary = mContext.getString(R.string.advanced_settings_summary);
+        assertThat(summary).isEqualTo(expSummary);
+    }
+
+    @Test
+    public void testDataEntriesValid_ReturnsPreferenceEntries() {
+        XmlResourceParser parser = getChildByType(R.xml.application_settings, "ListPreference");
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String entries = XmlParserUtils.getDataEntries(mContext, attrs);
+        String[] expEntries = mContext.getResources()
+                .getStringArray(R.array.app_install_location_entries);
+        for (int i = 0; i < expEntries.length; i++) {
+            assertThat(entries).contains(expEntries[i]);
+        }
+    }
+
+    // Null checks
+
+    @Test
+    public void testDataKeyInvalid_ReturnsNull() {
+        XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings);
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String key = XmlParserUtils.getDataKey(mContext, attrs);
+        assertThat(key).isNull();
+    }
+
+    @Test
+    public void testDataSummaryInvalid_ReturnsNull() {
+        XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings);
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String summary = XmlParserUtils.getDataSummary(mContext, attrs);
+        assertThat(summary).isNull();
+    }
+
+    @Test
+    public void testDataSummaryOffInvalid_ReturnsNull() {
+        XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings);
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String summaryOff = XmlParserUtils.getDataSummaryOff(mContext, attrs);
+        assertThat(summaryOff).isNull();
+    }
+
+    @Test
+    public void testDataEntriesInvalid_ReturnsNull() {
+        XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings);
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        String entries = XmlParserUtils.getDataEntries(mContext, attrs);
+        assertThat(entries).isNull();
+    }
+
+    /**
+     * @param resId the ID for the XML preference
+     * @return an XML resource parser that points to the start tag
+     */
+    private XmlResourceParser getParentPrimedParser(int resId) {
+        XmlResourceParser parser = null;
+        try {
+            parser = mContext.getResources().getXml(resId);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+        } catch (Exception e) {
+
+        }
+        return parser;
+    }
+
+    private XmlResourceParser getChildByType(int resId, String xmlType) {
+        XmlResourceParser parser = null;
+        try {
+            parser = mContext.getResources().getXml(resId);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+            while(parser.getName() != xmlType) {
+                parser.next();
+            }
+        } catch (Exception e) {
+
+        }
+        return parser;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
new file mode 100644
index 0000000..20c50ce
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
@@ -0,0 +1,143 @@
+package com.android.settings.survey;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.overlay.SurveyFeatureProvider;
+import com.android.settings.testutils.FakeFeatureFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SurveyMixinTest {
+
+    private static final String FAKE_KEY = "fake_key";
+    private static final String FAKE_SURVEY_ID = "fake_id";
+
+    private FakeFeatureFactory mFactory;
+    private Context mContext;
+    private SurveyFeatureProvider mProvider;
+    @Mock
+    private BroadcastReceiver mReceiver;
+    @Mock
+    private InstrumentedPreferenceFragment mFragment;
+
+    @Before
+    public void setUp() {
+        // set up the fakefeature factory to mock out the survey provider
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application.getApplicationContext());
+        FakeFeatureFactory.setupForTest(mContext);
+        mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+        mProvider = mFactory.getSurveyFeatureProvider(mContext);
+        when(mProvider.getSurveyId(any(), eq(FAKE_KEY))).thenReturn(FAKE_SURVEY_ID);
+    }
+
+    @Test
+    public void onResume_triesRegisteringReceiverAndDownloadingWhenNoSurveyDetected() {
+        // Pretend there is no survey in memory
+        when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L);
+
+        // Pretend we are an activity that is starting up
+        Activity temp = Robolectric.setupActivity(Activity.class);
+        when(mFragment.getActivity()).thenReturn(temp);
+        SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY);
+        mixin.onResume();
+
+        // Verify that a download was attempted
+        verify(mProvider, times(1)).downloadSurvey(any(), any(), any());
+        // Verify that we registered a receiver for download completion broadcasts
+        verify(mProvider, times(1)).createAndRegisterReceiver(any());
+        // Verify we did not try to show a survey
+        verify(mProvider, never()).showSurveyIfAvailable(any(), any());
+    }
+
+    @Test
+    public void onResume_triesShowingSurveyWhenOneIsPresent() {
+        // Pretend there is a survey in memory
+        when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(0L);
+
+        // Pretend we are an activity that is starting up
+        Activity temp = Robolectric.setupActivity(Activity.class);
+        when(mFragment.getActivity()).thenReturn(temp);
+        SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY);
+        mixin.onResume();
+
+        // Verify that a download was not attempted
+        verify(mProvider, never()).downloadSurvey(any(), any(), any());
+        // Verify that we did not register a receiver
+        verify(mProvider, never()).createAndRegisterReceiver(any());
+        // Verify we tried to show a survey
+        verify(mProvider, times(1)).showSurveyIfAvailable(any(), any());
+    }
+
+    @Test
+    public void onResume_doesNothingWhenActivityIsNull() {
+        // Pretend the activity died somewhere in the process
+        when(mFragment.getActivity()).thenReturn(null);
+        SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY);
+        mixin.onResume();
+
+        // Verify we don't try showing or downloading a survey
+        verify(mProvider, never()).showSurveyIfAvailable(any(), any());
+        verify(mProvider, never()).downloadSurvey(any(), any(), any());
+    }
+
+    @Test
+    public void onPause_removesReceiverWhenInstantiated() {
+        // Pretend there is a survey in memory
+        when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L);
+
+        // Pretend we are an activity that starts and stops
+        Activity temp = Robolectric.setupActivity(Activity.class);
+        when(mFragment.getActivity()).thenReturn(temp);
+        when(mProvider.createAndRegisterReceiver(any())).thenReturn(mReceiver);
+        SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY);
+        mixin.onResume();
+        mixin.onPause();
+
+        // Verify we remove the receiver
+        verify(mProvider, times(1)).unregisterReceiver(any(), any());
+    }
+
+    @Test
+    public void onPause_doesNothingWhenActivityOrReceiverNull() {
+        // Pretend there is a survey in memory
+        when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L);
+
+        // Pretend we are an activity that fails to create a receiver properly
+        Activity temp = Robolectric.setupActivity(Activity.class);
+        when(mFragment.getActivity()).thenReturn(temp);
+        SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY);
+        mixin.onPause();
+
+        // Verify we do nothing
+        verify(mProvider, never()).unregisterReceiver(any(), any());
+
+        // pretend the activity died before onPause
+        when(mFragment.getActivity()).thenReturn(null);
+        mixin.onPause();
+
+        // Verify we do nothing
+        verify(mProvider, never()).unregisterReceiver(any(), any());
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/widget/FooterPreferenceMixinTest.java b/tests/robotests/src/com/android/settings/widget/FooterPreferenceMixinTest.java
new file mode 100644
index 0000000..21ebb25
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/FooterPreferenceMixinTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.widget;
+
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+
+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.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+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)
+public class FooterPreferenceMixinTest {
+
+    @Mock
+    private PreferenceFragment mFragment;
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private Lifecycle mLifecycle;
+    private FooterPreferenceMixin mMixin;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLifecycle = new Lifecycle();
+        when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
+        when(mFragment.getPreferenceManager().getContext())
+                .thenReturn(ShadowApplication.getInstance().getApplicationContext());
+        mMixin = new FooterPreferenceMixin(mFragment, mLifecycle);
+    }
+
+    @Test
+    public void createFooter_screenNotAvailable_noCrash() {
+        assertThat(mMixin.createFooterPreference()).isNotNull();
+    }
+
+    @Test
+    public void createFooter_screenAvailable_canAttachToScreen() {
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+        final FooterPreference preference = mMixin.createFooterPreference();
+
+        assertThat(preference).isNotNull();
+        verify(mScreen).addPreference(preference);
+    }
+
+    @Test
+    public void createFooter_screenAvailableDelayed_canAttachToScreen() {
+        final FooterPreference preference = mMixin.createFooterPreference();
+
+        mLifecycle.setPreferenceScreen(mScreen);
+
+        assertThat(preference).isNotNull();
+        verify(mScreen).addPreference(preference);
+    }
+
+    @Test
+    public void createFooterTwice_screenAvailable_replaceOldFooter() {
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+        mMixin.createFooterPreference();
+        mMixin.createFooterPreference();
+
+        verify(mScreen).removePreference(any(FooterPreference.class));
+        verify(mScreen, times(2)).addPreference(any(FooterPreference.class));
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/widget/FooterPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/FooterPreferenceTest.java
new file mode 100644
index 0000000..8dbcc20
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/FooterPreferenceTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class FooterPreferenceTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = ShadowApplication.getInstance().getApplicationContext();
+    }
+
+    @Test
+    public void createNewPreference_shouldSetKeyAndOrder() {
+        final FooterPreference preference = new FooterPreference(mContext);
+
+        assertThat(preference.getKey()).isEqualTo(FooterPreference.KEY_FOOTER);
+        assertThat(preference.getOrder()).isEqualTo(FooterPreference.ORDER_FOOTER);
+    }
+
+    @Test
+    public void bindPreference_shouldLinkifyContent() {
+        final FooterPreference preference = new FooterPreference(mContext);
+        final PreferenceViewHolder holder = new PreferenceViewHolder(
+                LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null));
+
+        preference.onBindViewHolder(holder);
+        assertThat(((TextView) holder.findViewById(android.R.id.title)).getMovementMethod())
+                .isInstanceOf(LinkMovementMethod.class);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/widget/PreferenceDividerDecorationTest.java b/tests/robotests/src/com/android/settings/widget/PreferenceDividerDecorationTest.java
new file mode 100644
index 0000000..f69de54
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/PreferenceDividerDecorationTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceGroupAdapter;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+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.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.never;
+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)
+public class PreferenceDividerDecorationTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private Drawable mDrawable;
+    @Mock
+    private Canvas mCanvas;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private RecyclerView mRecyclerView;
+    @Mock
+    private PreferenceGroupAdapter mAdapter;
+    @Mock
+    private PreferenceCategory mPrefCategory;
+    @Mock
+    private Preference mNormalPref;
+    @Mock
+    private FooterPreference mFooterPref;
+    private PreferenceDividerDecoration mDecoration;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mDecoration = new PreferenceDividerDecoration();
+        mDecoration.setDivider(mDrawable);
+        mDecoration.setDividerHeight(3);
+    }
+
+    @Test
+    public void drawOver_footerPreference_shouldDrawDivider() {
+        when(mRecyclerView.getAdapter()).thenReturn(mAdapter);
+        when(mRecyclerView.getChildCount()).thenReturn(1);
+        when(mAdapter.getItem(anyInt())).thenReturn(mFooterPref);
+
+        mDecoration.onDrawOver(mCanvas, mRecyclerView, null /* state */);
+
+        verify(mDrawable).draw(mCanvas);
+    }
+
+    @Test
+    public void drawOver_preferenceCategory_shouldSkipFirst() {
+        when(mRecyclerView.getAdapter()).thenReturn(mAdapter);
+        when(mRecyclerView.getChildCount()).thenReturn(3);
+        when(mAdapter.getItem(anyInt())).thenReturn(mPrefCategory);
+        when(mRecyclerView.getChildAdapterPosition(any(View.class)))
+                .thenReturn(0)
+                .thenReturn(1)
+                .thenReturn(2);
+
+        mDecoration.onDrawOver(mCanvas, mRecyclerView, null /* state */);
+
+        // 3 prefCategory, should skip first draw
+        verify(mDrawable, times(2)).draw(mCanvas);
+    }
+
+    @Test
+    public void drawOver_preference_doNotDrawDivider() {
+        when(mRecyclerView.getAdapter()).thenReturn(mAdapter);
+        when(mRecyclerView.getChildCount()).thenReturn(1);
+        when(mAdapter.getItem(anyInt())).thenReturn(mNormalPref);
+
+        mDecoration.onDrawOver(mCanvas, mRecyclerView, null /* state */);
+
+        verify(mDrawable, never()).draw(mCanvas);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/widget/RingProgressBarTest.java b/tests/robotests/src/com/android/settings/widget/RingProgressBarTest.java
index b7a5fd9..1a7161c 100644
--- a/tests/robotests/src/com/android/settings/widget/RingProgressBarTest.java
+++ b/tests/robotests/src/com/android/settings/widget/RingProgressBarTest.java
@@ -16,21 +16,21 @@
 
 package com.android.settings.widget;
 
-import static junit.framework.Assert.assertEquals;
-
 import android.content.Context;
 import android.view.View.MeasureSpec;
 
+import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-@RunWith(RobolectricTestRunner.class)
+import static junit.framework.Assert.assertEquals;
+
+@RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class RingProgressBarTest {
 
diff --git a/tests/unit/src/com/android/settings/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/unit/src/com/android/settings/fingerprint/FingerprintEnrollIntroductionTest.java
new file mode 100644
index 0000000..235ba1e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/fingerprint/FingerprintEnrollIntroductionTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.fingerprint;
+
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.test.ActivityUnitTestCase;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FingerprintEnrollIntroductionTest
+        extends ActivityUnitTestCase<FingerprintEnrollIntroduction> {
+
+    private TestContext mContext;
+
+    @Mock
+    private FingerprintManager mFingerprintManager;
+
+    private FingerprintEnrollIntroduction mActivity;
+
+    public FingerprintEnrollIntroductionTest() {
+        super(FingerprintEnrollIntroduction.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mContext = new TestContext(getInstrumentation().getTargetContext());
+        setActivityContext(mContext);
+
+        getInstrumentation().runOnMainSync(() -> {
+            final Intent intent = new Intent();
+            mActivity = startActivity(intent,
+                    null /* savedInstanceState */, null /* lastNonConfigurationInstance */);
+        });
+    }
+
+    public void testMaxFingerprint_shouldShowErrorMessage() {
+        final int max = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+        doReturn(generateFingerprintList(max)).when(mFingerprintManager)
+                .getEnrolledFingerprints(anyInt());
+
+        getInstrumentation().runOnMainSync(() -> {
+            getInstrumentation().callActivityOnCreate(mActivity, null);
+            getInstrumentation().callActivityOnResume(mActivity);
+        });
+
+        final TextView errorTextView = (TextView) mActivity.findViewById(R.id.error_text);
+        assertNotNull(errorTextView.getText().toString());
+
+        final Button nextButton = (Button) mActivity.findViewById(R.id.fingerprint_next_button);
+        assertEquals(View.GONE, nextButton.getVisibility());
+    }
+
+    private List<Fingerprint> generateFingerprintList(int num) {
+        ArrayList<Fingerprint> list = new ArrayList<>();
+        for (int i = 0; i < num; i++) {
+            list.add(new Fingerprint("Fingerprint " + i, 0, i, 0));
+        }
+        return list;
+    }
+
+    public class TestContext extends ContextWrapper {
+
+        public TestContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.FINGERPRINT_SERVICE.equals(name)) {
+                return mFingerprintManager;
+            }
+            return super.getSystemService(name);
+        }
+    }
+}