Merge "Prevent monkey from opening dev options"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 062ebdc..2268977 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -223,6 +223,10 @@
                   android:theme="@android:style/Theme.NoDisplay"
                   android:excludeFromRecents="true"
                   android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.settings.SEARCH_RESULT_TRAMPOLINE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
 
         <!-- Top-level settings -->
diff --git a/res/layout/dialog_hardware_info.xml b/res/layout/dialog_hardware_info.xml
index 9431961..7ea4783 100644
--- a/res/layout/dialog_hardware_info.xml
+++ b/res/layout/dialog_hardware_info.xml
@@ -26,46 +26,40 @@
         android:padding="24dp">
 
         <TextView
+            style="@style/device_info_dialog_label"
             android:id="@+id/model_label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="?android:attr/textColorSecondary"
             android:text="@string/model_info" />
         <TextView
+            style="@style/device_info_dialog_value"
             android:id="@+id/model_value"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingBottom="24dp"
-            android:textAppearance="@android:style/TextAppearance.Material.Body2" />
+            android:layout_height="wrap_content" />
 
         <TextView
+            style="@style/device_info_dialog_label"
             android:id="@+id/serial_number_label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="?android:attr/textColorSecondary"
             android:text="@string/status_serial_number" />
         <TextView
+            style="@style/device_info_dialog_value"
             android:id="@+id/serial_number_value"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingBottom="24dp"
-            android:textAppearance="@android:style/TextAppearance.Material.Body2" />
+            android:layout_height="wrap_content" />
 
         <TextView
+            style="@style/device_info_dialog_label"
             android:id="@+id/hardware_rev_label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="?android:attr/textColorSecondary"
             android:text="@string/hardware_revision" />
         <TextView
+            style="@style/device_info_dialog_value"
             android:id="@+id/hardware_rev_value"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingBottom="24dp"
-            android:textAppearance="@android:style/TextAppearance.Material.Body2" />
+            android:layout_height="wrap_content" />
 
     </LinearLayout>
 </ScrollView>
\ No newline at end of file
diff --git a/res/values-ar/arrays.xml b/res/values-ar/arrays.xml
index 69be316..311e5d4 100644
--- a/res/values-ar/arrays.xml
+++ b/res/values-ar/arrays.xml
@@ -217,7 +217,7 @@
     <item msgid="7471182818083460781">"IS95A"</item>
   </string-array>
   <string-array name="mvno_type_entries">
-    <item msgid="4367119357633573465">"None"</item>
+    <item msgid="4367119357633573465">"بدون"</item>
     <item msgid="6062567900587138000">"SPN"</item>
     <item msgid="2454085083342423481">"IMSI"</item>
     <item msgid="2681427309183221543">"GID"</item>
diff --git a/res/values/config.xml b/res/values/config.xml
index 4ba73cc..097350b 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -41,9 +41,6 @@
     <!-- Whether to show Connectivity Monitor switch in Developer Options -->
     <bool name="config_show_connectivity_monitor">false</bool>
 
-    <!-- Whether to show Camera HAL HDR+ switch in Developer Options -->
-    <bool name="config_show_camera_hal_hdrplus">false</bool>
-
     <!-- Whether to show Camera laser sensor switch in Developer Options -->
     <bool name="config_show_camera_laser_sensor">false</bool>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4582bc4..3b474ab 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1572,9 +1572,9 @@
     <!-- Bluetooth settings.  Message for disconnecting from the PAN profile (user role). [CHAR LIMIT=80] -->
     <string name="bluetooth_disconnect_pan_user_profile">Internet access via <xliff:g id="device_name">%1$s</xliff:g> will be disconnected.</string>
     <!-- Bluetooth settings.  Message for disconnecting from the PAN profile (NAP role). [CHAR LIMIT=80] -->
-    <string name="bluetooth_disconnect_pan_nap_profile" product="tablet"><xliff:g id="device_name">%1$s</xliff:g> will be disconnected from sharing this tablet\u2019s Internet connection.</string>
+    <string name="bluetooth_disconnect_pan_nap_profile" product="tablet"><xliff:g id="device_name">%1$s</xliff:g> will be disconnected from sharing this tablet\u2019s internet connection.</string>
     <!-- Bluetooth settings.  Message for disconnecting from the PAN profile (NAP role). [CHAR LIMIT=80] -->
-    <string name="bluetooth_disconnect_pan_nap_profile" product="default"><xliff:g id="device_name">%1$s</xliff:g> will be disconnected from sharing this phone\u2019s Internet connection.</string>
+    <string name="bluetooth_disconnect_pan_nap_profile" product="default"><xliff:g id="device_name">%1$s</xliff:g> will be disconnected from sharing this phone\u2019s internet connection.</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The title of the screen. [CHAR LIMIT=40] -->
     <string name="bluetooth_device_advanced_title">Paired Bluetooth device</string>
@@ -1590,9 +1590,9 @@
     <!-- Bluetooth settings. Connection options screen. Title for checkbox to enable incoming file transfers [CHAR LIMIT=30] -->
     <string name="bluetooth_device_advanced_enable_opp_title">Allow incoming file transfers</string>
     <!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (user role). [CHAR LIMIT=25]-->
-    <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for Internet access</string>
+    <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for internet access</string>
     <!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (NAP role). [CHAR LIMIT=25]-->
-    <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local Internet connection with device</string>
+    <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local internet connection with device</string>
 
     <!-- Bluetooth settings.  Dock Setting Title -->
     <string name="bluetooth_dock_settings">Dock Settings</string>
@@ -1726,9 +1726,9 @@
     <!-- Checkbox title for option to toggle poor network detection -->
     <string name="wifi_poor_network_detection">Avoid poor connections</string>
     <!-- Checkbox summary for option to toggle poor network detection -->
-    <string name="wifi_poor_network_detection_summary">Don\u2019t use a Wi\u2011Fi network unless it has a good Internet connection</string>
+    <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>
+    <string name="wifi_avoid_poor_network_detection_summary">Only use networks that have a good internet connection</string>
     <!-- Checkbox title for option to connect to open Wi-Fi automatically [CHAR LIMIT=40] -->
     <string name="use_open_wifi_automatically_title">Connect to open networks</string>
     <!-- Checkbox summary for option to connect to open Wi-Fi automatically  [CHAR LIMIT=100] -->
@@ -1770,11 +1770,11 @@
     <!-- Checkbox summary for option to toggle suspend power optimizations [CHAR LIMIT=30] -->
     <string name="wifi_limit_optimizations_summary">Limit battery used by Wi\u2011Fi</string>
     <!-- Checkbox title. Should we switch to using cellular data if Wi-Fi is still connected but the Wi-Fi network we're connected to no longer has Internet access (e.g., due to an outage)? -->
-    <string name="wifi_switch_away_when_unvalidated">Switch to mobile data if Wi\u2011Fi loses Internet access.</string>
+    <string name="wifi_switch_away_when_unvalidated">Switch to mobile data if Wi\u2011Fi loses internet access.</string>
     <!-- Preference title for option to automatically switch away from bad wifi networks [CHAR LIMIT=60]-->
     <string name="wifi_cellular_data_fallback_title">Switch to mobile data automatically</string>
     <!-- Preference summary to automatically switch away from bad wifi networks [CHAR LIMIT=None]-->
-    <string name="wifi_cellular_data_fallback_summary">Use mobile data when Wi\u2011Fi has no Internet access. Data usage may apply.</string>
+    <string name="wifi_cellular_data_fallback_summary">Use mobile data when Wi\u2011Fi has no internet access. Data usage charges may apply.</string>
     <!-- Action bar text message to manually add a wifi network [CHAR LIMIT=20]-->
     <string name="wifi_add_network">Add network</string>
     <!-- Action bar title to open additional Wi-Fi settings-->
@@ -1948,11 +1948,11 @@
     <string name="wifi_hotspot_connect">CONNECT</string>
 
     <!-- Dialog text to tell the user that the selected network does not have Internet access. -->
-    <string name="no_internet_access_text">This network has no Internet access. Stay connected?</string>
+    <string name="no_internet_access_text">This network has no internet access. Stay connected?</string>
     <string name="no_internet_access_remember">Don\u2019t ask again for this network</string>
 
     <!-- Dialog text to tell the user that the selected network has lost Internet access, and asking the user whether they want to avoid this network. -->
-    <string name="lost_internet_access_title">Wi\u2011Fi is not connected to the Internet</string>
+    <string name="lost_internet_access_title">Wi\u2011Fi is not connected to the internet</string>
     <string name="lost_internet_access_text">You can switch to the mobile network whenever Wi\u2011Fi has a bad connection. Data usage charges may apply.</string>
     <!-- Button text to let user switch to mobile data -->
     <string name="lost_internet_access_switch">Switch to mobile</string>
@@ -1994,7 +1994,7 @@
 
     <!-- Wifi Network Details -->
     <!-- Wifi details title-->
-    <string name="wifi_details_title">Network info</string>
+    <string name="wifi_details_title">Network details</string>
     <!-- Wifi details preference title to display router IP subnet mask -->
     <string name="wifi_details_subnet_mask">Subnet mask</string>
     <!-- Wifi details preference title to display router DNS info -->
@@ -2065,13 +2065,13 @@
     <!-- Label for Wifi tether checkbox. Toggles Access Point on/off  [CHAR LIMIT=30] -->
     <string name="wifi_hotspot_checkbox_text">Wi\u2011Fi hotspot</string>
     <!-- Summary text when turning hotspot off -->
-    <string name="wifi_hotspot_off_subtext">Not sharing Internet or content with other devices</string>
+    <string name="wifi_hotspot_off_subtext">Not sharing internet or content with other devices</string>
     <!-- Summary text when tethering is on -->
-    <string name="wifi_hotspot_tethering_on_subtext" product="tablet">Sharing this tablet\u2019s Internet connection via hotspot</string>
+    <string name="wifi_hotspot_tethering_on_subtext" product="tablet">Sharing this tablet\u2019s internet connection via hotspot</string>
     <!-- Summary text when tethering is on -->
-    <string name="wifi_hotspot_tethering_on_subtext" product="default">Sharing this phone\u2019s Internet connection via hotspot</string>
+    <string name="wifi_hotspot_tethering_on_subtext" product="default">Sharing this phone\u2019s internet connection via hotspot</string>
     <!-- Summary text when hotspot is on for local-only -->
-    <string name="wifi_hotspot_on_local_only_subtext">App is sharing content. To share Internet connection, turn hotspot off, then on</string>
+    <string name="wifi_hotspot_on_local_only_subtext">App is sharing content. To share internet connection, turn hotspot off, then on</string>
 
     <!-- Wifi hotspot settings -->
     <!-- Label for Wifi hotspot name. -->
@@ -2085,7 +2085,7 @@
     <!-- Label for Wifi hotspot AP Band. -->
     <string name="wifi_hotspot_ap_band_title">AP Band</string>
     <!-- Wifi hotspot footer info for regular hotspot [CHAR LIMIT=NONE]-->
-    <string name="wifi_hotspot_footer_info_regular">Use hotspot to create a Wi\u2011Fi network for your other devices. Hotspot provides Internet using your mobile data connection. Additional mobile data charges may apply.</string>
+    <string name="wifi_hotspot_footer_info_regular">Use hotspot to create a Wi\u2011Fi network for your other devices. Hotspot provides internet using your mobile data connection. Additional mobile data charges may apply.</string>
     <!-- Wifi hotspot footer info [CHAR LIMIT=NONE]-->
     <string name="wifi_hotspot_footer_info_local_only">Apps can create a hotspot to share content with nearby devices.</string>
 
@@ -2949,7 +2949,7 @@
     <string name="storage_detail_explore">Explore <xliff:g id="name" example="SD card">^1</xliff:g></string>
 
     <!-- Body of dialog informing user about other files on a storage device [CHAR LIMIT=NONE]-->
-    <string name="storage_detail_dialog_other">Other includes shared files saved by apps, files downloaded from the Internet or Bluetooth, Android files, and so on.
+    <string name="storage_detail_dialog_other">Other includes shared files saved by apps, files downloaded from the internet or Bluetooth, Android files, and so on.
 \n\nTo see the visible contents of this <xliff:g id="name" example="SD card">^1</xliff:g>, tap Explore.</string>
 
     <!-- Body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
@@ -3252,25 +3252,25 @@
     <!-- USB Tethering options -->
     <string name="usb_title">USB</string>
     <string name="usb_tethering_button_text">USB tethering</string>
-    <string name="usb_tethering_subtext" product="default">Share phone\u2019s Internet connection via USB</string>
-    <string name="usb_tethering_subtext" product="tablet">Share tablet\u2019s Internet connection via USB</string>
+    <string name="usb_tethering_subtext" product="default">Share phone\u2019s internet connection via USB</string>
+    <string name="usb_tethering_subtext" product="tablet">Share tablet\u2019s internet connection via USB</string>
 
     <!-- Bluetooth Tethering settings-->
     <!-- Label for bluetooth tether checkbox [CHAR LIMIT=25]-->
     <string name="bluetooth_tether_checkbox_text">Bluetooth tethering</string>
     <!-- Bluetooth Tethering subtext [CHAR LIMIT=70]-->
-    <string name="bluetooth_tethering_subtext" product="tablet">Share tablet\u2019s Internet connection via Bluetooth</string>
+    <string name="bluetooth_tethering_subtext" product="tablet">Share tablet\u2019s internet connection via Bluetooth</string>
     <!-- Bluetooth Tethering subtext [CHAR LIMIT=70]-->
-    <string name="bluetooth_tethering_subtext" product="default">Share phone\u2019s Internet connection via Bluetooth</string>
+    <string name="bluetooth_tethering_subtext" product="default">Share phone\u2019s internet connection via Bluetooth</string>
     <!-- Bluetooth tethering off subtext - shown when Bluetooth Tethering is turned off [CHAR LIMIT=80]-->
-    <string name="bluetooth_tethering_off_subtext_config">Sharing this <xliff:g id="device_name">%1$d</xliff:g>\u2019s Internet connection via Bluetooth</string>
+    <string name="bluetooth_tethering_off_subtext_config">Sharing this <xliff:g id="device_name">%1$d</xliff:g>\u2019s internet connection via Bluetooth</string>
     <!-- Bluetooth Tethering settings. Error message shown when trying to connect an 8th device [CHAR LIMIT=50]-->
     <string name="bluetooth_tethering_overflow_error">Can\u2019t tether to more than <xliff:g id="maxConnection">%1$d</xliff:g> devices.</string>
     <!-- Bluetooth Tethering settings.  Message for untethering from a bluetooth device [CHAR LIMIT=50]-->
     <string name="bluetooth_untether_blank"><xliff:g id="device_name">%1$s</xliff:g> will be untethered.</string>
 
     <!-- Tethering footer info [CHAR LIMIT=NONE]-->
-    <string name="tethering_footer_info">Use hotspot and tethering to provide Internet to other devices through your mobile data connection. Apps can also create a hotspot to share content with nearby devices.</string>
+    <string name="tethering_footer_info">Use hotspot and tethering to provide internet to other devices through your mobile data connection. Apps can also create a hotspot to share content with nearby devices.</string>
 
     <!-- Tethering help button - calls up a web view with general tethering info -->
     <string name="tethering_help_button_text">Help</string>
@@ -3442,7 +3442,7 @@
     <!-- About phone settings, Safety Legal information setting option name and title of dialog box holding safety legal info -->
     <string name="settings_safetylegal_activity_title">Safety information</string>
     <!-- About phone settings screen, Safety legal dialog message when data network is not connected -->
-    <string name="settings_safetylegal_activity_unreachable">You don\u2019t have a data connection. To view this information now, go to %s from any computer connected to the Internet.</string>
+    <string name="settings_safetylegal_activity_unreachable">You don\u2019t have a data connection. To view this information now, go to %s from any computer connected to the internet.</string>
     <!-- About phone settings screen, Safety Legal dialog title until the link is fully loaded -->
     <string name="settings_safetylegal_activity_loading">Loading\u2026</string>
 
@@ -4057,6 +4057,8 @@
 
     <!-- Title of setting on main settings screen.  This item will take the user to the screen to tweak settings realted to locale and text -->
     <string name="language_settings">Languages&#160;&amp; input</string>
+    <!-- Text displayed when user has restriction DISALLOW_CONFIG_LOCALE [CHAR LIMIT=NONE]-->
+    <string name="language_empty_list_user_restricted">You don\u2019t have permission to change the device language.</string>
     <!-- Title of Languages & input settings screen -->
     <string name="language_keyboard_settings_title">Languages&#160;&amp; input</string>
     <!-- Title of preference category that lists all settings about helping user text input such as spell checker [CHAR LIMIT=60]-->
@@ -5861,9 +5863,9 @@
     <!-- Dialog message title to set always-on VPN when another app was not already set. -->
     <string name="vpn_set_vpn_title">Set always-on VPN?</string>
     <!-- Dialog message body to explain that always-on VPN will disable network traffic while the VPN is connecting. -->
-    <string name="vpn_first_always_on_vpn_message">By turning on this setting, you won\'t have an Internet connection until the VPN successfully connects</string>
+    <string name="vpn_first_always_on_vpn_message">When this setting is on, you won\'t have an internet connection until the VPN successfully connects</string>
     <!-- Dialog message body to explain that always-on VPN will disable network traffic while the VPN is connecting, and that this will replace the current VPN. -->
-    <string name="vpn_replace_always_on_vpn_enable_message">Your existing VPN will be replaced, and you won\'t have an Internet connection until the VPN successfully connects</string>
+    <string name="vpn_replace_always_on_vpn_enable_message">Your existing VPN will be replaced, and you won\'t have an internet connection until the VPN successfully connects</string>
     <!-- Dialog message body to connect a VPN app, replacing another VPN app that is already always-on [CHAR LIMIT=NONE] -->
     <string name="vpn_replace_always_on_vpn_disable_message">You\'re already connected to an always-on VPN. If you connect to a different one, your existing VPN will be replaced, and always-on mode will turn off.</string>
     <!-- Dialog message body to set another VPN app to be always-on [CHAR LIMIT=NONE] -->
@@ -8189,7 +8191,7 @@
     <string name="condition_airplane_title">Airplane mode is on</string>
 
     <!-- Summary of condition that airplane mode is on [CHAR LIMIT=NONE] -->
-    <string name="condition_airplane_summary">Wi-Fi, Bluetooth, and mobile network are turned off. You can\'t make phone calls or connect to the Internet.</string>
+    <string name="condition_airplane_summary">Wi-Fi, Bluetooth, and mobile network are turned off. You can\'t make phone calls or connect to the internet.</string>
 
     <!-- Title of condition that do not disturb is on [CHAR LIMIT=30] -->
     <string name="condition_zen_title">Do not disturb is on (<xliff:g name="zen_mode_type" example="Alarms only">%1$s</xliff:g>)</string>
@@ -8275,12 +8277,6 @@
     <!-- Toast message letting the user know the how to apply connectivity monitor change -->
     <string name="connectivity_monitor_toast">To apply connectivity monitor change, reboot device</string>
 
-    <!-- Title for Camera HAL HDR+ switch [CHAR LIMIT=50] -->
-    <string name="camera_hal_hdrplus_switch">Camera HAL HDR+</string>
-
-    <!-- Toast message letting the user know how to enable Camera HAL HDR+ -->
-    <string name="camera_hal_hdrplus_toast">To apply Camera HAL HDR+ change, reboot device</string>
-
     <!-- Title for Camera laser sensor switch [CHAR LIMIT=NONE] -->
     <string name="camera_laser_sensor_switch">Camera Laser Sensor</string>
 
@@ -8794,9 +8790,9 @@
     <!-- setting enable OEM unlock Checkbox's summary to explain this Checkbox is disabled because the bootloader has been unlocked [CHAR_LIMIT=60] -->
     <string name="oem_unlock_enable_disabled_summary_bootloader_unlocked">Bootloader is already unlocked</string>
     <!-- setting enable OEM unlock Checkbox's summary to explain this Checkbox is disabled because there is no connectivity. [CHAR_LIMIT=60] -->
-    <string name="oem_unlock_enable_disabled_summary_connectivity">Connect to the Internet first</string>
+    <string name="oem_unlock_enable_disabled_summary_connectivity">Connect to the internet first</string>
     <!-- setting enable OEM unlock Checkbox's summary to explain this Checkbox is disabled because there is no connectivity or the device is locked by the carrier [CHAR_LIMIT=60] -->
-    <string name="oem_unlock_enable_disabled_summary_connectivity_or_locked">Connect to the Internet or contact your carrier</string>
+    <string name="oem_unlock_enable_disabled_summary_connectivity_or_locked">Connect to the internet or contact your carrier</string>
     <!-- setting enable OEM unlock Checkbox's summary to explain this Checkbox is disabled because this setting is unavailable on sim-locked devices. [CHAR_LIMIT=60] -->
     <string name="oem_unlock_enable_disabled_summary_sim_locked_device">Unavailable on carrier-locked devices</string>
     <!-- Information displayed after user locks OEM lock [Char Limit=None]-->
diff --git a/res/xml/app_default_settings.xml b/res/xml/app_default_settings.xml
index 0204c64..4d5c9d7 100644
--- a/res/xml/app_default_settings.xml
+++ b/res/xml/app_default_settings.xml
@@ -33,7 +33,7 @@
         <extra android:name="for_work" android:value="false" />
     </com.android.settings.widget.AppPreference>
 
-    <com.android.settings.widget.AppPreference
+    <com.android.settings.widget.GearPreference
         android:key="default_home"
         android:title="@string/home_app"
         android:fragment="com.android.settings.applications.defaultapps.DefaultHomePicker"
diff --git a/res/xml/app_notification_settings.xml b/res/xml/app_notification_settings.xml
new file mode 100644
index 0000000..00a9142
--- /dev/null
+++ b/res/xml/app_notification_settings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="pref_app_header"
+        android:layout="@layout/settings_entity_header" />
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="block"
+        android:layout="@layout/styled_switch_bar" />
+
+    <!-- Show badge -->
+    <com.android.settingslib.RestrictedSwitchPreference
+        android:key="badge"
+        android:title="@string/notification_badge_title"
+        settings:useAdditionalSummary="true"
+        settings:restrictedSwitchSummary="@string/enabled_by_admin" />
+
+    <!-- Channels/Channel groups added here -->
+
+    <Preference
+        android:key="app_link"
+        android:title="@string/app_settings_link"
+        android:order="500"
+        settings:allowDividerAbove="true"/>
+
+    <com.android.settings.notification.NotificationFooterPreference
+        android:key="block_desc"
+        android:order="1000" />
+
+    <com.android.settings.notification.NotificationFooterPreference
+        android:key="desc"
+        android:order="5000" />
+
+    <com.android.settings.notification.NotificationFooterPreference
+        android:key="deleted"
+        android:order="8000" />
+
+</PreferenceScreen>
diff --git a/res/xml/channel_notification_settings.xml b/res/xml/channel_notification_settings.xml
new file mode 100644
index 0000000..aaadce4
--- /dev/null
+++ b/res/xml/channel_notification_settings.xml
@@ -0,0 +1,90 @@
+<?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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" >
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="pref_app_header"
+        android:layout="@layout/settings_entity_header" />
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="block"
+        android:layout="@layout/styled_switch_bar" />
+
+    <!-- Importance -->
+    <Preference
+        android:key="importance"
+        android:title="@string/notification_importance_title" />
+
+    <!-- Importance toggle -->
+    <com.android.settingslib.RestrictedSwitchPreference
+        android:key="allow_sound"
+        android:title="@string/allow_interruption"
+        android:summary="@string/allow_interruption_summary" />
+
+    <!-- Default ringtone -->
+    <com.android.settings.notification.NotificationSoundPreference
+        android:key="ringtone"
+        android:title="@string/notification_channel_sound_title"
+        android:dialogTitle="@string/notification_channel_sound_title"
+        android:showSilent="true"
+        android:showDefault="true"
+        android:ringtoneType="notification" />
+
+    <!-- Vibration -->
+    <com.android.settingslib.RestrictedSwitchPreference
+        android:key="vibrate"
+        android:title="@string/notification_vibrate_title"
+        settings:useAdditionalSummary="true" />
+
+    <!-- Visibility Override -->
+    <com.android.settings.notification.RestrictedDropDownPreference
+        android:key="visibility_override"
+        android:title="@string/app_notification_visibility_override_title"/>
+
+    <!-- Lights -->
+    <com.android.settingslib.RestrictedSwitchPreference
+        android:key="lights"
+        android:title="@string/notification_show_lights_title"
+        settings:useAdditionalSummary="true"/>
+
+    <!-- Show badge -->
+    <com.android.settingslib.RestrictedSwitchPreference
+        android:key="badge"
+        android:title="@string/notification_channel_badge_title"
+        settings:useAdditionalSummary="true"
+        settings:restrictedSwitchSummary="@string/enabled_by_admin"/>
+
+    <!-- Bypass DND -->
+    <com.android.settingslib.RestrictedSwitchPreference
+        android:key="bypass_dnd"
+        android:title="@string/app_notification_override_dnd_title"
+        android:summary="@string/app_notification_override_dnd_summary"
+        settings:useAdditionalSummary="true"/>
+
+    <Preference
+        android:key="app_link"
+        android:title="@string/app_settings_link"
+        settings:allowDividerAbove="true"/>
+
+    <com.android.settings.notification.NotificationFooterPreference
+        android:key="desc" />
+
+    <com.android.settings.notification.NotificationFooterPreference
+        android:key="block_desc" />
+
+</PreferenceScreen>
diff --git a/res/xml/development_prefs.xml b/res/xml/development_prefs.xml
index c8c57a5..669f4fb 100644
--- a/res/xml/development_prefs.xml
+++ b/res/xml/development_prefs.xml
@@ -172,10 +172,6 @@
             android:key="camera_laser_sensor_switch"
             android:title="@string/camera_laser_sensor_switch" />
 
-        <SwitchPreference
-            android:key="camera_hal_hdrplus_switch"
-            android:title="@string/camera_hal_hdrplus_switch" />
-
         <Preference
             android:key="feature_flags_dashboard"
             android:title="@string/feature_flags_dashboard_title"
diff --git a/res/xml/legacy_channel_notification_settings.xml b/res/xml/legacy_channel_notification_settings.xml
deleted file mode 100644
index 519bf5d..0000000
--- a/res/xml/legacy_channel_notification_settings.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" >
-
-    <!-- Show badge -->
-    <com.android.settingslib.RestrictedSwitchPreference
-        android:key="badge"
-        android:title="@string/notification_badge_title"
-        settings:useAdditionalSummary="true"
-        settings:restrictedSwitchSummary="@string/enabled_by_admin" />
-
-    <!-- Importance toggle -->
-    <com.android.settingslib.RestrictedSwitchPreference
-        android:key="allow_sound"
-        android:title="@string/allow_interruption"
-	android:summary="@string/allow_interruption_summary"/>
-
-    <!-- Visibility Override -->
-    <com.android.settings.notification.RestrictedDropDownPreference
-        android:key="visibility_override"
-        android:title="@string/app_notification_visibility_override_title" />
-
-    <!-- Bypass DND -->
-    <com.android.settingslib.RestrictedSwitchPreference
-        android:key="bypass_dnd"
-        android:title="@string/app_notification_override_dnd_title"
-        android:summary="@string/app_notification_override_dnd_summary"
-        settings:useAdditionalSummary="true" />
-
-</PreferenceScreen>
diff --git a/res/xml/notification_group_settings.xml b/res/xml/notification_group_settings.xml
new file mode 100644
index 0000000..c138197
--- /dev/null
+++ b/res/xml/notification_group_settings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="pref_app_header"
+        android:layout="@layout/settings_entity_header" />
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="block"
+        android:layout="@layout/styled_switch_bar" />
+
+    <!-- Channels added here -->
+
+    <Preference
+        android:key="app_link"
+        android:title="@string/app_settings_link"
+        settings:allowDividerAbove="true"/>
+
+    <com.android.settings.notification.NotificationFooterPreference
+        android:key="desc" />
+
+    <com.android.settings.notification.NotificationFooterPreference
+        android:key="block_desc" />
+</PreferenceScreen>
diff --git a/res/xml/upgraded_app_notification_settings.xml b/res/xml/upgraded_app_notification_settings.xml
deleted file mode 100644
index f9a3304..0000000
--- a/res/xml/upgraded_app_notification_settings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
-
-    <!-- Show badge -->
-    <com.android.settingslib.RestrictedSwitchPreference
-        android:key="badge"
-        android:title="@string/notification_badge_title"
-        settings:useAdditionalSummary="true"
-        settings:restrictedSwitchSummary="@string/enabled_by_admin" />
-
-</PreferenceScreen>
diff --git a/res/xml/upgraded_channel_notification_settings.xml b/res/xml/upgraded_channel_notification_settings.xml
deleted file mode 100644
index ee23435..0000000
--- a/res/xml/upgraded_channel_notification_settings.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?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.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" >
-
-    <!-- Importance -->
-    <Preference
-        android:key="importance"
-        android:title="@string/notification_importance_title" />
-
-    <!-- Default ringtone -->
-    <com.android.settings.notification.NotificationSoundPreference
-        android:key="ringtone"
-        android:title="@string/notification_channel_sound_title"
-        android:dialogTitle="@string/notification_channel_sound_title"
-        android:showSilent="true"
-        android:showDefault="true"
-        android:ringtoneType="notification" />
-
-    <!-- Vibration -->
-    <com.android.settingslib.RestrictedSwitchPreference
-        android:key="vibrate"
-        android:title="@string/notification_vibrate_title"
-        settings:useAdditionalSummary="true" />
-
-    <PreferenceCategory
-        android:key="advanced"
-        android:title="@string/advanced_apps">
-
-        <!-- Visibility Override -->
-        <com.android.settings.notification.RestrictedDropDownPreference
-            android:key="visibility_override"
-            android:title="@string/app_notification_visibility_override_title" />
-
-        <!-- Lights -->
-        <com.android.settingslib.RestrictedSwitchPreference
-            android:key="lights"
-            android:title="@string/notification_show_lights_title"
-            settings:useAdditionalSummary="true" />
-
-        <!-- Show badge -->
-        <com.android.settingslib.RestrictedSwitchPreference
-            android:key="badge"
-            android:title="@string/notification_channel_badge_title"
-            settings:useAdditionalSummary="true"
-            settings:restrictedSwitchSummary="@string/enabled_by_admin" />
-
-        <!-- Bypass DND -->
-        <com.android.settingslib.RestrictedSwitchPreference
-            android:key="bypass_dnd"
-            android:title="@string/app_notification_override_dnd_title"
-            android:summary="@string/app_notification_override_dnd_summary"
-            settings:useAdditionalSummary="true" />
-
-    </PreferenceCategory>
-
-</PreferenceScreen>
diff --git a/src/com/android/settings/DeviceInfoSettings.java b/src/com/android/settings/DeviceInfoSettings.java
index 5c26645..4cabccd 100644
--- a/src/com/android/settings/DeviceInfoSettings.java
+++ b/src/com/android/settings/DeviceInfoSettings.java
@@ -16,6 +16,8 @@
 
 package com.android.settings;
 
+import static com.android.settings.core.FeatureFlags.DEVICE_INFO_V2;
+
 import android.app.Activity;
 import android.app.Fragment;
 import android.content.Context;
@@ -56,8 +58,6 @@
 
 public class DeviceInfoSettings extends DashboardFragment implements Indexable {
 
-    public static final String DEVICE_INFO_V2_FEATURE_FLAG = "device_info_v2";
-
     private static final String LOG_TAG = "DeviceInfoSettings";
 
     private static final String KEY_LEGAL_CONTAINER = "legal_container";
@@ -89,7 +89,7 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return FeatureFlagUtils.isEnabled(DEVICE_INFO_V2_FEATURE_FLAG)
+        return FeatureFlagUtils.isEnabled(DEVICE_INFO_V2)
                 ? R.xml.device_info_settings_v2 : R.xml.device_info_settings;
     }
 
@@ -126,7 +126,7 @@
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
             Activity activity, Fragment fragment, Lifecycle lifecycle) {
-        if (FeatureFlagUtils.isEnabled(DEVICE_INFO_V2_FEATURE_FLAG)) {
+        if (FeatureFlagUtils.isEnabled(DEVICE_INFO_V2)) {
             final List<AbstractPreferenceController> controllers = new ArrayList<>();
             // Device name
 
diff --git a/src/com/android/settings/PrivacySettings.java b/src/com/android/settings/PrivacySettings.java
index a44e182..e547570 100644
--- a/src/com/android/settings/PrivacySettings.java
+++ b/src/com/android/settings/PrivacySettings.java
@@ -63,7 +63,6 @@
     @VisibleForTesting
     static final String DATA_MANAGEMENT = "data_management";
     private static final String BACKUP_INACTIVE = "backup_inactive";
-    private static final String FACTORY_RESET = "factory_reset";
     private static final String TAG = "PrivacySettings";
     private IBackupManager mBackupManager;
     private Preference mBackup;
@@ -245,9 +244,5 @@
             nonVisibleKeys.add(AUTO_RESTORE);
             nonVisibleKeys.add(CONFIGURE_ACCOUNT);
         }
-        if (RestrictedLockUtils.hasBaseUserRestriction(context,
-                UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId())) {
-            nonVisibleKeys.add(FACTORY_RESET);
-        }
     }
 }
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index cbae80c..279ff88 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -16,6 +16,8 @@
 
 package com.android.settings;
 
+import static com.android.settings.core.FeatureFlags.DEV_OPTION_V1;
+
 import android.os.Bundle;
 import android.util.FeatureFlagUtils;
 
@@ -65,7 +67,7 @@
     @Deprecated
     public static class DevelopmentSettingsActivity extends SettingsActivity {
         public static final boolean isEnabled() {
-            return FeatureFlagUtils.isEnabled("dev_option_v1");
+            return FeatureFlagUtils.isEnabled(DEV_OPTION_V1);
         }
     }
     public static class DevelopmentSettingsDashboardActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 7203e24..1a013cc 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -62,7 +62,6 @@
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.DashboardSummary;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.search.SearchActivity;
 import com.android.settings.wfd.WifiDisplaySettings;
 import com.android.settings.widget.SwitchBar;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
@@ -76,7 +75,7 @@
 public class SettingsActivity extends SettingsDrawerActivity
         implements PreferenceManager.OnPreferenceTreeClickListener,
         PreferenceFragment.OnPreferenceStartFragmentCallback,
-        ButtonBarHandler, FragmentManager.OnBackStackChangedListener, OnClickListener {
+        ButtonBarHandler, FragmentManager.OnBackStackChangedListener {
 
     private static final String LOG_TAG = "Settings";
 
@@ -329,8 +328,9 @@
         if (mIsShowingDashboard) {
             findViewById(R.id.search_bar).setVisibility(View.VISIBLE);
             findViewById(R.id.action_bar).setVisibility(View.GONE);
-            Toolbar toolbar = findViewById(R.id.search_action_bar);
-            toolbar.setOnClickListener(this);
+            final Toolbar toolbar = findViewById(R.id.search_action_bar);
+            FeatureFactory.getFactory(this).getSearchFeatureProvider()
+                    .initSearchToolbar(this, toolbar);
             setActionBar(toolbar);
 
             // Please forgive me for what I am about to do.
@@ -959,10 +959,4 @@
 
         return bitmap;
     }
-
-    @Override
-    public void onClick(View v) {
-        Intent intent = new Intent(this, SearchActivity.class);
-        startActivity(intent);
-    }
 }
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index b108c62..cab3139 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -48,7 +48,6 @@
 import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -62,7 +61,6 @@
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.Network;
-import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
 import android.os.Bundle;
@@ -97,7 +95,6 @@
 import android.text.style.TtsSpan;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -108,13 +105,10 @@
 
 import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.UserIcons;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
 import com.android.settings.wrapper.FingerprintManagerWrapper;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -131,11 +125,6 @@
     public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
 
     /**
-     * The opacity level of a disabled icon.
-     */
-    public static final float DISABLED_ALPHA = 0.4f;
-
-    /**
      * Color spectrum to use to indicate badness.  0 is completely transparent (no data),
      * 1 is most bad (red), the last value is least bad (green).
      */
@@ -152,8 +141,6 @@
 
     public static final String OS_PKG = "os";
 
-    private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
-
     /**
      * Finds a matching activity for a preference's intent. If a matching
      * activity is not found, it will remove the preference.
@@ -344,46 +331,6 @@
         view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
     }
 
-    /* Used by UserSettings as well. Call this on a non-ui thread. */
-    public static void copyMeProfilePhoto(Context context, UserInfo user) {
-        Uri contactUri = Profile.CONTENT_URI;
-
-        int userId = user != null ? user.id : UserHandle.myUserId();
-
-        InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
-                    context.getContentResolver(),
-                    contactUri, true);
-        // If there's no profile photo, assign a default avatar
-        if (avatarDataStream == null) {
-            assignDefaultPhoto(context, userId);
-            return;
-        }
-
-        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
-        um.setUserIcon(userId, icon);
-        try {
-            avatarDataStream.close();
-        } catch (IOException ioe) { }
-    }
-
-    /**
-     * Assign the default photo to user with {@paramref userId}
-     * @param context used to get the {@link UserManager}
-     * @param userId  used to get the icon bitmap
-     * @return true if assign photo successfully, false if failed
-     */
-    public static boolean assignDefaultPhoto(Context context, int userId) {
-        if (context == null) {
-            return false;
-        }
-        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
-        um.setUserIcon(userId, bitmap);
-
-        return true;
-    }
-
     public static String getMeProfileName(Context context, boolean full) {
         if (full) {
             return getProfileDisplayName(context);
@@ -504,6 +451,26 @@
                 metricsCategory);
     }
 
+
+    /**
+     * Start a new instance of the activity, showing only the given fragment.
+     * When launched in this mode, the given preference fragment will be instantiated and fill the
+     * entire activity.
+     *
+     * @param context The context.
+     * @param fragmentName The name of the fragment to display.
+     * @param titleResId resource id for the String to display for the title of this set
+     *                   of preferences.
+     * @param metricsCategory The current metricsCategory for logging source when fragment starts
+     * @param intentFlags flag that should be added to the intent.
+     */
+    public static void startWithFragment(Context context, String fragmentName, int titleResId,
+            int metricsCategory, int intentFlags) {
+        startWithFragment(context, fragmentName, null, null, 0,
+                null /* titleResPackageName */, titleResId, null, false /* not a shortcut */,
+                metricsCategory, intentFlags);
+    }
+
     /**
      * Start a new instance of the activity, showing only the given fragment.
      * When launched in this mode, the given preference fragment will be instantiated and fill the
@@ -544,8 +511,17 @@
     public static void startWithFragment(Context context, String fragmentName, Bundle args,
             Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
             CharSequence title, boolean isShortcut, int metricsCategory) {
+        startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
+                titleResPackageName, titleResId, title, isShortcut, metricsCategory, 0);
+    }
+
+
+    public static void startWithFragment(Context context, String fragmentName, Bundle args,
+            Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
+            CharSequence title, boolean isShortcut, int metricsCategory, int flags) {
         Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
                 titleResId, title, isShortcut, metricsCategory);
+        intent.addFlags(flags);
         if (resultTo == null) {
             context.startActivity(intent);
         } else {
@@ -936,23 +912,6 @@
         return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
     }
 
-    /**
-     * Returns a default user icon (as a {@link Bitmap}) for the given user.
-     *
-     * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
-     * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
-     */
-    public static Bitmap getDefaultUserIconAsBitmap(int userId) {
-        Bitmap bitmap = null;
-        // Try finding the corresponding bitmap in the dark bitmap cache
-        bitmap = sDarkDefaultUserBitmapCache.get(userId);
-        if (bitmap == null) {
-            bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
-            // Save it to cache
-            sDarkDefaultUserBitmapCache.put(userId, bitmap);
-        }
-        return bitmap;
-    }
 
     public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
         // Get list of preferred activities
@@ -969,7 +928,7 @@
         List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
 
         ArraySet<String> result = new ArraySet<>();
-        if (iviList.size() > 0) {
+        if (iviList != null && iviList.size() > 0) {
             for (IntentFilterVerificationInfo ivi : iviList) {
                 for (String host : ivi.getDomains()) {
                     result.add(host);
diff --git a/src/com/android/settings/applications/ManageDomainUrls.java b/src/com/android/settings/applications/ManageDomainUrls.java
index 53cad4a..93416ad 100644
--- a/src/com/android/settings/applications/ManageDomainUrls.java
+++ b/src/com/android/settings/applications/ManageDomainUrls.java
@@ -23,6 +23,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.support.annotation.VisibleForTesting;
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
@@ -37,6 +38,7 @@
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
+import com.android.settings.widget.AppPreference;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 
@@ -175,7 +177,7 @@
             String key = entry.info.packageName + "|" + entry.info.uid;
             DomainAppPreference preference = (DomainAppPreference) getCachedPreference(key);
             if (preference == null) {
-                preference = new DomainAppPreference(getPrefContext(), entry);
+                preference = new DomainAppPreference(getPrefContext(), mApplicationsState, entry);
                 preference.setKey(key);
                 preference.setOnPreferenceClickListener(this);
                 group.addPreference(preference);
@@ -225,12 +227,16 @@
         return false;
     }
 
-    private class DomainAppPreference extends Preference {
+    @VisibleForTesting
+    static class DomainAppPreference extends AppPreference {
         private final AppEntry mEntry;
         private final PackageManager mPm;
+        private final ApplicationsState mApplicationsState;
 
-        public DomainAppPreference(final Context context, AppEntry entry) {
+        public DomainAppPreference(final Context context, ApplicationsState applicationsState,
+                AppEntry entry) {
             super(context);
+            mApplicationsState = applicationsState;
             mPm = context.getPackageManager();
             mEntry = entry;
             mEntry.ensureLabel(getContext());
diff --git a/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
index bd8b5d1..a7d65d3 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
@@ -63,7 +63,7 @@
         if (currentDefaultHome != null) {
             return new DefaultAppInfo(mContext, mPackageManager, mUserId, currentDefaultHome);
         }
-        final ActivityInfo onlyAppInfo = getOnlyAppInfo();
+        final ActivityInfo onlyAppInfo = getOnlyAppInfo(homeActivities);
         if (onlyAppInfo != null) {
             return new DefaultAppInfo(mContext, mPackageManager, mUserId,
                     onlyAppInfo.getComponentName());
@@ -71,8 +71,7 @@
         return null;
     }
 
-    private ActivityInfo getOnlyAppInfo() {
-        final List<ResolveInfo> homeActivities = new ArrayList<>();
+    private ActivityInfo getOnlyAppInfo(List<ResolveInfo> homeActivities) {
         final List<ActivityInfo> appLabels = new ArrayList<>();
 
         mPackageManager.getHomeActivities(homeActivities);
@@ -88,6 +87,23 @@
                 : null;
     }
 
+    @Override
+    protected Intent getSettingIntent(DefaultAppInfo info) {
+        final String packageName;
+        if (info.componentName != null) {
+            packageName = info.componentName.getPackageName();
+        } else if (info.packageItemInfo != null) {
+            packageName = info.packageItemInfo.packageName;
+        } else {
+            return null;
+        }
+
+        Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+                .setPackage(packageName)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return mPackageManager.queryIntentActivities(intent, 0).size() == 1 ? intent : null;
+    }
+
     public static boolean hasHomePreference(String pkg, Context context) {
         ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
         PackageManager pm = context.getPackageManager();
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsController.java
index 265690b..63d7b5c 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsController.java
@@ -72,8 +72,8 @@
 
     @Override
     public final void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
         init(screen);
+        super.displayPreference(screen);
     }
 
     /**
diff --git a/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java b/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java
deleted file mode 100644
index 04a561c..0000000
--- a/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.core;
-
-import android.content.Context;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnResume;
-
-public abstract class DynamicAvailabilityPreferenceController extends AbstractPreferenceController
-        implements PreferenceControllerMixin, LifecycleObserver, OnResume {
-
-    private Preference mPreference;
-    private PreferenceScreen mScreen;
-    private PreferenceAvailabilityObserver mAvailabilityObserver = null;
-
-    public DynamicAvailabilityPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context);
-        if (lifecycle != null) {
-            lifecycle.addObserver(this);
-        }
-    }
-
-    public void setAvailabilityObserver(PreferenceAvailabilityObserver observer) {
-        mAvailabilityObserver = observer;
-    }
-
-    public PreferenceAvailabilityObserver getAvailabilityObserver() {
-        return mAvailabilityObserver;
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        mScreen = screen;
-        mPreference = screen.findPreference(getPreferenceKey());
-        super.displayPreference(screen);
-    }
-
-    @Override
-    public void onResume() {
-        if (!isAvailable()) {
-            removePreference(mScreen, getPreferenceKey());
-            return;
-        }
-
-        updateState(mPreference);
-        if (mScreen.findPreference(getPreferenceKey()) == null) {
-            mScreen.addPreference(mPreference);
-        }
-    }
-
-    protected void notifyOnAvailabilityUpdate(boolean available) {
-        if (mAvailabilityObserver != null) {
-            mAvailabilityObserver.onPreferenceAvailabilityUpdated(getPreferenceKey(), available);
-        }
-    }
-}
diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java
new file mode 100644
index 0000000..f3bf3e7
--- /dev/null
+++ b/src/com/android/settings/core/FeatureFlags.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.core;
+
+/**
+ * This class keeps track of all feature flags in Settings.
+ */
+public class FeatureFlags {
+    public static final String DEVICE_INFO_V2 = "device_info_v2";
+    public static final String DEV_OPTION_V1 = "dev_option_v1";
+    public static final String SEARCH_V2 = "settings_search_v2";
+    public static final String SUGGESTIONS_V2 = "new_settings_suggestion";
+    public static final String USE_PREFERENCE_SCREEN_TITLE = "settings_use_preference_screen_title";
+}
diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
index 5b95d66..31a0c27 100644
--- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java
+++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.os.Bundle;
-import android.support.annotation.VisibleForTesting;
 import android.support.annotation.XmlRes;
 import android.support.v7.preference.PreferenceScreen;
 import android.text.TextUtils;
@@ -39,9 +38,8 @@
         implements Instrumentable {
 
     private static final String TAG = "InstrumentedPrefFrag";
-    @VisibleForTesting
-    static final String FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE =
-            "settings_use_preference_screen_title";
+
+
     protected MetricsFeatureProvider mMetricsFeatureProvider;
 
     // metrics placeholder value. Only use this for development.
@@ -85,7 +83,7 @@
     }
 
     public static boolean usePreferenceScreenTitle() {
-        return FeatureFlagUtils.isEnabled(FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE) || true;
+        return FeatureFlagUtils.isEnabled(FeatureFlags.USE_PREFERENCE_SCREEN_TITLE) || true;
     }
 
     protected final Context getPrefContext() {
diff --git a/src/com/android/settings/core/PreferenceAvailabilityObserver.java b/src/com/android/settings/core/PreferenceAvailabilityObserver.java
deleted file mode 100644
index 46ff3ba..0000000
--- a/src/com/android/settings/core/PreferenceAvailabilityObserver.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.core;
-
-/**
- * @deprecated This interface allows a {@link android.support.v7.preference.PreferenceGroup}'s
- * controller to observe the availability of the {@link android.support.v7.preference.Preference}s
- * inside it, hiding the group when all preferences become unavailable. In the future,
- * {@link android.support.v7.preference.PreferenceGroup} will have native support for that
- * functionality, removing the need for this interface.
- */
-public interface PreferenceAvailabilityObserver {
-
-    /**
-     * Notifies the observer that the availability of the preference identified by {@code key} has
-     * been updated.
-     */
-    void onPreferenceAvailabilityUpdated(String key, boolean available);
-}
diff --git a/src/com/android/settings/dashboard/conditional/NightDisplayCondition.java b/src/com/android/settings/dashboard/conditional/NightDisplayCondition.java
index bfcab7f..4d885ae 100644
--- a/src/com/android/settings/dashboard/conditional/NightDisplayCondition.java
+++ b/src/com/android/settings/dashboard/conditional/NightDisplayCondition.java
@@ -18,20 +18,20 @@
 
 import android.graphics.drawable.Icon;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.display.NightDisplaySettings;
 
 public final class NightDisplayCondition extends Condition
-        implements NightDisplayController.Callback {
+        implements ColorDisplayController.Callback {
 
-    private NightDisplayController mController;
+    private ColorDisplayController mController;
 
     NightDisplayCondition(ConditionManager manager) {
         super(manager);
-        mController = new NightDisplayController(manager.getContext());
+        mController = new ColorDisplayController(manager.getContext());
         mController.setListener(this);
     }
 
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
index 1be31b4..14f0e2c 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.dashboard.suggestions;
 
+import static com.android.settings.core.FeatureFlags.SUGGESTIONS_V2;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -62,8 +64,6 @@
     private static final int EXCLUSIVE_SUGGESTION_MAX_COUNT = 3;
 
     private static final String SHARED_PREF_FILENAME = "suggestions";
-    @VisibleForTesting
-    static final String FEATURE_FLAG_SUGGESTIONS_V2 = "new_settings_suggestion";
 
     private final SuggestionRanker mSuggestionRanker;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
@@ -92,7 +92,7 @@
     }
 
     private static boolean isV2Enabled() {
-        return FeatureFlagUtils.isEnabled(FEATURE_FLAG_SUGGESTIONS_V2) || true;
+        return FeatureFlagUtils.isEnabled(SUGGESTIONS_V2) || true;
     }
 
     @Override
diff --git a/src/com/android/settings/datetime/ZonePicker.java b/src/com/android/settings/datetime/ZonePicker.java
index 00a77df..57f9b7d 100644
--- a/src/com/android/settings/datetime/ZonePicker.java
+++ b/src/com/android/settings/datetime/ZonePicker.java
@@ -22,6 +22,7 @@
 import android.app.ListFragment;
 import android.content.Context;
 import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -39,6 +40,7 @@
 import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.datetime.ZoneGetter;
 
+import java.text.Collator;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -261,15 +263,21 @@
         mVisibilityLoggerMixin.onPause();
     }
 
-    private static class MyComparator implements Comparator<Map<?, ?>> {
+    @VisibleForTesting
+    static class MyComparator implements Comparator<Map<?, ?>> {
+        private final Collator mCollator;
         private String mSortingKey;
+        private boolean mSortedByName;
 
         public MyComparator(String sortingKey) {
+            mCollator = Collator.getInstance();
             mSortingKey = sortingKey;
+            mSortedByName = ZoneGetter.KEY_DISPLAY_LABEL.equals(sortingKey);
         }
 
         public void setSortingKey(String sortingKey) {
             mSortingKey = sortingKey;
+            mSortedByName = ZoneGetter.KEY_DISPLAY_LABEL.equals(sortingKey);
         }
 
         public int compare(Map<?, ?> map1, Map<?, ?> map2) {
@@ -286,7 +294,11 @@
                 return -1;
             }
 
-            return ((Comparable) value1).compareTo(value2);
+            if (mSortedByName) {
+                return mCollator.compare(value1, value2);
+            } else {
+                return ((Comparable) value1).compareTo(value2);
+            }
         }
 
         private boolean isComparable(Object value) {
diff --git a/src/com/android/settings/development/CameraHalHdrplusPreferenceController.java b/src/com/android/settings/development/CameraHalHdrplusPreferenceController.java
deleted file mode 100644
index a5390cc..0000000
--- a/src/com/android/settings/development/CameraHalHdrplusPreferenceController.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import android.content.Context;
-import android.os.SystemProperties;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.widget.Toast;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
-
-/**
- * deprecated in favor of {@link CameraHalHdrPlusPreferenceControllerV2}
- */
-@Deprecated
-public class CameraHalHdrplusPreferenceController extends AbstractPreferenceController
-        implements PreferenceControllerMixin {
-
-    private static final String KEY_CAMERA_HAL_HDRPLUS_SWITCH = "camera_hal_hdrplus_switch";
-    @VisibleForTesting
-    static final String BUILD_TYPE = "ro.build.type";
-    @VisibleForTesting
-    static final String PROPERTY_CAMERA_HAL_HDRPLUS = "persist.camera.hdrplus.enable";
-    @VisibleForTesting
-    static final String ENABLED = "1";
-    @VisibleForTesting
-    static final String DISABLED = "0";
-
-    private SwitchPreference mPreference;
-
-    public CameraHalHdrplusPreferenceController(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        if (isAvailable()) {
-            mPreference = (SwitchPreference) screen.findPreference(KEY_CAMERA_HAL_HDRPLUS_SWITCH);
-            mPreference.setChecked(isHalHdrplusEnabled());
-        }
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_CAMERA_HAL_HDRPLUS_SWITCH;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return mContext.getResources().getBoolean(R.bool.config_show_camera_hal_hdrplus);
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        updatePreference();
-    }
-
-    @Override
-    public boolean handlePreferenceTreeClick(Preference preference) {
-        if (KEY_CAMERA_HAL_HDRPLUS_SWITCH.equals(preference.getKey())) {
-            final SwitchPreference switchPreference = (SwitchPreference)preference;
-            SystemProperties.set(PROPERTY_CAMERA_HAL_HDRPLUS,
-                    switchPreference.isChecked() ? ENABLED : DISABLED);
-            Toast.makeText(mContext, R.string.camera_hal_hdrplus_toast,
-                    Toast.LENGTH_LONG).show();
-            return true;
-        }
-        return false;
-    }
-
-    public void enablePreference(boolean enabled) {
-        if (isAvailable()) {
-            mPreference.setEnabled(enabled);
-        }
-    }
-
-    public boolean updatePreference() {
-        if (!isAvailable()) {
-            return false;
-        }
-        final boolean enabled = isHalHdrplusEnabled();
-        mPreference.setChecked(enabled);
-        return enabled;
-    }
-
-    private boolean isHalHdrplusEnabled() {
-        return SystemProperties.getBoolean(PROPERTY_CAMERA_HAL_HDRPLUS, true);
-    }
-}
diff --git a/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerV2.java b/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerV2.java
deleted file mode 100644
index b8828fe..0000000
--- a/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerV2.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import android.content.Context;
-import android.os.SystemProperties;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
-import android.widget.Toast;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class CameraHalHdrplusPreferenceControllerV2 extends
-        DeveloperOptionsPreferenceController implements
-        Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
-
-    private static final String KEY_CAMERA_HAL_HDRPLUS_SWITCH = "camera_hal_hdrplus_switch";
-    @VisibleForTesting
-    static final String BUILD_TYPE = "ro.build.type";
-    @VisibleForTesting
-    static final String PROPERTY_CAMERA_HAL_HDRPLUS = "persist.camera.hdrplus.enable";
-    @VisibleForTesting
-    static final String ENABLED = "1";
-    @VisibleForTesting
-    static final String DISABLED = "0";
-
-    private SwitchPreference mPreference;
-
-    public CameraHalHdrplusPreferenceControllerV2(Context context) {
-        super(context);
-    }
-
-    @Override
-    public boolean isAvailable() {
-        final String buildType = SystemProperties.get(BUILD_TYPE);
-
-        return mContext.getResources().getBoolean(R.bool.config_show_camera_hal_hdrplus);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_CAMERA_HAL_HDRPLUS_SWITCH;
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-
-        mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey());
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean isEnabled = (Boolean) newValue;
-        SystemProperties.set(PROPERTY_CAMERA_HAL_HDRPLUS, isEnabled ? ENABLED : DISABLED);
-        Toast.makeText(mContext, R.string.camera_hal_hdrplus_toast, Toast.LENGTH_LONG).show();
-        return true;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        final boolean enabled = isHalHdrplusEnabled();
-        mPreference.setChecked(enabled);
-    }
-
-    @Override
-    protected void onDeveloperOptionsSwitchEnabled() {
-        mPreference.setEnabled(true);
-    }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        SystemProperties.set(PROPERTY_CAMERA_HAL_HDRPLUS, DISABLED);
-        mPreference.setChecked(false);
-        mPreference.setEnabled(false);
-    }
-
-    private boolean isHalHdrplusEnabled() {
-        return SystemProperties.getBoolean(PROPERTY_CAMERA_HAL_HDRPLUS, true /* default */);
-    }
-}
diff --git a/src/com/android/settings/development/DevelopmentSettings.java b/src/com/android/settings/development/DevelopmentSettings.java
index 558ebcf..37296cf 100644
--- a/src/com/android/settings/development/DevelopmentSettings.java
+++ b/src/com/android/settings/development/DevelopmentSettings.java
@@ -336,7 +336,6 @@
     private BugReportPreferenceController mBugReportController;
     private BugReportInPowerPreferenceController mBugReportInPowerController;
     private ConnectivityMonitorPreferenceController mConnectivityMonitorController;
-    private CameraHalHdrplusPreferenceController mCameraHalHdrplusController;
     private CameraLaserSensorPreferenceController mCameraLaserSensorController;
 
     private BroadcastReceiver mEnableAdbReceiver;
@@ -384,7 +383,6 @@
         mLogpersistController = new LogpersistPreferenceController(getActivity(), getLifecycle());
         mWebViewAppPrefController = new WebViewAppPreferenceController(getActivity());
         mVerifyAppsOverUsbController = new VerifyAppsOverUsbPreferenceController(getActivity());
-        mCameraHalHdrplusController = new CameraHalHdrplusPreferenceController(getActivity());
         mCameraLaserSensorController = new CameraLaserSensorPreferenceController(getActivity());
 
         setIfOnlyAvailableForAdmins(true);
@@ -421,7 +419,6 @@
         mLogdSizeController.displayPreference(preferenceScreen);
         mLogpersistController.displayPreference(preferenceScreen);
         mWebViewAppPrefController.displayPreference(preferenceScreen);
-        mCameraHalHdrplusController.displayPreference(preferenceScreen);
         mEnableAdbController.displayPreference(preferenceScreen);
 
         mCameraLaserSensorController.displayPreference(getPreferenceScreen());
@@ -646,7 +643,6 @@
         mLogdSizeController.enablePreference(enabled);
         mLogpersistController.enablePreference(enabled);
         mWebViewAppPrefController.enablePreference(enabled);
-        mCameraHalHdrplusController.enablePreference(enabled);
         mCameraLaserSensorController.enablePreference(enabled);
         updateAllOptions();
     }
@@ -793,7 +789,6 @@
         }
         mHaveDebugSettings |= mBugReportInPowerController.updatePreference();
         mHaveDebugSettings |= mConnectivityMonitorController.updatePreference();
-        mHaveDebugSettings |= mCameraHalHdrplusController.updatePreference();
         mHaveDebugSettings |= mCameraLaserSensorController.updatePreference();
         updateSwitchPreference(mKeepScreenOn, Settings.Global.getInt(cr,
                 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0) != 0);
@@ -2260,10 +2255,6 @@
             return true;
         }
 
-        if (mCameraHalHdrplusController.handlePreferenceTreeClick(preference)) {
-            return true;
-        }
-
         if (mEnableAdbController.handlePreferenceTreeClick(preference)) {
             return true;
         }
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 038f183..50dfeff 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -386,7 +386,6 @@
         controllers.add(new LogPersistPreferenceControllerV2(context, fragment, lifecycle));
         controllers.add(new ConnectivityMonitorPreferenceControllerV2(context));
         controllers.add(new CameraLaserSensorPreferenceControllerV2(context));
-        controllers.add(new CameraHalHdrplusPreferenceControllerV2(context));
         controllers.add(new WifiDisplayCertificationPreferenceController(context));
         controllers.add(new WifiVerboseLoggingPreferenceController(context));
         controllers.add(new WifiAggressiveHandoverPreferenceController(context));
diff --git a/src/com/android/settings/deviceinfo/BasebandVersionPreferenceController.java b/src/com/android/settings/deviceinfo/BasebandVersionPreferenceController.java
index 90d4c11..06ed872 100644
--- a/src/com/android/settings/deviceinfo/BasebandVersionPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/BasebandVersionPreferenceController.java
@@ -21,9 +21,14 @@
 
 import com.android.settings.R;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.deviceinfo.firmwareversion.BasebandVersionDialogController;
 import com.android.settingslib.Utils;
 import com.android.settingslib.core.AbstractPreferenceController;
 
+/**
+ * deprecated in favor of {@link BasebandVersionDialogController}
+ */
+@Deprecated
 public class BasebandVersionPreferenceController extends AbstractPreferenceController implements
         PreferenceControllerMixin {
 
diff --git a/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java b/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java
index e7fab5a..5e7cd88 100644
--- a/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java
@@ -23,8 +23,8 @@
 import android.text.TextUtils;
 import android.util.FeatureFlagUtils;
 
-import com.android.settings.DeviceInfoSettings;
 import com.android.settings.R;
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.DeviceInfoUtils;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -51,7 +51,7 @@
         super.displayPreference(screen);
         final Preference pref = screen.findPreference(KEY_DEVICE_MODEL);
         if (pref != null) {
-            if (FeatureFlagUtils.isEnabled(DeviceInfoSettings.DEVICE_INFO_V2_FEATURE_FLAG)) {
+            if (FeatureFlagUtils.isEnabled(FeatureFlags.DEVICE_INFO_V2)) {
                 pref.setSummary(mContext.getResources().getString(R.string.model_summary,
                         getDeviceModel()));
             } else {
diff --git a/src/com/android/settings/deviceinfo/HardwareInfoDialogFragment.java b/src/com/android/settings/deviceinfo/HardwareInfoDialogFragment.java
index 3d825b9..c57a4e0 100644
--- a/src/com/android/settings/deviceinfo/HardwareInfoDialogFragment.java
+++ b/src/com/android/settings/deviceinfo/HardwareInfoDialogFragment.java
@@ -29,8 +29,8 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.DeviceInfoSettings;
 import com.android.settings.R;
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 
 public class HardwareInfoDialogFragment extends InstrumentedDialogFragment {
@@ -59,7 +59,7 @@
                 DeviceModelPreferenceController.getDeviceModel());
 
         // Serial number
-        if (FeatureFlagUtils.isEnabled(DeviceInfoSettings.DEVICE_INFO_V2_FEATURE_FLAG)) {
+        if (FeatureFlagUtils.isEnabled(FeatureFlags.DEVICE_INFO_V2)) {
             setText(content, R.id.serial_number_label, R.id.serial_number_value, getSerialNumber());
         } else {
             content.findViewById(R.id.serial_number_label).setVisibility(View.GONE);
@@ -73,7 +73,7 @@
         return builder.setView(content).create();
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting
     void setText(View content, int labelViewId, int valueViewId, String value) {
         if (content == null) {
             return;
diff --git a/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java b/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java
index f38602a..9b0120e 100644
--- a/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java
@@ -23,9 +23,14 @@
 import android.util.Log;
 
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.deviceinfo.firmwareversion.SecurityPatchLevelDialogController;
 import com.android.settingslib.DeviceInfoUtils;
 import com.android.settingslib.core.AbstractPreferenceController;
 
+/**
+ * deprecated in favor of {@link SecurityPatchLevelDialogController}
+ */
+@Deprecated
 public class SecurityPatchPreferenceController extends AbstractPreferenceController implements
         PreferenceControllerMixin {
 
diff --git a/src/com/android/settings/deviceinfo/StorageWizardBase.java b/src/com/android/settings/deviceinfo/StorageWizardBase.java
index c7bea30..c2ea2d2 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardBase.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardBase.java
@@ -16,15 +16,19 @@
 
 package com.android.settings.deviceinfo;
 
+import static com.android.settings.deviceinfo.StorageSettings.TAG;
+
 import android.annotation.LayoutRes;
 import android.app.Activity;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.os.storage.DiskInfo;
 import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -202,13 +206,27 @@
     }
 
     protected VolumeInfo findFirstVolume(int type) {
-        final List<VolumeInfo> vols = mStorage.getVolumes();
-        for (VolumeInfo vol : vols) {
-            if (Objects.equals(mDisk.getId(), vol.getDiskId()) && (vol.getType() == type)) {
-                return vol;
+        return findFirstVolume(type, 1);
+    }
+
+    protected VolumeInfo findFirstVolume(int type, int attempts) {
+        while (true) {
+            final List<VolumeInfo> vols = mStorage.getVolumes();
+            for (VolumeInfo vol : vols) {
+                if (Objects.equals(mDisk.getId(), vol.getDiskId()) && (vol.getType() == type)
+                        && (vol.getState() == VolumeInfo.STATE_MOUNTED)) {
+                    return vol;
+                }
+            }
+
+            if (--attempts > 0) {
+                Log.w(TAG, "Missing mounted volume of type " + type + " hosted by disk "
+                        + mDisk.getId() + "; trying again");
+                SystemClock.sleep(250);
+            } else {
+                return null;
             }
         }
-        return null;
     }
 
     private final StorageEventListener mStorageListener = new StorageEventListener() {
diff --git a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
index 59a1866..3dfc74b 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
@@ -16,15 +16,20 @@
 
 package com.android.settings.deviceinfo;
 
+import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
+
+import static com.android.settings.deviceinfo.StorageSettings.TAG;
+
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.app.DialogFragment;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.IPackageMoveObserver;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.IVoldTaskListener;
+import android.os.PersistableBundle;
 import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
@@ -38,8 +43,8 @@
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 
 import java.util.Objects;
-
-import static com.android.settings.deviceinfo.StorageSettings.TAG;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 
 public class StorageWizardFormatProgress extends StorageWizardBase {
     private static final String TAG_SLOW_WARNING = "slow_warning";
@@ -99,9 +104,21 @@
                     storage.partitionPrivate(activity.mDisk.getId());
                     publishProgress(40);
 
-                    final VolumeInfo privateVol = activity.findFirstVolume(VolumeInfo.TYPE_PRIVATE);
-                    mPrivateBench = storage.benchmark(privateVol.getId());
-                    mPrivateBench /= 1000000;
+                    final VolumeInfo privateVol = activity.findFirstVolume(TYPE_PRIVATE, 5);
+                    final CompletableFuture<PersistableBundle> result = new CompletableFuture<>();
+                    storage.benchmark(privateVol.getId(), new IVoldTaskListener.Stub() {
+                        @Override
+                        public void onStatus(int status, PersistableBundle extras) {
+                            // Map benchmark 0-100% progress onto 40-80%
+                            publishProgress(40 + ((status * 40) / 100));
+                        }
+
+                        @Override
+                        public void onFinished(int status, PersistableBundle extras) {
+                            result.complete(extras);
+                        }
+                    });
+                    mPrivateBench = result.get(60, TimeUnit.SECONDS).getLong("run", Long.MAX_VALUE);
 
                     // If we just adopted the device that had been providing
                     // physical storage, then automatically move storage to the
diff --git a/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java b/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java
index d2ad6d8..d8a64a8 100644
--- a/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java
+++ b/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java
@@ -15,6 +15,8 @@
  */
 package com.android.settings.deviceinfo;
 
+import static android.content.Context.CARRIER_CONFIG_SERVICE;
+
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
@@ -31,8 +33,6 @@
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.core.AbstractPreferenceController;
 
-import static android.content.Context.CARRIER_CONFIG_SERVICE;
-
 public class SystemUpdatePreferenceController extends AbstractPreferenceController implements
         PreferenceControllerMixin {
 
@@ -59,12 +59,11 @@
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
         if (isAvailable()) {
             Utils.updatePreferenceToSpecificActivityOrRemove(mContext, screen,
                     KEY_SYSTEM_UPDATE_SETTINGS,
                     Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
-        } else {
-            removePreference(screen, KEY_SYSTEM_UPDATE_SETTINGS);
         }
     }
 
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionDialogController.java b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionDialogController.java
new file mode 100644
index 0000000..c857f19
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionDialogController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+
+public class BasebandVersionDialogController {
+
+    @VisibleForTesting
+    static final int BASEBAND_VERSION_LABEL_ID = R.id.baseband_version_label;
+    @VisibleForTesting
+    static final int BASEBAND_VERSION_VALUE_ID = R.id.baseband_version_value;
+    @VisibleForTesting
+    static final String BASEBAND_PROPERTY = "gsm.version.baseband";
+
+    private final FirmwareVersionDialogFragment mDialog;
+
+    public BasebandVersionDialogController(FirmwareVersionDialogFragment dialog) {
+        mDialog = dialog;
+    }
+
+    /**
+     * Updates the baseband version field of the dialog.
+     */
+    public void initialize() {
+        final Context context = mDialog.getContext();
+        if (Utils.isWifiOnly(context)) {
+            mDialog.removeSettingFromScreen(BASEBAND_VERSION_LABEL_ID);
+            mDialog.removeSettingFromScreen(BASEBAND_VERSION_VALUE_ID);
+            return;
+        }
+
+        mDialog.setText(BASEBAND_VERSION_VALUE_ID, SystemProperties.get(BASEBAND_PROPERTY,
+                context.getString(R.string.device_info_default)));
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/BuildNumberDialogController.java b/src/com/android/settings/deviceinfo/firmwareversion/BuildNumberDialogController.java
new file mode 100644
index 0000000..d995867
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/BuildNumberDialogController.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import android.os.Build;
+import android.support.annotation.VisibleForTesting;
+import android.text.BidiFormatter;
+
+import com.android.settings.R;
+
+public class BuildNumberDialogController {
+
+    @VisibleForTesting
+    static final int BUILD_NUMBER_VALUE_ID = R.id.build_number_value;
+
+    private final FirmwareVersionDialogFragment mDialog;
+
+    public BuildNumberDialogController(FirmwareVersionDialogFragment dialog) {
+        mDialog = dialog;
+    }
+
+    /**
+     * Updates the build number to the dialog.
+     */
+    public void initialize() {
+        mDialog.setText(BUILD_NUMBER_VALUE_ID,
+                BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY));
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogController.java b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogController.java
new file mode 100644
index 0000000..5794e12
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogController.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settingslib.RestrictedLockUtils;
+
+public class FirmwareVersionDialogController implements View.OnClickListener {
+
+    private static final String TAG = "firmwareDialogCtrl";
+    private static final int DELAY_TIMER_MILLIS = 500;
+    private static final int ACTIVITY_TRIGGER_COUNT = 3;
+
+    @VisibleForTesting
+    static final int FIRMWARE_VERSION_VALUE_ID = R.id.firmware_version_value;
+    @VisibleForTesting
+    static final int FIRMWARE_VERSION_LABEL_ID = R.id.firmware_version_label;
+
+    private final FirmwareVersionDialogFragment mDialog;
+    private final Context mContext;
+    private final UserManager mUserManager;
+    private final long[] mHits = new long[ACTIVITY_TRIGGER_COUNT];
+
+    private RestrictedLockUtils.EnforcedAdmin mFunDisallowedAdmin;
+    private boolean mFunDisallowedBySystem;
+
+    public FirmwareVersionDialogController(FirmwareVersionDialogFragment dialog) {
+        mDialog = dialog;
+        mContext = dialog.getContext();
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+    }
+
+    @Override
+    public void onClick(View v) {
+        arrayCopy();
+        mHits[mHits.length - 1] = SystemClock.uptimeMillis();
+        if (mHits[0] >= (SystemClock.uptimeMillis() - DELAY_TIMER_MILLIS)) {
+            if (mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN)) {
+                if (mFunDisallowedAdmin != null && !mFunDisallowedBySystem) {
+                    RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext,
+                            mFunDisallowedAdmin);
+                }
+                Log.d(TAG, "Sorry, no fun for you!");
+                return;
+            }
+
+            final Intent intent = new Intent(Intent.ACTION_MAIN)
+                    .setClassName(
+                            "android", com.android.internal.app.PlatLogoActivity.class.getName());
+            try {
+                mContext.startActivity(intent);
+            } catch (Exception e) {
+                Log.e(TAG, "Unable to start activity " + intent.toString());
+            }
+        }
+    }
+
+    /**
+     * Populates the Android version field in the dialog and registers click listeners.
+     */
+    public void initialize() {
+        initializeAdminPermissions();
+        registerClickListeners();
+
+        mDialog.setText(FIRMWARE_VERSION_VALUE_ID, Build.VERSION.RELEASE);
+    }
+
+    private void registerClickListeners() {
+        mDialog.registerClickListener(FIRMWARE_VERSION_LABEL_ID, this /* listener */);
+        mDialog.registerClickListener(FIRMWARE_VERSION_VALUE_ID, this /* listener */);
+    }
+
+    /**
+     * Copies the array onto itself to remove the oldest hit.
+     */
+    @VisibleForTesting
+    void arrayCopy() {
+        System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
+    }
+
+    @VisibleForTesting
+    void initializeAdminPermissions() {
+        mFunDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(
+                mContext, UserManager.DISALLOW_FUN, UserHandle.myUserId());
+        mFunDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(
+                mContext, UserManager.DISALLOW_FUN, UserHandle.myUserId());
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java
index 3af21a9..0087444 100644
--- a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java
+++ b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
@@ -32,6 +33,8 @@
 
     private static final String TAG = "firmwareVersionDialog";
 
+    private View mRootView;
+
     public static void show(Fragment host) {
         final FragmentManager manager = host.getChildFragmentManager();
         if (manager.findFragmentByTag(TAG) == null) {
@@ -51,9 +54,40 @@
                 .setTitle(R.string.firmware_title)
                 .setPositiveButton(android.R.string.ok, null /* listener */);
 
-        final View view = LayoutInflater.from(getActivity()).inflate(
+        mRootView = LayoutInflater.from(getActivity()).inflate(
                 R.layout.dialog_firmware_version, null /* parent */);
 
-        return builder.setView(view).create();
+        initializeControllers();
+
+        return builder.setView(mRootView).create();
+    }
+
+    public void setText(int viewId, CharSequence text) {
+        final TextView view = mRootView.findViewById(viewId);
+        if (view != null) {
+            view.setText(text);
+        }
+    }
+
+    public void removeSettingFromScreen(int viewId) {
+        final View view = mRootView.findViewById(viewId);
+        if (view != null) {
+            view.setVisibility(View.GONE);
+        }
+    }
+
+    public void registerClickListener(int viewId, View.OnClickListener listener) {
+        final View view = mRootView.findViewById(viewId);
+        if (view != null) {
+            view.setOnClickListener(listener);
+        }
+    }
+
+    private void initializeControllers() {
+        new FirmwareVersionDialogController(this).initialize();
+        new SecurityPatchLevelDialogController(this).initialize();
+        new BasebandVersionDialogController(this).initialize();
+        new KernelVersionDialogController(this).initialize();
+        new BuildNumberDialogController(this).initialize();
     }
 }
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionDialogController.java b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionDialogController.java
new file mode 100644
index 0000000..c6c84f6
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionDialogController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import android.support.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settingslib.DeviceInfoUtils;
+
+public class KernelVersionDialogController {
+
+    @VisibleForTesting
+    static int KERNEL_VERSION_VALUE_ID = R.id.kernel_version_value;
+
+    private final FirmwareVersionDialogFragment mDialog;
+
+    public KernelVersionDialogController(FirmwareVersionDialogFragment dialog) {
+        mDialog = dialog;
+    }
+
+    /**
+     * Updates kernel version to the dialog.
+     */
+    public void initialize() {
+        mDialog.setText(KERNEL_VERSION_VALUE_ID, DeviceInfoUtils.getFormattedKernelVersion());
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogController.java b/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogController.java
new file mode 100644
index 0000000..01f440d
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settingslib.DeviceInfoUtils;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+public class SecurityPatchLevelDialogController implements View.OnClickListener {
+
+    private static final String TAG = "SecurityPatchCtrl";
+    private static final Uri INTENT_URI_DATA = Uri.parse(
+            "https://source.android.com/security/bulletin/");
+
+    @VisibleForTesting
+    static final int SECURITY_PATCH_VALUE_ID = R.id.security_patch_level_value;
+    @VisibleForTesting
+    static final int SECURITY_PATCH_LABEL_ID = R.id.security_patch_level_label;
+
+    private final FirmwareVersionDialogFragment mDialog;
+    private final Context mContext;
+    private final PackageManagerWrapper mPackageManager;
+    private final String mCurrentPatch;
+
+    public SecurityPatchLevelDialogController(FirmwareVersionDialogFragment dialog) {
+        mDialog = dialog;
+        mContext = dialog.getContext();
+        mPackageManager = new PackageManagerWrapper(mContext.getPackageManager());
+        mCurrentPatch = DeviceInfoUtils.getSecurityPatch();
+    }
+
+    @Override
+    public void onClick(View v) {
+        final Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_VIEW);
+        intent.setData(INTENT_URI_DATA);
+        if (mPackageManager.queryIntentActivities(intent, 0).isEmpty()) {
+            // Don't send out the intent to stop crash
+            Log.w(TAG, "Stop click action on " + SECURITY_PATCH_VALUE_ID + ": "
+                    + "queryIntentActivities() returns empty");
+            return;
+        }
+
+        mContext.startActivity(intent);
+    }
+
+    /**
+     * Populates the security patch level field in the dialog and registers click listeners.
+     */
+    public void initialize() {
+        if (TextUtils.isEmpty(mCurrentPatch)) {
+            mDialog.removeSettingFromScreen(SECURITY_PATCH_LABEL_ID);
+            mDialog.removeSettingFromScreen(SECURITY_PATCH_VALUE_ID);
+            return;
+        }
+        registerListeners();
+        mDialog.setText(SECURITY_PATCH_VALUE_ID, mCurrentPatch);
+    }
+
+    private void registerListeners() {
+        mDialog.registerClickListener(SECURITY_PATCH_LABEL_ID, this /* listener */);
+        mDialog.registerClickListener(SECURITY_PATCH_VALUE_ID, this /* listener */);
+    }
+}
diff --git a/src/com/android/settings/display/ColorModePreferenceFragment.java b/src/com/android/settings/display/ColorModePreferenceFragment.java
index 9f18fd8..e3fb65d 100644
--- a/src/com/android/settings/display/ColorModePreferenceFragment.java
+++ b/src/com/android/settings/display/ColorModePreferenceFragment.java
@@ -17,7 +17,7 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.VisibleForTesting;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.logging.nano.MetricsProto;
 
 import com.android.settings.R;
@@ -36,12 +36,12 @@
     @VisibleForTesting
     static final String KEY_COLOR_MODE_SATURATED = "color_mode_saturated";
 
-    private NightDisplayController mController;
+    private ColorDisplayController mController;
 
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        mController = new NightDisplayController(context);
+        mController = new ColorDisplayController(context);
     }
 
     @Override
@@ -64,10 +64,10 @@
 
     @Override
     protected String getDefaultKey() {
-        if (mController.getColorMode() == NightDisplayController.COLOR_MODE_SATURATED) {
+        if (mController.getColorMode() == ColorDisplayController.COLOR_MODE_SATURATED) {
             return KEY_COLOR_MODE_SATURATED;
         }
-        if (mController.getColorMode() == NightDisplayController.COLOR_MODE_BOOSTED) {
+        if (mController.getColorMode() == ColorDisplayController.COLOR_MODE_BOOSTED) {
             return KEY_COLOR_MODE_BOOSTED;
         }
         return KEY_COLOR_MODE_NATURAL;
@@ -77,13 +77,13 @@
     protected boolean setDefaultKey(String key) {
         switch (key) {
             case KEY_COLOR_MODE_NATURAL:
-                mController.setColorMode(NightDisplayController.COLOR_MODE_NATURAL);
+                mController.setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
                 break;
             case KEY_COLOR_MODE_BOOSTED:
-                mController.setColorMode(NightDisplayController.COLOR_MODE_BOOSTED);
+                mController.setColorMode(ColorDisplayController.COLOR_MODE_BOOSTED);
                 break;
             case KEY_COLOR_MODE_SATURATED:
-                mController.setColorMode(NightDisplayController.COLOR_MODE_SATURATED);
+                mController.setColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
                 break;
         }
         return true;
diff --git a/src/com/android/settings/display/NightDisplayPreference.java b/src/com/android/settings/display/NightDisplayPreference.java
index b966530..ea39f75 100644
--- a/src/com/android/settings/display/NightDisplayPreference.java
+++ b/src/com/android/settings/display/NightDisplayPreference.java
@@ -18,7 +18,7 @@
 import android.support.v14.preference.SwitchPreference;
 import android.util.AttributeSet;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.settings.R;
 
 import java.text.DateFormat;
@@ -27,15 +27,15 @@
 import java.util.TimeZone;
 
 public class NightDisplayPreference extends SwitchPreference
-        implements NightDisplayController.Callback {
+        implements ColorDisplayController.Callback {
 
-    private NightDisplayController mController;
+    private ColorDisplayController mController;
     private DateFormat mTimeFormatter;
 
     public NightDisplayPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mController = new NightDisplayController(context);
+        mController = new ColorDisplayController(context);
         mTimeFormatter = android.text.format.DateFormat.getTimeFormat(context);
         mTimeFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
     }
@@ -78,12 +78,12 @@
         final String autoModeSummary;
         switch (autoMode) {
             default:
-            case NightDisplayController.AUTO_MODE_DISABLED:
+            case ColorDisplayController.AUTO_MODE_DISABLED:
                 autoModeSummary = context.getString(isActivated
                         ? R.string.night_display_summary_on_auto_mode_never
                         : R.string.night_display_summary_off_auto_mode_never);
                 break;
-            case NightDisplayController.AUTO_MODE_CUSTOM:
+            case ColorDisplayController.AUTO_MODE_CUSTOM:
                 if (isActivated) {
                     autoModeSummary = context.getString(
                             R.string.night_display_summary_on_auto_mode_custom,
@@ -94,7 +94,7 @@
                             getFormattedTimeString(mController.getCustomStartTime()));
                 }
                 break;
-            case NightDisplayController.AUTO_MODE_TWILIGHT:
+            case ColorDisplayController.AUTO_MODE_TWILIGHT:
                 autoModeSummary = context.getString(isActivated
                         ? R.string.night_display_summary_on_auto_mode_twilight
                         : R.string.night_display_summary_off_auto_mode_twilight);
diff --git a/src/com/android/settings/display/NightDisplayPreferenceController.java b/src/com/android/settings/display/NightDisplayPreferenceController.java
index f42e324..643f1d4 100644
--- a/src/com/android/settings/display/NightDisplayPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayPreferenceController.java
@@ -15,7 +15,7 @@
 
 import android.content.Context;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.core.AbstractPreferenceController;
 
@@ -30,7 +30,7 @@
 
     @Override
     public boolean isAvailable() {
-        return NightDisplayController.isAvailable(mContext);
+        return ColorDisplayController.isAvailable(mContext);
     }
 
     @Override
diff --git a/src/com/android/settings/display/NightDisplaySettings.java b/src/com/android/settings/display/NightDisplaySettings.java
index 5879297..ab94720 100644
--- a/src/com/android/settings/display/NightDisplaySettings.java
+++ b/src/com/android/settings/display/NightDisplaySettings.java
@@ -25,7 +25,7 @@
 import android.support.v7.preference.TwoStatePreference;
 import android.widget.TimePicker;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.widget.SeekBarPreference;
@@ -40,7 +40,7 @@
  * Settings screen for Night display.
  */
 public class NightDisplaySettings extends SettingsPreferenceFragment
-        implements NightDisplayController.Callback, Preference.OnPreferenceChangeListener {
+        implements ColorDisplayController.Callback, Preference.OnPreferenceChangeListener {
 
     private static final String KEY_NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
     private static final String KEY_NIGHT_DISPLAY_START_TIME = "night_display_start_time";
@@ -51,7 +51,7 @@
     private static final int DIALOG_START_TIME = 0;
     private static final int DIALOG_END_TIME = 1;
 
-    private NightDisplayController mController;
+    private ColorDisplayController mController;
     private DateFormat mTimeFormatter;
 
     private DropDownPreference mAutoModePreference;
@@ -65,7 +65,7 @@
         super.onCreate(savedInstanceState);
 
         final Context context = getContext();
-        mController = new NightDisplayController(context);
+        mController = new ColorDisplayController(context);
 
         mTimeFormatter = android.text.format.DateFormat.getTimeFormat(context);
         mTimeFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
@@ -98,9 +98,9 @@
                 getString(R.string.night_display_auto_mode_twilight)
         });
         mAutoModePreference.setEntryValues(new CharSequence[] {
-                String.valueOf(NightDisplayController.AUTO_MODE_DISABLED),
-                String.valueOf(NightDisplayController.AUTO_MODE_CUSTOM),
-                String.valueOf(NightDisplayController.AUTO_MODE_TWILIGHT)
+                String.valueOf(ColorDisplayController.AUTO_MODE_DISABLED),
+                String.valueOf(ColorDisplayController.AUTO_MODE_CUSTOM),
+                String.valueOf(ColorDisplayController.AUTO_MODE_TWILIGHT)
         });
         mAutoModePreference.setOnPreferenceChangeListener(this);
         mActivatedPreference.setOnPreferenceChangeListener(this);
@@ -192,7 +192,7 @@
     public void onAutoModeChanged(int autoMode) {
         mAutoModePreference.setValue(String.valueOf(autoMode));
 
-        final boolean showCustomSchedule = autoMode == NightDisplayController.AUTO_MODE_CUSTOM;
+        final boolean showCustomSchedule = autoMode == ColorDisplayController.AUTO_MODE_CUSTOM;
         mStartTimePreference.setVisible(showCustomSchedule);
         mEndTimePreference.setVisible(showCustomSchedule);
     }
diff --git a/src/com/android/settings/display/NightModePreferenceController.java b/src/com/android/settings/display/NightModePreferenceController.java
index 7e9701b..96ef938 100644
--- a/src/com/android/settings/display/NightModePreferenceController.java
+++ b/src/com/android/settings/display/NightModePreferenceController.java
@@ -13,6 +13,8 @@
  */
 package com.android.settings.display;
 
+import static android.content.Context.UI_MODE_SERVICE;
+
 import android.app.UiModeManager;
 import android.content.Context;
 import android.support.v7.preference.ListPreference;
@@ -23,8 +25,6 @@
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.core.AbstractPreferenceController;
 
-import static android.content.Context.UI_MODE_SERVICE;
-
 public class NightModePreferenceController extends AbstractPreferenceController implements
         PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
 
@@ -48,7 +48,7 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         if (!isAvailable()) {
-            removePreference(screen, KEY_NIGHT_MODE);
+            setVisible(screen, KEY_NIGHT_MODE, false /* visible */);
             return;
         }
         ListPreference mNightModePreference = (ListPreference) screen.findPreference(
diff --git a/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceController.java b/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceController.java
index be41e4d..6b4e70d 100644
--- a/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceController.java
+++ b/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceController.java
@@ -17,18 +17,14 @@
 import android.Manifest;
 import android.content.Context;
 
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
 public class AdminGrantedCameraPermissionPreferenceController extends
         AdminGrantedPermissionsPreferenceControllerBase {
 
     private static final String KEY_ENTERPRISE_PRIVACY_NUMBER_CAMERA_ACCESS_PACKAGES
             = "enterprise_privacy_number_camera_access_packages";
 
-    public AdminGrantedCameraPermissionPreferenceController(Context context, Lifecycle lifecycle,
-            boolean async) {
-        super(context, lifecycle, async, new String[] {Manifest.permission.CAMERA},
-                Manifest.permission_group.CAMERA);
+    public AdminGrantedCameraPermissionPreferenceController(Context context, boolean async) {
+        super(context, async, new String[] {Manifest.permission.CAMERA});
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceController.java b/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceController.java
index 77c6040..5c6dfc2 100644
--- a/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceController.java
+++ b/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceController.java
@@ -17,18 +17,15 @@
 import android.Manifest;
 import android.content.Context;
 
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
 public class AdminGrantedLocationPermissionsPreferenceController extends
         AdminGrantedPermissionsPreferenceControllerBase {
 
     private static final String KEY_ENTERPRISE_PRIVACY_NUMBER_LOCATION_ACCESS_PACKAGES
             = "enterprise_privacy_number_location_access_packages";
 
-    public AdminGrantedLocationPermissionsPreferenceController(Context context, Lifecycle lifecycle,
-            boolean async) {
-        super(context, lifecycle, async, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION,
-                Manifest.permission.ACCESS_FINE_LOCATION}, Manifest.permission_group.LOCATION);
+    public AdminGrantedLocationPermissionsPreferenceController(Context context, boolean async) {
+        super(context, async, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION});
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceController.java b/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceController.java
index 9896420..74e260a 100644
--- a/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceController.java
+++ b/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceController.java
@@ -17,18 +17,14 @@
 import android.Manifest;
 import android.content.Context;
 
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
 public class AdminGrantedMicrophonePermissionPreferenceController extends
         AdminGrantedPermissionsPreferenceControllerBase {
 
     private static final String KEY_ENTERPRISE_PRIVACY_NUMBER_MICROPHONE_ACCESS_PACKAGES
             = "enterprise_privacy_number_microphone_access_packages";
 
-    public AdminGrantedMicrophonePermissionPreferenceController(Context context,
-            Lifecycle lifecycle, boolean async) {
-        super(context, lifecycle, async, new String[] {Manifest.permission.RECORD_AUDIO},
-                Manifest.permission_group.MICROPHONE);
+    public AdminGrantedMicrophonePermissionPreferenceController(Context context, boolean async) {
+        super(context, async, new String[] {Manifest.permission.RECORD_AUDIO});
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
index 9187cec..dd5ab34 100644
--- a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
+++ b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
@@ -20,24 +20,22 @@
 
 import com.android.settings.R;
 import com.android.settings.applications.ApplicationFeatureProvider;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 public abstract class AdminGrantedPermissionsPreferenceControllerBase
-        extends DynamicAvailabilityPreferenceController {
+        extends AbstractPreferenceController implements PreferenceControllerMixin {
 
     private final String[] mPermissions;
-    private final String mPermissionGroup;
     private final ApplicationFeatureProvider mFeatureProvider;
     private final boolean mAsync;
     private boolean mHasApps;
 
-    public AdminGrantedPermissionsPreferenceControllerBase(Context context, Lifecycle lifecycle,
-            boolean async, String[] permissions, String permissionGroup) {
-        super(context, lifecycle);
+    public AdminGrantedPermissionsPreferenceControllerBase(Context context, boolean async,
+            String[] permissions) {
+        super(context);
         mPermissions = permissions;
-        mPermissionGroup = permissionGroup;
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getApplicationFeatureProvider(context);
         mAsync = async;
@@ -58,7 +56,6 @@
                         mHasApps = true;
                     }
                     preference.setVisible(mHasApps);
-                    notifyOnAvailabilityUpdate(mHasApps);
                 });
     }
 
@@ -80,7 +77,6 @@
         mFeatureProvider.calculateNumberOfAppsWithAdminGrantedPermissions(mPermissions,
                 false /* async */, (num) -> haveAppsWithAdminGrantedPermissions[0] = num > 0);
         mHasApps = haveAppsWithAdminGrantedPermissions[0];
-        notifyOnAvailabilityUpdate(mHasApps);
         return mHasApps;
     }
 
diff --git a/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java b/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java
index ed91fef..883fd36 100644
--- a/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java
+++ b/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java
@@ -17,18 +17,18 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 public class AlwaysOnVpnCurrentUserPreferenceController
-        extends DynamicAvailabilityPreferenceController {
+        extends AbstractPreferenceController implements PreferenceControllerMixin {
 
     private static final String KEY_ALWAYS_ON_VPN_PRIMARY_USER = "always_on_vpn_primary_user";
     private final EnterprisePrivacyFeatureProvider mFeatureProvider;
 
-    public AlwaysOnVpnCurrentUserPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public AlwaysOnVpnCurrentUserPreferenceController(Context context) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getEnterprisePrivacyFeatureProvider(context);
     }
@@ -42,9 +42,7 @@
 
     @Override
     public boolean isAvailable() {
-        final boolean available = mFeatureProvider.isAlwaysOnVpnSetInCurrentUser();
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return mFeatureProvider.isAlwaysOnVpnSetInCurrentUser();
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java b/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java
index db6fc1d..a23af0c 100644
--- a/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java
+++ b/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java
@@ -15,27 +15,25 @@
 
 import android.content.Context;
 
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 public class AlwaysOnVpnManagedProfilePreferenceController
-        extends DynamicAvailabilityPreferenceController {
+        extends AbstractPreferenceController implements PreferenceControllerMixin {
 
     private static final String KEY_ALWAYS_ON_VPN_MANAGED_PROFILE = "always_on_vpn_managed_profile";
     private final EnterprisePrivacyFeatureProvider mFeatureProvider;
 
-    public AlwaysOnVpnManagedProfilePreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public AlwaysOnVpnManagedProfilePreferenceController(Context context) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getEnterprisePrivacyFeatureProvider(context);
     }
 
     @Override
     public boolean isAvailable() {
-        final boolean available = mFeatureProvider.isAlwaysOnVpnSetInManagedProfile();
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return mFeatureProvider.isAlwaysOnVpnSetInManagedProfile();
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceController.java b/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceController.java
index 30da907..e328fc5 100644
--- a/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceController.java
+++ b/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceController.java
@@ -20,16 +20,13 @@
 
 import com.android.settings.R;
 
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
 public class CaCertsCurrentUserPreferenceController extends CaCertsPreferenceControllerBase {
 
     @VisibleForTesting
     static final String CA_CERTS_CURRENT_USER = "ca_certs_current_user";
 
-    public CaCertsCurrentUserPreferenceController(Context context,
-            Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public CaCertsCurrentUserPreferenceController(Context context) {
+        super(context);
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceController.java b/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceController.java
index 2b4e72d..94b923f 100644
--- a/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceController.java
+++ b/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceController.java
@@ -17,16 +17,13 @@
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
 
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
 public class CaCertsManagedProfilePreferenceController extends CaCertsPreferenceControllerBase {
 
     @VisibleForTesting
     static final String CA_CERTS_MANAGED_PROFILE = "ca_certs_managed_profile";
 
-    public CaCertsManagedProfilePreferenceController(Context context,
-            Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public CaCertsManagedProfilePreferenceController(Context context) {
+        super(context);
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/CaCertsPreferenceControllerBase.java b/src/com/android/settings/enterprise/CaCertsPreferenceControllerBase.java
index 8009caf..b9df20a 100644
--- a/src/com/android/settings/enterprise/CaCertsPreferenceControllerBase.java
+++ b/src/com/android/settings/enterprise/CaCertsPreferenceControllerBase.java
@@ -18,17 +18,17 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 public abstract class CaCertsPreferenceControllerBase
-        extends DynamicAvailabilityPreferenceController {
+        extends AbstractPreferenceController implements PreferenceControllerMixin {
 
     protected final EnterprisePrivacyFeatureProvider mFeatureProvider;
 
-    public CaCertsPreferenceControllerBase(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public CaCertsPreferenceControllerBase(Context context) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getEnterprisePrivacyFeatureProvider(context);
     }
@@ -42,9 +42,7 @@
 
     @Override
     public boolean isAvailable() {
-        final boolean available = getNumberOfCaCerts() > 0;
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return getNumberOfCaCerts() > 0;
     }
 
     protected abstract int getNumberOfCaCerts();
diff --git a/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java
index cced8e8..1086c0e 100644
--- a/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java
@@ -18,21 +18,20 @@
 
 import com.android.settings.R;
 import com.android.settings.applications.ApplicationFeatureProvider;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 public class EnterpriseInstalledPackagesPreferenceController
-        extends DynamicAvailabilityPreferenceController {
+        extends AbstractPreferenceController implements PreferenceControllerMixin {
 
     private static final String KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES
             = "number_enterprise_installed_packages";
     private final ApplicationFeatureProvider mFeatureProvider;
     private final boolean mAsync;
 
-    public EnterpriseInstalledPackagesPreferenceController(Context context, Lifecycle lifecycle,
-            boolean async) {
-        super(context, lifecycle);
+    public EnterpriseInstalledPackagesPreferenceController(Context context, boolean async) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getApplicationFeatureProvider(context);
         mAsync = async;
@@ -53,7 +52,6 @@
 
                     }
                     preference.setVisible(available);
-                    notifyOnAvailabilityUpdate(available);
                 });
     }
 
@@ -72,9 +70,8 @@
         final Boolean[] haveEnterpriseInstalledPackages = { null };
         mFeatureProvider.calculateNumberOfPolicyInstalledApps(false /* async */,
                 (num) -> haveEnterpriseInstalledPackages[0] = num > 0);
-        final boolean available = haveEnterpriseInstalledPackages[0];
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return haveEnterpriseInstalledPackages[0];
+
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
index 6ec091b..11fc29e 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
@@ -17,23 +17,27 @@
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
-public class EnterprisePrivacyPreferenceController extends DynamicAvailabilityPreferenceController {
+public class EnterprisePrivacyPreferenceController extends AbstractPreferenceController implements
+        PreferenceControllerMixin {
 
     private static final String KEY_ENTERPRISE_PRIVACY = "enterprise_privacy";
     private final EnterprisePrivacyFeatureProvider mFeatureProvider;
 
-    public EnterprisePrivacyPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public EnterprisePrivacyPreferenceController(Context context) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getEnterprisePrivacyFeatureProvider(context);
     }
 
     @Override
     public void updateState(Preference preference) {
+        if (preference == null) {
+            return;
+        }
         final String organizationName = mFeatureProvider.getDeviceOwnerOrganizationName();
         if (organizationName == null) {
             preference.setSummary(R.string.enterprise_privacy_settings_summary_generic);
@@ -45,9 +49,7 @@
 
     @Override
     public boolean isAvailable() {
-        final boolean available = mFeatureProvider.hasDeviceOwner();
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return mFeatureProvider.hasDeviceOwner();
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
index 4caec30..0628dbb 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
@@ -21,12 +21,11 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.widget.PreferenceCategoryController;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -53,45 +52,41 @@
 
     @Override
     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context, getLifecycle(), true /* async */);
+        return buildPreferenceControllers(context, true /* async */);
     }
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
-            Lifecycle lifecycle, boolean async) {
+            boolean async) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new NetworkLogsPreferenceController(context));
         controllers.add(new BugReportsPreferenceController(context));
         controllers.add(new SecurityLogsPreferenceController(context));
-        final List<DynamicAvailabilityPreferenceController> exposureChangesCategoryControllers =
+        final List<AbstractPreferenceController> exposureChangesCategoryControllers =
                 new ArrayList<>();
         exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
-                context, lifecycle, async));
+                context, async));
         exposureChangesCategoryControllers.add(
-                new AdminGrantedLocationPermissionsPreferenceController(context, lifecycle, async));
+                new AdminGrantedLocationPermissionsPreferenceController(context, async));
         exposureChangesCategoryControllers.add(
-                new AdminGrantedMicrophonePermissionPreferenceController(context, lifecycle,
-                        async));
+                new AdminGrantedMicrophonePermissionPreferenceController(context, async));
         exposureChangesCategoryControllers.add(new AdminGrantedCameraPermissionPreferenceController(
-                context, lifecycle, async));
+                context, async));
         exposureChangesCategoryControllers.add(new EnterpriseSetDefaultAppsPreferenceController(
-                context, lifecycle));
+                context));
         exposureChangesCategoryControllers.add(new AlwaysOnVpnCurrentUserPreferenceController(
-                context, lifecycle));
+                context));
         exposureChangesCategoryControllers.add(new AlwaysOnVpnManagedProfilePreferenceController(
-                context, lifecycle));
-        exposureChangesCategoryControllers.add(new ImePreferenceController(context, lifecycle));
-        exposureChangesCategoryControllers.add(new GlobalHttpProxyPreferenceController(context,
-                lifecycle));
-        exposureChangesCategoryControllers.add(new CaCertsCurrentUserPreferenceController(
-                context, lifecycle));
+                context));
+        exposureChangesCategoryControllers.add(new ImePreferenceController(context));
+        exposureChangesCategoryControllers.add(new GlobalHttpProxyPreferenceController(context));
+        exposureChangesCategoryControllers.add(new CaCertsCurrentUserPreferenceController(context));
         exposureChangesCategoryControllers.add(new CaCertsManagedProfilePreferenceController(
-                context, lifecycle));
+                context));
         controllers.addAll(exposureChangesCategoryControllers);
-        controllers.add(new ExposureChangesCategoryPreferenceController(context, lifecycle,
-                exposureChangesCategoryControllers, async));
-        controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(context, lifecycle));
-        controllers.add(new FailedPasswordWipeManagedProfilePreferenceController(context,
-                lifecycle));
+        controllers.add(new PreferenceCategoryController(context, "exposure_changes_category",
+                exposureChangesCategoryControllers));
+        controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(context));
+        controllers.add(new FailedPasswordWipeManagedProfilePreferenceController(context));
         return controllers;
     }
 
@@ -114,11 +109,12 @@
                     final SearchIndexableResource sir = new SearchIndexableResource(context);
                     sir.xmlResId = R.xml.enterprise_privacy_settings;
                     return Arrays.asList(sir);
-            }
+                }
 
-            @Override
-            public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
-                return buildPreferenceControllers(context, null /* lifecycle */, false /* async */);
+                @Override
+                public List<AbstractPreferenceController> getPreferenceControllers(
+                        Context context) {
+                    return buildPreferenceControllers(context, false /* async */);
                 }
             };
 }
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
index 537ef78..0f9584b 100644
--- a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
@@ -21,20 +21,20 @@
 import com.android.settings.R;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.applications.EnterpriseDefaultApps;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.users.UserFeatureProvider;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 public class EnterpriseSetDefaultAppsPreferenceController
-        extends DynamicAvailabilityPreferenceController {
+        extends AbstractPreferenceController implements PreferenceControllerMixin {
 
     private static final String KEY_DEFAULT_APPS = "number_enterprise_set_default_apps";
     private final ApplicationFeatureProvider mApplicationFeatureProvider;
     private final UserFeatureProvider mUserFeatureProvider;
 
-    public EnterpriseSetDefaultAppsPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public EnterpriseSetDefaultAppsPreferenceController(Context context) {
+        super(context);
         final FeatureFactory factory = FeatureFactory.getFactory(context);
         mApplicationFeatureProvider = factory.getApplicationFeatureProvider(context);
         mUserFeatureProvider = factory.getUserFeatureProvider(context);
@@ -49,9 +49,7 @@
 
     @Override
     public boolean isAvailable() {
-        final boolean available = getNumberOfEnterpriseSetDefaultApps() > 0;
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return getNumberOfEnterpriseSetDefaultApps() > 0;
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceController.java b/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceController.java
deleted file mode 100644
index 7833325..0000000
--- a/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceController.java
+++ /dev/null
@@ -1,111 +0,0 @@
-
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.settings.enterprise;
-
-import android.content.Context;
-import android.support.v7.preference.Preference;
-
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
-import com.android.settings.core.PreferenceAvailabilityObserver;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A controller that hides a {@link android.support.v7.preference.PreferenceGroup} when none of the
- * {@link Preference}s inside it are visible.
- *
- * TODO(b/62051162): Use {@link android.support.v7.preference.PreferenceGroup}'s native ability to
- * hide itself when all {@link Preference}s inside it are invisible when that functionality becomes
- * available. This custom controller will still be needed to remove the
- * {@link android.support.v7.preference.PreferenceGroup} from the search index as required (by
- * having {@link #isAvailable()} return {@code false} if the method returns {@code false} for all
- * {@link Preference}s in the {@link android.support.v7.preference.PreferenceGroup}).
- */
-public class ExposureChangesCategoryPreferenceController
-        extends DynamicAvailabilityPreferenceController implements PreferenceAvailabilityObserver {
-
-    private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
-    private final Set<String> mAvailablePrefs = new HashSet<String>();
-    private Preference mPreference = null;
-    private boolean mControllingUi;
-
-    /**
-     * When {@code controllingUi} is {@code true}, some of the preferences may have their visibility
-     * determined asynchronously. In this case, {@link #isAvailable()} must always return {@code
-     * true} and the group should be hidden using {@link Preference#setVisible()} if all preferences
-     * report that they are invisible.
-     * When {@code controllingUi} is {@code false}, we are running on the search indexer thread and
-     * visibility must be determined synchronously. {@link #isAvailable()} can rely on all
-     * preferences having their visibility determined already and should return whether the group is
-     * visible or not.
-     */
-    public ExposureChangesCategoryPreferenceController(Context context, Lifecycle lifecycle,
-            List<DynamicAvailabilityPreferenceController> controllers, boolean controllingUi) {
-        super(context, lifecycle);
-        mControllingUi = controllingUi;
-        for (final DynamicAvailabilityPreferenceController controller : controllers) {
-            controller.setAvailabilityObserver(this);
-        }
-    }
-
-    @Override
-    public void onPreferenceAvailabilityUpdated(String key, boolean available) {
-        if (available) {
-            mAvailablePrefs.add(key);
-        } else {
-            mAvailablePrefs.remove(key);
-        }
-        available = haveAnyVisiblePreferences();
-        if (mControllingUi) {
-            notifyOnAvailabilityUpdate(available);
-        }
-        if (mPreference != null) {
-            mPreference.setVisible(available);
-        }
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        mPreference = preference;
-        mPreference.setVisible(haveAnyVisiblePreferences());
-    }
-
-    @Override
-    public boolean isAvailable() {
-        if (mControllingUi) {
-            // When running on the main UI thread, some preferences determine their visibility
-            // asynchronously. Always return true here and determine the pref group's actual
-            // visibility as the other preferences report their visibility asynchronously via
-            // onPreferenceAvailabilityUpdated().
-            return true;
-        }
-        final boolean available = haveAnyVisiblePreferences();
-        notifyOnAvailabilityUpdate(available);
-        return available;
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_EXPOSURE_CHANGES_CATEGORY;
-    }
-
-    private boolean haveAnyVisiblePreferences() {
-        return mAvailablePrefs.size() > 0;
-    }
-}
diff --git a/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceController.java b/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceController.java
index d7f8dc0..f93025c 100644
--- a/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceController.java
+++ b/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceController.java
@@ -15,16 +15,14 @@
 
 import android.content.Context;
 
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
 public class FailedPasswordWipeCurrentUserPreferenceController
         extends FailedPasswordWipePreferenceControllerBase {
 
     private static final String KEY_FAILED_PASSWORD_WIPE_CURRENT_USER
             = "failed_password_wipe_current_user";
 
-    public FailedPasswordWipeCurrentUserPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public FailedPasswordWipeCurrentUserPreferenceController(Context context) {
+        super(context);
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceController.java b/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceController.java
index 739003a..5a0e7c8 100644
--- a/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceController.java
+++ b/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceController.java
@@ -14,7 +14,6 @@
 package com.android.settings.enterprise;
 
 import android.content.Context;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 
 public class FailedPasswordWipeManagedProfilePreferenceController
         extends FailedPasswordWipePreferenceControllerBase {
@@ -22,9 +21,8 @@
     private static final String KEY_FAILED_PASSWORD_WIPE_MANAGED_PROFILE
             = "failed_password_wipe_managed_profile";
 
-    public FailedPasswordWipeManagedProfilePreferenceController(Context context,
-            Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public FailedPasswordWipeManagedProfilePreferenceController(Context context) {
+        super(context);
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java b/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java
index 298f911..3c78c21 100644
--- a/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java
+++ b/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java
@@ -15,21 +15,20 @@
 package com.android.settings.enterprise;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 public abstract class FailedPasswordWipePreferenceControllerBase
-        extends DynamicAvailabilityPreferenceController {
+        extends AbstractPreferenceController implements PreferenceControllerMixin {
 
     protected final EnterprisePrivacyFeatureProvider mFeatureProvider;
 
-    public FailedPasswordWipePreferenceControllerBase(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public FailedPasswordWipePreferenceControllerBase(Context context) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getEnterprisePrivacyFeatureProvider(context);
     }
@@ -46,8 +45,6 @@
 
     @Override
     public boolean isAvailable() {
-        final boolean available = getMaximumFailedPasswordsBeforeWipe() > 0;
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return getMaximumFailedPasswordsBeforeWipe() > 0;
     }
 }
diff --git a/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java b/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java
index 55552b6..04e63aa 100644
--- a/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java
+++ b/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java
@@ -15,26 +15,25 @@
 
 import android.content.Context;
 
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
-public class GlobalHttpProxyPreferenceController extends DynamicAvailabilityPreferenceController {
+public class GlobalHttpProxyPreferenceController extends AbstractPreferenceController implements
+        PreferenceControllerMixin {
 
     private static final String KEY_GLOBAL_HTTP_PROXY = "global_http_proxy";
     private final EnterprisePrivacyFeatureProvider mFeatureProvider;
 
-    public GlobalHttpProxyPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public GlobalHttpProxyPreferenceController(Context context) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getEnterprisePrivacyFeatureProvider(context);
     }
 
     @Override
     public boolean isAvailable() {
-        final boolean available = mFeatureProvider.isGlobalHttpProxySet();
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return mFeatureProvider.isGlobalHttpProxySet();
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/ImePreferenceController.java b/src/com/android/settings/enterprise/ImePreferenceController.java
index ca52fc0..7a4ea2c 100644
--- a/src/com/android/settings/enterprise/ImePreferenceController.java
+++ b/src/com/android/settings/enterprise/ImePreferenceController.java
@@ -15,21 +15,21 @@
 package com.android.settings.enterprise;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.AbstractPreferenceController;
 
-public class ImePreferenceController extends DynamicAvailabilityPreferenceController {
+public class ImePreferenceController extends AbstractPreferenceController implements
+        PreferenceControllerMixin {
 
     private static final String KEY_INPUT_METHOD = "input_method";
     private final EnterprisePrivacyFeatureProvider mFeatureProvider;
 
-    public ImePreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, lifecycle);
+    public ImePreferenceController(Context context) {
+        super(context);
         mFeatureProvider = FeatureFactory.getFactory(context)
                 .getEnterprisePrivacyFeatureProvider(context);
     }
@@ -37,15 +37,13 @@
     @Override
     public void updateState(Preference preference) {
         preference.setSummary(mContext.getResources().getString(
-            R.string.enterprise_privacy_input_method_name,
-            mFeatureProvider.getImeLabelIfOwnerSet()));
+                R.string.enterprise_privacy_input_method_name,
+                mFeatureProvider.getImeLabelIfOwnerSet()));
     }
 
     @Override
     public boolean isAvailable() {
-        final boolean available = mFeatureProvider.getImeLabelIfOwnerSet() != null;
-        notifyOnAvailabilityUpdate(available);
-        return available;
+        return mFeatureProvider.getImeLabelIfOwnerSet() != null;
     }
 
     @Override
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index 0b40bbd..1d21c12 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -82,14 +82,13 @@
     }
 
     public LocaleDragAndDropAdapter(Context context, List<LocaleStore.LocaleInfo> feedItemList) {
-        this.mFeedItemList = feedItemList;
-
-        this.mContext = context;
+        mFeedItemList = feedItemList;
+        mContext = context;
 
         final float dragElevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
                 context.getResources().getDisplayMetrics());
 
-        this.mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
+        mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
                 ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0 /* no swipe */) {
 
             @Override
@@ -248,15 +247,6 @@
         return result;
     }
 
-    LocaleStore.LocaleInfo getFirstChecked() {
-        for (LocaleStore.LocaleInfo li : mFeedItemList) {
-            if (li.getChecked()) {
-                return li;
-            }
-        }
-        return null;
-    }
-
     void addLocale(LocaleStore.LocaleInfo li) {
         mFeedItemList.add(li);
         notifyItemInserted(mFeedItemList.size() - 1);
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 165be90..79d538f 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -18,7 +18,6 @@
 
 import android.app.AlertDialog;
 import android.app.FragmentTransaction;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -30,21 +29,24 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import android.widget.TextView;
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.LocalePickerWithRegion;
 import com.android.internal.app.LocaleStore;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.RestrictedSettingsFragment;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
+import static android.os.UserManager.DISALLOW_CONFIG_LOCALE;
+
 /**
  * Drag-and-drop editor for the user-ordered locale lists.
  */
-public class LocaleListEditor extends SettingsPreferenceFragment
+public class LocaleListEditor extends RestrictedSettingsFragment
         implements LocalePickerWithRegion.LocaleSelectedListener {
 
     private static final String CFGKEY_REMOVE_MODE = "localeRemoveMode";
@@ -56,6 +58,11 @@
     private View mAddLanguage;
     private boolean mRemoveMode;
     private boolean mShowingRemoveDialog;
+    private boolean mIsUiRestricted;
+
+    public LocaleListEditor() {
+        super(DISALLOW_CONFIG_LOCALE);
+    }
 
     @Override
     public int getMetricsCategory() {
@@ -68,7 +75,7 @@
         setHasOptionsMenu(true);
 
         LocaleStore.fillCache(this.getContext());
-        final List<LocaleStore.LocaleInfo> feedsList = getUserLocaleList(this.getContext());
+        final List<LocaleStore.LocaleInfo> feedsList = getUserLocaleList();
         mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList);
     }
 
@@ -82,6 +89,25 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+
+        final boolean previouslyRestricted = mIsUiRestricted;
+        mIsUiRestricted = isUiRestricted();
+        final TextView emptyView = getEmptyTextView();
+        if (mIsUiRestricted && !previouslyRestricted) {
+            // Lock it down.
+            emptyView.setText(R.string.language_empty_list_user_restricted);
+            emptyView.setVisibility(View.VISIBLE);
+            updateVisibilityOfRemoveMenu();
+        } else if (!mIsUiRestricted && previouslyRestricted) {
+            // Unlock it.
+            emptyView.setVisibility(View.GONE);
+            updateVisibilityOfRemoveMenu();
+        }
+    }
+
+    @Override
     public void onViewStateRestored(Bundle savedInstanceState) {
         super.onViewStateRestored(savedInstanceState);
         if (savedInstanceState != null) {
@@ -217,20 +243,18 @@
         updateVisibilityOfRemoveMenu();
     }
 
-    private static List<LocaleStore.LocaleInfo> getUserLocaleList(Context context) {
+    private List<LocaleStore.LocaleInfo> getUserLocaleList() {
         final List<LocaleStore.LocaleInfo> result = new ArrayList<>();
-
         final LocaleList localeList = LocalePicker.getLocales();
         for (int i = 0; i < localeList.size(); i++) {
             Locale locale = localeList.get(i);
             result.add(LocaleStore.getLocaleInfo(locale));
         }
-
         return result;
     }
 
     private void configureDragAndDrop(View view) {
-        final RecyclerView list = (RecyclerView) view.findViewById(R.id.dragList);
+        final RecyclerView list = view.findViewById(R.id.dragList);
         final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter);
         llm.setAutoMeasureEnabled(true);
         list.setLayoutManager(llm);
@@ -272,7 +296,8 @@
         if (menuItemRemove != null) {
             menuItemRemove.setShowAsAction(
                     mRemoveMode ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
-            menuItemRemove.setVisible(mAdapter.getItemCount() > 1);
+            final boolean hasMultipleLanguages = mAdapter.getItemCount() > 1;
+            menuItemRemove.setVisible(hasMultipleLanguages && !mIsUiRestricted);
         }
     }
 }
diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java
index d2015df..0620f14 100644
--- a/src/com/android/settings/network/AirplaneModePreferenceController.java
+++ b/src/com/android/settings/network/AirplaneModePreferenceController.java
@@ -81,7 +81,7 @@
                         mMetricsFeatureProvider);
             }
         } else {
-            removePreference(screen, getPreferenceKey());
+            setVisible(screen, getPreferenceKey(), false /* visible */);
         }
     }
 
@@ -91,7 +91,7 @@
     }
 
     public static boolean isAvailable(Context context) {
-        return !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
+        return !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
     @Override
diff --git a/src/com/android/settings/nfc/NfcPreferenceController.java b/src/com/android/settings/nfc/NfcPreferenceController.java
index a034777..76977d2 100644
--- a/src/com/android/settings/nfc/NfcPreferenceController.java
+++ b/src/com/android/settings/nfc/NfcPreferenceController.java
@@ -26,9 +26,9 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
@@ -55,8 +55,8 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         if (!isAvailable()) {
-            removePreference(screen, KEY_TOGGLE_NFC);
-            removePreference(screen, KEY_ANDROID_BEAM_SETTINGS);
+            setVisible(screen, KEY_TOGGLE_NFC, false /* visible */);
+            setVisible(screen, KEY_ANDROID_BEAM_SETTINGS, false /* visible */);
             mNfcEnabler = null;
             return;
         }
diff --git a/src/com/android/settings/notification/AllowSoundPreferenceController.java b/src/com/android/settings/notification/AllowSoundPreferenceController.java
new file mode 100644
index 0000000..dcd5e45
--- /dev/null
+++ b/src/com/android/settings/notification/AllowSoundPreferenceController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class AllowSoundPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+    private static final String TAG = "AllowSoundPrefContr";
+    private static final String KEY_IMPORTANCE = "allow_sound";
+    private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+    public AllowSoundPreferenceController(Context context,
+            NotificationSettingsBase.ImportanceListener importanceListener,
+            NotificationBackend backend) {
+        super(context, backend);
+        mImportanceListener = importanceListener;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_IMPORTANCE;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        return mChannel != null && NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+
+    }
+
+    public void updateState(Preference preference) {
+        if (mChannel != null) {
+            RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+            pref.setDisabledByAdmin(mAdmin);
+            pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+            pref.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT
+                    || mChannel.getImportance() == IMPORTANCE_UNSPECIFIED);
+        } else { Log.i(TAG, "tried to updatestate on a null channel?!"); }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mChannel != null) {
+            final int importance =
+                    ((Boolean) newValue ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_LOW);
+            mChannel.setImportance(importance);
+            mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+            saveChannel();
+            mImportanceListener.onImportanceChanged();
+        }
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/AppLinkPreferenceController.java b/src/com/android/settings/notification/AppLinkPreferenceController.java
new file mode 100644
index 0000000..ff5945b
--- /dev/null
+++ b/src/com/android/settings/notification/AppLinkPreferenceController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+
+/**
+ * Controls link to reach more preference settings inside the app.
+ */
+public class AppLinkPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String TAG = "AppLinkPrefContr";
+    private static final String KEY_APP_LINK = "app_link";
+
+    public AppLinkPreferenceController(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_APP_LINK;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        return mAppRow.settingsIntent != null;
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow != null) {
+            preference.setIntent(mAppRow.settingsIntent);
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index 95c9560..af168d6 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -16,12 +16,10 @@
 
 package com.android.settings.notification;
 
-import android.app.Activity;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
-import android.app.NotificationManager;
+import android.content.Context;
 import android.content.Intent;
-import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -29,43 +27,29 @@
 import android.support.v7.preference.PreferenceCategory;
 import android.support.v7.preference.PreferenceGroup;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.applications.AppInfoBase;
-import com.android.settings.applications.LayoutPreference;
-import com.android.settings.notification.NotificationBackend.AppRow;
-import com.android.settings.widget.EntityHeaderController;
 import com.android.settings.widget.MasterSwitchPreference;
-import com.android.settings.widget.SwitchBar;
-import com.android.settingslib.RestrictedSwitchPreference;
-import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-
 /** These settings are per app, so should not be returned in global search results. */
 public class AppNotificationSettings extends NotificationSettingsBase {
     private static final String TAG = "AppNotificationSettings";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static String KEY_GENERAL_CATEGORY = "categories";
-    private static String KEY_DELETED = "deleted";
 
     private List<NotificationChannelGroup> mChannelGroupList;
-    private List<PreferenceCategory> mChannelGroups = new ArrayList();
-    private FooterPreference mDeletedChannels;
 
     @Override
     public int getMetricsCategory() {
@@ -84,24 +68,13 @@
 
         if (getPreferenceScreen() != null) {
             getPreferenceScreen().removeAll();
-            mChannelGroups.clear();
-            mDeletedChannels = null;
-            mShowLegacyChannelConfig = false;
+            mDynamicPreferences.clear();
         }
 
-        addPreferencesFromResource(R.xml.notification_settings);
-        getPreferenceScreen().setOrderingAsAdded(true);
-        setupBlock();
-        addHeaderPref();
-
-        mShowLegacyChannelConfig = mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid);
         if (mShowLegacyChannelConfig) {
-            mChannel = mBackend.getChannel(
-                    mAppRow.pkg, mAppRow.uid, NotificationChannel.DEFAULT_CHANNEL_ID);
-            populateDefaultChannelPrefs();
+            addPreferencesFromResource(R.xml.channel_notification_settings);
         } else {
-            addPreferencesFromResource(R.xml.upgraded_app_notification_settings);
-            setupBadge();
+            addPreferencesFromResource(R.xml.app_notification_settings);
             // Load channel settings
             new AsyncTask<Void, Void, Void>() {
                 @Override
@@ -117,41 +90,59 @@
                         return;
                     }
                     populateList();
-                    addAppLinkPref();
                 }
             }.execute();
         }
+        getPreferenceScreen().setOrderingAsAdded(true);
 
-        updateDependents(mAppRow.banned);
+        for (NotificationPreferenceController controller : mControllers) {
+            controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
+            controller.displayPreference(getPreferenceScreen());
+        }
+        updatePreferenceStates();
     }
 
-    private void addHeaderPref() {
-        ArrayMap<String, AppRow> rows = new ArrayMap<>();
-        rows.put(mAppRow.pkg, mAppRow);
-        collectConfigActivities(rows);
-        final Activity activity = getActivity();
-        final Preference pref = EntityHeaderController
-                .newInstance(activity, this /* fragment */, null /* header */)
-                .setRecyclerView(getListView(), getLifecycle())
-                .setIcon(mAppRow.icon)
-                .setLabel(mAppRow.label)
-                .setPackageName(mAppRow.pkg)
-                .setUid(mAppRow.uid)
-                .setHasAppInfoLink(true)
-                .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
-                        EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
-                .done(activity, getPrefContext());
-        pref.setKey(KEY_HEADER);
-        getPreferenceScreen().addPreference(pref);
+    @Override
+    protected String getLogTag() {
+        return TAG;
     }
 
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.notification_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        mControllers = new ArrayList<>();
+        mControllers.add(new HeaderPreferenceController(context, this));
+        mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
+        mControllers.add(new BadgePreferenceController(context, mBackend));
+        mControllers.add(new AllowSoundPreferenceController(
+                context, mImportanceListener, mBackend));
+        mControllers.add(new ImportancePreferenceController(context));
+        mControllers.add(new SoundPreferenceController(context, this,
+                mImportanceListener, mBackend));
+        mControllers.add(new LightsPreferenceController(context, mBackend));
+        mControllers.add(new VibrationPreferenceController(context, mBackend));
+        mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
+                mBackend));
+        mControllers.add(new DndPreferenceController(context, getLifecycle(), mBackend));
+        mControllers.add(new AppLinkPreferenceController(context));
+        mControllers.add(new DescriptionPreferenceController(context));
+        mControllers.add(new NotificationsOffPreferenceController(context));
+        mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
+        return new ArrayList<>(mControllers);
+    }
+
+
     private void populateList() {
-        if (!mChannelGroups.isEmpty()) {
+        if (!mDynamicPreferences.isEmpty()) {
             // If there's anything in mChannelGroups, we've called populateChannelList twice.
             // Clear out existing channels and log.
             Log.w(TAG, "Notification channel group posted twice to settings - old size " +
-                    mChannelGroups.size() + ", new size " + mChannelGroupList.size());
-            for (Preference p : mChannelGroups) {
+                    mDynamicPreferences.size() + ", new size " + mChannelGroupList.size());
+            for (Preference p : mDynamicPreferences) {
                 getPreferenceScreen().removePreference(p);
             }
         }
@@ -160,7 +151,7 @@
             groupCategory.setTitle(R.string.notification_channels);
             groupCategory.setKey(KEY_GENERAL_CATEGORY);
             getPreferenceScreen().addPreference(groupCategory);
-            mChannelGroups.add(groupCategory);
+            mDynamicPreferences.add(groupCategory);
 
             Preference empty = new Preference(getPrefContext());
             empty.setTitle(R.string.no_channels);
@@ -168,20 +159,8 @@
             groupCategory.addPreference(empty);
         } else {
             populateGroupList();
-            int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
-            if (deletedChannelCount > 0 &&
-                    getPreferenceScreen().findPreference(KEY_DELETED) == null) {
-                mDeletedChannels = new FooterPreference(getPrefContext());
-                mDeletedChannels.setSelectable(false);
-                mDeletedChannels.setTitle(getResources().getQuantityString(
-                        R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
-                mDeletedChannels.setEnabled(false);
-                mDeletedChannels.setKey(KEY_DELETED);
-                mDeletedChannels.setOrder(ORDER_LAST);
-                getPreferenceScreen().addPreference(mDeletedChannels);
-            }
+            mImportanceListener.onImportanceChanged();
         }
-        updateDependents(mAppRow.banned);
     }
 
     private void populateGroupList() {
@@ -190,7 +169,7 @@
         groupCategory.setKey(KEY_GENERAL_CATEGORY);
         groupCategory.setOrderingAsAdded(true);
         getPreferenceScreen().addPreference(groupCategory);
-        mChannelGroups.add(groupCategory);
+        mDynamicPreferences.add(groupCategory);
         for (NotificationChannelGroup group : mChannelGroupList) {
             final List<NotificationChannel> channels = group.getChannels();
             int N = channels.size();
@@ -240,91 +219,6 @@
         parent.addPreference(groupPref);
     }
 
-    void setupBadge() {
-        mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
-        mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
-        if (mChannel == null) {
-            mBadge.setChecked(mAppRow.showBadge);
-        } else {
-            mBadge.setChecked(mChannel.canShowBadge());
-        }
-        mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                final boolean value = (Boolean) newValue;
-                if (mChannel == null) {
-                    mBackend.setShowBadge(mPkg, mUid, value);
-                } else {
-                    mChannel.setShowBadge(value);
-                    mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
-                    mBackend.updateChannel(mPkg, mUid, mChannel);
-                }
-                return true;
-            }
-        });
-    }
-
-    protected void setupBlock() {
-        View switchBarContainer = LayoutInflater.from(
-                getPrefContext()).inflate(R.layout.styled_switch_bar, null);
-        mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
-        mSwitchBar.show();
-        mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mSwitchBar.setChecked(!mAppRow.banned);
-        mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
-            @Override
-            public void onSwitchChanged(Switch switchView, boolean isChecked) {
-                if (mShowLegacyChannelConfig && mChannel != null) {
-                    final int importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
-                    mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
-                    mChannel.setImportance(importance);
-                    mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-                    mBackend.updateChannel(mPkg, mUid, mChannel);
-                }
-                mBackend.setNotificationsEnabledForPackage(mPkgInfo.packageName, mUid, isChecked);
-                mAppRow.banned = true;
-                updateDependents(!isChecked);
-            }
-        });
-
-        mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
-        mBlockBar.setOrder(ORDER_FIRST);
-        mBlockBar.setKey(KEY_BLOCK);
-        getPreferenceScreen().addPreference(mBlockBar);
-
-        if (mAppRow.systemApp && !mAppRow.banned) {
-            setVisible(mBlockBar, false);
-        }
-
-        setupBlockDesc(R.string.app_notifications_off_desc);
-    }
-
-    protected void updateDependents(boolean banned) {
-        for (PreferenceCategory category : mChannelGroups) {
-            setVisible(category, !banned);
-        }
-        if (mDeletedChannels != null) {
-            setVisible(mDeletedChannels, !banned);
-        }
-        setVisible(mBlockedDesc, banned);
-        setVisible(mBadge, !banned);
-        if (mShowLegacyChannelConfig) {
-            setVisible(mImportanceToggle, !banned);
-            setVisible(mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
-                    || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
-                    && mDndVisualEffectsSuppressed));
-            setVisible(mVisibilityOverride, !banned &&
-                    checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && isLockScreenSecure());
-        }
-        if (mAppLink != null) {
-            setVisible(mAppLink, !banned);
-        }
-        if (mAppRow.systemApp && !mAppRow.banned) {
-            setVisible(mBlockBar, false);
-        }
-    }
-
-
     private Comparator<NotificationChannelGroup> mChannelGroupComparator =
             new Comparator<NotificationChannelGroup>() {
 
diff --git a/src/com/android/settings/notification/BadgePreferenceController.java b/src/com/android/settings/notification/BadgePreferenceController.java
new file mode 100644
index 0000000..6b72c50
--- /dev/null
+++ b/src/com/android/settings/notification/BadgePreferenceController.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Secure.NOTIFICATION_BADGING;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class BadgePreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+    private static final String TAG = "BadgePrefContr";
+    private static final String KEY_BADGE = "badge";
+    private static final int SYSTEM_WIDE_ON = 1;
+    private static final int SYSTEM_WIDE_OFF = 0;
+
+    public BadgePreferenceController(Context context,
+            NotificationBackend backend) {
+        super(context, backend);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BADGE;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        if (mAppRow == null && mChannel == null) {
+            return false;
+        }
+        if (Settings.Secure.getInt(mContext.getContentResolver(),
+                NOTIFICATION_BADGING, SYSTEM_WIDE_ON) == SYSTEM_WIDE_OFF) {
+            return false;
+        }
+        if (mChannel != null && !mAppRow.showBadge) {
+            return false;
+        }
+        return true;
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow != null) {
+            RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+            pref.setDisabledByAdmin(mAdmin);
+            if (mChannel != null) {
+                pref.setChecked(mChannel.canShowBadge());
+                pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+            } else {
+                pref.setChecked(mAppRow.showBadge);
+            }
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean showBadge = (Boolean) newValue;
+        if (mChannel != null) {
+            mChannel.setShowBadge(showBadge);
+            saveChannel();
+        } else if (mAppRow != null){
+            mAppRow.showBadge = showBadge;
+            mBackend.setShowBadge(mAppRow.pkg, mAppRow.uid, showBadge);
+        }
+        return true;
+    }
+
+}
diff --git a/src/com/android/settings/notification/BlockPreferenceController.java b/src/com/android/settings/notification/BlockPreferenceController.java
new file mode 100644
index 0000000..5c366ea
--- /dev/null
+++ b/src/com/android/settings/notification/BlockPreferenceController.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.widget.Switch;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.SwitchBar;
+
+public class BlockPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, SwitchBar.OnSwitchChangeListener {
+
+    private static final String KEY_BLOCK = "block";
+    private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+    public BlockPreferenceController(Context context,
+            NotificationSettingsBase.ImportanceListener importanceListener,
+            NotificationBackend backend) {
+        super(context, backend);
+        mImportanceListener = importanceListener;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BLOCK;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (mAppRow == null) {
+            return false;
+        }
+        if (mChannel != null) {
+            return isChannelBlockable();
+        } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+            return isChannelGroupBlockable();
+        } else {
+            return !mAppRow.systemApp || (mAppRow.systemApp && mAppRow.banned);
+        }
+    }
+
+    public void updateState(Preference preference) {
+        LayoutPreference pref = (LayoutPreference) preference;
+        SwitchBar bar = pref.findViewById(R.id.switch_bar);
+        if (bar != null) {
+            bar.show();
+            try {
+                bar.addOnSwitchChangeListener(this);
+            } catch (IllegalStateException e) {
+                // an exception is thrown if you try to add the listener twice
+            }
+            bar.setDisabledByAdmin(mAdmin);
+
+            if (mChannel != null) {
+                bar.setChecked(!mAppRow.banned
+                        && mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
+            } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+                bar.setChecked(!mAppRow.banned && !mChannelGroup.isBlocked());
+            } else {
+                bar.setChecked(!mAppRow.banned);
+            }
+        }
+    }
+
+    @Override
+    public void onSwitchChanged(Switch switchView, boolean isChecked) {
+        boolean blocked = !isChecked;
+        if (mChannel != null) {
+            final int originalImportance = mChannel.getImportance();
+            // setting the initial state of the switch in updateState() triggers this callback.
+            // It's always safe to override the importance if it's meant to be blocked or if
+            // it was blocked and we are unblocking it.
+            if (blocked || originalImportance == IMPORTANCE_NONE) {
+                final int importance = blocked ? IMPORTANCE_NONE
+                        : DEFAULT_CHANNEL_ID.equals(mChannel.getId())
+                                ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_DEFAULT;
+                mChannel.setImportance(importance);
+                saveChannel();
+            }
+        } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+            mChannelGroup.setBlocked(blocked);
+            mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, mChannelGroup.getGroup());
+        } else if (mAppRow != null) {
+            mAppRow.banned = blocked;
+            mBackend.setNotificationsEnabledForPackage(mAppRow.pkg, mAppRow.uid, !blocked);
+        }
+        mImportanceListener.onImportanceChanged();
+    }
+}
diff --git a/src/com/android/settings/notification/ChannelGroupNotificationSettings.java b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
index 7837ec8..68dd91b 100644
--- a/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
@@ -16,20 +16,15 @@
 
 package com.android.settings.notification;
 
-import android.app.Activity;
 import android.app.NotificationChannel;
+import android.content.Context;
 import android.support.v7.preference.Preference;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
-import com.android.settings.applications.LayoutPreference;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -38,12 +33,6 @@
 public class ChannelGroupNotificationSettings extends NotificationSettingsBase {
     private static final String TAG = "ChannelGroupSettings";
 
-    private static String KEY_DELETED = "deleted";
-
-    private EntityHeaderController mHeaderPref;
-    private List<Preference> mChannels = new ArrayList();
-    private FooterPreference mDeletedChannels;
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.NOTIFICATION_CHANNEL_GROUP;
@@ -52,137 +41,68 @@
     @Override
     public void onResume() {
         super.onResume();
-        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannelGroup == null) {
+        if (mAppRow == null || mChannelGroup == null || mChannelGroup.getGroup() == null) {
             Log.w(TAG, "Missing package or uid or packageinfo or group");
             finish();
             return;
         }
 
-        if (getPreferenceScreen() != null) {
-            getPreferenceScreen().removeAll();
-        }
-        addPreferencesFromResource(R.xml.notification_settings);
-        setupBlock();
-        addHeaderPref();
-        addAppLinkPref();
-        addFooterPref();
         populateChannelList();
-
-        updateDependents(mChannelGroup.isBlocked());
+        for (NotificationPreferenceController controller : mControllers) {
+            controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
+            controller.displayPreference(getPreferenceScreen());
+        }
+        updatePreferenceStates();
     }
 
     @Override
-    void setupBadge() {
+    protected String getLogTag() {
+        return TAG;
+    }
 
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.notification_group_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        mControllers = new ArrayList<>();
+        mControllers.add(new HeaderPreferenceController(context, this));
+        mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
+        mControllers.add(new AppLinkPreferenceController(context));
+        mControllers.add(new NotificationsOffPreferenceController(context));
+        mControllers.add(new DescriptionPreferenceController(context));
+        return new ArrayList<>(mControllers);
     }
 
     private void populateChannelList() {
-        if (!mChannels.isEmpty()) {
-            // If there's anything in mChannels, we've called populateChannelList twice.
+        if (!mDynamicPreferences.isEmpty()) {
+            // If there's anything in mDynamicPreferences, we've called populateChannelList twice.
             // Clear out existing channels and log.
             Log.w(TAG, "Notification channel group posted twice to settings - old size " +
-                    mChannels.size() + ", new size " + mChannels.size());
-            for (Preference p : mChannels) {
+                    mDynamicPreferences.size() + ", new size " + mDynamicPreferences.size());
+            for (Preference p : mDynamicPreferences) {
                 getPreferenceScreen().removePreference(p);
             }
         }
-        if (mChannelGroup.getChannels().isEmpty()) {
+        if (mChannelGroup.getGroup().getChannels().isEmpty()) {
             Preference empty = new Preference(getPrefContext());
             empty.setTitle(R.string.no_channels);
             empty.setEnabled(false);
             getPreferenceScreen().addPreference(empty);
-            mChannels.add(empty);
+            mDynamicPreferences.add(empty);
 
         } else {
-            final List<NotificationChannel> channels = mChannelGroup.getChannels();
+            final List<NotificationChannel> channels = mChannelGroup.getGroup().getChannels();
             Collections.sort(channels, mChannelComparator);
             for (NotificationChannel channel : channels) {
-                mChannels.add(populateSingleChannelPrefs(
-                        getPreferenceScreen(), channel, getImportanceSummary(channel)));
+                mDynamicPreferences.add(populateSingleChannelPrefs(
+                        getPreferenceScreen(), channel,
+                        ImportancePreferenceController.getImportanceSummary(
+                                getPrefContext(), channel)));
             }
 
-            int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
-            if (deletedChannelCount > 0) {
-                mDeletedChannels = new FooterPreference(getPrefContext());
-                mDeletedChannels.setSelectable(false);
-                mDeletedChannels.setTitle(getResources().getQuantityString(
-                        R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
-                mDeletedChannels.setEnabled(false);
-                mDeletedChannels.setKey(KEY_DELETED);
-                mDeletedChannels.setOrder(ORDER_LAST);
-                getPreferenceScreen().addPreference(mDeletedChannels);
-                mChannels.add(mDeletedChannels);
-            }
         }
-
-        updateDependents(mAppRow.banned);
-    }
-
-    private void addHeaderPref() {
-        ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<>();
-        rows.put(mAppRow.pkg, mAppRow);
-        collectConfigActivities(rows);
-        final Activity activity = getActivity();
-        mHeaderPref = EntityHeaderController
-                .newInstance(activity, this /* fragment */, null /* header */)
-                .setRecyclerView(getListView(), getLifecycle());
-        final Preference pref = mHeaderPref
-                .setIcon(mAppRow.icon)
-                .setLabel(mChannelGroup.getName())
-                .setSummary(mAppRow.label)
-                .setPackageName(mAppRow.pkg)
-                .setUid(mAppRow.uid)
-                .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
-                        EntityHeaderController.ActionType.ACTION_NONE)
-                .setHasAppInfoLink(true)
-                .done(activity, getPrefContext());
-        getPreferenceScreen().addPreference(pref);
-    }
-
-    private void addFooterPref() {
-        if (!TextUtils.isEmpty(mChannelGroup.getDescription())) {
-            FooterPreference descPref = new FooterPreference(getPrefContext());
-            descPref.setOrder(ORDER_LAST);
-            descPref.setSelectable(false);
-            descPref.setTitle(mChannelGroup.getDescription());
-            getPreferenceScreen().addPreference(descPref);
-            mChannels.add(descPref);
-        }
-    }
-
-    private void setupBlock() {
-        View switchBarContainer = LayoutInflater.from(
-                getPrefContext()).inflate(R.layout.styled_switch_bar, null);
-        mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
-        mSwitchBar.show();
-        mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mSwitchBar.setChecked(!mChannelGroup.isBlocked());
-        mSwitchBar.addOnSwitchChangeListener((switchView, isChecked) -> {
-            mChannelGroup.setBlocked(!isChecked);
-            mBackend.updateChannelGroup(mPkg, mUid, mChannelGroup);
-            updateDependents(!isChecked);
-        });
-
-        mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
-        mBlockBar.setOrder(ORDER_FIRST);
-        mBlockBar.setKey(KEY_BLOCK);
-        getPreferenceScreen().addPreference(mBlockBar);
-
-        if (!isChannelGroupBlockable(mChannelGroup)) {
-            setVisible(mBlockBar, false);
-        }
-
-        setupBlockDesc(R.string.channel_group_notifications_off_desc);
-    }
-
-    protected void updateDependents(boolean banned) {
-        for (Preference channel : mChannels) {
-            setVisible(channel, !banned);
-        }
-        if (mAppLink != null) {
-            setVisible(mAppLink, !banned);
-        }
-        setVisible(mBlockBar, isChannelGroupBlockable(mChannelGroup));
-        setVisible(mBlockedDesc, mAppRow.banned || mChannelGroup.isBlocked());
     }
 }
diff --git a/src/com/android/settings/notification/ChannelImportanceSettings.java b/src/com/android/settings/notification/ChannelImportanceSettings.java
index 9e9ffd6..27b23b8 100644
--- a/src/com/android/settings/notification/ChannelImportanceSettings.java
+++ b/src/com/android/settings/notification/ChannelImportanceSettings.java
@@ -37,6 +37,7 @@
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.widget.RadioButtonPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -60,8 +61,8 @@
     @Override
     public void onResume() {
         super.onResume();
-        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
-            Log.w(TAG, "Missing package or uid or packageinfo or channel");
+        if (mAppRow == null || mChannel == null) {
+            Log.w(TAG, "Missing package or channel");
             finish();
             return;
         }
@@ -69,10 +70,19 @@
     }
 
     @Override
-    void setupBadge() {}
+    protected String getLogTag() {
+        return TAG;
+    }
 
     @Override
-    void updateDependents(boolean banned) {}
+    protected int getPreferenceScreenResId() {
+        return R.xml.notification_importance;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
 
     @Override
     public void onPause() {
@@ -81,11 +91,6 @@
 
     private PreferenceScreen createPreferenceHierarchy() {
         PreferenceScreen root = getPreferenceScreen();
-        if (root != null) {
-            root.removeAll();
-        }
-        addPreferencesFromResource(R.xml.notification_importance);
-        root = getPreferenceScreen();
 
         for (int i = 0; i < root.getPreferenceCount(); i++) {
             Preference pref = root.getPreference(i);
@@ -148,8 +153,9 @@
         // but the sound you had selected was "Silence",
         // then set sound for this channel to your default sound,
         // because you probably intended to cause this channel to actually start making sound.
-        if (oldImportance < IMPORTANCE_DEFAULT && !hasValidSound(mChannel) &&
-                mChannel.getImportance() >= IMPORTANCE_DEFAULT) {
+        if (oldImportance < IMPORTANCE_DEFAULT
+                && !SoundPreferenceController.hasValidSound(mChannel)
+                && mChannel.getImportance() >= IMPORTANCE_DEFAULT) {
             mChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION),
                     mChannel.getAudioAttributes());
             mChannel.lockFields(USER_LOCKED_SOUND);
@@ -157,15 +163,4 @@
         mChannel.lockFields(USER_LOCKED_IMPORTANCE);
         mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, mChannel);
     }
-
-    // This page exists per notification channel; should not be included
-    // in search
-    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider() {
-                @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
-                    return null;
-                }
-            };
 }
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index 9484f7e..ea17a05 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -16,59 +16,23 @@
 
 package com.android.settings.notification;
 
-import android.app.Activity;
-import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
-import android.app.NotificationManager;
+import android.content.Context;
 import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.AsyncTask;
-import android.provider.Settings;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroup;
+import android.preference.PreferenceManager;
 import android.text.TextUtils;
-import android.text.BidiFormatter;
-import android.text.SpannableStringBuilder;
-import android.util.ArrayMap;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
-import com.android.settings.RingtonePreference;
-import com.android.settings.Utils;
-import com.android.settings.applications.AppInfoBase;
-import com.android.settings.applications.LayoutPreference;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settings.widget.SwitchBar;
-import com.android.settingslib.RestrictedSwitchPreference;
-import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import java.util.ArrayList;
+import java.util.List;
 
 public class ChannelNotificationSettings extends NotificationSettingsBase {
     private static final String TAG = "ChannelSettings";
 
-    private static final String KEY_LIGHTS = "lights";
-    private static final String KEY_VIBRATE = "vibrate";
-    private static final String KEY_RINGTONE = "ringtone";
-    private static final String KEY_IMPORTANCE = "importance";
-    private static final String KEY_ADVANCED = "advanced";
-
-    private Preference mImportance;
-    private RestrictedSwitchPreference mLights;
-    private RestrictedSwitchPreference mVibrate;
-    private NotificationSoundPreference mRingtone;
-    private FooterPreference mFooter;
-    private NotificationChannelGroup mChannelGroup;
-    private EntityHeaderController mHeaderPref;
-    private PreferenceGroup mAdvanced;
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
@@ -83,308 +47,52 @@
             return;
         }
 
-        if (getPreferenceScreen() != null) {
-            getPreferenceScreen().removeAll();
+        for (NotificationPreferenceController controller : mControllers) {
+            controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
+            controller.displayPreference(getPreferenceScreen());
         }
-        addPreferencesFromResource(R.xml.notification_settings);
-        setupBlock();
-        addHeaderPref();
-        addAppLinkPref();
-        addFooterPref();
-
-        if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
-            populateDefaultChannelPrefs();
-            mShowLegacyChannelConfig = true;
-        } else {
-            populateUpgradedChannelPrefs();
-
-            if (mChannel.getGroup() != null) {
-                mChannelGroup = mBackend.getGroup(mPkg, mUid, mChannel.getGroup());
-                if (mChannelGroup != null) {
-                    setChannelGroupLabel(mChannelGroup.getName());
-                }
-            }
-        }
-
-        updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
-    }
-
-    private void populateUpgradedChannelPrefs() {
-        addPreferencesFromResource(R.xml.upgraded_channel_notification_settings);
-        setupBadge();
-        setupPriorityPref(mChannel.canBypassDnd());
-        setupVisOverridePref(mChannel.getLockscreenVisibility());
-        setupLights();
-        setupVibrate();
-        setupRingtone();
-        setupImportance();
-        mAdvanced = (PreferenceGroup) findPreference(KEY_ADVANCED);
-    }
-
-    private void addHeaderPref() {
-        ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<>();
-        rows.put(mAppRow.pkg, mAppRow);
-        collectConfigActivities(rows);
-        final Activity activity = getActivity();
-        mHeaderPref = EntityHeaderController
-                .newInstance(activity, this /* fragment */, null /* header */)
-                .setRecyclerView(getListView(), getLifecycle());
-        final Preference pref = mHeaderPref
-                .setIcon(mAppRow.icon)
-                .setLabel(mChannel.getName())
-                .setSummary(mAppRow.label)
-                .setPackageName(mAppRow.pkg)
-                .setUid(mAppRow.uid)
-                .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
-                        EntityHeaderController.ActionType.ACTION_NONE)
-                .setHasAppInfoLink(true)
-                .done(activity, getPrefContext());
-        getPreferenceScreen().addPreference(pref);
-    }
-
-    private void setChannelGroupLabel(CharSequence groupName) {
-        final SpannableStringBuilder summary = new SpannableStringBuilder();
-        BidiFormatter bidi = BidiFormatter.getInstance();
-        summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
-        if (groupName != null) {
-            summary.append(bidi.unicodeWrap(mContext.getText(
-                    R.string.notification_header_divider_symbol_with_spaces)));
-            summary.append(bidi.unicodeWrap(groupName.toString()));
-        }
-        final Activity activity = getActivity();
-        mHeaderPref.setSummary(summary.toString());
-        mHeaderPref.done(activity, getPrefContext());
-    }
-
-    private void addFooterPref() {
-        if (!TextUtils.isEmpty(mChannel.getDescription())) {
-            FooterPreference descPref = new FooterPreference(getPrefContext());
-            descPref.setOrder(ORDER_LAST);
-            descPref.setSelectable(false);
-            descPref.setTitle(mChannel.getDescription());
-            getPreferenceScreen().addPreference(descPref);
-        }
-    }
-
-    protected void setupBadge() {
-        mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
-        mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mBadge.setEnabled(mAppRow.showBadge);
-        mBadge.setChecked(mChannel.canShowBadge());
-
-        mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                final boolean value = (Boolean) newValue;
-                mChannel.setShowBadge(value);
-                mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
-                mBackend.updateChannel(mPkg, mUid, mChannel);
-                return true;
-            }
-        });
-    }
-
-    private void setupLights() {
-        mLights = (RestrictedSwitchPreference) findPreference(KEY_LIGHTS);
-        mLights.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mLights.setChecked(mChannel.shouldShowLights());
-        mLights.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                final boolean lights = (Boolean) newValue;
-                mChannel.enableLights(lights);
-                mChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
-                mBackend.updateChannel(mPkg, mUid, mChannel);
-                return true;
-            }
-        });
-    }
-
-    private void setupVibrate() {
-        mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
-        mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mVibrate.setEnabled(!mVibrate.isDisabledByAdmin() && isChannelConfigurable(mChannel));
-        mVibrate.setChecked(mChannel.shouldVibrate());
-        mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                final boolean vibrate = (Boolean) newValue;
-                mChannel.enableVibration(vibrate);
-                mChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
-                mBackend.updateChannel(mPkg, mUid, mChannel);
-                return true;
-            }
-        });
-    }
-
-    private void setupRingtone() {
-        mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
-        mRingtone.setRingtone(mChannel.getSound());
-        mRingtone.setEnabled(isChannelConfigurable(mChannel));
-        mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mChannel.setSound((Uri) newValue, mChannel.getAudioAttributes());
-                mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
-                mBackend.updateChannel(mPkg, mUid, mChannel);
-                return false;
-            }
-        });
-    }
-
-    private void setupBlock() {
-        View switchBarContainer = LayoutInflater.from(
-                getPrefContext()).inflate(R.layout.styled_switch_bar, null);
-        mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
-        mSwitchBar.show();
-        mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mSwitchBar.setChecked(mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
-        mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
-            @Override
-            public void onSwitchChanged(Switch switchView, boolean isChecked) {
-                int importance = 0;
-                if (mShowLegacyChannelConfig) {
-                    importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
-                    mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
-                } else {
-                    importance = isChecked ? IMPORTANCE_LOW : IMPORTANCE_NONE;
-                    mImportance.setSummary(getImportanceSummary(importance));
-                }
-                mChannel.setImportance(importance);
-                mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-                mBackend.updateChannel(mPkg, mUid, mChannel);
-                updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
-            }
-        });
-
-        mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
-        mBlockBar.setOrder(ORDER_FIRST);
-        mBlockBar.setKey(KEY_BLOCK);
-        getPreferenceScreen().addPreference(mBlockBar);
-
-        if (!isChannelBlockable(mChannel)) {
-            setVisible(mBlockBar, false);
-        }
-
-        setupBlockDesc(R.string.channel_notifications_off_desc);
-    }
-
-    private void setupImportance() {
-        mImportance = findPreference(KEY_IMPORTANCE);
-        Bundle channelArgs = new Bundle();
-        channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
-        channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
-        channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
-        mImportance.setEnabled(mSuspendedAppsAdmin == null && isChannelConfigurable(mChannel));
-        // Set up intent to show importance selection only if this setting is enabled.
-        if (mImportance.isEnabled()) {
-            Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
-                    ChannelImportanceSettings.class.getName(),
-                    channelArgs, null, R.string.notification_importance_title, null,
-                    false, getMetricsCategory());
-            mImportance.setIntent(channelIntent);
-        }
-        mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
-    }
-
-    private String getImportanceSummary(int importance) {
-        String title;
-        String summary = null;
-        switch (importance) {
-            case IMPORTANCE_UNSPECIFIED:
-                title = getContext().getString(R.string.notification_importance_unspecified);
-                break;
-            case NotificationManager.IMPORTANCE_MIN:
-                title = getContext().getString(R.string.notification_importance_min_title);
-                summary = getContext().getString(R.string.notification_importance_min);
-                break;
-            case NotificationManager.IMPORTANCE_LOW:
-                title = getContext().getString(R.string.notification_importance_low_title);
-                summary = getContext().getString(R.string.notification_importance_low);
-                break;
-            case NotificationManager.IMPORTANCE_DEFAULT:
-                title = getContext().getString(R.string.notification_importance_default_title);
-                if (hasValidSound(mChannel)) {
-                    summary = getContext().getString(R.string.notification_importance_default);
-                } else {
-                    summary = getContext().getString(R.string.notification_importance_low);
-                }
-                break;
-            case NotificationManager.IMPORTANCE_HIGH:
-            case NotificationManager.IMPORTANCE_MAX:
-                title = getContext().getString(R.string.notification_importance_high_title);
-                if (hasValidSound(mChannel)) {
-                    summary = getContext().getString(R.string.notification_importance_high);
-                } else {
-                    summary = getContext().getString(R.string.notification_importance_high_silent);
-                }
-                break;
-            default:
-                return "";
-        }
-
-        if (summary != null) {
-            return getContext().getString(R.string.notification_importance_divider, title, summary);
-        } else {
-            return title;
-        }
-    }
-
-    @Override
-    public boolean onPreferenceTreeClick(Preference preference) {
-        if (preference instanceof RingtonePreference) {
-            mRingtone.onPrepareRingtonePickerIntent(mRingtone.getIntent());
-            startActivityForResult(preference.getIntent(), 200);
-            return true;
-        }
-        return super.onPreferenceTreeClick(preference);
+        updatePreferenceStates();
     }
 
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (mRingtone != null) {
-            mRingtone.onActivityResult(requestCode, resultCode, data);
-        }
-        if (mChannel != null) {
-            mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
+        for (NotificationPreferenceController controller : mControllers) {
+            if (controller instanceof PreferenceManager.OnActivityResultListener) {
+                ((PreferenceManager.OnActivityResultListener) controller)
+                        .onActivityResult(requestCode, resultCode, data);
+            }
         }
     }
 
-    boolean canPulseLight() {
-        if (!getResources()
-                .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
-            return false;
-        }
-        return Settings.System.getInt(getContentResolver(),
-                Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
+    @Override
+    protected String getLogTag() {
+        return TAG;
     }
 
-    void updateDependents(boolean banned) {
-        PreferenceGroup parent;
-        if (mShowLegacyChannelConfig) {
-            parent = getPreferenceScreen();
-            setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
-        } else {
-            setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
-            setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
-            setVisible(mAdvanced, mLights, checkCanBeVisible(
-                    NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
-            setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
-            setVisible(mRingtone, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
-            parent = mAdvanced;
-        }
-        setVisible(parent, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
-        setVisible(parent, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
-                || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
-                && mDndVisualEffectsSuppressed));
-        setVisible(parent, mVisibilityOverride, isLockScreenSecure()
-                &&checkCanBeVisible(NotificationManager.IMPORTANCE_LOW));
-        setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE);
-        if (mAppLink != null) {
-            setVisible(mAppLink, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
-        }
-        if (mFooter != null) {
-            setVisible(mFooter, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
-        }
+    @Override
+    protected int getPreferenceScreenResId() {
+        return  R.xml.channel_notification_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        mControllers = new ArrayList<>();
+        mControllers.add(new HeaderPreferenceController(context, this));
+        mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
+        mControllers.add(new ImportancePreferenceController(context));
+        mControllers.add(new AllowSoundPreferenceController(
+                context, mImportanceListener, mBackend));
+        mControllers.add(new SoundPreferenceController(context, this,
+                mImportanceListener, mBackend));
+        mControllers.add(new VibrationPreferenceController(context, mBackend));
+        mControllers.add(new AppLinkPreferenceController(context));
+        mControllers.add(new DescriptionPreferenceController(context));
+        mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
+                mBackend));
+        mControllers.add(new LightsPreferenceController(context, mBackend));
+        mControllers.add(new BadgePreferenceController(context, mBackend));
+        mControllers.add(new DndPreferenceController(context, getLifecycle(), mBackend));
+        mControllers.add(new NotificationsOffPreferenceController(context));
+        return new ArrayList<>(mControllers);
     }
 }
diff --git a/src/com/android/settings/notification/DeletedChannelsPreferenceController.java b/src/com/android/settings/notification/DeletedChannelsPreferenceController.java
new file mode 100644
index 0000000..16eb9ed
--- /dev/null
+++ b/src/com/android/settings/notification/DeletedChannelsPreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class DeletedChannelsPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String  KEY_DELETED = "deleted";
+
+    public DeletedChannelsPreferenceController(Context context, NotificationBackend backend) {
+        super(context, backend);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_DELETED;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        // only visible on app screen
+        if (mChannel != null || hasValidGroup()) {
+            return false;
+        }
+
+        return mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid) > 0;
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow != null) {
+            int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
+            preference.setTitle(mContext.getResources().getQuantityString(
+                    R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
+        }
+        preference.setEnabled(false);
+        preference.setSelectable(false);
+    }
+}
diff --git a/src/com/android/settings/notification/DescriptionPreferenceController.java b/src/com/android/settings/notification/DescriptionPreferenceController.java
new file mode 100644
index 0000000..fae2f5f
--- /dev/null
+++ b/src/com/android/settings/notification/DescriptionPreferenceController.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class DescriptionPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String KEY_DESC = "desc";
+
+    public DescriptionPreferenceController(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_DESC;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        if (mChannel == null && !hasValidGroup()) {
+            return false;
+        }
+        if (mChannel != null && !TextUtils.isEmpty(mChannel.getDescription())) {
+            return true;
+        }
+        if (hasValidGroup() && !TextUtils.isEmpty(mChannelGroup.getDescription())) {
+            return true;
+        }
+        return false;
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow != null) {
+            if (mChannel != null) {
+                preference.setTitle(mChannel.getDescription());
+            } else if (hasValidGroup()) {
+                preference.setTitle(mChannelGroup.getDescription());
+            }
+        }
+        preference.setEnabled(false);
+        preference.setSelectable(false);
+    }
+}
diff --git a/src/com/android/settings/notification/DndPreferenceController.java b/src/com/android/settings/notification/DndPreferenceController.java
new file mode 100644
index 0000000..af60401
--- /dev/null
+++ b/src/com/android/settings/notification/DndPreferenceController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+public class DndPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener,
+        LifecycleObserver, OnResume {
+
+    private static final String KEY_BYPASS_DND = "bypass_dnd";
+    private boolean mVisualEffectsSuppressed;
+
+    public DndPreferenceController(Context context, Lifecycle lifecycle,
+            NotificationBackend backend) {
+        super(context, backend);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        NotificationManager.Policy policy = mNm.getNotificationPolicy();
+        mVisualEffectsSuppressed = policy != null && policy.suppressedVisualEffects != 0;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BYPASS_DND;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
+                || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
+                && mVisualEffectsSuppressed);
+    }
+
+    public void updateState(Preference preference) {
+        if (mChannel != null) {
+            RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+            pref.setDisabledByAdmin(mAdmin);
+            pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+            pref.setChecked(mChannel.canBypassDnd());
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mChannel != null) {
+            final boolean bypassZenMode = (Boolean) newValue;
+            mChannel.setBypassDnd(bypassZenMode);
+            mChannel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+            saveChannel();
+        }
+        return true;
+    }
+
+}
diff --git a/src/com/android/settings/notification/HeaderPreferenceController.java b/src/com/android/settings/notification/HeaderPreferenceController.java
new file mode 100644
index 0000000..3d51b25
--- /dev/null
+++ b/src/com/android/settings/notification/HeaderPreferenceController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.android.settings.widget.EntityHeaderController.PREF_KEY_APP_HEADER;
+
+import android.content.Context;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.Preference;
+import android.text.BidiFormatter;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.EntityHeaderController;
+
+public class HeaderPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin {
+
+    private final PreferenceFragment mFragment;
+
+    public HeaderPreferenceController(Context context, PreferenceFragment fragment) {
+        super(context, null);
+        mFragment = fragment;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY_APP_HEADER;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mAppRow != null;
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow != null && mFragment != null) {
+            LayoutPreference pref = (LayoutPreference) preference;
+            EntityHeaderController controller = EntityHeaderController
+                    .newInstance(mFragment.getActivity(), mFragment,
+                            pref.findViewById(R.id.entity_header));
+            pref = controller.setIcon(mAppRow.icon)
+                    .setLabel(getLabel())
+                    .setSummary(getSummary())
+                    .setPackageName(mAppRow.pkg)
+                    .setUid(mAppRow.uid)
+                    .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
+                            EntityHeaderController.ActionType.ACTION_NONE)
+                    .setHasAppInfoLink(true)
+                    .done(mFragment.getActivity(), mContext);
+            pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE);
+        }
+    }
+
+    CharSequence getLabel() {
+        return mChannel != null ? mChannel.getName()
+                : mChannelGroup != null && mChannelGroup.getGroup() != null
+                        ? mChannelGroup.getGroup().getName()
+                        : mAppRow.label;
+    }
+
+    CharSequence getSummary() {
+        if (mChannel != null) {
+           if (mChannelGroup != null && mChannelGroup.getGroup() != null
+                && !TextUtils.isEmpty(mChannelGroup.getGroup().getName())) {
+               final SpannableStringBuilder summary = new SpannableStringBuilder();
+               BidiFormatter bidi = BidiFormatter.getInstance();
+               summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
+               summary.append(bidi.unicodeWrap(mContext.getText(
+                       R.string.notification_header_divider_symbol_with_spaces)));
+               summary.append(bidi.unicodeWrap(mChannelGroup.getGroup().getName().toString()));
+               return summary;
+           } else {
+               return mAppRow.label;
+           }
+        } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+            return mAppRow.label;
+        } else {
+            return "";
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ImportancePreferenceController.java b/src/com/android/settings/notification/ImportancePreferenceController.java
new file mode 100644
index 0000000..ba47c54
--- /dev/null
+++ b/src/com/android/settings/notification/ImportancePreferenceController.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class ImportancePreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String KEY_IMPORTANCE = "importance";
+
+    // Ironically doesn't take an importance listener because the importance is not changed
+    // by this controller's preference but by the screen it links to.
+    public ImportancePreferenceController(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_IMPORTANCE;
+    }
+    
+    private int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        if (mChannel == null) {
+            return false;
+        }
+        return !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow!= null && mChannel != null) {
+            preference.setEnabled(mAdmin == null && isChannelConfigurable());
+            Bundle channelArgs = new Bundle();
+            channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid);
+            channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg);
+            channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
+            if (preference.isEnabled()) {
+                Intent channelIntent = Utils.onBuildStartFragmentIntent(mContext,
+                        ChannelImportanceSettings.class.getName(),
+                        channelArgs, null, R.string.notification_importance_title, null,
+                        false, getMetricsCategory());
+                preference.setIntent(channelIntent);
+                preference.setSummary(getImportanceSummary(mContext, mChannel));
+            }
+        }
+    }
+
+    protected static String getImportanceSummary(Context context, NotificationChannel channel) {
+        String title;
+        String summary = null;
+        int importance = channel.getImportance();
+        switch (importance) {
+            case IMPORTANCE_UNSPECIFIED:
+                title = context.getString(R.string.notification_importance_unspecified);
+                break;
+            case NotificationManager.IMPORTANCE_MIN:
+                title = context.getString(R.string.notification_importance_min_title);
+                summary = context.getString(R.string.notification_importance_min);
+                break;
+            case NotificationManager.IMPORTANCE_LOW:
+                title = context.getString(R.string.notification_importance_low_title);
+                summary = context.getString(R.string.notification_importance_low);
+                break;
+            case NotificationManager.IMPORTANCE_DEFAULT:
+                title = context.getString(R.string.notification_importance_default_title);
+                if (SoundPreferenceController.hasValidSound(channel)) {
+                    summary = context.getString(R.string.notification_importance_default);
+                } else {
+                    summary = context.getString(R.string.notification_importance_low);
+                }
+                break;
+            case NotificationManager.IMPORTANCE_HIGH:
+            case NotificationManager.IMPORTANCE_MAX:
+                title = context.getString(R.string.notification_importance_high_title);
+                if (SoundPreferenceController.hasValidSound(channel)) {
+                    summary = context.getString(R.string.notification_importance_high);
+                } else {
+                    summary = context.getString(R.string.notification_importance_high_silent);
+                }
+                break;
+            default:
+                return "";
+        }
+
+        if (summary != null) {
+            return context.getString(R.string.notification_importance_divider, title, summary);
+        } else {
+            return title;
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/LightsPreferenceController.java b/src/com/android/settings/notification/LightsPreferenceController.java
new file mode 100644
index 0000000..230c3e2
--- /dev/null
+++ b/src/com/android/settings/notification/LightsPreferenceController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+public class LightsPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+    private static final String KEY_LIGHTS = "lights";
+
+    public LightsPreferenceController(Context context, NotificationBackend backend) {
+        super(context, backend);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_LIGHTS;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        if (mChannel == null) {
+            return false;
+        }
+        return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight()
+                && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+    }
+
+    public void updateState(Preference preference) {
+        if (mChannel != null) {
+            RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+            pref.setDisabledByAdmin(mAdmin);
+            pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+            pref.setChecked(mChannel.shouldShowLights());
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mChannel != null) {
+            final boolean lights = (Boolean) newValue;
+            mChannel.enableLights(lights);
+            saveChannel();
+        }
+        return true;
+    }
+
+    boolean canPulseLight() {
+        if (!mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
+            return false;
+        }
+        return Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
+    }
+
+}
diff --git a/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java b/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java
index 9855751..dd7b0fb 100644
--- a/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java
+++ b/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java
@@ -98,8 +98,8 @@
             mLockscreenProfile = (RestrictedDropDownPreference) screen.findPreference(
                     mWorkSettingKey);
         } else {
-            removePreference(screen, mWorkSettingKey);
-            removePreference(screen, mWorkSettingCategoryKey);
+            setVisible(screen, mWorkSettingKey, false /* visible */);
+            setVisible(screen, mWorkSettingCategoryKey, false /* visible */);
         }
         mSettingObserver = new SettingObserver();
         initLockScreenNotificationPrefDisplay();
diff --git a/src/com/android/settings/notification/NotificationFooterPreference.java b/src/com/android/settings/notification/NotificationFooterPreference.java
new file mode 100644
index 0000000..d44ebee
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationFooterPreference.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v4.content.res.TypedArrayUtils;
+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.settingslib.R;
+
+/**
+ * FooterPreference that can have any key or ordering.
+ */
+public class NotificationFooterPreference extends Preference {
+
+    public NotificationFooterPreference(Context context, AttributeSet attrs) {
+        super(context, attrs, TypedArrayUtils.getAttr(
+                context, R.attr.footerPreferenceStyle, android.R.attr.preferenceStyle));
+        init();
+    }
+
+    public NotificationFooterPreference(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        TextView title = holder.itemView.findViewById(android.R.id.title);
+        title.setMovementMethod(new LinkMovementMethod());
+        title.setClickable(false);
+        title.setLongClickable(false);
+    }
+
+    private void init() {
+        setIcon(R.drawable.ic_info_outline_24dp);
+        setSelectable(false);
+    }
+}
diff --git a/src/com/android/settings/notification/NotificationPreferenceController.java b/src/com/android/settings/notification/NotificationPreferenceController.java
new file mode 100644
index 0000000..b1ef69e
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationPreferenceController.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import android.annotation.Nullable;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.Objects;
+
+/**
+ * Parent class for preferences appearing on notification setting pages at the app,
+ * notification channel group, or notification channel level.
+ */
+public abstract class NotificationPreferenceController extends AbstractPreferenceController
+{
+    private static final String TAG = "ChannelPrefContr";
+    @Nullable protected NotificationChannel mChannel;
+    @Nullable protected NotificationChannelGroupWrapper mChannelGroup;
+    protected RestrictedLockUtils.EnforcedAdmin mAdmin;
+    protected NotificationBackend.AppRow mAppRow;
+    protected final NotificationManager mNm;
+    protected final NotificationBackend mBackend;
+    protected final Context mContext;
+    protected final UserManager mUm;
+    protected final PackageManager mPm;
+    protected Preference mPreference;
+
+    public NotificationPreferenceController(Context context, NotificationBackend backend) {
+        super(context);
+        mContext = context;
+        mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mBackend = backend;
+        mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mPm = mContext.getPackageManager();
+    }
+
+    /**
+     * Returns true if field's parent object is not blocked.
+     */
+    @Override
+    public boolean isAvailable() {
+        if (mAppRow == null) {
+            return false;
+        }
+        if (mAppRow.banned) {
+            return false;
+        }
+        if (mChannel != null) {
+            return mChannel.getImportance() != IMPORTANCE_NONE;
+        }
+        if (mChannelGroup != null && mChannelGroup.getGroup() == null) {
+            return !mChannelGroup.isBlocked();
+        }
+        return true;
+    }
+
+    /**
+     * Displays or removes preference in this controller.
+     */
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        if (isAvailable()) {
+            final Preference preference = screen.findPreference(getPreferenceKey());
+            if (mPreference != null && preference == null) {
+                screen.addPreference(mPreference);
+            }
+            if (preference != null) {
+                mPreference = preference;
+            }
+            if (this instanceof Preference.OnPreferenceChangeListener) {
+                mPreference.setOnPreferenceChangeListener(
+                        (Preference.OnPreferenceChangeListener) this);
+            }
+        } else {
+            findAndRemovePreference(screen, getPreferenceKey());
+        }
+    }
+
+    // finds the preference recursively and removes it from its parent
+    private void findAndRemovePreference(PreferenceGroup prefGroup, String key) {
+        final int preferenceCount = prefGroup.getPreferenceCount();
+        for (int i = preferenceCount - 1; i >= 0; i--) {
+            final Preference preference = prefGroup.getPreference(i);
+            final String curKey = preference.getKey();
+
+            if (curKey != null && curKey.equals(key)) {
+                mPreference = preference;
+                prefGroup.removePreference(preference);
+            }
+
+            if (preference instanceof PreferenceGroup) {
+                findAndRemovePreference((PreferenceGroup) preference, key);
+            }
+        }
+    }
+
+    protected void onResume(NotificationBackend.AppRow appRow,
+            @Nullable NotificationChannel channel, @Nullable NotificationChannelGroupWrapper group,
+            RestrictedLockUtils.EnforcedAdmin admin) {
+        mAppRow = appRow;
+        mChannel = channel;
+        mChannelGroup = group;
+        mAdmin = admin;
+    }
+
+    protected boolean checkCanBeVisible(int minImportanceVisible) {
+        if (mChannel == null) {
+            Log.w(TAG, "No channel");
+            return false;
+        }
+
+        int importance = mChannel.getImportance();
+        if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
+            return true;
+        }
+        return importance >= minImportanceVisible;
+    }
+
+    protected void saveChannel() {
+        if (mChannel != null && mAppRow != null) {
+            mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, mChannel);
+        }
+    }
+
+    protected boolean isChannelConfigurable() {
+        if (mChannel != null && mAppRow != null) {
+            return !Objects.equals(mChannel.getId(), mAppRow.lockedChannelId);
+        }
+        return false;
+    }
+
+    protected boolean isChannelBlockable() {
+        if (mChannel != null && mAppRow != null) {
+            if (!mAppRow.systemApp) {
+                return true;
+            }
+
+            return mChannel.isBlockableSystem()
+                    || mChannel.getImportance() == IMPORTANCE_NONE;
+        }
+        return false;
+    }
+
+    protected boolean isChannelGroupBlockable() {
+        if (mChannelGroup != null && mChannelGroup.getGroup() == null && mAppRow != null) {
+            if (!mAppRow.systemApp) {
+                return true;
+            }
+
+            return mChannelGroup.isBlocked();
+        }
+        return false;
+    }
+
+    protected boolean hasValidGroup() {
+        return mChannelGroup != null && mChannelGroup.getGroup() != null;
+    }
+}
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 3f366e1..9afb618 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -27,10 +27,15 @@
 import com.android.settings.Utils;
 import com.android.settings.applications.AppInfoBase;
 import com.android.settings.applications.LayoutPreference;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
 import com.android.settings.widget.MasterSwitchPreference;
 import com.android.settings.widget.SwitchBar;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.widget.FooterPreference;
 
 import android.app.Notification;
@@ -53,10 +58,12 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.SearchIndexableResource;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -68,66 +75,30 @@
 import java.util.Comparator;
 import java.util.List;
 
-abstract public class NotificationSettingsBase extends SettingsPreferenceFragment {
+abstract public class NotificationSettingsBase extends DashboardFragment {
     private static final String TAG = "NotifiSettingsBase";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
-            = new Intent(Intent.ACTION_MAIN)
-            .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES);
-
-    protected static final int ORDER_FIRST = -500;
-    protected static final int ORDER_LAST = 1000;
-
-    protected static final String KEY_APP_LINK = "app_link";
-    protected static final String KEY_HEADER = "header";
-    protected static final String KEY_BLOCK = "block";
-    protected static final String KEY_BADGE = "badge";
-    protected static final String KEY_BYPASS_DND = "bypass_dnd";
-    protected static final String KEY_VISIBILITY_OVERRIDE = "visibility_override";
-    protected static final String KEY_BLOCKED_DESC = "block_desc";
-    protected static final String KEY_ALLOW_SOUND = "allow_sound";
-
     protected PackageManager mPm;
-    protected UserManager mUm;
     protected NotificationBackend mBackend = new NotificationBackend();
-    protected LockPatternUtils mLockPatternUtils;
     protected NotificationManager mNm;
     protected Context mContext;
-    protected boolean mCreated;
+
     protected int mUid;
     protected int mUserId;
     protected String mPkg;
     protected PackageInfo mPkgInfo;
-    protected RestrictedSwitchPreference mBadge;
-    protected RestrictedSwitchPreference mPriority;
-    protected RestrictedDropDownPreference mVisibilityOverride;
-    protected RestrictedSwitchPreference mImportanceToggle;
-    protected LayoutPreference mBlockBar;
-    protected SwitchBar mSwitchBar;
-    protected FooterPreference mBlockedDesc;
-    protected Preference mAppLink;
-
     protected EnforcedAdmin mSuspendedAppsAdmin;
-    protected boolean mDndVisualEffectsSuppressed;
-
-    protected NotificationChannelGroup mChannelGroup;
+    protected NotificationChannelGroupWrapper mChannelGroup;
     protected NotificationChannel mChannel;
     protected NotificationBackend.AppRow mAppRow;
-    protected boolean mShowLegacyChannelConfig = false;
 
+    protected boolean mShowLegacyChannelConfig = false;
     protected boolean mListeningToPackageRemove;
 
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        if (DEBUG) Log.d(TAG, "onActivityCreated mCreated=" + mCreated);
-        if (mCreated) {
-            Log.w(TAG, "onActivityCreated: ignoring duplicate call");
-            return;
-        }
-        mCreated = true;
-    }
+    protected List<NotificationPreferenceController> mControllers = new ArrayList<>();
+    protected List<Preference> mDynamicPreferences = new ArrayList<>();
+    protected ImportanceListener mImportanceListener = new ImportanceListener();
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -143,7 +114,6 @@
         }
 
         mPm = getPackageManager();
-        mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mNm = NotificationManager.from(mContext);
 
         mPkg = args != null && args.containsKey(AppInfoBase.ARG_PACKAGE_NAME)
@@ -187,35 +157,41 @@
             return;
         }
         mAppRow = mBackend.loadAppRow(mContext, mPm, mPkgInfo);
+        if (mAppRow == null) {
+            Log.w(TAG, "Can't load package");
+            finish();
+            return;
+        }
+        collectConfigActivities();
         Bundle args = getArguments();
         mChannel = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_ID)) ?
                 mBackend.getChannel(mPkg, mUid, args.getString(Settings.EXTRA_CHANNEL_ID)) : null;
 
-        mChannelGroup = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_GROUP_ID)) ?
-                mBackend.getGroupWithChannels(mPkg, mUid,
+        NotificationChannelGroup group =
+                (args != null && args.containsKey(Settings.EXTRA_CHANNEL_GROUP_ID))
+                        ? mBackend.getGroupWithChannels(mPkg, mUid,
                         args.getString(Settings.EXTRA_CHANNEL_GROUP_ID))
-                : null;
+                        : null;
+        if (group != null) {
+            mChannelGroup = new NotificationChannelGroupWrapper(group);
+        }
 
         mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
                 mContext, mPkg, mUserId);
-        NotificationManager.Policy policy = mNm.getNotificationPolicy();
-        mDndVisualEffectsSuppressed = policy == null ? false : policy.suppressedVisualEffects != 0;
 
-        mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
-                mContext, mPkg, mUserId);
-    }
+        mShowLegacyChannelConfig = mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
+                || (mChannel != null
+                && NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId()));
 
-    protected void setVisible(Preference p, boolean visible) {
-        setVisible(getPreferenceScreen(), p, visible);
-    }
-
-    protected void setVisible(PreferenceGroup parent, Preference p, boolean visible) {
-        final boolean isVisible = parent.findPreference(p.getKey()) != null;
-        if (isVisible == visible) return;
-        if (visible) {
-            parent.addPreference(p);
-        } else {
-            parent.removePreference(p);
+        if (mShowLegacyChannelConfig) {
+            mChannel = mBackend.getChannel(
+                    mAppRow.pkg, mAppRow.uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+        }
+        if (mChannel != null && !TextUtils.isEmpty(mChannel.getGroup())) {
+            group = mBackend.getGroup(mPkg, mUid, mChannel.getGroup());
+            if (group != null) {
+                mChannelGroup = new NotificationChannelGroupWrapper(group);
+            }
         }
     }
 
@@ -224,49 +200,38 @@
         getActivity().finish();
     }
 
-    private List<ResolveInfo> queryNotificationConfigActivities() {
-        if (DEBUG) Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is "
-                + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
+    protected void collectConfigActivities() {
+        Intent intent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
+                .setPackage(mAppRow.pkg);
         final List<ResolveInfo> resolveInfos = mPm.queryIntentActivities(
-                APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
+                intent,
                 0 //PackageManager.MATCH_DEFAULT_ONLY
         );
-        return resolveInfos;
-    }
-
-    protected void collectConfigActivities(ArrayMap<String, NotificationBackend.AppRow> rows) {
-        final List<ResolveInfo> resolveInfos = queryNotificationConfigActivities();
-        applyConfigActivities(rows, resolveInfos);
-    }
-
-    private void applyConfigActivities(ArrayMap<String, NotificationBackend.AppRow> rows,
-            List<ResolveInfo> resolveInfos) {
-        if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities"
-                + (resolveInfos.size() == 0 ? " ;_;" : ""));
+        if (DEBUG) {
+            Log.d(TAG, "Found " + resolveInfos.size() + " preference activities"
+                    + (resolveInfos.size() == 0 ? " ;_;" : ""));
+        }
         for (ResolveInfo ri : resolveInfos) {
             final ActivityInfo activityInfo = ri.activityInfo;
             final ApplicationInfo appInfo = activityInfo.applicationInfo;
-            final NotificationBackend.AppRow row = rows.get(appInfo.packageName);
-            if (row == null) {
-                if (DEBUG) Log.v(TAG, "Ignoring notification preference activity ("
-                        + activityInfo.name + ") for unknown package "
-                        + activityInfo.packageName);
+            if (mAppRow.settingsIntent != null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Ignoring duplicate notification preference activity ("
+                            + activityInfo.name + ") for package "
+                            + activityInfo.packageName);
+                }
                 continue;
             }
-            if (row.settingsIntent != null) {
-                if (DEBUG) Log.v(TAG, "Ignoring duplicate notification preference activity ("
-                        + activityInfo.name + ") for package "
-                        + activityInfo.packageName);
-                continue;
-            }
-            row.settingsIntent = new Intent(APP_NOTIFICATION_PREFS_CATEGORY_INTENT)
+            mAppRow.settingsIntent = intent
+                    .setPackage(null)
                     .setClassName(activityInfo.packageName, activityInfo.name);
             if (mChannel != null) {
-                row.settingsIntent.putExtra(Notification.EXTRA_CHANNEL_ID, mChannel.getId());
+                mAppRow.settingsIntent.putExtra(Notification.EXTRA_CHANNEL_ID, mChannel.getId());
             }
             if (mChannelGroup != null) {
-                row.settingsIntent.putExtra(
-                        Notification.EXTRA_CHANNEL_GROUP_ID, mChannelGroup.getId());
+                mAppRow.settingsIntent.putExtra(
+                        Notification.EXTRA_CHANNEL_GROUP_ID, mChannelGroup.getGroup().getId());
             }
         }
     }
@@ -292,134 +257,6 @@
         return null;
     }
 
-    protected void addAppLinkPref() {
-        if (mAppRow.settingsIntent != null && mAppLink == null) {
-            addPreferencesFromResource(R.xml.inapp_notification_settings);
-            mAppLink = findPreference(KEY_APP_LINK);
-            mAppLink.setIntent(mAppRow.settingsIntent);
-        }
-    }
-
-    protected void populateDefaultChannelPrefs() {
-        if (mPkgInfo != null && mChannel != null) {
-            addPreferencesFromResource(R.xml.legacy_channel_notification_settings);
-            setupPriorityPref(mChannel.canBypassDnd());
-            setupVisOverridePref(mChannel.getLockscreenVisibility());
-            setupImportanceToggle();
-            setupBadge();
-        }
-        mSwitchBar.setChecked(!mAppRow.banned
-                && mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
-    }
-
-    abstract void setupBadge();
-
-    abstract void updateDependents(boolean banned);
-
-    // 'allow sound'
-    private void setupImportanceToggle() {
-        mImportanceToggle = (RestrictedSwitchPreference) findPreference(KEY_ALLOW_SOUND);
-        mImportanceToggle.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mImportanceToggle.setEnabled(isChannelConfigurable(mChannel)
-                && !mImportanceToggle.isDisabledByAdmin());
-        mImportanceToggle.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT
-                || mChannel.getImportance() == IMPORTANCE_UNSPECIFIED);
-        mImportanceToggle.setOnPreferenceChangeListener(
-                new Preference.OnPreferenceChangeListener() {
-                    @Override
-                    public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        final int importance =
-                                ((Boolean) newValue ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_LOW);
-                        mChannel.setImportance(importance);
-                        mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-                        mBackend.updateChannel(mPkg, mUid, mChannel);
-                        updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
-                        return true;
-                    }
-                });
-    }
-
-    protected void setupPriorityPref(boolean priority) {
-        mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
-        mPriority.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mPriority.setEnabled(isChannelConfigurable(mChannel) && !mPriority.isDisabledByAdmin());
-        mPriority.setChecked(priority);
-        mPriority.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                final boolean bypassZenMode = (Boolean) newValue;
-                mChannel.setBypassDnd(bypassZenMode);
-                mChannel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
-                mBackend.updateChannel(mPkg, mUid, mChannel);
-                return true;
-            }
-        });
-    }
-
-    protected void setupVisOverridePref(int sensitive) {
-        mVisibilityOverride =
-                (RestrictedDropDownPreference) findPreference(KEY_VISIBILITY_OVERRIDE);
-        ArrayList<CharSequence> entries = new ArrayList<>();
-        ArrayList<CharSequence> values = new ArrayList<>();
-
-        mVisibilityOverride.clearRestrictedItems();
-        if (getLockscreenNotificationsEnabled() && getLockscreenAllowPrivateNotifications()) {
-            final String summaryShowEntry =
-                    getString(R.string.lock_screen_notifications_summary_show);
-            final String summaryShowEntryValue =
-                    Integer.toString(NotificationManager.VISIBILITY_NO_OVERRIDE);
-            entries.add(summaryShowEntry);
-            values.add(summaryShowEntryValue);
-            setRestrictedIfNotificationFeaturesDisabled(summaryShowEntry, summaryShowEntryValue,
-                    DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
-                            | DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-        }
-
-        final String summaryHideEntry = getString(R.string.lock_screen_notifications_summary_hide);
-        final String summaryHideEntryValue = Integer.toString(Notification.VISIBILITY_PRIVATE);
-        entries.add(summaryHideEntry);
-        values.add(summaryHideEntryValue);
-        setRestrictedIfNotificationFeaturesDisabled(summaryHideEntry, summaryHideEntryValue,
-                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-        entries.add(getString(R.string.lock_screen_notifications_summary_disable));
-        values.add(Integer.toString(Notification.VISIBILITY_SECRET));
-        mVisibilityOverride.setEntries(entries.toArray(new CharSequence[entries.size()]));
-        mVisibilityOverride.setEntryValues(values.toArray(new CharSequence[values.size()]));
-
-        if (sensitive == NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
-            mVisibilityOverride.setValue(Integer.toString(getGlobalVisibility()));
-        } else {
-            mVisibilityOverride.setValue(Integer.toString(sensitive));
-        }
-        mVisibilityOverride.setSummary("%s");
-
-        mVisibilityOverride.setOnPreferenceChangeListener(
-                new Preference.OnPreferenceChangeListener() {
-                    @Override
-                    public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        int sensitive = Integer.parseInt((String) newValue);
-                        if (sensitive == getGlobalVisibility()) {
-                            sensitive = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
-                        }
-                        mChannel.setLockscreenVisibility(sensitive);
-                        mChannel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
-                        mBackend.updateChannel(mPkg, mUid, mChannel);
-                        return true;
-                    }
-                });
-        mVisibilityOverride.setDisabledByAdmin(mSuspendedAppsAdmin);
-    }
-
-    protected void setupBlockDesc(int summaryResId) {
-        mBlockedDesc = new FooterPreference(getPrefContext());
-        mBlockedDesc.setSelectable(false);
-        mBlockedDesc.setTitle(summaryResId);
-        mBlockedDesc.setEnabled(false);
-        mBlockedDesc.setOrder(50);
-        mBlockedDesc.setKey(KEY_BLOCKED_DESC);
-        getPreferenceScreen().addPreference(mBlockedDesc);
-    }
-
     protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
             final NotificationChannel channel, String summary) {
         MasterSwitchPreference channelPref = new MasterSwitchPreference(
@@ -447,7 +284,7 @@
                     public boolean onPreferenceChange(Preference preference,
                             Object o) {
                         boolean value = (Boolean) o;
-                        int importance = value ?  IMPORTANCE_LOW : IMPORTANCE_NONE;
+                        int importance = value ? IMPORTANCE_LOW : IMPORTANCE_NONE;
                         channel.setImportance(importance);
                         channel.lockFields(
                                 NotificationChannel.USER_LOCKED_IMPORTANCE);
@@ -461,105 +298,48 @@
         return channelPref;
     }
 
-    protected boolean checkCanBeVisible(int minImportanceVisible) {
-        int importance = mChannel.getImportance();
-        if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
-            return true;
-        }
-        return importance >= minImportanceVisible;
-    }
-
-    protected String getImportanceSummary(NotificationChannel channel) {
-        switch (channel.getImportance()) {
-            case NotificationManager.IMPORTANCE_UNSPECIFIED:
-                return getContext().getString(R.string.notification_importance_unspecified);
-            case NotificationManager.IMPORTANCE_NONE:
-                return getContext().getString(R.string.notification_toggle_off);
-            case NotificationManager.IMPORTANCE_MIN:
-                return getContext().getString(R.string.notification_importance_min);
-            case NotificationManager.IMPORTANCE_LOW:
-                return getContext().getString(R.string.notification_importance_low);
-            case NotificationManager.IMPORTANCE_DEFAULT:
-                if (hasValidSound(channel)) {
-                    return getContext().getString(R.string.notification_importance_default);
-                } else { // Silent
-                    return getContext().getString(R.string.notification_importance_low);
-                }
-            case NotificationManager.IMPORTANCE_HIGH:
-            case NotificationManager.IMPORTANCE_MAX:
-            default:
-                if (hasValidSound(channel)) {
-                    return getContext().getString(R.string.notification_importance_high);
-                } else { // Silent
-                    return getContext().getString(R.string.notification_importance_high_silent);
-                }
-        }
-    }
-
-    private void setRestrictedIfNotificationFeaturesDisabled(CharSequence entry,
-            CharSequence entryValue, int keyguardNotificationFeatures) {
-        RestrictedLockUtils.EnforcedAdmin admin =
-                RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
-                        mContext, keyguardNotificationFeatures, mUserId);
-        if (admin != null) {
-            RestrictedDropDownPreference.RestrictedItem item =
-                    new RestrictedDropDownPreference.RestrictedItem(entry, entryValue, admin);
-            mVisibilityOverride.addRestrictedItem(item);
-        }
-    }
-
-    private int getGlobalVisibility() {
-        int globalVis = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
-        if (!getLockscreenNotificationsEnabled()) {
-            globalVis = Notification.VISIBILITY_SECRET;
-        } else if (!getLockscreenAllowPrivateNotifications()) {
-            globalVis = Notification.VISIBILITY_PRIVATE;
-        }
-        return globalVis;
-    }
-
-    private boolean getLockscreenNotificationsEnabled() {
-        return Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
-    }
-
-    private boolean getLockscreenAllowPrivateNotifications() {
-        return Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
-    }
-
-    protected boolean isLockScreenSecure() {
-        if (mLockPatternUtils == null) {
-            mLockPatternUtils = new LockPatternUtils(getActivity());
-        }
-        boolean lockscreenSecure = mLockPatternUtils.isSecure(UserHandle.myUserId());
-        UserInfo parentUser = mUm.getProfileParent(UserHandle.myUserId());
-        if (parentUser != null){
-            lockscreenSecure |= mLockPatternUtils.isSecure(parentUser.id);
-        }
-
-        return lockscreenSecure;
-    }
-
     protected boolean isChannelConfigurable(NotificationChannel channel) {
-        return !channel.getId().equals(mAppRow.lockedChannelId);
+        if (channel != null && mAppRow != null) {
+            return !channel.getId().equals(mAppRow.lockedChannelId);
+        }
+        return false;
     }
 
     protected boolean isChannelBlockable(NotificationChannel channel) {
-        if (!mAppRow.systemApp) {
-            return true;
-        }
+        if (channel != null && mAppRow != null) {
+            if (!mAppRow.systemApp) {
+                return true;
+            }
 
-        return channel.isBlockableSystem()
-                || channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
+            return channel.isBlockableSystem()
+                    || channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
+        }
+        return false;
     }
 
     protected boolean isChannelGroupBlockable(NotificationChannelGroup group) {
-        if (!mAppRow.systemApp) {
-            return true;
-        }
+        if (group != null && mAppRow != null) {
+            if (!mAppRow.systemApp) {
+                return true;
+            }
 
-        return group.isBlocked();
+            return group.isBlocked();
+        }
+        return false;
+    }
+
+    protected void setVisible(Preference p, boolean visible) {
+        setVisible(getPreferenceScreen(), p, visible);
+    }
+
+    protected void setVisible(PreferenceGroup parent, Preference p, boolean visible) {
+        final boolean isVisible = parent.findPreference(p.getKey()) != null;
+        if (isVisible == visible) return;
+        if (visible) {
+            parent.addPreference(p);
+        } else {
+            parent.removePreference(p);
+        }
     }
 
     protected void startListeningToPackageRemove() {
@@ -589,8 +369,10 @@
         public void onReceive(Context context, Intent intent) {
             String packageName = intent.getData().getSchemeSpecificPart();
             if (mPkgInfo == null || TextUtils.equals(mPkgInfo.packageName, packageName)) {
-                if (DEBUG) Log.d(TAG, "Package (" + packageName + ") removed. Removing"
-                        + "NotificationSettingsBase.");
+                if (DEBUG) {
+                    Log.d(TAG, "Package (" + packageName + ") removed. Removing"
+                            + "NotificationSettingsBase.");
+                }
                 onPackageRemoved();
             }
         }
@@ -604,7 +386,27 @@
                 return left.getId().compareTo(right.getId());
             };
 
-    boolean hasValidSound(NotificationChannel channel) {
-        return channel.getSound() != null && !Uri.EMPTY.equals(channel.getSound());
+    protected class ImportanceListener {
+        protected void onImportanceChanged() {
+            final PreferenceScreen screen = getPreferenceScreen();
+            for (NotificationPreferenceController controller : mControllers) {
+                controller.displayPreference(screen);
+            }
+            updatePreferenceStates();
+
+            boolean hideDynamicFields = false;
+            if (mAppRow == null || mAppRow.banned) {
+                hideDynamicFields = true;
+            } else {
+                if (mChannel != null) {
+                    hideDynamicFields = mChannel.getImportance() == IMPORTANCE_NONE;
+                } else if (mChannelGroup != null) {
+                    hideDynamicFields = mChannelGroup.isBlocked();
+                }
+            }
+            for (Preference preference : mDynamicPreferences) {
+                setVisible(getPreferenceScreen(), preference, !hideDynamicFields);
+            }
+        }
     }
 }
diff --git a/src/com/android/settings/notification/NotificationsOffPreferenceController.java b/src/com/android/settings/notification/NotificationsOffPreferenceController.java
new file mode 100644
index 0000000..74591cf
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationsOffPreferenceController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.widget.FooterPreference;
+
+public class NotificationsOffPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin {
+
+    private static final String KEY_BLOCKED_DESC = "block_desc";
+
+    public NotificationsOffPreferenceController(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BLOCKED_DESC;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (mAppRow == null) {
+            return false;
+        }
+        // Available only when other controllers are unavailable - this UI replaces the UI that
+        // would give more detailed notification controls.
+        return !super.isAvailable();
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow != null) {
+            if (mChannel != null) {
+                preference.setTitle(R.string.channel_notifications_off_desc);
+            } else if (mChannelGroup != null && mChannelGroup.getGroup() == null) {
+                preference.setTitle(R.string.channel_group_notifications_off_desc);
+            } else {
+                preference.setTitle(R.string.app_notifications_off_desc);
+            }
+        }
+        preference.setEnabled(false);
+        preference.setSelectable(false);
+    }
+}
diff --git a/src/com/android/settings/notification/SoundPreferenceController.java b/src/com/android/settings/notification/SoundPreferenceController.java
new file mode 100644
index 0000000..e4414b6
--- /dev/null
+++ b/src/com/android/settings/notification/SoundPreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class SoundPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener,
+        PreferenceManager.OnActivityResultListener {
+
+    private static final String KEY_SOUND = "ringtone";
+    private final SettingsPreferenceFragment mFragment;
+    private final NotificationSettingsBase.ImportanceListener mListener;
+    private NotificationSoundPreference mPreference;
+    protected static final int CODE = 200;
+
+    public SoundPreferenceController(Context context, SettingsPreferenceFragment hostFragment,
+            NotificationSettingsBase.ImportanceListener importanceListener,
+            NotificationBackend backend) {
+        super(context, backend);
+        mFragment = hostFragment;
+        mListener = importanceListener;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_SOUND;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        if (mChannel == null) {
+            return false;
+        }
+        return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
+                && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreference = (NotificationSoundPreference) screen.findPreference(getPreferenceKey());
+    }
+
+    public void updateState(Preference preference) {
+        if (mAppRow!= null && mChannel != null) {
+            NotificationSoundPreference pref = (NotificationSoundPreference) preference;
+            pref.setEnabled(mAdmin == null && isChannelConfigurable());
+            pref.setRingtone(mChannel.getSound());
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mChannel != null) {
+            mChannel.setSound((Uri) newValue, mChannel.getAudioAttributes());
+            saveChannel();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (KEY_SOUND.equals(preference.getKey()) && mFragment != null) {
+            NotificationSoundPreference pref = (NotificationSoundPreference) preference;
+            pref.onPrepareRingtonePickerIntent(pref.getIntent());
+            mFragment.startActivityForResult(preference.getIntent(), CODE);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (CODE == requestCode) {
+            if (mPreference != null) {
+                mPreference.onActivityResult(requestCode, resultCode, data);
+            }
+            // the importance hasn't changed, but the importance description might as a result of
+            // user's selection.
+            mListener.onImportanceChanged();
+            return true;
+        }
+        return false;
+    }
+
+    protected static boolean hasValidSound(NotificationChannel channel) {
+        return channel != null
+                && channel.getSound() != null && !Uri.EMPTY.equals(channel.getSound());
+    }
+}
diff --git a/src/com/android/settings/notification/VibrationPreferenceController.java b/src/com/android/settings/notification/VibrationPreferenceController.java
new file mode 100644
index 0000000..f9b786d
--- /dev/null
+++ b/src/com/android/settings/notification/VibrationPreferenceController.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Vibrator;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class VibrationPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+    private static final String KEY_VIBRATE = "vibrate";
+    private final Vibrator mVibrator;
+
+    public VibrationPreferenceController(Context context, NotificationBackend backend) {
+        super(context, backend);
+        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_VIBRATE;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable() || mChannel == null) {
+            return false;
+       }
+        return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
+                && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())
+                && mVibrator != null
+                && mVibrator.hasVibrator();
+    }
+
+    public void updateState(Preference preference) {
+        if (mChannel != null) {
+            RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+            pref.setDisabledByAdmin(mAdmin);
+            pref.setEnabled(!pref.isDisabledByAdmin() && isChannelConfigurable());
+            pref.setChecked(mChannel.shouldVibrate());
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mChannel != null) {
+            final boolean vibrate = (Boolean) newValue;
+            mChannel.enableVibration(vibrate);
+            saveChannel();
+        }
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/VisibilityPreferenceController.java b/src/com/android/settings/notification/VisibilityPreferenceController.java
new file mode 100644
index 0000000..76caac0
--- /dev/null
+++ b/src/com/android/settings/notification/VisibilityPreferenceController.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.support.v7.preference.Preference;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedLockUtils;
+
+import java.util.ArrayList;
+
+public class VisibilityPreferenceController extends NotificationPreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+    private static final String TAG = "ChannelVisPrefContr";
+    private static final String KEY_VISIBILITY_OVERRIDE = "visibility_override";
+    private LockPatternUtils mLockPatternUtils;
+
+    public VisibilityPreferenceController(Context context, LockPatternUtils utils,
+            NotificationBackend backend) {
+        super(context, backend);
+        mLockPatternUtils = utils;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_VISIBILITY_OVERRIDE;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (!super.isAvailable()) {
+            return false;
+        }
+        if (mChannel == null || mAppRow.banned) {
+            return false;
+        }
+        return checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && isLockScreenSecure();
+    }
+
+    public void updateState(Preference preference) {
+        if (mChannel != null && mAppRow != null) {
+            RestrictedDropDownPreference pref = (RestrictedDropDownPreference) preference;
+            ArrayList<CharSequence> entries = new ArrayList<>();
+            ArrayList<CharSequence> values = new ArrayList<>();
+
+            pref.clearRestrictedItems();
+            if (getLockscreenNotificationsEnabled() && getLockscreenAllowPrivateNotifications()) {
+                final String summaryShowEntry =
+                        mContext.getString(R.string.lock_screen_notifications_summary_show);
+                final String summaryShowEntryValue =
+                        Integer.toString(NotificationManager.VISIBILITY_NO_OVERRIDE);
+                entries.add(summaryShowEntry);
+                values.add(summaryShowEntryValue);
+                setRestrictedIfNotificationFeaturesDisabled(pref, summaryShowEntry,
+                        summaryShowEntryValue,
+                        DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
+                                | DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+            }
+
+            final String summaryHideEntry =
+                    mContext.getString(R.string.lock_screen_notifications_summary_hide);
+            final String summaryHideEntryValue = Integer.toString(Notification.VISIBILITY_PRIVATE);
+            entries.add(summaryHideEntry);
+            values.add(summaryHideEntryValue);
+            setRestrictedIfNotificationFeaturesDisabled(pref,
+                    summaryHideEntry, summaryHideEntryValue,
+                    DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+            entries.add(mContext.getString(R.string.lock_screen_notifications_summary_disable));
+            values.add(Integer.toString(Notification.VISIBILITY_SECRET));
+            pref.setEntries(entries.toArray(new CharSequence[entries.size()]));
+            pref.setEntryValues(values.toArray(new CharSequence[values.size()]));
+
+            if (mChannel.getLockscreenVisibility()
+                    == NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
+                pref.setValue(Integer.toString(getGlobalVisibility()));
+            } else {
+                pref.setValue(Integer.toString(mChannel.getLockscreenVisibility()));
+            }
+            pref.setSummary("%s");
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mChannel != null) {
+            int sensitive = Integer.parseInt((String) newValue);
+            if (sensitive == getGlobalVisibility()) {
+                sensitive = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+            }
+            mChannel.setLockscreenVisibility(sensitive);
+            mChannel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+            saveChannel();
+        }
+        return true;
+    }
+
+    private void setRestrictedIfNotificationFeaturesDisabled(RestrictedDropDownPreference pref,
+            CharSequence entry, CharSequence entryValue, int keyguardNotificationFeatures) {
+        RestrictedLockUtils.EnforcedAdmin admin =
+                RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+                        mContext, keyguardNotificationFeatures, mAppRow.userId);
+        if (admin != null) {
+            RestrictedDropDownPreference.RestrictedItem item =
+                    new RestrictedDropDownPreference.RestrictedItem(entry, entryValue, admin);
+            pref.addRestrictedItem(item);
+        }
+    }
+
+    private int getGlobalVisibility() {
+        int globalVis = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+        if (!getLockscreenNotificationsEnabled()) {
+            globalVis = Notification.VISIBILITY_SECRET;
+        } else if (!getLockscreenAllowPrivateNotifications()) {
+            globalVis = Notification.VISIBILITY_PRIVATE;
+        }
+        return globalVis;
+    }
+
+    private boolean getLockscreenNotificationsEnabled() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
+    }
+
+    private boolean getLockscreenAllowPrivateNotifications() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
+    }
+
+    protected boolean isLockScreenSecure() {
+        boolean lockscreenSecure = mLockPatternUtils.isSecure(UserHandle.myUserId());
+        UserInfo parentUser = mUm.getProfileParent(UserHandle.myUserId());
+        if (parentUser != null){
+            lockscreenSecure |= mLockPatternUtils.isSecure(parentUser.id);
+        }
+
+        return lockscreenSecure;
+    }
+
+}
diff --git a/src/com/android/settings/search/DatabaseIndexingUtils.java b/src/com/android/settings/search/DatabaseIndexingUtils.java
index 8d63ea5..39bcdf84 100644
--- a/src/com/android/settings/search/DatabaseIndexingUtils.java
+++ b/src/com/android/settings/search/DatabaseIndexingUtils.java
@@ -60,8 +60,9 @@
         args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
         final Intent searchDestination = Utils.onBuildStartFragmentIntent(context,
                 className, args, null, 0, screenTitle, false, sourceMetricsCategory);
-        searchDestination.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
-        searchDestination.setClass(context, SearchResultTrampoline.class);
+        searchDestination.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key)
+                .setAction("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
+                .setComponent(null);
         return searchDestination;
     }
 
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 42afee9..d365ae5 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -19,9 +19,13 @@
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.util.FeatureFlagUtils;
 import android.util.Pair;
 import android.view.View;
+import android.widget.Toolbar;
 
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.dashboard.SiteMapManager;
 
 import java.util.List;
@@ -164,4 +168,22 @@
     default FutureTask<List<Pair<String, Float>>> getRankerTask(Context context, String query) {
         return null;
     }
+
+    /**
+     * Initializes the search toolbar.
+     */
+    default void initSearchToolbar(Context context, Toolbar toolbar) {
+        if (context == null || toolbar == null) {
+            return;
+        }
+        toolbar.setOnClickListener(tb -> {
+            final Intent intent;
+            if (FeatureFlagUtils.isEnabled(FeatureFlags.SEARCH_V2)) {
+                intent = new Intent("com.android.settings.action.SETTINGS_SEARCH");
+            } else {
+                intent = new Intent(context, SearchActivity.class);
+            }
+            context.startActivity(intent);
+        });
+    }
 }
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
deleted file mode 100644
index 47d17b6..0000000
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.search;
-
-import android.provider.SearchIndexableResource;
-import android.support.annotation.VisibleForTesting;
-import android.support.annotation.XmlRes;
-
-import com.android.settings.DateTimeSettings;
-import com.android.settings.DeviceInfoSettings;
-import com.android.settings.DisplaySettings;
-import com.android.settings.LegalSettings;
-import com.android.settings.ScreenPinningSettings;
-import com.android.settings.accessibility.AccessibilitySettings;
-import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
-import com.android.settings.accessibility.MagnificationPreferenceFragment;
-import com.android.settings.accounts.UserAndAccountDashboardFragment;
-import com.android.settings.applications.AppAndNotificationDashboardFragment;
-import com.android.settings.applications.DefaultAppSettings;
-import com.android.settings.applications.SpecialAccessSettings;
-import com.android.settings.applications.assist.ManageAssist;
-import com.android.settings.backup.BackupSettingsActivity;
-import com.android.settings.backup.BackupSettingsFragment;
-import com.android.settings.bluetooth.BluetoothSettings;
-import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
-import com.android.settings.datausage.DataUsageMeteredSettings;
-import com.android.settings.datausage.DataUsageSummary;
-import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
-import com.android.settings.development.DevelopmentSettingsDashboardFragment;
-import com.android.settings.deviceinfo.Status;
-import com.android.settings.deviceinfo.StorageDashboardFragment;
-import com.android.settings.deviceinfo.StorageSettings;
-import com.android.settings.display.AmbientDisplaySettings;
-import com.android.settings.display.ScreenZoomSettings;
-import com.android.settings.dream.DreamSettings;
-import com.android.settings.enterprise.EnterprisePrivacySettings;
-import com.android.settings.fuelgauge.BatterySaverSettings;
-import com.android.settings.fuelgauge.PowerUsageAdvanced;
-import com.android.settings.fuelgauge.PowerUsageSummary;
-import com.android.settings.gestures.AssistGestureSettings;
-import com.android.settings.gestures.DoubleTapPowerSettings;
-import com.android.settings.gestures.DoubleTapScreenSettings;
-import com.android.settings.gestures.DoubleTwistGestureSettings;
-import com.android.settings.gestures.GestureSettings;
-import com.android.settings.gestures.PickupGestureSettings;
-import com.android.settings.gestures.SwipeToNotificationSettings;
-import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
-import com.android.settings.inputmethod.PhysicalKeyboardFragment;
-import com.android.settings.inputmethod.VirtualKeyboardFragment;
-import com.android.settings.language.LanguageAndInputSettings;
-import com.android.settings.location.LocationSettings;
-import com.android.settings.location.ScanningSettings;
-import com.android.settings.network.NetworkDashboardFragment;
-import com.android.settings.nfc.PaymentSettings;
-import com.android.settings.notification.ChannelImportanceSettings;
-import com.android.settings.notification.ConfigureNotificationSettings;
-import com.android.settings.notification.SoundSettings;
-import com.android.settings.notification.ZenModeAutomationSettings;
-import com.android.settings.notification.ZenModeBehaviorSettings;
-import com.android.settings.notification.ZenModeSettings;
-import com.android.settings.print.PrintSettingsFragment;
-import com.android.settings.security.EncryptionAndCredential;
-import com.android.settings.security.LockscreenDashboardFragment;
-import com.android.settings.security.SecuritySettings;
-import com.android.settings.security.screenlock.ScreenLockSettings;
-import com.android.settings.sim.SimSettings;
-import com.android.settings.support.SupportDashboardActivity;
-import com.android.settings.system.ResetDashboardFragment;
-import com.android.settings.system.SystemDashboardFragment;
-import com.android.settings.tts.TextToSpeechSettings;
-import com.android.settings.tts.TtsEnginePreferenceFragment;
-import com.android.settings.users.UserSettings;
-import com.android.settings.wallpaper.WallpaperTypeSettings;
-import com.android.settings.wifi.ConfigureWifiSettings;
-import com.android.settings.wifi.WifiSettings;
-
-import java.util.Collection;
-import java.util.HashMap;
-
-public final class SearchIndexableResources {
-    @XmlRes
-    public static final int NO_RES_ID = 0;
-
-    @VisibleForTesting
-    static final HashMap<String, SearchIndexableResource> sResMap = new HashMap<>();
-
-    @VisibleForTesting
-    static void addIndex(Class<?> indexClass) {
-        String className = indexClass.getName();
-        SearchIndexableResource resource = new SearchIndexableResource(
-                0 /* rank */, NO_RES_ID, className, NO_RES_ID);
-
-        sResMap.put(className, resource);
-    }
-
-    static {
-        addIndex(WifiSettings.class);
-        addIndex(NetworkDashboardFragment.class);
-        addIndex(ConfigureWifiSettings.class);
-        addIndex(BluetoothSettings.class);
-        addIndex(SimSettings.class);
-        addIndex(DataUsageSummary.class);
-        addIndex(DataUsageMeteredSettings.class);
-        addIndex(ScreenZoomSettings.class);
-        addIndex(DisplaySettings.class);
-        addIndex(AmbientDisplaySettings.class);
-        addIndex(WallpaperTypeSettings.class);
-        addIndex(AppAndNotificationDashboardFragment.class);
-        addIndex(SoundSettings.class);
-        addIndex(ZenModeSettings.class);
-        addIndex(StorageSettings.class);
-        addIndex(PowerUsageAdvanced.class);
-        addIndex(DefaultAppSettings.class);
-        addIndex(ManageAssist.class);
-        addIndex(SpecialAccessSettings.class);
-        addIndex(UserSettings.class);
-        addIndex(AssistGestureSettings.class);
-        addIndex(PickupGestureSettings.class);
-        addIndex(DoubleTapScreenSettings.class);
-        addIndex(DoubleTapPowerSettings.class);
-        addIndex(DoubleTwistGestureSettings.class);
-        addIndex(SwipeToNotificationSettings.class);
-        addIndex(GestureSettings.class);
-        addIndex(LanguageAndInputSettings.class);
-        addIndex(LocationSettings.class);
-        addIndex(ScanningSettings.class);
-        addIndex(SecuritySettings.class);
-        addIndex(ScreenLockSettings.class);
-        addIndex(EncryptionAndCredential.class);
-        addIndex(ScreenPinningSettings.class);
-        addIndex(UserAndAccountDashboardFragment.class);
-        addIndex(VirtualKeyboardFragment.class);
-        addIndex(AvailableVirtualKeyboardFragment.class);
-        addIndex(PhysicalKeyboardFragment.class);
-        addIndex(BackupSettingsActivity.class);
-        addIndex(BackupSettingsFragment.class);
-        addIndex(DateTimeSettings.class);
-        addIndex(AccessibilitySettings.class);
-        addIndex(PrintSettingsFragment.class);
-        addIndex(DevelopmentSettingsDashboardFragment.class);
-        addIndex(DeviceInfoSettings.class);
-        addIndex(Status.class);
-        addIndex(LegalSettings.class);
-        addIndex(SystemDashboardFragment.class);
-        addIndex(ResetDashboardFragment.class);
-        addIndex(StorageDashboardFragment.class);
-        addIndex(ConnectedDeviceDashboardFragment.class);
-        addIndex(EnterprisePrivacySettings.class);
-        addIndex(PaymentSettings.class);
-        addIndex(TextToSpeechSettings.class);
-        addIndex(TtsEnginePreferenceFragment.class);
-        addIndex(MagnificationPreferenceFragment.class);
-        addIndex(AccessibilityShortcutPreferenceFragment.class);
-        addIndex(ChannelImportanceSettings.class);
-        addIndex(DreamSettings.class);
-        addIndex(SupportDashboardActivity.class);
-        addIndex(AutomaticStorageManagerSettings.class);
-        addIndex(ConfigureNotificationSettings.class);
-        addIndex(PowerUsageSummary.class);
-        addIndex(BatterySaverSettings.class);
-        addIndex(LockscreenDashboardFragment.class);
-        addIndex(ZenModeBehaviorSettings.class);
-        addIndex(ZenModeAutomationSettings.class);
-    }
-
-    private SearchIndexableResources() {
-    }
-
-    public static int size() {
-        return sResMap.size();
-    }
-
-    public static SearchIndexableResource getResourceByName(String className) {
-        return sResMap.get(className);
-    }
-
-    public static Collection<SearchIndexableResource> values() {
-        return sResMap.values();
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
index 622378b..0422e67 100644
--- a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
+++ b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
@@ -17,6 +17,19 @@
 package com.android.settings.search;
 
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
@@ -33,20 +46,164 @@
 import android.database.MatrixCursor;
 import android.provider.SearchIndexableResource;
 import android.provider.SearchIndexablesProvider;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.settings.DateTimeSettings;
+import com.android.settings.DeviceInfoSettings;
+import com.android.settings.DisplaySettings;
+import com.android.settings.LegalSettings;
+import com.android.settings.ScreenPinningSettings;
+import com.android.settings.accessibility.AccessibilitySettings;
+import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
+import com.android.settings.accessibility.MagnificationPreferenceFragment;
+import com.android.settings.accounts.UserAndAccountDashboardFragment;
+import com.android.settings.applications.AppAndNotificationDashboardFragment;
+import com.android.settings.applications.DefaultAppSettings;
+import com.android.settings.applications.SpecialAccessSettings;
+import com.android.settings.applications.assist.ManageAssist;
+import com.android.settings.backup.BackupSettingsActivity;
+import com.android.settings.backup.BackupSettingsFragment;
+import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
+import com.android.settings.datausage.DataUsageMeteredSettings;
+import com.android.settings.datausage.DataUsageSummary;
+import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
+import com.android.settings.development.DevelopmentSettingsDashboardFragment;
+import com.android.settings.deviceinfo.Status;
+import com.android.settings.deviceinfo.StorageDashboardFragment;
+import com.android.settings.deviceinfo.StorageSettings;
+import com.android.settings.display.AmbientDisplaySettings;
+import com.android.settings.display.ScreenZoomSettings;
+import com.android.settings.dream.DreamSettings;
+import com.android.settings.enterprise.EnterprisePrivacySettings;
+import com.android.settings.fuelgauge.BatterySaverSettings;
+import com.android.settings.fuelgauge.PowerUsageAdvanced;
+import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.gestures.AssistGestureSettings;
+import com.android.settings.gestures.DoubleTapPowerSettings;
+import com.android.settings.gestures.DoubleTapScreenSettings;
+import com.android.settings.gestures.DoubleTwistGestureSettings;
+import com.android.settings.gestures.GestureSettings;
+import com.android.settings.gestures.PickupGestureSettings;
+import com.android.settings.gestures.SwipeToNotificationSettings;
+import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
+import com.android.settings.inputmethod.PhysicalKeyboardFragment;
+import com.android.settings.inputmethod.VirtualKeyboardFragment;
+import com.android.settings.language.LanguageAndInputSettings;
+import com.android.settings.location.LocationSettings;
+import com.android.settings.location.ScanningSettings;
+import com.android.settings.network.NetworkDashboardFragment;
+import com.android.settings.nfc.PaymentSettings;
+import com.android.settings.notification.ConfigureNotificationSettings;
+import com.android.settings.notification.SoundSettings;
+import com.android.settings.notification.ZenModeAutomationSettings;
+import com.android.settings.notification.ZenModeBehaviorSettings;
+import com.android.settings.notification.ZenModeSettings;
+import com.android.settings.print.PrintSettingsFragment;
+import com.android.settings.security.EncryptionAndCredential;
+import com.android.settings.security.LockscreenDashboardFragment;
+import com.android.settings.security.SecuritySettings;
+import com.android.settings.security.screenlock.ScreenLockSettings;
+import com.android.settings.sim.SimSettings;
+import com.android.settings.support.SupportDashboardActivity;
+import com.android.settings.system.ResetDashboardFragment;
+import com.android.settings.system.SystemDashboardFragment;
+import com.android.settings.tts.TextToSpeechSettings;
+import com.android.settings.tts.TtsEnginePreferenceFragment;
+import com.android.settings.users.UserSettings;
+import com.android.settings.wallpaper.WallpaperTypeSettings;
+import com.android.settings.wifi.ConfigureWifiSettings;
+import com.android.settings.wifi.WifiSettings;
+
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
     public static final boolean DEBUG = false;
     private static final String TAG = "SettingsSearchProvider";
 
+    public static final Set<Class> INDEXABLES = new HashSet<>();
     private static final Collection<String> INVALID_KEYS;
 
+    @VisibleForTesting
+    static void addIndex(Class indexClass) {
+        INDEXABLES.add(indexClass);
+    }
+
     static {
+        addIndex(WifiSettings.class);
+        addIndex(NetworkDashboardFragment.class);
+        addIndex(ConfigureWifiSettings.class);
+        addIndex(BluetoothSettings.class);
+        addIndex(SimSettings.class);
+        addIndex(DataUsageSummary.class);
+        addIndex(DataUsageMeteredSettings.class);
+        addIndex(ScreenZoomSettings.class);
+        addIndex(DisplaySettings.class);
+        addIndex(AmbientDisplaySettings.class);
+        addIndex(WallpaperTypeSettings.class);
+        addIndex(AppAndNotificationDashboardFragment.class);
+        addIndex(SoundSettings.class);
+        addIndex(ZenModeSettings.class);
+        addIndex(StorageSettings.class);
+        addIndex(PowerUsageAdvanced.class);
+        addIndex(DefaultAppSettings.class);
+        addIndex(ManageAssist.class);
+        addIndex(SpecialAccessSettings.class);
+        addIndex(UserSettings.class);
+        addIndex(AssistGestureSettings.class);
+        addIndex(PickupGestureSettings.class);
+        addIndex(DoubleTapScreenSettings.class);
+        addIndex(DoubleTapPowerSettings.class);
+        addIndex(DoubleTwistGestureSettings.class);
+        addIndex(SwipeToNotificationSettings.class);
+        addIndex(GestureSettings.class);
+        addIndex(LanguageAndInputSettings.class);
+        addIndex(LocationSettings.class);
+        addIndex(ScanningSettings.class);
+        addIndex(SecuritySettings.class);
+        addIndex(ScreenLockSettings.class);
+        addIndex(EncryptionAndCredential.class);
+        addIndex(ScreenPinningSettings.class);
+        addIndex(UserAndAccountDashboardFragment.class);
+        addIndex(VirtualKeyboardFragment.class);
+        addIndex(AvailableVirtualKeyboardFragment.class);
+        addIndex(PhysicalKeyboardFragment.class);
+        addIndex(BackupSettingsActivity.class);
+        addIndex(BackupSettingsFragment.class);
+        addIndex(DateTimeSettings.class);
+        addIndex(AccessibilitySettings.class);
+        addIndex(PrintSettingsFragment.class);
+        addIndex(DevelopmentSettingsDashboardFragment.class);
+        addIndex(DeviceInfoSettings.class);
+        addIndex(Status.class);
+        addIndex(LegalSettings.class);
+        addIndex(SystemDashboardFragment.class);
+        addIndex(ResetDashboardFragment.class);
+        addIndex(StorageDashboardFragment.class);
+        addIndex(ConnectedDeviceDashboardFragment.class);
+        addIndex(EnterprisePrivacySettings.class);
+        addIndex(PaymentSettings.class);
+        addIndex(TextToSpeechSettings.class);
+        addIndex(TtsEnginePreferenceFragment.class);
+        addIndex(MagnificationPreferenceFragment.class);
+        addIndex(AccessibilityShortcutPreferenceFragment.class);
+        addIndex(DreamSettings.class);
+        addIndex(SupportDashboardActivity.class);
+        addIndex(AutomaticStorageManagerSettings.class);
+        addIndex(ConfigureNotificationSettings.class);
+        addIndex(PowerUsageSummary.class);
+        addIndex(BatterySaverSettings.class);
+        addIndex(LockscreenDashboardFragment.class);
+        addIndex(ZenModeBehaviorSettings.class);
+        addIndex(ZenModeAutomationSettings.class);
+
         INVALID_KEYS = new ArraySet<>();
         INVALID_KEYS.add(null);
         INVALID_KEYS.add("");
@@ -60,8 +217,9 @@
     @Override
     public Cursor queryXmlResources(String[] projection) {
         MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
-        Collection<SearchIndexableResource> values = SearchIndexableResources.values();
-        for (SearchIndexableResource val : values) {
+        final List<SearchIndexableResource> resources =
+                getSearchIndexableResourcesFromProvider(getContext());
+        for (SearchIndexableResource val : resources) {
             Object[] ref = new Object[INDEXABLES_XML_RES_COLUMNS.length];
             ref[COLUMN_INDEX_XML_RES_RANK] = val.rank;
             ref[COLUMN_INDEX_XML_RES_RESID] = val.xmlResId;
@@ -72,13 +230,33 @@
             ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = null; // intent target class
             cursor.addRow(ref);
         }
+
         return cursor;
     }
 
     @Override
     public Cursor queryRawData(String[] projection) {
-        MatrixCursor result = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
-        return result;
+        MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
+        final List<SearchIndexableRaw> raws = getSearchIndexableRawFromProvider(getContext());
+        for (SearchIndexableRaw val : raws) {
+            Object[] ref = new Object[INDEXABLES_RAW_COLUMNS.length];
+            ref[COLUMN_INDEX_RAW_TITLE] = val.title;
+            ref[COLUMN_INDEX_RAW_SUMMARY_ON] = val.summaryOn;
+            ref[COLUMN_INDEX_RAW_SUMMARY_OFF] = val.summaryOff;
+            ref[COLUMN_INDEX_RAW_ENTRIES] = val.entries;
+            ref[COLUMN_INDEX_RAW_KEYWORDS] = val.keywords;
+            ref[COLUMN_INDEX_RAW_SCREEN_TITLE] = val.screenTitle;
+            ref[COLUMN_INDEX_RAW_CLASS_NAME] = val.className;
+            ref[COLUMN_INDEX_RAW_ICON_RESID] = val.iconResId;
+            ref[COLUMN_INDEX_RAW_INTENT_ACTION] = val.intentAction;
+            ref[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = val.intentTargetPackage;
+            ref[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = val.intentTargetClass;
+            ref[COLUMN_INDEX_RAW_KEY] = val.key;
+            ref[COLUMN_INDEX_RAW_USER_ID] = val.userId;
+            cursor.addRow(ref);
+        }
+
+        return cursor;
     }
 
     /**
@@ -89,29 +267,23 @@
     @Override
     public Cursor queryNonIndexableKeys(String[] projection) {
         MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
-        final Collection<String> values = new HashSet<>();
-        final Context context = getContext();
+        final List<String> nonIndexableKeys = getNonIndexableKeysFromProvider(getContext());
+        for (String nik : nonIndexableKeys) {
+            final Object[] ref = new Object[NON_INDEXABLES_KEYS_COLUMNS.length];
+            ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = nik;
+            cursor.addRow(ref);
+        }
 
-        for (SearchIndexableResource sir : SearchIndexableResources.values()) {
-            if (DEBUG) {
-                Log.d(TAG, "Getting non-indexable from " + sir.className);
-            }
+        return cursor;
+    }
+
+    private List<String> getNonIndexableKeysFromProvider(Context context) {
+        final List<String> nonIndexableKeys = new ArrayList<>();
+
+        for (Class clazz : INDEXABLES) {
             final long startTime = System.currentTimeMillis();
-            final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(sir.className);
-            if (clazz == null) {
-                Log.d(TAG, "SearchIndexableResource '" + sir.className +
-                        "' should implement the " + Indexable.class.getName() + " interface!");
-                continue;
-            }
-
-            final Indexable.SearchIndexProvider provider =
-                    DatabaseIndexingUtils.getSearchIndexProvider(clazz);
-
-            if (provider == null) {
-                Log.d(TAG, "Unable to get SearchIndexableProvider from " + clazz);
-                continue;
-            }
-
+            Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
+                    clazz);
             List<String> providerNonIndexableKeys = provider.getNonIndexableKeys(context);
 
             if (providerNonIndexableKeys == null || providerNonIndexableKeys.isEmpty()) {
@@ -123,22 +295,69 @@
             }
 
             if (providerNonIndexableKeys.removeAll(INVALID_KEYS)) {
-                Log.v(TAG, clazz.getName() + " tried to add an empty non-indexable key");
+                Log.v(TAG, provider + " tried to add an empty non-indexable key");
             }
+
             if (DEBUG) {
                 final long totalTime = System.currentTimeMillis() - startTime;
                 Log.d(TAG, "Non-indexables " + providerNonIndexableKeys.size() + ", total time "
                         + totalTime);
             }
-            values.addAll(providerNonIndexableKeys);
+
+            nonIndexableKeys.addAll(providerNonIndexableKeys);
         }
 
-        for (String nik : values) {
+        return nonIndexableKeys;
+    }
 
-            final Object[] ref = new Object[NON_INDEXABLES_KEYS_COLUMNS.length];
-            ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = nik;
-            cursor.addRow(ref);
+    private List<SearchIndexableResource> getSearchIndexableResourcesFromProvider(Context context) {
+        List<SearchIndexableResource> resourceList = new ArrayList<>();
+
+        for (Class clazz : INDEXABLES) {
+            Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
+                    clazz);
+
+            final List<SearchIndexableResource> resList =
+                    provider.getXmlResourcesToIndex(context, true);
+
+            if (resList == null) {
+                continue;
+            }
+
+            for (SearchIndexableResource item : resList) {
+                item.className = TextUtils.isEmpty(item.className)
+                        ? clazz.getName()
+                        : item.className;
+            }
+
+            resourceList.addAll(resList);
         }
-        return cursor;
+
+        return resourceList;
+    }
+
+    private List<SearchIndexableRaw> getSearchIndexableRawFromProvider(Context context) {
+        final List<SearchIndexableRaw> rawList = new ArrayList<>();
+
+        for (Class clazz : INDEXABLES) {
+            Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
+                    clazz);
+            final List<SearchIndexableRaw> providerRaws = provider.getRawDataToIndex(context,
+                    true /* enabled */);
+
+            if (providerRaws == null) {
+                continue;
+            }
+
+            for (SearchIndexableRaw raw : providerRaws) {
+                // The classname and intent information comes from the PreIndexData
+                // This will be more clear when provider conversion is done at PreIndex time.
+                raw.className = clazz.getName();
+
+            }
+            rawList.addAll(providerRaws);
+        }
+
+        return rawList;
     }
 }
diff --git a/src/com/android/settings/search/indexing/IndexDataConverter.java b/src/com/android/settings/search/indexing/IndexDataConverter.java
index ab60f62..65fa279 100644
--- a/src/com/android/settings/search/indexing/IndexDataConverter.java
+++ b/src/com/android/settings/search/indexing/IndexDataConverter.java
@@ -31,7 +31,6 @@
 
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.search.DatabaseIndexingUtils;
-import com.android.settings.search.Indexable;
 import com.android.settings.search.ResultPayload;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.search.XmlParserUtils;
@@ -89,21 +88,8 @@
                 final SearchIndexableResource sir = (SearchIndexableResource) data;
                 final Set<String> resourceNonIndexableKeys =
                         getNonIndexableKeysForResource(nonIndexableKeys, sir.packageName);
-
-                if (sir.xmlResId == 0) {
-                    // Index from provider
-                    final Indexable.SearchIndexProvider provider = getSearchProvider(sir);
-                    if (provider == null) {
-                        continue;
-                    }
-                    indexData.addAll(convertIndexProvider(provider, sir, resourceNonIndexableKeys));
-
-                } else {
-                    final List<IndexData> resourceData = convertResource(sir,
-                            resourceNonIndexableKeys);
-                    indexData.addAll(resourceData);
-                }
-
+                final List<IndexData> resourceData = convertResource(sir, resourceNonIndexableKeys);
+                indexData.addAll(resourceData);
             }
         }
 
@@ -305,84 +291,10 @@
         return resourceIndexData;
     }
 
-    private List<IndexData> convertIndexProvider(Indexable.SearchIndexProvider provider,
-            SearchIndexableResource sir, Set<String> nonIndexableKeys) {
-        final List<IndexData> indexData = new ArrayList<>();
-
-        final String className = sir.className;
-        final String intentAction = sir.intentAction;
-        final String intentTargetPackage = sir.intentTargetPackage;
-
-        // TODO (b/65376542) Move provider conversion to PreIndexTime
-        // TODO (b/37741509) Providers don't use general non-indexable keys
-        nonIndexableKeys.addAll(provider.getNonIndexableKeys(mContext));
-
-        final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(mContext,
-                true /* enabled */);
-
-        if (rawList != null) {
-            for (SearchIndexableRaw raw : rawList) {
-                // The classname and intent information comes from the PreIndexData
-                // This will be more clear when provider conversion is done at PreIndex time.
-                raw.className = className;
-                raw.intentAction = intentAction;
-                raw.intentTargetPackage = intentTargetPackage;
-
-                IndexData.Builder builder = convertRaw(raw, nonIndexableKeys);
-                if (builder != null) {
-                    indexData.add(builder.build(mContext));
-                }
-            }
-        }
-
-        final List<SearchIndexableResource> resList =
-                provider.getXmlResourcesToIndex(mContext, true);
-
-        if (resList != null) {
-            for (SearchIndexableResource item : resList) {
-                item.className = TextUtils.isEmpty(item.className)
-                        ? className
-                        : item.className;
-                item.intentAction = TextUtils.isEmpty(item.intentAction)
-                        ? intentAction
-                        : item.intentAction;
-                item.intentTargetPackage = TextUtils.isEmpty(item.intentTargetPackage)
-                        ? intentTargetPackage
-                        : item.intentTargetPackage;
-
-                indexData.addAll(convertResource(item, nonIndexableKeys));
-            }
-        }
-
-        return indexData;
-    }
-
     private Set<String> getNonIndexableKeysForResource(Map<String, Set<String>> nonIndexableKeys,
             String packageName) {
         return nonIndexableKeys.containsKey(packageName)
                 ? nonIndexableKeys.get(packageName)
                 : new HashSet<>();
     }
-
-    /**
-     * @return Return the {@link Indexable.SearchIndexProvider} corresponding to the
-     * class specified by the Class name specified by {@param sir}.
-     */
-    private Indexable.SearchIndexProvider getSearchProvider(SearchIndexableResource sir) {
-        if (TextUtils.isEmpty(sir.className)) {
-            Log.w(LOG_TAG, "Cannot index an empty Search Provider name!");
-            return null;
-        }
-
-        final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(sir.className);
-        if (clazz == null) {
-            Log.d(LOG_TAG, "SearchIndexableResource '" + sir.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
-        return DatabaseIndexingUtils.getSearchIndexProvider(clazz);
-    }
 }
diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java
index 02beaaa..35ce909 100644
--- a/src/com/android/settings/security/SecuritySettings.java
+++ b/src/com/android/settings/security/SecuritySettings.java
@@ -206,7 +206,7 @@
         mManageDeviceAdminPreferenceController
                 = new ManageDeviceAdminPreferenceController(activity);
         mEnterprisePrivacyPreferenceController
-                = new EnterprisePrivacyPreferenceController(activity, null /* lifecycle */);
+                = new EnterprisePrivacyPreferenceController(activity);
         mLockScreenNotificationPreferenceController
                 = new LockScreenNotificationPreferenceController(activity);
     }
@@ -400,7 +400,9 @@
         mManageDeviceAdminPreferenceController.updateState(
                 root.findPreference(KEY_MANAGE_DEVICE_ADMIN));
         mEnterprisePrivacyPreferenceController.displayPreference(root);
-        mEnterprisePrivacyPreferenceController.onResume();
+        final Preference enterprisePrivacyPreference = root.findPreference(
+                mEnterprisePrivacyPreferenceController.getPreferenceKey());
+        mEnterprisePrivacyPreferenceController.updateState(enterprisePrivacyPreference);
 
         return root;
     }
@@ -893,7 +895,7 @@
                 keys.add(KEY_MANAGE_TRUST_AGENTS);
             }
 
-            if (!(new EnterprisePrivacyPreferenceController(context, null /* lifecycle */))
+            if (!(new EnterprisePrivacyPreferenceController(context))
                     .isAvailable()) {
                 keys.add(KEY_ENTERPRISE_PRIVACY);
             }
diff --git a/src/com/android/settings/security/screenlock/ScreenLockSettings.java b/src/com/android/settings/security/screenlock/ScreenLockSettings.java
index 8d48325..029a556 100644
--- a/src/com/android/settings/security/screenlock/ScreenLockSettings.java
+++ b/src/com/android/settings/security/screenlock/ScreenLockSettings.java
@@ -39,6 +39,8 @@
 
     private static final String TAG = "ScreenLockSettings";
 
+    private static final String KEY_LOCK_SCREEN_TITLE = "security_settings_password_sub_screen";
+
     private static final int MY_USER_ID = UserHandle.myUserId();
     private LockPatternUtils mLockPatternUtils;
 
@@ -102,5 +104,12 @@
                     return buildPreferenceControllers(context, null /* parent */,
                             null /* lifecycle */, new LockPatternUtils(context));
                 }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    final List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add(KEY_LOCK_SCREEN_TITLE);
+                    return keys;
+                }
             };
 }
diff --git a/src/com/android/settings/users/ProfileUpdateReceiver.java b/src/com/android/settings/users/ProfileUpdateReceiver.java
index d532089..80fa10a 100644
--- a/src/com/android/settings/users/ProfileUpdateReceiver.java
+++ b/src/com/android/settings/users/ProfileUpdateReceiver.java
@@ -38,13 +38,13 @@
         // Profile changed, lets get the photo and write to user manager
         new Thread() {
             public void run() {
-                Utils.copyMeProfilePhoto(context, null);
+                UserSettings.copyMeProfilePhoto(context, null);
                 copyProfileName(context);
             }
         }.start();
     }
 
-    static void copyProfileName(Context context) {
+    private static void copyProfileName(Context context) {
         SharedPreferences prefs = context.getSharedPreferences("profile", Context.MODE_PRIVATE);
         if (prefs.contains(KEY_PROFILE_NAME_COPIED_ONCE)) {
             return;
@@ -55,7 +55,8 @@
         String profileName = Utils.getMeProfileName(context, false /* partial name */);
         if (profileName != null && profileName.length() > 0) {
             um.setUserName(userId, profileName);
-            // Flag that we've written the profile one time at least. No need to do it in the future.
+            // Flag that we've written the profile one time at least. No need to do it in the
+            // future.
             prefs.edit().putBoolean(KEY_PROFILE_NAME_COPIED_ONCE, true).commit();
         }
     }
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 642f289..906c9d4 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -30,7 +30,9 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,7 +40,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.ContactsContract;
 import android.provider.Settings.Global;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
 import android.support.v7.preference.PreferenceGroup;
@@ -53,6 +58,7 @@
 import android.widget.SimpleAdapter;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.UserIcons;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
@@ -68,6 +74,8 @@
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -134,7 +142,8 @@
     private boolean mShouldUpdateUserList = true;
     private final Object mUserLock = new Object();
     private UserManager mUserManager;
-    private SparseArray<Bitmap> mUserIcons = new SparseArray<Bitmap>();
+    private SparseArray<Bitmap> mUserIcons = new SparseArray<>();
+    private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>();
 
     private EditUserInfoController mEditUserInfoController =
             new EditUserInfoController();
@@ -324,7 +333,7 @@
                 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
                 if (user.iconPath == null || user.iconPath.equals("")) {
                     // Assign profile photo.
-                    Utils.copyMeProfilePhoto(getActivity(), user);
+                    copyMeProfilePhoto(getActivity(), user);
                 }
                 return user.name;
             }
@@ -397,7 +406,7 @@
 
     private UserInfo createRestrictedProfile() {
         UserInfo newUserInfo = mUserManager.createRestrictedProfile(mAddingUserName);
-        if (newUserInfo != null && !Utils.assignDefaultPhoto(getActivity(), newUserInfo.id)) {
+        if (newUserInfo != null && !assignDefaultPhoto(getActivity(), newUserInfo.id)) {
             return null;
         }
         return newUserInfo;
@@ -405,7 +414,7 @@
 
     private UserInfo createTrustedUser() {
         UserInfo newUserInfo = mUserManager.createUser(mAddingUserName, 0);
-        if (newUserInfo != null && !Utils.assignDefaultPhoto(getActivity(), newUserInfo.id)) {
+        if (newUserInfo != null && !assignDefaultPhoto(getActivity(), newUserInfo.id)) {
             return null;
         }
         return newUserInfo;
@@ -914,7 +923,7 @@
                 for (int userId : values[0]) {
                     Bitmap bitmap = mUserManager.getUserIcon(userId);
                     if (bitmap == null) {
-                        bitmap = Utils.getDefaultUserIconAsBitmap(userId);
+                        bitmap = getDefaultUserIconAsBitmap(userId);
                     }
                     mUserIcons.append(userId, bitmap);
                 }
@@ -925,7 +934,7 @@
 
     private Drawable getEncircledDefaultIcon() {
         if (mDefaultIconDrawable == null) {
-            mDefaultIconDrawable = encircle(Utils.getDefaultUserIconAsBitmap(UserHandle.USER_NULL));
+            mDefaultIconDrawable = encircle(getDefaultUserIconAsBitmap(UserHandle.USER_NULL));
         }
         return mDefaultIconDrawable;
     }
@@ -1025,6 +1034,65 @@
         mMePreference.setTitle(label);
     }
 
+    /**
+     * Returns a default user icon (as a {@link Bitmap}) for the given user.
+     *
+     * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
+     * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
+     */
+    private static Bitmap getDefaultUserIconAsBitmap(int userId) {
+        Bitmap bitmap = null;
+        // Try finding the corresponding bitmap in the dark bitmap cache
+        bitmap = sDarkDefaultUserBitmapCache.get(userId);
+        if (bitmap == null) {
+            bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
+            // Save it to cache
+            sDarkDefaultUserBitmapCache.put(userId, bitmap);
+        }
+        return bitmap;
+    }
+
+    /**
+     * Assign the default photo to user with {@paramref userId}
+     * @param context used to get the {@link UserManager}
+     * @param userId  used to get the icon bitmap
+     * @return true if assign photo successfully, false if failed
+     */
+    @VisibleForTesting
+    static boolean assignDefaultPhoto(Context context, int userId) {
+        if (context == null) {
+            return false;
+        }
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
+        um.setUserIcon(userId, bitmap);
+
+        return true;
+    }
+
+    @WorkerThread
+    static void copyMeProfilePhoto(Context context, UserInfo user) {
+        Uri contactUri = ContactsContract.Profile.CONTENT_URI;
+
+        int userId = user != null ? user.id : UserHandle.myUserId();
+
+        InputStream avatarDataStream = ContactsContract.Contacts.openContactPhotoInputStream(
+                context.getContentResolver(),
+                contactUri, true);
+        // If there's no profile photo, assign a default avatar
+        if (avatarDataStream == null) {
+            assignDefaultPhoto(context, userId);
+            return;
+        }
+
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
+        um.setUserIcon(userId, icon);
+        try {
+            avatarDataStream.close();
+        } catch (IOException ioe) { }
+    }
+
     private static class SummaryProvider implements SummaryLoader.SummaryProvider {
 
         private final Context mContext;
diff --git a/src/com/android/settings/wallpaper/WallpaperSuggestionActivity.java b/src/com/android/settings/wallpaper/WallpaperSuggestionActivity.java
index ca37b64..77142ed 100644
--- a/src/com/android/settings/wallpaper/WallpaperSuggestionActivity.java
+++ b/src/com/android/settings/wallpaper/WallpaperSuggestionActivity.java
@@ -51,9 +51,9 @@
     @VisibleForTesting
     void startFallbackSuggestion() {
         // fall back to default wallpaper picker
-        Utils.startWithFragment(this, WallpaperTypeSettings.class.getName(), null, null, 0,
-                R.string.wallpaper_suggestion_title, null,
-                MetricsProto.MetricsEvent.DASHBOARD_SUMMARY);
+        Utils.startWithFragment(this, WallpaperTypeSettings.class.getName(),
+                R.string.wallpaper_suggestion_title, MetricsProto.MetricsEvent.DASHBOARD_SUMMARY,
+                Intent.FLAG_ACTIVITY_FORWARD_RESULT);
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/wallpaper/WallpaperTypeSettings.java b/src/com/android/settings/wallpaper/WallpaperTypeSettings.java
index 1ca8ac7..3c95785 100644
--- a/src/com/android/settings/wallpaper/WallpaperTypeSettings.java
+++ b/src/com/android/settings/wallpaper/WallpaperTypeSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.wallpaper;
 
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -67,7 +68,7 @@
         // Add Preference items for each of the matching activities
         for (ResolveInfo info : rList) {
             Preference pref = new Preference(getPrefContext());
-            Intent prefIntent = new Intent(intent);
+            Intent prefIntent = new Intent(intent).addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
             prefIntent.setComponent(new ComponentName(
                     info.activityInfo.packageName, info.activityInfo.name));
             pref.setIntent(prefIntent);
@@ -79,6 +80,16 @@
         }
     }
 
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference.getIntent() == null) {
+            return super.onPreferenceTreeClick(preference);
+        }
+        startActivity(preference.getIntent());
+        finish();
+        return true;
+    }
+
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
         new BaseSearchIndexProvider() {
             @Override
diff --git a/src/com/android/settings/wifi/ConnectedAccessPointPreference.java b/src/com/android/settings/wifi/ConnectedAccessPointPreference.java
new file mode 100644
index 0000000..6b9c788
--- /dev/null
+++ b/src/com/android/settings/wifi/ConnectedAccessPointPreference.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+
+/**
+ * An AP preference for the currently connected AP
+ */
+public class ConnectedAccessPointPreference extends AccessPointPreference implements
+        View.OnClickListener {
+
+    private OnGearClickListener mOnGearClickListener;
+
+    public ConnectedAccessPointPreference(AccessPoint accessPoint, Context context,
+            UserBadgeCache cache, @DrawableRes int iconResId, boolean forSavedNetworks) {
+        super(accessPoint, context, cache, iconResId, forSavedNetworks);
+    }
+
+    public void setOnGearClickListener(OnGearClickListener l) {
+        mOnGearClickListener = l;
+        notifyChanged();
+    }
+
+    @Override
+    protected int getSecondTargetResId() {
+        return R.layout.preference_widget_gear;
+    }
+
+    @Override
+    protected boolean shouldHideSecondTarget() {
+        return mOnGearClickListener == null;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        final View gear = holder.findViewById(R.id.settings_button);
+        if (gear != null) {
+            gear.setOnClickListener(this);
+        }
+        setDividerVisibility(holder, View.VISIBLE);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == R.id.settings_button) {
+            if (mOnGearClickListener != null) {
+                mOnGearClickListener.onGearClick(this);
+            }
+        }
+    }
+
+    public interface OnGearClickListener {
+        void onGearClick(ConnectedAccessPointPreference p);
+    }
+}
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 5490889..14db3d3 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -782,7 +782,7 @@
                     continue;
                 }
                 LongPressAccessPointPreference preference =
-                        createLongPressActionPointPreference(accessPoint);
+                        createLongPressAccessPointPreference(accessPoint);
                 preference.setKey(key);
                 preference.setOrder(index);
                 if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
@@ -817,10 +817,17 @@
     }
 
     @NonNull
-    private LongPressAccessPointPreference createLongPressActionPointPreference(
+    private LongPressAccessPointPreference createLongPressAccessPointPreference(
             AccessPoint accessPoint) {
         return new LongPressAccessPointPreference(accessPoint, getPrefContext(), mUserBadgeCache,
-                false, R.drawable.ic_wifi_signal_0, this);
+                false /* forSavedNetworks */, R.drawable.ic_wifi_signal_0, this);
+    }
+
+    @NonNull
+    private ConnectedAccessPointPreference createConnectedAccessPointPreference(
+            AccessPoint accessPoint) {
+        return new ConnectedAccessPointPreference(accessPoint, getPrefContext(), mUserBadgeCache,
+                R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */);
     }
 
     /**
@@ -859,7 +866,7 @@
 
         // Else same AP is connected, simply refresh the connected access point preference
         // (first and only access point in this category).
-        ((LongPressAccessPointPreference) mConnectedAccessPointPreferenceCategory.getPreference(0))
+        ((AccessPointPreference) mConnectedAccessPointPreferenceCategory.getPreference(0))
                 .refresh();
         return true;
     }
@@ -869,20 +876,19 @@
      * {@link #mConnectedAccessPointPreferenceCategory}.
      */
     private void addConnectedAccessPointPreference(AccessPoint connectedAp) {
-        final LongPressAccessPointPreference pref = getOrCreatePreference(connectedAp);
+        final ConnectedAccessPointPreference pref = createConnectedAccessPointPreference(
+                connectedAp);
 
         // Launch details page on click.
-        pref.setOnPreferenceClickListener(preference -> {
-            // Save the state of the current access point in the bundle so that we can restore it
-            // in the Wifi Network Details Fragment
+        pref.setOnGearClickListener(l -> {
             pref.getAccessPoint().saveWifiState(pref.getExtras());
 
             SettingsActivity activity = (SettingsActivity) WifiSettings.this.getActivity();
             activity.startPreferencePanel(this,
                     WifiNetworkDetailsFragment.class.getName(), pref.getExtras(),
                     R.string.wifi_details_title, null, null, 0);
-            return true;
         });
+
         pref.refresh();
 
         mConnectedAccessPointPreferenceCategory.addPreference(pref);
@@ -893,15 +899,6 @@
         }
     }
 
-    private LongPressAccessPointPreference getOrCreatePreference(AccessPoint ap) {
-        LongPressAccessPointPreference pref = (LongPressAccessPointPreference)
-                getCachedPreference(AccessPointPreference.generatePreferenceKey(ap));
-        if (pref == null) {
-            pref = createLongPressActionPointPreference(ap);
-        }
-        return pref;
-    }
-
     /** Removes all preferences and hide the {@link #mConnectedAccessPointPreferenceCategory}. */
     private void removeConnectedAccessPointPreference() {
         mConnectedAccessPointPreferenceCategory.removeAll();
@@ -1093,7 +1090,7 @@
                 public void run() {
                     Object tag = accessPoint.getTag();
                     if (tag != null) {
-                        ((LongPressAccessPointPreference) tag).refresh();
+                        ((AccessPointPreference) tag).refresh();
                     }
                 }
             });
@@ -1102,7 +1099,7 @@
 
     @Override
     public void onLevelChanged(AccessPoint accessPoint) {
-        ((LongPressAccessPointPreference) accessPoint.getTag()).onLevelChanged();
+        ((AccessPointPreference) accessPoint.getTag()).onLevelChanged();
     }
 
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
diff --git a/src/com/android/settings/wrapper/NotificationChannelGroupWrapper.java b/src/com/android/settings/wrapper/NotificationChannelGroupWrapper.java
new file mode 100644
index 0000000..dbfff1a
--- /dev/null
+++ b/src/com/android/settings/wrapper/NotificationChannelGroupWrapper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wrapper;
+
+import android.app.NotificationChannelGroup;
+
+/**
+ * Wrapper for {@link NotificationChannelGroup} until roboletric supports O MR1.
+ */
+public class NotificationChannelGroupWrapper {
+
+    private final NotificationChannelGroup mGroup;
+
+    public NotificationChannelGroupWrapper(NotificationChannelGroup group) {
+        mGroup = group;
+    }
+
+    /**
+     * Get the real group object so we can call APIs directly on it.
+     */
+    public NotificationChannelGroup getGroup() {
+        return mGroup;
+    }
+
+    public String getDescription() {
+        if (mGroup != null) {
+            return mGroup.getDescription();
+        }
+        return null;
+    }
+
+    public void setDescription(String desc) {
+        if (mGroup != null) {
+            mGroup.setDescription(desc);
+        }
+    }
+
+    public boolean isBlocked() {
+        if (mGroup != null) {
+            return mGroup.isBlocked();
+        }
+        return true;
+    }
+
+    public void setBlocked(boolean blocked) {
+        if (mGroup != null) {
+            mGroup.setBlocked(blocked);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index fda5c8a..6704fc3 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -7,6 +7,10 @@
 com.android.settings.development.featureflags.FeatureFlagsDashboard
 com.android.settings.development.qstile.DevelopmentTileConfigFragment
 com.android.settings.deviceinfo.StorageProfileFragment
+com.android.settings.notification.ChannelNotificationSettings
+com.android.settings.notification.ChannelImportanceSettings
+com.android.settings.notification.ChannelGroupNotificationSettings
+com.android.settings.notification.AppNotificationSettings
 com.android.settings.wifi.details.WifiNetworkDetailsFragment
 com.android.settings.wifi.p2p.WifiP2pSettings
 com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera
@@ -15,4 +19,4 @@
 com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages
 com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment
 com.android.settings.wifi.tether.WifiTetherSettings
-com.android.settings.wifi.SavedAccessPointsWifiSettings
+com.android.settings.wifi.SavedAccessPointsWifiSettings
\ No newline at end of file
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 4ea5338..c2a084c 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -19,8 +19,6 @@
 com.android.settings.applications.VrListenerSettings
 com.android.settings.inputmethod.UserDictionaryList
 com.android.settings.datausage.DataSaverSummary
-com.android.settings.notification.ChannelNotificationSettings
-com.android.settings.notification.ChannelGroupNotificationSettings
 com.android.settings.datausage.AppDataUsage
 com.android.settings.datausage.DataPlanUsageSummary
 com.android.settings.accessibility.FontSizePreferenceFragmentForSetupWizard
@@ -55,7 +53,6 @@
 com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment
 com.android.settings.print.PrintServiceSettingsFragment
 com.android.settings.wfd.WifiDisplaySettings
-com.android.settings.notification.AppNotificationSettings
 com.android.settings.deviceinfo.PrivateVolumeSettings
 com.android.settings.users.AppRestrictionsFragment
 com.android.settings.deviceinfo.PrivateVolumeUnmount
@@ -82,4 +79,4 @@
 com.android.settings.TetherSettings
 com.android.settings.ApnEditor
 com.android.settings.UserCredentialsSettings
-com.android.settings.TestingSettings
+com.android.settings.TestingSettings
\ No newline at end of file
diff --git a/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider b/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider
index b329072..a71b040 100644
--- a/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider
+++ b/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider
@@ -1 +1 @@
-com.android.settings.fuelgauge.PowerUsageSummary
+com.android.settings.fuelgauge.PowerUsageSummary
\ No newline at end of file
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 33a1d95..c2909ac 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -15,7 +15,6 @@
   -->
 
 <resources>
-    <bool name="config_show_camera_hal_hdrplus">false</bool>
     <bool name="config_enableColorTemperature">false</bool>
     <bool name="config_show_camera_laser_sensor">false</bool>
     <bool name="config_show_connectivity_monitor">false</bool>
diff --git a/tests/robotests/res/values/config.xml b/tests/robotests/res/values/config.xml
index 0afbe29..9e2d911 100644
--- a/tests/robotests/res/values/config.xml
+++ b/tests/robotests/res/values/config.xml
@@ -19,6 +19,5 @@
     <bool name="config_new_device_intro_suggestion_supported">true</bool>
     <bool name="config_enableColorTemperature">true</bool>
     <bool name="config_show_camera_laser_sensor">true</bool>
-    <bool name="config_show_camera_hal_hdrplus">true</bool>
     <bool name="config_show_connectivity_monitor">true</bool>
 </resources>
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/internal/app/NightDisplayController.java b/tests/robotests/src/com/android/internal/app/ColorDisplayController.java
similarity index 97%
rename from tests/robotests/src/com/android/internal/app/NightDisplayController.java
rename to tests/robotests/src/com/android/internal/app/ColorDisplayController.java
index b20de68..74e7d8a 100644
--- a/tests/robotests/src/com/android/internal/app/NightDisplayController.java
+++ b/tests/robotests/src/com/android/internal/app/ColorDisplayController.java
@@ -19,7 +19,7 @@
  * Fake controller to make robolectric test compile. Should be removed when Robolectric supports
  * API 25.
  */
-public class NightDisplayController {
+public class ColorDisplayController {
 
     public static final int AUTO_MODE_DISABLED = 0;
     public static final int AUTO_MODE_CUSTOM = 1;
diff --git a/tests/robotests/src/com/android/settings/SettingsActivityTest.java b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
index fac136c..509ecda 100644
--- a/tests/robotests/src/com/android/settings/SettingsActivityTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
@@ -17,7 +17,6 @@
 package com.android.settings;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -29,13 +28,11 @@
 import android.app.ActivityManager;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.Bundle;
 
-import com.android.settings.search.SearchActivity;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
@@ -46,7 +43,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -97,15 +93,4 @@
 
         assertThat((boolean) bundle.get(SettingsActivity.SAVE_KEY_SHOW_HOME_AS_UP)).isTrue();
     }
-
-    @Test
-    public void testOnClick() {
-        doReturn("com.android.settings").when(mActivity).getPackageName();
-
-        mActivity.onClick(null);
-
-        Intent intent = ShadowApplication.getInstance().getNextStartedActivity();
-        assertThat(intent.getComponent()).isEqualTo(
-                new ComponentName("com.android.settings", SearchActivity.class.getName()));
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index ca02c1d..f813457 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -47,9 +47,8 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class UtilsTest {
 
-    private static final String TIME_DESCRIPTION = "1 day 20 hours 30 minutes";
     private static final String PACKAGE_NAME = "com.android.app";
-    private Context mContext;
+
     @Mock
     private WifiManager wifiManager;
     @Mock
@@ -60,6 +59,7 @@
     private DevicePolicyManagerWrapper mDevicePolicyManager;
     @Mock
     private UserManager mUserManager;
+    private Context mContext;
 
     @Before
     public void setUp() {
@@ -100,12 +100,6 @@
     }
 
     @Test
-    public void testAssignDefaultPhoto_ContextNull_ReturnFalseAndNotCrash() {
-        // Should not crash here
-        assertThat(Utils.assignDefaultPhoto(null, 0)).isFalse();
-    }
-
-    @Test
     public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
         final String expectedTime = "5m 30s";
diff --git a/tests/robotests/src/com/android/settings/accounts/AddUserWhenLockedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AddUserWhenLockedPreferenceControllerTest.java
index d399838..faaf7db 100644
--- a/tests/robotests/src/com/android/settings/accounts/AddUserWhenLockedPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AddUserWhenLockedPreferenceControllerTest.java
@@ -17,7 +17,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -27,7 +26,6 @@
 import android.content.pm.UserInfo;
 import android.os.UserManager;
 import android.provider.Settings.Global;
-import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.TestConfig;
@@ -70,13 +68,12 @@
         when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo);
         when(mUserInfo.isAdmin()).thenReturn(false);
         final RestrictedSwitchPreference preference = mock(RestrictedSwitchPreference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
         when(preference.getKey()).thenReturn(mController.getPreferenceKey());
+        when(mScreen.findPreference(preference.getKey())).thenReturn(preference);
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        verify(preference).setVisible(false);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/accounts/AutoSyncDataPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AutoSyncDataPreferenceControllerTest.java
index b8ad837..4715f88 100644
--- a/tests/robotests/src/com/android/settings/accounts/AutoSyncDataPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AutoSyncDataPreferenceControllerTest.java
@@ -15,19 +15,23 @@
  */
 package com.android.settings.accounts;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.when;
+
 import android.app.Fragment;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
+import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
-import android.support.v14.preference.SwitchPreference;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,14 +40,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import java.util.ArrayList;
+import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -55,9 +53,8 @@
     private UserManager mUserManager;
     @Mock(answer = RETURNS_DEEP_STUBS)
     private Fragment mFragment;
-    @Mock
-    private Preference mPreference;
 
+    private Preference mPreference;
     private Context mContext;
     private AutoSyncDataPreferenceController mController;
     private AutoSyncDataPreferenceController.ConfirmAutoSyncChangeFragment mConfirmSyncFragment;
@@ -71,9 +68,9 @@
         mController = new AutoSyncDataPreferenceController(mContext, mFragment);
         mConfirmSyncFragment = new AutoSyncDataPreferenceController.ConfirmAutoSyncChangeFragment();
         mConfirmSyncFragment.setTargetFragment(mFragment, 0);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
-        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
+        mPreference = new Preference(mContext);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
@@ -82,7 +79,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -92,7 +89,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
@@ -105,7 +102,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
@@ -119,7 +116,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -135,5 +132,4 @@
         mConfirmSyncFragment.onClick(null, DialogInterface.BUTTON_NEGATIVE);
         assertThat(preference.isChecked()).isFalse();
     }
-
 }
diff --git a/tests/robotests/src/com/android/settings/accounts/AutoSyncPersonalDataPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AutoSyncPersonalDataPreferenceControllerTest.java
index ab462c0..597fbd4 100644
--- a/tests/robotests/src/com/android/settings/accounts/AutoSyncPersonalDataPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AutoSyncPersonalDataPreferenceControllerTest.java
@@ -15,17 +15,21 @@
  */
 package com.android.settings.accounts;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.when;
+
 import android.app.Fragment;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,12 +38,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import java.util.ArrayList;
+import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -51,10 +51,9 @@
     private UserManager mUserManager;
     @Mock(answer = RETURNS_DEEP_STUBS)
     private Fragment mFragment;
-    @Mock
-    private Preference mPreference;
 
     private Context mContext;
+    private Preference mPreference;
     private AutoSyncPersonalDataPreferenceController mController;
 
     @Before
@@ -64,9 +63,9 @@
         shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
         mContext = shadowContext.getApplicationContext();
         mController = new AutoSyncPersonalDataPreferenceController(mContext, mFragment);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
-        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
+        mPreference = new Preference(mContext);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
@@ -75,7 +74,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -85,7 +84,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -98,11 +97,11 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
-    public void displayPref_prefAvaiable_shouldDisplay() {
+    public void displayPref_prefAvailable_shouldDisplay() {
         List<UserInfo> infos = new ArrayList<>();
         infos.add(new UserInfo(1, "user 1", 0));
         infos.add(new UserInfo(2, "user 2", 0));
@@ -112,7 +111,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
 }
diff --git a/tests/robotests/src/com/android/settings/accounts/EmergencyInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/EmergencyInfoPreferenceControllerTest.java
index 0f7de46..29a011e 100644
--- a/tests/robotests/src/com/android/settings/accounts/EmergencyInfoPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/EmergencyInfoPreferenceControllerTest.java
@@ -15,31 +15,6 @@
  */
 package com.android.settings.accounts;
 
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.os.UserManager;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-import com.android.settings.search.SearchIndexableRaw;
-import com.android.settings.testutils.shadow.ShadowAccountManager;
-import com.android.settings.testutils.shadow.ShadowContentResolver;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
-
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Answers.RETURNS_DEEP_STUBS;
 import static org.mockito.Matchers.any;
@@ -49,8 +24,35 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.search.SearchIndexableRaw;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowAccountManager;
+import com.android.settings.testutils.shadow.ShadowContentResolver;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class EmergencyInfoPreferenceControllerTest {
 
     @Mock(answer = RETURNS_DEEP_STUBS)
@@ -61,15 +63,19 @@
     private UserManager mUserManager;
 
     private EmergencyInfoPreferenceController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mController = new EmergencyInfoPreferenceController(mContext);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
-    public void updateRawDataToIndex_prefUnavaiable_shouldNotUpdate() {
+    public void updateRawDataToIndex_prefUnavailable_shouldNotUpdate() {
         final List<SearchIndexableRaw> data = new ArrayList<>();
         when(mContext.getPackageManager().queryIntentActivities(
                 any(Intent.class), anyInt()))
@@ -81,7 +87,7 @@
     }
 
     @Test
-    public void updateRawDataToIndex_prefAvaiable_shouldUpdate() {
+    public void updateRawDataToIndex_prefAvailable_shouldUpdate() {
         final List<SearchIndexableRaw> data = new ArrayList<>();
         final List<ResolveInfo> infos = new ArrayList<>();
         infos.add(new ResolveInfo());
@@ -95,23 +101,18 @@
     }
 
     @Test
-    public void displayPref_prefUnAvaiable_shouldNotDisplay() {
+    public void displayPref_prefUnAvailable_shouldNotDisplay() {
         when(mContext.getPackageManager().queryIntentActivities(
                 any(Intent.class), anyInt()))
                 .thenReturn(null);
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
-    public void displayPref_prefAvaiable_shouldDisplay() {
-        final List<SearchIndexableRaw> data = new ArrayList<>();
+    public void displayPref_prefAvailable_shouldDisplay() {
         final List<ResolveInfo> infos = new ArrayList<>();
         infos.add(new ResolveInfo());
         when(mContext.getPackageManager().queryIntentActivities(
diff --git a/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java b/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java
index 583a004..35d1194 100644
--- a/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/applications/LayoutPreferenceTest.java
@@ -24,11 +24,10 @@
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
 import android.support.v7.preference.PreferenceViewHolder;
 import android.view.LayoutInflater;
-import android.view.View;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -37,19 +36,17 @@
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class LayoutPreferenceTest {
 
     private Context mContext;
     private LayoutPreference mPreference;
-    private View mRootView;
     private PreferenceViewHolder mHolder;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
         mPreference = new LayoutPreference(mContext, R.layout.two_action_buttons);
-        mRootView = mPreference.mRootView;
         mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext)
                 .inflate(R.layout.layout_preference_frame, null, false));
     }
diff --git a/tests/robotests/src/com/android/settings/applications/ManageDomainUrlsTest.java b/tests/robotests/src/com/android/settings/applications/ManageDomainUrlsTest.java
new file mode 100644
index 0000000..3e89647
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/ManageDomainUrlsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.applications.ApplicationsState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ManageDomainUrlsTest {
+
+    @Mock
+    private ApplicationsState.AppEntry mAppEntry;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void domainAppPreferenceShouldUseAppPreferenceLayout() {
+        mAppEntry.info = new ApplicationInfo();
+        mAppEntry.info.packageName = "com.android.settings.test";
+        final ManageDomainUrls.DomainAppPreference pref =
+                new ManageDomainUrls.DomainAppPreference(mContext, null, mAppEntry);
+
+        assertThat(pref.getLayoutResource()).isEqualTo(R.layout.preference_app);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceControllerTest.java
index ca5c10b..8a8cc29 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceControllerTest.java
@@ -17,6 +17,9 @@
 package com.android.settings.applications.defaultapps;
 
 import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.anyList;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
@@ -26,6 +29,8 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.os.UserManager;
 import android.support.v7.preference.Preference;
 
@@ -42,6 +47,9 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
+import java.util.Arrays;
+
+
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class DefaultHomePreferenceControllerTest {
@@ -112,4 +120,32 @@
         assertThat(DefaultHomePreferenceController.isHomeDefault(pkgName, mPackageManager))
                 .isFalse();
     }
+
+    @Test
+    public void testGetSettingIntent_homeHasNoSetting_shouldNotReturnSettingIntent() {
+        when(mPackageManager.getHomeActivities(anyList())).thenReturn(
+                new ComponentName("test.pkg", "class"));
+        assertThat(mController.getSettingIntent(mController.getDefaultAppInfo())).isNull();
+    }
+
+    @Test
+    public void testGetSettingIntent_homeHasOneSetting_shouldReturnSettingIntent() {
+        when(mPackageManager.getHomeActivities(anyList())).thenReturn(
+                new ComponentName("test.pkg", "class"));
+        when(mPackageManager.queryIntentActivities(any(), eq(0))).thenReturn(
+                Arrays.asList(mock(ResolveInfo.class)));
+
+        Intent intent = mController.getSettingIntent(mController.getDefaultAppInfo());
+        assertThat(intent).isNotNull();
+        assertThat(intent.getPackage()).isEqualTo("test.pkg");
+    }
+
+    @Test
+    public void testGetSettingIntent_homeHasMultipleSettings_shouldNotReturnSettingIntent() {
+        when(mPackageManager.getHomeActivities(anyList())).thenReturn(
+                new ComponentName("test.pkg", "class"));
+        when(mPackageManager.queryIntentActivities(any(), eq(0))).thenReturn(
+                Arrays.asList(mock(ResolveInfo.class), mock(ResolveInfo.class)));
+        assertThat(mController.getSettingIntent(mController.getDefaultAppInfo())).isNull();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerEventsTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerEventsTest.java
index 240ece1..62e4986 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerEventsTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerEventsTest.java
@@ -36,7 +36,7 @@
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O,
         shadows=SettingsShadowBluetoothDevice.class)
 public class BluetoothDetailsControllerEventsTest extends BluetoothDetailsControllerTestBase {
 
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
index 3b8db04..a02a0c1 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
@@ -37,7 +37,7 @@
 import org.robolectric.RuntimeEnvironment;
 
 public class BluetoothDetailsControllerTestBase {
-    protected Context mContext = RuntimeEnvironment.application;
+    protected Context mContext;
     protected Lifecycle mLifecycle;
     protected DeviceConfig mDeviceConfig;
     protected BluetoothDevice mDevice;
@@ -58,6 +58,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
         mPreferenceManager = new PreferenceManager(mContext);
         mScreen = mPreferenceManager.createPreferenceScreen(mContext);
         mDeviceConfig = makeDefaultDeviceConfig();
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java
index 98a3580..2dc411c 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHeaderControllerTest.java
@@ -45,7 +45,7 @@
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O,
         shadows={SettingsShadowBluetoothDevice.class, ShadowEntityHeaderController.class,
                 SettingsShadowResources.class})
 public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsControllerTestBase {
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressControllerTest.java
index 24b28a1..4edcc74 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressControllerTest.java
@@ -17,8 +17,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
 import com.android.settingslib.widget.FooterPreference;
 
@@ -27,9 +27,10 @@
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
-        shadows=SettingsShadowBluetoothDevice.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O,
+        shadows = SettingsShadowBluetoothDevice.class)
 public class BluetoothDetailsMacAddressControllerTest extends BluetoothDetailsControllerTestBase {
+
     private BluetoothDetailsMacAddressController mController;
 
     @Override
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
index 445e4e3..eca5df9 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
@@ -54,7 +54,7 @@
 import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O,
         shadows=SettingsShadowBluetoothDevice.class)
 public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsControllerTestBase {
     private BluetoothDetailsProfilesController mController;
diff --git a/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java
deleted file mode 100644
index 9bf73ce..0000000
--- a/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.core;
-
-import android.content.Context;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-import com.android.settingslib.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 static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * Tests for {@link DynamicAvailabilityPreferenceController}.
- */
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public final class DynamicAvailabilityPreferenceControllerTest {
-
-    private final String PREFERENCE_KEY = "preference_key";
-
-    private @Mock Context mContext;
-    private @Mock Preference mPreference;
-    private @Mock PreferenceScreen mScreen;
-    private @Mock Lifecycle mLifecycle;
-    private @Mock PreferenceAvailabilityObserver mObserver;
-
-    private boolean mIsAvailable;
-    private Preference mUpdatedPreference = null;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mPreference.getKey()).thenReturn(PREFERENCE_KEY);
-        when(mScreen.findPreference(PREFERENCE_KEY)).thenReturn(mPreference);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
-    }
-
-    @Test
-    public void testAvailableToUnavailable() {
-        mIsAvailable = true;
-
-        final DynamicAvailabilityPreferenceController controller
-                = new DynamicAvailabilityPreferenceControllerTestable(mLifecycle);
-        verify(mLifecycle).addObserver(controller);
-
-        controller.displayPreference(mScreen);
-        verify(mScreen, never()).removePreference(mPreference);
-        verify(mScreen, never()).addPreference(mPreference);
-        assertThat(mUpdatedPreference).isNull();
-
-        controller.onResume();
-        verify(mScreen, never()).removePreference(mPreference);
-        verify(mScreen, never()).addPreference(mPreference);
-        assertThat(mUpdatedPreference).isEqualTo(mPreference);
-
-        mUpdatedPreference = null;
-        mIsAvailable = false;
-        controller.onResume();
-        verify(mScreen).removePreference(mPreference);
-        verify(mScreen, never()).addPreference(mPreference);
-        assertThat(mUpdatedPreference).isNull();
-    }
-
-    @Test
-    public void testUnavailableToAvailable() {
-        mIsAvailable = false;
-
-        final DynamicAvailabilityPreferenceController controller
-                = new DynamicAvailabilityPreferenceControllerTestable(mLifecycle);
-        verify(mLifecycle).addObserver(controller);
-
-        controller.displayPreference(mScreen);
-        verify(mScreen).removePreference(mPreference);
-        verify(mScreen, never()).addPreference(mPreference);
-        assertThat(mUpdatedPreference).isNull();
-
-        reset(mScreen);
-        controller.onResume();
-        verify(mScreen, never()).removePreference(mPreference);
-        verify(mScreen, never()).addPreference(mPreference);
-        assertThat(mUpdatedPreference).isNull();
-
-        mIsAvailable = true;
-        controller.onResume();
-        verify(mScreen, never()).removePreference(mPreference);
-        verify(mScreen).addPreference(mPreference);
-        assertThat(mUpdatedPreference).isEqualTo(mPreference);
-    }
-
-    @Test
-    public void testNotifyOnAvailabilityUpdate() {
-        final DynamicAvailabilityPreferenceController controller
-                = new DynamicAvailabilityPreferenceControllerTestable(mLifecycle);
-        controller.setAvailabilityObserver(mObserver);
-        assertThat(controller.getAvailabilityObserver()).isEqualTo(mObserver);
-
-        mIsAvailable = false;
-        controller.isAvailable();
-        verify(mObserver).onPreferenceAvailabilityUpdated(PREFERENCE_KEY, false);
-
-        mIsAvailable = true;
-        controller.isAvailable();
-        verify(mObserver).onPreferenceAvailabilityUpdated(PREFERENCE_KEY, true);
-    }
-
-    private class DynamicAvailabilityPreferenceControllerTestable
-            extends DynamicAvailabilityPreferenceController {
-        public DynamicAvailabilityPreferenceControllerTestable(Lifecycle lifecycle) {
-            super(DynamicAvailabilityPreferenceControllerTest.this.mContext, lifecycle);
-        }
-
-        @Override
-        public boolean isAvailable() {
-            notifyOnAvailabilityUpdate(mIsAvailable);
-            return mIsAvailable;
-        }
-
-        @Override
-        public void updateState(Preference preference) {
-            mUpdatedPreference = preference;
-        }
-
-        @Override
-        public String getPreferenceKey() {
-            return PREFERENCE_KEY;
-        }
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
index 30d60cc..3837d81 100644
--- a/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
@@ -31,9 +31,9 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.TestConfig;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
-import com.android.settings.TestConfig;
 
 import org.junit.After;
 import org.junit.Before;
@@ -45,7 +45,7 @@
 import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = {
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O, shadows = {
         SettingsShadowSystemProperties.class
 })
 public class InstrumentedPreferenceFragmentTest {
@@ -76,7 +76,7 @@
     @Test
     public void onCreatePreferences_noPreferenceScreenResId_shouldNotAddPreference() {
         SettingsShadowSystemProperties.set(
-                FeatureFlagUtils.FFLAG_PREFIX + mFragment.FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE,
+                FeatureFlagUtils.FFLAG_PREFIX + FeatureFlags.USE_PREFERENCE_SCREEN_TITLE,
                 "true");
 
         mFragment.onCreatePreferences(Bundle.EMPTY, null /* rootKey */);
@@ -87,7 +87,7 @@
     @Test
     public void onCreatePreferences_gotPreferenceScreenResId_shouldAddPreferences() {
         SettingsShadowSystemProperties.set(
-                FeatureFlagUtils.FFLAG_PREFIX + mFragment.FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE,
+                FeatureFlagUtils.FFLAG_PREFIX + FeatureFlags.USE_PREFERENCE_SCREEN_TITLE,
                 "true");
         mFragment.setPreferenceScreenResId(R.xml.screen_pinning_settings);
         when(mFragment.getActivity()).thenReturn(mActivity);
@@ -101,7 +101,7 @@
     @Test
     public void onCreatePreferences_gotPrefScreenResIdAndTitle_shouldAddPreferencesAndSetTitle() {
         SettingsShadowSystemProperties.set(
-                FeatureFlagUtils.FFLAG_PREFIX + mFragment.FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE,
+                FeatureFlagUtils.FFLAG_PREFIX + FeatureFlags.USE_PREFERENCE_SCREEN_TITLE,
                 "true");
         mFragment.setPreferenceScreenResId(R.xml.screen_pinning_settings);
         when(mFragment.getActivity()).thenReturn(mActivity);
@@ -114,14 +114,11 @@
         verify(mActivity).setTitle(title);
     }
 
-    private static class InstrumentedPreferenceFragmentTestable
+    public static class InstrumentedPreferenceFragmentTestable
             extends InstrumentedPreferenceFragment {
 
         private int mScreenId = -1;
 
-        public InstrumentedPreferenceFragmentTestable() {
-        }
-
         @Override
         public int getMetricsCategory() {
             return MetricsEvent.VIEW_UNKNOWN;
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
index 8feef92..b74453c 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
@@ -42,6 +42,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.Settings.NightDisplaySuggestionActivity;
 import com.android.settings.TestConfig;
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
@@ -67,7 +68,7 @@
 import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = {
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O, shadows = {
         ShadowSecureSettings.class,
         SettingsShadowResources.class,
         SettingsShadowSystemProperties.class
@@ -136,7 +137,7 @@
     public void isSuggestionV2Enabled_isNotLowMemoryDevice_sysPropOn_shouldReturnTrue() {
         when(mActivityManager.isLowRamDevice()).thenReturn(false);
         SettingsShadowSystemProperties.set(
-                FeatureFlagUtils.FFLAG_PREFIX + mProvider.FEATURE_FLAG_SUGGESTIONS_V2, "true");
+                FeatureFlagUtils.FFLAG_PREFIX + FeatureFlags.SUGGESTIONS_V2, "true");
         assertThat(mProvider.isSuggestionV2Enabled(mContext)).isTrue();
     }
 
diff --git a/tests/robotests/src/com/android/settings/datetime/ZonePickerComparatorTest.java b/tests/robotests/src/com/android/settings/datetime/ZonePickerComparatorTest.java
new file mode 100644
index 0000000..4c1794c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datetime/ZonePickerComparatorTest.java
@@ -0,0 +1,123 @@
+package com.android.settings.datetime;
+
+import com.android.settings.datetime.ZonePicker;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settingslib.datetime.ZoneGetter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ZonePickerComparatorTest {
+
+    // Strings in Chinese are sorted by alphabet order of their Pinyin.
+    // "伦敦" -> "lundun";  "纽约" -> "niuyue";  "悉尼" -> "xini"
+    // "开罗" -> "kailuo";  "雅典" -> "yadian";  "上海" -> "shanghai"
+    private static final String[] TEST_CHINESE_NAME =
+            new String[]{"伦敦", "纽约", "悉尼", "开罗", "雅典", "上海"};
+    private static final String[] ORDERED_CHINESE_NAME =
+            new String[]{"开罗", "伦敦", "纽约", "上海", "悉尼", "雅典"};
+
+    private static final String[] TEST_ENGLISH_NAME =
+            new String[]{"London", "New York", "Sydney", "Cairo", "Athens", "Shanghai"};
+    private static final String[] ORDERED_ENGLISH_NAME =
+            new String[]{"Athens", "Cairo", "London", "New York", "Shanghai", "Sydney"};
+
+    private static final Locale INIT_LOCALE = Locale.getDefault();
+
+    private Map<String, List> mTestDataMap;
+    private List<Map<String, Object>> mTestList;
+
+    @Before
+    public void setUp() {
+        mTestDataMap = new HashMap<>();
+        mTestDataMap.put("zh_CN", Arrays.asList(TEST_CHINESE_NAME));
+        mTestDataMap.put("en_US", Arrays.asList(TEST_ENGLISH_NAME));
+    }
+
+    @After
+    public void tearDown() {
+        Locale.setDefault(INIT_LOCALE);
+    }
+
+    @Test
+    public void testComparator_sortChineseString() {
+        String sortKey = ZoneGetter.KEY_DISPLAY_LABEL;
+        mTestList = getMockZonesList("zh_CN");
+        Locale.setDefault(new Locale("zh"));
+        final ZonePicker.MyComparator comparator = new ZonePicker.MyComparator(sortKey);
+        assertThat(comparator).isNotNull();
+        Collections.sort(mTestList, comparator);
+        for (int i = 0; i < mTestList.size(); i++) {
+            assertThat(mTestList.get(i).get(sortKey).toString())
+                    .isEqualTo(ORDERED_CHINESE_NAME[i]);
+        }
+    }
+
+    @Test
+    public void testComparator_sortEnglishString() {
+        String sortKey = ZoneGetter.KEY_DISPLAY_LABEL;
+        mTestList = getMockZonesList("en_US");
+        Locale.setDefault(new Locale("en"));
+        final ZonePicker.MyComparator comparator = new ZonePicker.MyComparator(sortKey);
+        assertThat(comparator).isNotNull();
+        Collections.sort(mTestList, comparator);
+        for (int i = 0; i < mTestList.size(); i++) {
+            assertThat(mTestList.get(i).get(sortKey).toString())
+                    .isEqualTo(ORDERED_ENGLISH_NAME[i]);
+        }
+    }
+
+    @Test
+    public void testComparator_sortInteger() {
+        String sortKey = ZoneGetter.KEY_OFFSET;
+        // TestList of any locale can be selected to test integer sorting.
+        mTestList = getMockZonesList("en_US");
+        final ZonePicker.MyComparator comparator = new ZonePicker.MyComparator(sortKey);
+        assertThat(comparator).isNotNull();
+        Collections.sort(mTestList, comparator);
+        for (int i = 0; i < mTestList.size(); i++) {
+            assertThat(mTestList.get(i).get(sortKey)).isEqualTo(i);
+        }
+    }
+
+    private List<Map<String, Object>> getMockZonesList(String locale) {
+         List<Map<String, Object>> zones = new ArrayList<>();
+         List<String> testData = mTestDataMap.get(locale);
+         TimeZone tz = TimeZone.getDefault();
+         int testSize = testData.size();
+         for (int i = 0; i < testSize; i++) {
+             zones.add(createMockDisplayEntry(tz, "GMT+08:00",
+                    testData.get(i), testSize - i - 1));
+         }
+         return zones;
+    }
+
+    private Map<String, Object> createMockDisplayEntry(
+            TimeZone tz, CharSequence gmtOffsetText, CharSequence displayName, int offsetMillis) {
+         Map<String, Object> map = new HashMap<>();
+         map.put(ZoneGetter.KEY_ID, tz.getID());
+         map.put(ZoneGetter.KEY_DISPLAYNAME, displayName.toString());
+         map.put(ZoneGetter.KEY_DISPLAY_LABEL, displayName);
+         map.put(ZoneGetter.KEY_GMT, gmtOffsetText.toString());
+         map.put(ZoneGetter.KEY_OFFSET_LABEL, gmtOffsetText);
+         map.put(ZoneGetter.KEY_OFFSET, offsetMillis);
+         return map;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java
index c61dfc6..2d75b8a 100644
--- a/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java
@@ -16,17 +16,25 @@
 
 package com.android.settings.development;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.provider.Settings;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,23 +44,12 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * deprecated in favor of {@link BugReportInPowerPreferenceControllerV2}
  */
 @Deprecated
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class BugReportInPowerPreferenceControllerTest {
 
     @Mock(answer = RETURNS_DEEP_STUBS)
@@ -82,12 +79,10 @@
     @Test
     public void displayPreference_hasDebugRestriction_shouldRemovePreference() {
         when(mUserManager.hasUserRestriction(anyString())).thenReturn(true);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -96,7 +91,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java
index d705610..dffa461 100644
--- a/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BugReportPreferenceControllerTest.java
@@ -16,66 +16,62 @@
 
 package com.android.settings.development;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.os.UserManager;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * deprecated in favor of {@link BugReportPreferenceControllerV2}
  */
 @Deprecated
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class BugReportPreferenceControllerTest {
 
     @Mock
     private Context mContext;
-    @Mock
-    private Preference mPreference;
     @Mock(answer = RETURNS_DEEP_STUBS)
     private PreferenceScreen mScreen;
     @Mock
     private UserManager mUserManager;
 
     private BugReportPreferenceController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
-        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
         mController = new BugReportPreferenceController(mContext);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
     public void displayPreference_hasDebugRestriction_shouldRemovePreference() {
         when(mUserManager.hasUserRestriction(anyString())).thenReturn(true);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
-        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -84,7 +80,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
@@ -92,9 +88,10 @@
         when(mUserManager.hasUserRestriction(anyString())).thenReturn(true);
         mController.displayPreference(mScreen);
 
+        mPreference.setEnabled(false);
         mController.enablePreference(true);
 
-        verify(mPreference, never()).setEnabled(anyBoolean());
+        assertThat(mPreference.isEnabled()).isFalse();
     }
 
     @Test
@@ -102,9 +99,10 @@
         when(mUserManager.hasUserRestriction(anyString())).thenReturn(false);
         mController.displayPreference(mScreen);
 
+        mPreference.setEnabled(false);
         mController.enablePreference(true);
 
-        verify(mPreference).setEnabled(anyBoolean());
+        assertThat(mPreference.isEnabled()).isTrue();
     }
 
 }
diff --git a/tests/robotests/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerTest.java
deleted file mode 100644
index 48833e4..0000000
--- a/tests/robotests/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import android.content.Context;
-import android.os.SystemProperties;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
-
-import org.junit.After;
-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 org.robolectric.RuntimeEnvironment;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * deprecated in favor of {@link CameraHalHdrPlusPreferenceControllerV2}
- */
-@Deprecated
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
-        shadows = {SettingsShadowSystemProperties.class})
-public class CameraHalHdrplusPreferenceControllerTest {
-
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
-    @Mock
-    private PreferenceScreen mScreen;
-    @Mock
-    private SwitchPreference mPreference;
-
-    static final String USERDEBUG_BUILD = "userdebug";
-
-    private CameraHalHdrplusPreferenceController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mController = new CameraHalHdrplusPreferenceController(mContext);
-        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
-        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
-    }
-
-    @After
-    public void tearDown() {
-        SettingsShadowSystemProperties.clear();
-    }
-
-    @Test
-    public void isAvailable_withConfigNoShow_shouldReturnFalse() {
-        when(mContext.getResources().getBoolean(R.bool.config_show_camera_hal_hdrplus))
-                .thenReturn(false);
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void displayPreference_cameraHalHdrplusEnabled_shouldCheckedPreference() {
-        when(mContext.getResources().getBoolean(R.bool.config_show_camera_hal_hdrplus))
-                .thenReturn(true);
-
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceController.PROPERTY_CAMERA_HAL_HDRPLUS,
-                CameraHalHdrplusPreferenceController.ENABLED);
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceController.BUILD_TYPE, USERDEBUG_BUILD);
-
-        mController.displayPreference(mScreen);
-
-        verify(mPreference).setChecked(true);
-    }
-
-    @Test
-    public void displayPreference_cameraHalHdrplusEnabled_shouldUncheckedPreference() {
-        when(mContext.getResources().getBoolean(R.bool.config_show_camera_hal_hdrplus))
-                .thenReturn(true);
-
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceController.PROPERTY_CAMERA_HAL_HDRPLUS,
-                CameraHalHdrplusPreferenceController.DISABLED);
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceController.BUILD_TYPE, USERDEBUG_BUILD);
-
-        mController.displayPreference(mScreen);
-
-        verify(mPreference).setChecked(false);
-    }
-
-    @Test
-    public void handlePreferenceTreeClick_preferenceChecked_shouldEnableCameraHalHdrplus() {
-        when(mContext.getResources().getBoolean(R.bool.config_show_camera_hal_hdrplus))
-                .thenReturn(true);
-
-        when(mPreference.isChecked()).thenReturn(true);
-
-        when(mContext.getResources().getString(R.string.camera_hal_hdrplus_toast)).thenReturn(
-            RuntimeEnvironment.application.getString(R.string.camera_hal_hdrplus_toast));
-
-        mController.handlePreferenceTreeClick(mPreference);
-
-        assertThat(CameraHalHdrplusPreferenceController.ENABLED.equals(
-            SystemProperties.get(
-                        CameraHalHdrplusPreferenceController.PROPERTY_CAMERA_HAL_HDRPLUS,
-                        CameraHalHdrplusPreferenceController.DISABLED))).isTrue();
-    }
-
-    @Test
-    public void handlePreferenceTreeClick_preferenceUnchecked_shouldDisableCameraHalHdrplus() {
-        when(mContext.getResources().getBoolean(R.bool.config_show_camera_hal_hdrplus))
-                .thenReturn(true);
-
-        when(mPreference.isChecked()).thenReturn(false);
-
-        when(mContext.getResources().getString(R.string.camera_hal_hdrplus_toast)).thenReturn(
-                RuntimeEnvironment.application.getString(R.string.camera_hal_hdrplus_toast));
-
-        mController.handlePreferenceTreeClick(mPreference);
-
-        assertThat(CameraHalHdrplusPreferenceController.DISABLED.equals(
-                SystemProperties.get(
-                        CameraHalHdrplusPreferenceController.PROPERTY_CAMERA_HAL_HDRPLUS,
-                        CameraHalHdrplusPreferenceController.DISABLED))).isTrue();
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerV2Test.java b/tests/robotests/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerV2Test.java
deleted file mode 100644
index e0e137c..0000000
--- a/tests/robotests/src/com/android/settings/development/CameraHalHdrplusPreferenceControllerV2Test.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.SystemProperties;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settings.TestConfig;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
-        shadows = {SettingsShadowSystemProperties.class})
-public class CameraHalHdrplusPreferenceControllerV2Test {
-
-    @Mock
-    private PreferenceScreen mScreen;
-    @Mock
-    private SwitchPreference mPreference;
-
-    private Context mContext;
-    private CameraHalHdrplusPreferenceControllerV2 mController;
-
-    static final String USERDEBUG_BUILD = "userdebug";
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mController = new CameraHalHdrplusPreferenceControllerV2(mContext);
-        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
-        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
-        mController.displayPreference(mScreen);
-    }
-
-    @After
-    public void tearDown() {
-        SettingsShadowSystemProperties.clear();
-    }
-
-    @Test
-    @Config(qualifiers = "mcc999")
-    public void isAvailable_withConfigNoShowAndUserDebugBuild_shouldReturnFalse() {
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceControllerV2.BUILD_TYPE, USERDEBUG_BUILD);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void updateState_cameraHalHdrplusEnabled_shouldCheckedPreference() {
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceControllerV2.PROPERTY_CAMERA_HAL_HDRPLUS,
-                CameraHalHdrplusPreferenceControllerV2.ENABLED);
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceControllerV2.BUILD_TYPE, USERDEBUG_BUILD);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setChecked(true);
-    }
-
-    @Test
-    public void updateState_cameraHalHdrplusEnabled_shouldUncheckedPreference() {
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceControllerV2.PROPERTY_CAMERA_HAL_HDRPLUS,
-                CameraHalHdrplusPreferenceControllerV2.DISABLED);
-        SettingsShadowSystemProperties.set(
-                CameraHalHdrplusPreferenceControllerV2.BUILD_TYPE, USERDEBUG_BUILD);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setChecked(false);
-    }
-
-    @Test
-    public void onPreferenceChange_preferenceChecked_shouldEnableCameraHalHdrplus() {
-        mController.onPreferenceChange(mPreference, true /* new value */);
-
-        assertThat(CameraHalHdrplusPreferenceControllerV2.ENABLED).isEqualTo(
-                SystemProperties.get(
-                        CameraHalHdrplusPreferenceControllerV2.PROPERTY_CAMERA_HAL_HDRPLUS,
-                        CameraHalHdrplusPreferenceControllerV2.DISABLED));
-    }
-
-    @Test
-    public void handlePreferenceTreeClick_preferenceUnchecked_shouldDisableCameraHalHdrplus() {
-        mController.onPreferenceChange(mPreference, false /* new value */);
-
-        assertThat(CameraHalHdrplusPreferenceControllerV2.DISABLED).isEqualTo(
-                SystemProperties.get(
-                        CameraHalHdrplusPreferenceControllerV2.PROPERTY_CAMERA_HAL_HDRPLUS,
-                        CameraHalHdrplusPreferenceControllerV2.DISABLED));
-    }
-
-    @Test
-    public void onDeveloperOptionsSwitchEnabled_shouldEnablePreference() {
-        mController.onDeveloperOptionsSwitchEnabled();
-
-        verify(mPreference).setEnabled(true);
-    }
-
-    @Test
-    public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() {
-        mController.onDeveloperOptionsSwitchDisabled();
-
-        verify(mPreference).setEnabled(false);
-        verify(mPreference).setChecked(false);
-        assertThat(CameraHalHdrplusPreferenceControllerV2.DISABLED).isEqualTo(
-                SystemProperties.get(
-                        CameraHalHdrplusPreferenceControllerV2.PROPERTY_CAMERA_HAL_HDRPLUS,
-                        CameraHalHdrplusPreferenceControllerV2.DISABLED));
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/development/VerifyAppsOverUsbPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/VerifyAppsOverUsbPreferenceControllerTest.java
index cea3d89..3208810 100644
--- a/tests/robotests/src/com/android/settings/development/VerifyAppsOverUsbPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/VerifyAppsOverUsbPreferenceControllerTest.java
@@ -30,8 +30,8 @@
 import android.provider.Settings.Global;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.RestrictedSwitchPreference;
 
@@ -76,6 +76,7 @@
             return this;
         }
     }
+
     private final GlobalSetter mGlobals = new GlobalSetter();
 
     @Before
@@ -175,7 +176,9 @@
         when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
         when(mScreen.getPreferenceCount()).thenReturn(1);
         when(mScreen.getPreference(anyInt())).thenReturn(mPreference);
+
         mController.displayPreference(mScreen);
-        verify(mScreen).removePreference(mPreference);
+
+        verify(mPreference).setVisible(false);
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java
index 5c62220..32bcd60 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.settings.deviceinfo;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.os.Build;
 import android.os.UserManager;
@@ -22,8 +26,8 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,30 +40,27 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class SystemUpdatePreferenceControllerTest {
 
     @Mock(answer = RETURNS_DEEP_STUBS)
     private Context mContext;
     @Mock
     private UserManager mUserManager;
-    @Mock(answer = RETURNS_DEEP_STUBS)
+    @Mock
     private PreferenceScreen mScreen;
 
     private SystemUpdatePreferenceController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mController = new SystemUpdatePreferenceController(mContext, mUserManager);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
     }
 
     @Test
@@ -86,41 +87,30 @@
 
     @Test
     public void displayPrefs_nothingAvailable_shouldNotDisplay() {
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
-
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
     public void updateState_shouldSetToAndroidVersion() {
-        final Preference preference = new Preference(RuntimeEnvironment.application);
         mController = new SystemUpdatePreferenceController(
                 RuntimeEnvironment.application, mUserManager);
-        mController.updateState(preference);
+        mController.updateState(mPreference);
 
-        assertThat(preference.getSummary())
+        assertThat(mPreference.getSummary())
                 .isEqualTo(RuntimeEnvironment.application.getString(R.string.about_summary,
                         Build.VERSION.RELEASE));
     }
 
     @Test
     public void displayPrefs_oneAvailable_shouldDisplayOne() {
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
-
         when(mContext.getResources().getBoolean(
                 R.bool.config_additional_system_update_setting_enable))
                 .thenReturn(true);
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionDialogControllerTest.java
new file mode 100644
index 0000000..0584a53
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionDialogControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import static com.android.settings.deviceinfo.firmwareversion.BasebandVersionDialogController
+        .BASEBAND_PROPERTY;
+import static com.android.settings.deviceinfo.firmwareversion.BasebandVersionDialogController
+        .BASEBAND_VERSION_LABEL_ID;
+import static com.android.settings.deviceinfo.firmwareversion.BasebandVersionDialogController
+        .BASEBAND_VERSION_VALUE_ID;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.os.SystemProperties;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
+import com.android.settings.testutils.shadow.ShadowConnectivityManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION_O,
+        shadows = {ShadowConnectivityManager.class, SettingsShadowSystemProperties.class})
+public class BasebandVersionDialogControllerTest {
+
+    @Mock
+    private FirmwareVersionDialogFragment mDialog;
+
+    private Context mContext;
+    private BasebandVersionDialogController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        when(mDialog.getContext()).thenReturn(mContext);
+        mController = new BasebandVersionDialogController(mDialog);
+    }
+
+    @After
+    public void teardown() {
+        SettingsShadowSystemProperties.clear();
+    }
+
+    @Test
+    public void initialize_wifiOnly_shouldRemoveSettingFromDialog() {
+        ShadowConnectivityManager connectivityManager =
+                extract(mContext.getSystemService(ConnectivityManager.class));
+        connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false);
+
+        mController.initialize();
+
+        verify(mDialog).removeSettingFromScreen(BASEBAND_VERSION_LABEL_ID);
+        verify(mDialog).removeSettingFromScreen(BASEBAND_VERSION_VALUE_ID);
+    }
+
+    @Test
+    public void initialize_hasMobile_shouldSetDialogTextToBasebandVersion() {
+        final String text = "test";
+        SystemProperties.set(BASEBAND_PROPERTY, text);
+        ShadowConnectivityManager connectivityManager =
+                extract(mContext.getSystemService(ConnectivityManager.class));
+        connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true);
+
+        mController.initialize();
+
+        verify(mDialog).setText(BASEBAND_VERSION_VALUE_ID, text);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BuildNumberDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BuildNumberDialogControllerTest.java
new file mode 100644
index 0000000..8bdf84c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BuildNumberDialogControllerTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import static com.android.settings.deviceinfo.firmwareversion.BuildNumberDialogController
+        .BUILD_NUMBER_VALUE_ID;
+
+import static org.mockito.Mockito.verify;
+
+import android.os.Build;
+import android.text.BidiFormatter;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class BuildNumberDialogControllerTest {
+
+    @Mock
+    private FirmwareVersionDialogFragment mDialog;
+
+    private BuildNumberDialogController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mController = new BuildNumberDialogController(mDialog);
+    }
+
+    @Test
+    public void initialize_shouldUpdateBuildNumberToDialog() {
+        mController.initialize();
+
+        verify(mDialog).setText(BUILD_NUMBER_VALUE_ID,
+                BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogControllerTest.java
new file mode 100644
index 0000000..00d1386
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import static com.android.settings.deviceinfo.firmwareversion.FirmwareVersionDialogController
+        .FIRMWARE_VERSION_LABEL_ID;
+import static com.android.settings.deviceinfo.firmwareversion.FirmwareVersionDialogController
+        .FIRMWARE_VERSION_VALUE_ID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.view.View;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class FirmwareVersionDialogControllerTest {
+
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private FirmwareVersionDialogFragment mDialog;
+    @Mock
+    private View mView;
+
+    private Context mContext;
+    private FirmwareVersionDialogController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mDialog.getContext()).thenReturn(mContext);
+        mController = spy(new FirmwareVersionDialogController(mDialog));
+        ReflectionHelpers.setField(mController, "mUserManager", mUserManager);
+        doNothing().when(mController).arrayCopy();
+        doNothing().when(mController).initializeAdminPermissions();
+    }
+
+    @Test
+    public void initialize_shouldRegisterListenersAndSetBuildVersion() {
+        mController.initialize();
+
+        verify(mDialog).registerClickListener(eq(FIRMWARE_VERSION_VALUE_ID), any());
+        verify(mDialog).registerClickListener(eq(FIRMWARE_VERSION_LABEL_ID), any());
+        verify(mDialog).setText(FIRMWARE_VERSION_VALUE_ID, Build.VERSION.RELEASE);
+    }
+
+    @Test
+    public void handleSettingClicked_userRestricted_shouldDoNothing() {
+        final long[] hits = ReflectionHelpers.getField(mController, "mHits");
+        hits[0] = Long.MAX_VALUE;
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN)).thenReturn(true);
+
+        mController.onClick(mView);
+
+        verify(mContext, never()).startActivity(any());
+    }
+
+    @Test
+    public void handleSettingClicked_userNotRestricted_shouldStartActivity() {
+        final long[] hits = ReflectionHelpers.getField(mController, "mHits");
+        hits[0] = Long.MAX_VALUE;
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN)).thenReturn(false);
+
+        mController.onClick(mView);
+
+        verify(mContext).startActivity(any());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionDialogControllerTest.java
new file mode 100644
index 0000000..c679af5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionDialogControllerTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import static com.android.settings.deviceinfo.firmwareversion.KernelVersionDialogController
+        .KERNEL_VERSION_VALUE_ID;
+
+import static org.mockito.Mockito.verify;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.DeviceInfoUtils;
+
+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;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class KernelVersionDialogControllerTest {
+
+    @Mock
+    private FirmwareVersionDialogFragment mDialog;
+
+    private KernelVersionDialogController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mController = new KernelVersionDialogController(mDialog);
+    }
+
+    @Test
+    public void initialize_shouldUpdateKernelVersionToDialog() {
+        mController.initialize();
+
+        verify(mDialog).setText(KERNEL_VERSION_VALUE_ID,
+                DeviceInfoUtils.getFormattedKernelVersion());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogControllerTest.java
new file mode 100644
index 0000000..ea37c2e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogControllerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.firmwareversion;
+
+import static com.android.settings.deviceinfo.firmwareversion.SecurityPatchLevelDialogController
+        .SECURITY_PATCH_LABEL_ID;
+import static com.android.settings.deviceinfo.firmwareversion.SecurityPatchLevelDialogController
+        .SECURITY_PATCH_VALUE_ID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Build;
+import android.view.View;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.Collections;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class SecurityPatchLevelDialogControllerTest {
+
+    @Mock
+    private PackageManagerWrapper mPackageManager;
+    @Mock
+    private FirmwareVersionDialogFragment mDialog;
+    @Mock
+    private View mView;
+
+    private Context mContext;
+    private SecurityPatchLevelDialogController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mDialog.getContext()).thenReturn(mContext);
+    }
+
+    @Test
+    public void initialize_noPatchInfo_shouldRemoveSettingFromDialog() {
+        ReflectionHelpers.setStaticField(Build.VERSION.class, "SECURITY_PATCH", "");
+        mController = new SecurityPatchLevelDialogController(mDialog);
+
+        mController.initialize();
+
+        verify(mDialog).removeSettingFromScreen(SECURITY_PATCH_VALUE_ID);
+        verify(mDialog).removeSettingFromScreen(SECURITY_PATCH_LABEL_ID);
+    }
+
+    @Test
+    public void initialize_patchInfoAvailable_shouldRegisterListeners() {
+        ReflectionHelpers.setStaticField(Build.VERSION.class, "SECURITY_PATCH", "foobar");
+        mController = new SecurityPatchLevelDialogController(mDialog);
+
+        mController.initialize();
+
+        verify(mDialog).registerClickListener(eq(SECURITY_PATCH_LABEL_ID), any());
+        verify(mDialog).registerClickListener(eq(SECURITY_PATCH_VALUE_ID), any());
+    }
+
+    @Test
+    public void onClick_noActivityIntent_shouldDoNothing() {
+        when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(
+                Collections.emptyList());
+        mController = new SecurityPatchLevelDialogController(mDialog);
+        ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager);
+
+        mController.onClick(mView);
+
+        verify(mContext, never()).startActivity(any());
+    }
+
+    @Test
+    public void onClick_activityIntentFound_shouldStartActivity() {
+        when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(
+                Collections.singletonList(null));
+        mController = new SecurityPatchLevelDialogController(mDialog);
+        ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager);
+
+        mController.onClick(mView);
+
+        verify(mContext).startActivity(any());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java
index fb9bb9f..dc3d27a 100644
--- a/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java
@@ -25,7 +25,7 @@
 
 import android.os.Bundle;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.TestConfig;
@@ -52,7 +52,7 @@
     private ColorModePreferenceFragment mFragment;
 
     @Mock
-    private NightDisplayController mController;
+    private ColorDisplayController mController;
 
     @Before
     public void setup() {
@@ -88,7 +88,7 @@
     @Test
     public void getKey_natural() {
         Mockito.when(mController.getColorMode()).thenReturn(
-            NightDisplayController.COLOR_MODE_NATURAL);
+                ColorDisplayController.COLOR_MODE_NATURAL);
 
         assertThat(mFragment.getDefaultKey())
                 .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL);
@@ -98,7 +98,7 @@
     @Test
     public void getKey_boosted() {
         Mockito.when(mController.getColorMode()).thenReturn(
-            NightDisplayController.COLOR_MODE_BOOSTED);
+                ColorDisplayController.COLOR_MODE_BOOSTED);
 
         assertThat(mFragment.getDefaultKey())
                 .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED);
@@ -108,7 +108,7 @@
     @Test
     public void getKey_saturated() {
         Mockito.when(mController.getColorMode()).thenReturn(
-            NightDisplayController.COLOR_MODE_SATURATED);
+                ColorDisplayController.COLOR_MODE_SATURATED);
 
         assertThat(mFragment.getDefaultKey())
             .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED);
@@ -118,21 +118,21 @@
     @Test
     public void setKey_natural() {
         mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL);
-        verify(mController).setColorMode(NightDisplayController.COLOR_MODE_NATURAL);
+        verify(mController).setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
     }
 
     @Config(shadows = {SettingsShadowSystemProperties.class})
     @Test
     public void setKey_boosted() {
         mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED);
-        verify(mController).setColorMode(NightDisplayController.COLOR_MODE_BOOSTED);
+        verify(mController).setColorMode(ColorDisplayController.COLOR_MODE_BOOSTED);
     }
 
     @Config(shadows = {SettingsShadowSystemProperties.class})
     @Test
     public void setKey_saturated() {
         mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED);
-        verify(mController).setColorMode(NightDisplayController.COLOR_MODE_SATURATED);
+        verify(mController).setColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceControllerTest.java
index 491fcdb..1154aa7 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceControllerTest.java
@@ -34,13 +34,11 @@
 
     public AdminGrantedCameraPermissionPreferenceControllerTest() {
         super("enterprise_privacy_number_camera_access_packages",
-                new String[] {Manifest.permission.CAMERA},
-                Manifest.permission_group.CAMERA);
+                new String[] {Manifest.permission.CAMERA});
     }
 
     @Override
     protected AdminGrantedPermissionsPreferenceControllerBase createController(boolean async) {
-        return new AdminGrantedCameraPermissionPreferenceController(mContext,null /* lifecycle */,
-                async);
+        return new AdminGrantedCameraPermissionPreferenceController(mContext, async);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceControllerTest.java
index 01a13d2..1c30da1 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceControllerTest.java
@@ -18,8 +18,8 @@
 
 import android.Manifest;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
@@ -35,13 +35,11 @@
     public AdminGrantedLocationPermissionsPreferenceControllerTest() {
         super("enterprise_privacy_number_location_access_packages",
                 new String[] {Manifest.permission.ACCESS_COARSE_LOCATION,
-                        Manifest.permission.ACCESS_FINE_LOCATION},
-                Manifest.permission_group.LOCATION);
+                        Manifest.permission.ACCESS_FINE_LOCATION});
     }
 
     @Override
     protected AdminGrantedPermissionsPreferenceControllerBase createController(boolean async) {
-        return new AdminGrantedLocationPermissionsPreferenceController(mContext,
-                null /* lifecycle */, async);
+        return new AdminGrantedLocationPermissionsPreferenceController(mContext, async);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceControllerTest.java
index fed1631..624022b 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceControllerTest.java
@@ -18,8 +18,8 @@
 
 import android.Manifest;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
@@ -34,13 +34,11 @@
 
     public AdminGrantedMicrophonePermissionPreferenceControllerTest() {
         super("enterprise_privacy_number_microphone_access_packages",
-                new String[] {Manifest.permission.RECORD_AUDIO},
-                Manifest.permission_group.MICROPHONE);
+                new String[] {Manifest.permission.RECORD_AUDIO});
     }
 
     @Override
     protected AdminGrantedPermissionsPreferenceControllerBase createController(boolean async) {
-        return new AdminGrantedMicrophonePermissionPreferenceController(mContext,
-                null /* lifecycle */, async);
+        return new AdminGrantedMicrophonePermissionPreferenceController(mContext, async);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBaseTest.java
index a209a46..a5d1d1a 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBaseTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBaseTest.java
@@ -16,8 +16,8 @@
 
 package com.android.settings.enterprise;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
@@ -31,7 +31,7 @@
         AdminGrantedPermissionsPreferenceControllerTestBase {
 
     public AdminGrantedPermissionsPreferenceControllerBaseTest() {
-        super("some.key", new String[] {"some.permission"}, "some.permission");
+        super("some.key", new String[] {"some.permission"});
     }
 
     @Override
@@ -44,7 +44,7 @@
 
         AdminGrantedPermissionsPreferenceControllerBaseTestable(boolean async) {
             super(AdminGrantedPermissionsPreferenceControllerBaseTest.this.mContext,
-                    null /* lifecycle */, async, mPermissions, mPermissionGroup);
+                    async, mPermissions);
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
index c1a3143..421fb0f 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
@@ -16,12 +16,17 @@
 
 package com.android.settings.enterprise;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
 import com.android.settings.applications.ApplicationFeatureProvider;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
@@ -32,35 +37,22 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Common base for testing subclasses of {@link AdminGrantedPermissionsPreferenceControllerBase}.
  */
 public abstract class AdminGrantedPermissionsPreferenceControllerTestBase {
     protected final String mKey;
     protected final String[] mPermissions;
-    protected final String mPermissionGroup;
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     protected Context mContext;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     protected AdminGrantedPermissionsPreferenceControllerBase mController;
 
-    public AdminGrantedPermissionsPreferenceControllerTestBase(String key, String[] permissions,
-            String permissionGroup) {
+    public AdminGrantedPermissionsPreferenceControllerTestBase(String key, String[] permissions) {
         mKey = key;
         mPermissions = permissions;
-        mPermissionGroup = permissionGroup;
     }
 
     @Before
@@ -69,12 +61,6 @@
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
         mController = createController(true /* async */);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
     }
 
     private void setNumberOfPackagesWithAdminGrantedPermissions(int number, boolean async) {
@@ -96,7 +82,6 @@
         setNumberOfPackagesWithAdminGrantedPermissions(0, true /* async */);
         mController.updateState(preference);
         assertThat(preference.isVisible()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(mKey, false);
 
         setNumberOfPackagesWithAdminGrantedPermissions(20, true /* async */);
         when(mContext.getResources().getQuantityString(
@@ -105,33 +90,27 @@
         mController.updateState(preference);
         assertThat(preference.getSummary()).isEqualTo("minimum 20 apps");
         assertThat(preference.isVisible()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(mKey, true);
     }
 
     @Test
     public void testIsAvailableSync() {
         final AdminGrantedPermissionsPreferenceControllerBase controller
                 = createController(false /* async */);
-        controller.setAvailabilityObserver(mObserver);
 
         setNumberOfPackagesWithAdminGrantedPermissions(0, false /* async */);
         assertThat(controller.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(mKey, false);
 
         setNumberOfPackagesWithAdminGrantedPermissions(20, false /* async */);
         assertThat(controller.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(mKey, true);
     }
 
     @Test
     public void testIsAvailableAsync() {
         setNumberOfPackagesWithAdminGrantedPermissions(0, true /* async */);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(eq(mKey), anyBoolean());
 
         setNumberOfPackagesWithAdminGrantedPermissions(20, true /* async */);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(eq(mKey), anyBoolean());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java
index 045acf2..365b9be 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java
@@ -16,14 +16,16 @@
 
 package com.android.settings.enterprise;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
-import com.android.settings.R;
 import android.support.v7.preference.Preference;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.R;
 import com.android.settings.TestConfig;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -33,10 +35,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link AlwaysOnVpnCurrentUserPreferenceController}.
  */
@@ -51,7 +49,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     private AlwaysOnVpnCurrentUserPreferenceController mController;
 
@@ -60,18 +57,11 @@
         MockitoAnnotations.initMocks(this);
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-        mController = new AlwaysOnVpnCurrentUserPreferenceController(mContext,
-                null /* lifecycle */);
+        mController = new AlwaysOnVpnCurrentUserPreferenceController(mContext);
         when(mContext.getString(R.string.enterprise_privacy_always_on_vpn_device))
                 .thenReturn(VPN_SET_DEVICE);
         when(mContext.getString(R.string.enterprise_privacy_always_on_vpn_personal))
                 .thenReturn(VPN_SET_PERSONAL);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
     }
 
     @Test
@@ -95,12 +85,10 @@
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInCurrentUser())
                 .thenReturn(false);
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_PRIMARY_USER, false);
 
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInCurrentUser())
                 .thenReturn(true);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_PRIMARY_USER, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java
index b321652..5f921cd 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java
@@ -16,13 +16,15 @@
 
 package com.android.settings.enterprise;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.support.v7.preference.Preference;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -32,10 +34,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link AlwaysOnVpnManagedProfilePreferenceController}.
  */
@@ -48,7 +46,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     private AlwaysOnVpnManagedProfilePreferenceController mController;
 
@@ -57,14 +54,7 @@
         MockitoAnnotations.initMocks(this);
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-        mController = new AlwaysOnVpnManagedProfilePreferenceController(mContext,
-                null /* lifecycle */);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
+        mController = new AlwaysOnVpnManagedProfilePreferenceController(mContext);
     }
 
     @Test
@@ -72,12 +62,10 @@
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInManagedProfile())
                 .thenReturn(false);
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_MANAGED_PROFILE, false);
 
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInManagedProfile())
                 .thenReturn(true);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_MANAGED_PROFILE, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java
index a6f95aa..80d4aaf 100644
--- a/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java
@@ -17,9 +17,7 @@
 package com.android.settings.enterprise;
 
 import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
-
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Answers.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.when;
 
@@ -30,10 +28,10 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.applications.UserAppInfo;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import org.junit.Before;
@@ -86,7 +84,8 @@
 
     @Test
     public void getPreferenceControllers() {
-        final List<AbstractPreferenceController> controllers = mFragment.getPreferenceControllers(mContext);
+        final List<AbstractPreferenceController> controllers = mFragment.getPreferenceControllers(
+                mContext);
         assertThat(controllers).isNotNull();
         assertThat(controllers.size()).isEqualTo(1);
         int position = 0;
@@ -94,7 +93,8 @@
                 ApplicationListPreferenceController.class);
     }
 
-    @Test public void getCategories() {
+    @Test
+    public void getCategories() {
         assertThat(new ApplicationListFragment.AdminGrantedPermissionCamera().getMetricsCategory())
                 .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS);
         assertThat(new ApplicationListFragment.AdminGrantedPermissionLocation().
diff --git a/tests/robotests/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceControllerTest.java
index 35d78a5..5ee1145 100644
--- a/tests/robotests/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/CaCertsCurrentUserPreferenceControllerTest.java
@@ -73,7 +73,7 @@
 
     @Override
     CaCertsPreferenceControllerBase createController() {
-        return new CaCertsCurrentUserPreferenceController(mContext, null /* lifecycle */);
+        return new CaCertsCurrentUserPreferenceController(mContext);
     }
 
     private void assertUpdateState(boolean isCompMode, String expectedTitle) {
diff --git a/tests/robotests/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceControllerTest.java
index 41b7f45..2aa5306 100644
--- a/tests/robotests/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/CaCertsManagedProfilePreferenceControllerTest.java
@@ -45,6 +45,6 @@
 
     @Override
     CaCertsPreferenceControllerBase createController() {
-        return new CaCertsManagedProfilePreferenceController(mContext, null /* lifecycle */);
+        return new CaCertsManagedProfilePreferenceController(mContext);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerBaseTest.java
index 37b903a..ec76b2b 100644
--- a/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerBaseTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerBaseTest.java
@@ -50,7 +50,7 @@
             CaCertsPreferenceControllerBase {
 
         public CaCertsPreferenceControllerBaseTestable(Context context) {
-            super(context, null);
+            super(context);
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTestBase.java
index c171fba..c2fb4da 100644
--- a/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTestBase.java
@@ -17,15 +17,12 @@
 package com.android.settings.enterprise;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
@@ -43,8 +40,6 @@
     protected Context mContext;
     protected FakeFeatureFactory mFeatureFactory;
     protected CaCertsPreferenceControllerBase mController;
-    @Mock
-    private PreferenceAvailabilityObserver mObserver;
 
     @Before
     public void setUp() {
@@ -52,12 +47,6 @@
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
         mController = createController();
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
     }
 
     @Test
@@ -75,11 +64,9 @@
     public void testIsAvailable() {
         mockGetNumberOfCaCerts(0);
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(getPreferenceKey(), false);
 
         mockGetNumberOfCaCerts(10);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(getPreferenceKey(), true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
index cc6335f..b4f7827 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
@@ -16,15 +16,20 @@
 
 package com.android.settings.enterprise;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.ApplicationFeatureProvider;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,15 +41,6 @@
 import org.mockito.stubbing.Answer;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link EnterpriseInstalledPackagesPreferenceController}.
  */
@@ -58,7 +54,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     private EnterpriseInstalledPackagesPreferenceController mController;
 
@@ -68,13 +63,7 @@
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
         mController = new EnterpriseInstalledPackagesPreferenceController(mContext,
-                null /* lifecycle */, true /* async */);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
+                true /* async */);
     }
 
     private void setNumberOfEnterpriseInstalledPackages(int number, boolean async) {
@@ -83,8 +72,9 @@
                 ((ApplicationFeatureProvider.NumberOfAppsCallback)
                         invocation.getArguments()[1]).onNumberOfAppsResult(number);
                 return null;
-            }}).when(mFeatureFactory.applicationFeatureProvider)
-                    .calculateNumberOfPolicyInstalledApps(eq(async), anyObject());
+            }
+        }).when(mFeatureFactory.applicationFeatureProvider)
+                .calculateNumberOfPolicyInstalledApps(eq(async), anyObject());
     }
 
     @Test
@@ -95,8 +85,6 @@
         setNumberOfEnterpriseInstalledPackages(0, true /* async */);
         mController.updateState(preference);
         assertThat(preference.isVisible()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES,
-                false);
 
         setNumberOfEnterpriseInstalledPackages(20, true /* async */);
         when(mContext.getResources().getQuantityString(
@@ -105,39 +93,27 @@
         mController.updateState(preference);
         assertThat(preference.getSummary()).isEqualTo("minimum 20 apps");
         assertThat(preference.isVisible()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES,
-                true);
     }
 
     @Test
     public void testIsAvailableSync() {
         final EnterpriseInstalledPackagesPreferenceController controller
-                = new EnterpriseInstalledPackagesPreferenceController(mContext,
-                        null /* lifecycle */, false /* async */);
-        controller.setAvailabilityObserver(mObserver);
+                = new EnterpriseInstalledPackagesPreferenceController(mContext, false /* async */);
 
         setNumberOfEnterpriseInstalledPackages(0, false /* async */);
         assertThat(controller.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(
-                KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES, false);
 
         setNumberOfEnterpriseInstalledPackages(20, false /* async */);
         assertThat(controller.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(
-                KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES, true);
     }
 
     @Test
     public void testIsAvailableAsync() {
         setNumberOfEnterpriseInstalledPackages(0, true /* async */);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES), anyBoolean());
 
         setNumberOfEnterpriseInstalledPackages(20, true /* async */);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES), anyBoolean());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
index 7077ad5..18f16f4 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
@@ -16,14 +16,16 @@
 
 package com.android.settings.enterprise;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -33,10 +35,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link EnterprisePrivacyPreferenceController}.
  */
@@ -52,7 +50,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     private EnterprisePrivacyPreferenceController mController;
 
@@ -61,13 +58,7 @@
         MockitoAnnotations.initMocks(this);
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-        mController = new EnterprisePrivacyPreferenceController(mContext, null /* lifecycle */);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
+        mController = new EnterprisePrivacyPreferenceController(mContext);
     }
 
     @Test
@@ -88,17 +79,15 @@
                 .thenReturn(MANAGING_ORGANIZATION);
         mController.updateState(preference);
         assertThat(preference.getSummary()).isEqualTo(MANAGED_WITH_NAME);
-      }
+    }
 
     @Test
     public void testIsAvailable() {
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner()).thenReturn(false);
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ENTERPRISE_PRIVACY, false);
 
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner()).thenReturn(true);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ENTERPRISE_PRIVACY, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
index 4da3289..a2b539d 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
@@ -17,18 +17,16 @@
 package com.android.settings.enterprise;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.res.XmlResourceParser;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.TestConfig;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.PreferenceCategoryController;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import org.junit.Before;
@@ -37,14 +35,10 @@
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
-import org.xmlpull.v1.XmlPullParser;
 
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Tests for {@link EnterprisePrivacySettings}.
@@ -53,9 +47,6 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public final class EnterprisePrivacySettingsTest {
 
-    private final static String RESOURCES_NAMESPACE = "http://schemas.android.com/apk/res/android";
-    private final static String ATTR_KEY = "key";
-
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
@@ -71,23 +62,11 @@
     }
 
     @Test
-    public void testGetMetricsCategory() {
+    public void verifyConstants() {
         assertThat(mSettings.getMetricsCategory())
                 .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_SETTINGS);
-    }
-
-    @Test
-    public void testGetCategoryKey() {
-        assertThat(mSettings.getCategoryKey()).isNull();
-    }
-
-    @Test
-    public void testGetLogTag() {
         assertThat(mSettings.getLogTag()).isEqualTo("EnterprisePrivacySettings");
-    }
-
-    @Test
-    public void testGetPreferenceScreenResId() {
+        assertThat(mSettings.getCategoryKey()).isNull();
         assertThat(mSettings.getPreferenceScreenResId())
                 .isEqualTo(R.xml.enterprise_privacy_settings);
     }
@@ -121,7 +100,7 @@
     public void getSearchIndexProviderPreferenceControllers() throws Exception {
         final List<AbstractPreferenceController> controllers
                 = EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers(
-                        ShadowApplication.getInstance().getApplicationContext());
+                ShadowApplication.getInstance().getApplicationContext());
         verifyPreferenceControllers(controllers);
     }
 
@@ -155,67 +134,11 @@
                 CaCertsCurrentUserPreferenceController.class);
         assertThat(controllers.get(position++)).isInstanceOf(
                 CaCertsManagedProfilePreferenceController.class);
-        final AbstractPreferenceController exposureChangesCategoryController =
-                controllers.get(position);
-        final int exposureChangesCategoryControllerIndex = position;
         assertThat(controllers.get(position++)).isInstanceOf(
-                ExposureChangesCategoryPreferenceController.class);
+                PreferenceCategoryController.class);
         assertThat(controllers.get(position++)).isInstanceOf(
                 FailedPasswordWipeCurrentUserPreferenceController.class);
         assertThat(controllers.get(position++)).isInstanceOf(
                 FailedPasswordWipeManagedProfilePreferenceController.class);
-
-        // The "Changes made by your organization's admin" category is hidden when all Preferences
-        // inside it become unavailable. To do this correctly, the category's controller must:
-        // a) Observe the availability of all Preferences in the category and
-        // b) Be listed after those Preferences' controllers, so that availability is updated in
-        //    the correct order
-
-        // Find all Preferences in the category.
-        final XmlResourceParser parser = RuntimeEnvironment.application.getResources().getXml(
-                R.xml.enterprise_privacy_settings);
-        boolean done = false;
-        int type;
-        final Set<String> expectedObserved = new HashSet<>();
-        while (!done && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-            if (type != XmlPullParser.START_TAG || !"exposure_changes_category".equals(
-                    parser.getAttributeValue(RESOURCES_NAMESPACE, ATTR_KEY))) {
-                continue;
-            }
-            int depth = 1;
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                if (type == XmlPullParser.START_TAG) {
-                    final String key = parser.getAttributeValue(RESOURCES_NAMESPACE, ATTR_KEY);
-                    if (key != null) {
-                        expectedObserved.add(key);
-                    }
-                    depth++;
-                } else if (type == XmlPullParser.END_TAG) {
-                    depth--;
-                    if (depth == 0) {
-                        done = true;
-                        break;
-                    }
-                }
-            }
-        }
-
-        // Find all Preferences the category's controller is observing.
-        final Set<String> actualObserved = new HashSet<>();
-        int maxObservedIndex = -1;
-        for (int i = 0; i < controllers.size(); i++) {
-            final AbstractPreferenceController controller = controllers.get(i);
-            if (controller instanceof DynamicAvailabilityPreferenceController &&
-                    ((DynamicAvailabilityPreferenceController) controller).getAvailabilityObserver()
-                            == exposureChangesCategoryController) {
-                actualObserved.add(controller.getPreferenceKey());
-                maxObservedIndex = i;
-            }
-        }
-
-        // Verify that the category's controller is observing the Preferences inside it.
-        assertThat(actualObserved).isEqualTo(expectedObserved);
-        // Verify that the category's controller is listed after the Preferences' controllers.
-        assertThat(maxObservedIndex).isLessThan(exposureChangesCategoryControllerIndex);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
index bb21bf7..9eb6d66 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
@@ -17,7 +17,6 @@
 package com.android.settings.enterprise;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.argThat;
@@ -29,16 +28,14 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.applications.EnterpriseDefaultApps;
 import com.android.settings.applications.UserAppInfo;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,8 +48,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-
-import static org.mockito.Mockito.verify;
 /**
  * Tests for {@link EnterpriseSetDefaultAppsPreferenceController}.
  */
@@ -65,9 +60,7 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private UserManager mUm;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     private EnterpriseSetDefaultAppsPreferenceController mController;
 
@@ -76,14 +69,7 @@
         MockitoAnnotations.initMocks(this);
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-        mController = new EnterpriseSetDefaultAppsPreferenceController(mContext,
-                null /* lifecycle */);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
+        mController = new EnterpriseSetDefaultAppsPreferenceController(mContext);
     }
 
     private void setEnterpriseSetDefaultApps(Intent[] intents, int number) {
@@ -130,12 +116,10 @@
         when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(anyInt(),
                 any(Intent[].class))).thenReturn(new ArrayList<>());
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_DEFAULT_APPS, false);
 
         setEnterpriseSetDefaultApps(EnterpriseDefaultApps.BROWSER.getIntents(), 1);
         configureUsers(1);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_DEFAULT_APPS, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceControllerTest.java
deleted file mode 100644
index f77aef8..0000000
--- a/tests/robotests/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceControllerTest.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.enterprise;
-
-import android.content.Context;
-import android.support.v7.preference.Preference;
-
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-import com.android.settings.core.DynamicAvailabilityPreferenceController;
-import com.android.settings.core.PreferenceAvailabilityObserver;
-
-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 java.util.Arrays;
-import java.util.List;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-
-/**
- * Tests for {@link ExposureChangesCategoryPreferenceController}.
- */
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public final class ExposureChangesCategoryPreferenceControllerTest {
-
-    private static final String KEY_1 = "key_1";
-    private static final String KEY_2 = "key_2";
-    private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
-
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
-    private List<DynamicAvailabilityPreferenceController> mControllers;
-    private ExposureChangesCategoryPreferenceController mController;
-    @Mock private PreferenceAvailabilityObserver mObserver;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mControllers = Arrays.asList(mock(DynamicAvailabilityPreferenceController.class),
-                mock(DynamicAvailabilityPreferenceController.class));
-        mController = new ExposureChangesCategoryPreferenceController(mContext,
-                null /* lifecycle */, mControllers, true /* controllingUi */);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testInitialization() {
-        verify(mControllers.get(0)).setAvailabilityObserver(mController);
-        verify(mControllers.get(1)).setAvailabilityObserver(mController);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
-    }
-
-    @Test
-    public void testOnPreferenceAvailabilityUpdated() {
-        final Preference preference = new Preference(mContext, null, 0, 0);
-        preference.setVisible(true);
-
-        mController.updateState(preference);
-        assertThat(preference.isVisible()).isFalse();
-
-        mController.onPreferenceAvailabilityUpdated(KEY_1, true);
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
-        assertThat(preference.isVisible()).isTrue();
-        reset(mObserver);
-
-        mController.onPreferenceAvailabilityUpdated(KEY_2, true);
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
-        assertThat(preference.isVisible()).isTrue();
-        reset(mObserver);
-
-        mController.onPreferenceAvailabilityUpdated(KEY_1, false);
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
-        assertThat(preference.isVisible()).isTrue();
-        reset(mObserver);
-
-        mController.onPreferenceAvailabilityUpdated(KEY_2, false);
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, false);
-        assertThat(preference.isVisible()).isFalse();
-    }
-
-    @Test
-    public void testUpdateState() {
-        final Preference preference = new Preference(mContext, null, 0, 0);
-        preference.setVisible(false);
-
-        mController.onPreferenceAvailabilityUpdated(KEY_1, true);
-        mController.updateState(preference);
-        assertThat(preference.isVisible()).isTrue();
-    }
-
-    @Test
-    public void testIsAvailableForUi() {
-        assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
-
-        mController.onPreferenceAvailabilityUpdated(KEY_1, true);
-        reset(mObserver);
-        assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
-
-        mController.onPreferenceAvailabilityUpdated(KEY_1, false);
-        reset(mObserver);
-        assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
-    }
-
-    @Test
-    public void testIsAvailableForSearch() {
-        final ExposureChangesCategoryPreferenceController controller
-                = new ExposureChangesCategoryPreferenceController(mContext, null /* lifecycle */,
-                        mControllers, false /* controllingUi */);
-        controller.setAvailabilityObserver(mObserver);
-        verify(mControllers.get(0)).setAvailabilityObserver(controller);
-        verify(mControllers.get(1)).setAvailabilityObserver(controller);
-
-        assertThat(controller.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, false);
-        reset(mObserver);
-
-        controller.onPreferenceAvailabilityUpdated(KEY_1, true);
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
-        assertThat(controller.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
-        reset(mObserver);
-
-        controller.onPreferenceAvailabilityUpdated(KEY_2, true);
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
-        assertThat(controller.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
-        reset(mObserver);
-
-        controller.onPreferenceAvailabilityUpdated(KEY_1, false);
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
-        assertThat(controller.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
-        reset(mObserver);
-
-        controller.onPreferenceAvailabilityUpdated(KEY_2, false);
-        verify(mObserver, never()).onPreferenceAvailabilityUpdated(
-                eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
-        assertThat(controller.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, false);
-    }
-
-    @Test
-    public void testHandlePreferenceTreeClick() {
-        assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0)))
-                .isFalse();
-    }
-
-    @Test
-    public void testGetPreferenceKey() {
-        assertThat(mController.getPreferenceKey()).isEqualTo(KEY_EXPOSURE_CHANGES_CATEGORY);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceControllerTest.java
index db9182b..8453d62 100644
--- a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeCurrentUserPreferenceControllerTest.java
@@ -16,14 +16,14 @@
 
 package com.android.settings.enterprise;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import static org.mockito.Mockito.when;
+
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link FailedPasswordWipeCurrentUserPreferenceController}.
  */
@@ -32,8 +32,6 @@
 public final class FailedPasswordWipeCurrentUserPreferenceControllerTest extends
         FailedPasswordWipePreferenceControllerTestBase {
 
-    private int mMaximumFailedPasswordsBeforeWipe = 0;
-
     public FailedPasswordWipeCurrentUserPreferenceControllerTest() {
         super("failed_password_wipe_current_user");
     }
@@ -41,8 +39,7 @@
     @Override
     public void setUp() {
         super.setUp();
-        mController = new FailedPasswordWipeCurrentUserPreferenceController(mContext,
-                null /* lifecycle */);
+        mController = new FailedPasswordWipeCurrentUserPreferenceController(mContext);
     }
 
     @Override
diff --git a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceControllerTest.java
index 329c526..a001a9c 100644
--- a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipeManagedProfilePreferenceControllerTest.java
@@ -16,14 +16,14 @@
 
 package com.android.settings.enterprise;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import static org.mockito.Mockito.when;
+
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link FailedPasswordWipeManagedProfilePreferenceController}.
  */
@@ -32,8 +32,6 @@
 public final class FailedPasswordWipeManagedProfilePreferenceControllerTest extends
         FailedPasswordWipePreferenceControllerTestBase {
 
-    private int mMaximumFailedPasswordsBeforeWipe = 0;
-
     public FailedPasswordWipeManagedProfilePreferenceControllerTest() {
         super("failed_password_wipe_managed_profile");
     }
@@ -41,8 +39,7 @@
     @Override
     public void setUp() {
         super.setUp();
-        mController = new FailedPasswordWipeManagedProfilePreferenceController(mContext,
-                null /* lifecycle */);
+        mController = new FailedPasswordWipeManagedProfilePreferenceController(mContext);
     }
 
     @Override
diff --git a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBaseTest.java
index 5d1b28a..f80667c 100644
--- a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBaseTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBaseTest.java
@@ -16,8 +16,8 @@
 
 package com.android.settings.enterprise;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
@@ -50,8 +50,7 @@
     private class FailedPasswordWipePreferenceControllerBaseTestable extends
             FailedPasswordWipePreferenceControllerBase {
         FailedPasswordWipePreferenceControllerBaseTestable() {
-            super(FailedPasswordWipePreferenceControllerBaseTest.this.mContext,
-                    null /* lifecycle */);
+            super(FailedPasswordWipePreferenceControllerBaseTest.this.mContext);
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java
index cbc220f..fe4fb6b 100644
--- a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java
@@ -16,12 +16,13 @@
 
 package com.android.settings.enterprise;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
-import android.content.res.Resources;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
@@ -30,10 +31,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Common base for testing subclasses of {@link FailedPasswordWipePreferenceControllerBase}.
  */
@@ -44,7 +41,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     protected Context mContext;
     protected FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     protected FailedPasswordWipePreferenceControllerBase mController;
 
@@ -59,12 +55,6 @@
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
     }
 
-    @Test
-    public void testGetAvailabilityObserver() {
-        mController.setAvailabilityObserver(mObserver);
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
-    }
-
     public abstract void setMaximumFailedPasswordsBeforeWipe(int maximum);
 
     @Test
@@ -81,15 +71,11 @@
 
     @Test
     public void testIsAvailable() {
-        mController.setAvailabilityObserver(mObserver);
-
         setMaximumFailedPasswordsBeforeWipe(0);
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(mKey, false);
 
         setMaximumFailedPasswordsBeforeWipe(10);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(mKey, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java
index d5ce102..b9c24bf 100644
--- a/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java
@@ -16,13 +16,15 @@
 
 package com.android.settings.enterprise;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.support.v7.preference.Preference;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -32,10 +34,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link GlobalHttpProxyPreferenceController}.
  */
@@ -48,7 +46,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     private GlobalHttpProxyPreferenceController mController;
 
@@ -57,13 +54,7 @@
         MockitoAnnotations.initMocks(this);
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-        mController = new GlobalHttpProxyPreferenceController(mContext, null /* lifecycle */);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
+        mController = new GlobalHttpProxyPreferenceController(mContext);
     }
 
     @Test
@@ -71,12 +62,10 @@
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.isGlobalHttpProxySet())
                 .thenReturn(false);
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_GLOBAL_HTTP_PROXY, false);
 
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.isGlobalHttpProxySet())
                 .thenReturn(true);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_GLOBAL_HTTP_PROXY, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java
index 22d2a7d..1e05383 100644
--- a/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java
@@ -16,15 +16,16 @@
 
 package com.android.settings.enterprise;
 
-import android.content.Context;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
 
-import com.android.settings.R;
+import android.content.Context;
 import android.support.v7.preference.Preference;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.R;
 import com.android.settings.TestConfig;
-import com.android.settings.core.PreferenceAvailabilityObserver;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -34,10 +35,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests for {@link ImePreferenceController}.
  */
@@ -52,7 +49,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
-    @Mock private PreferenceAvailabilityObserver mObserver;
 
     private ImePreferenceController mController;
 
@@ -61,15 +57,9 @@
         MockitoAnnotations.initMocks(this);
         FakeFeatureFactory.setupForTest(mContext);
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
-        mController = new ImePreferenceController(mContext, null /* lifecycle */);
+        mController = new ImePreferenceController(mContext);
         when(mContext.getResources().getString(R.string.enterprise_privacy_input_method_name,
                 DEFAULT_IME_LABEL)).thenReturn(DEFAULT_IME_TEXT);
-        mController.setAvailabilityObserver(mObserver);
-    }
-
-    @Test
-    public void testGetAvailabilityObserver() {
-        assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
     }
 
     @Test
@@ -77,7 +67,7 @@
         final Preference preference = new Preference(mContext, null, 0, 0);
 
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.getImeLabelIfOwnerSet())
-            .thenReturn(DEFAULT_IME_LABEL);
+                .thenReturn(DEFAULT_IME_LABEL);
         mController.updateState(preference);
         assertThat(preference.getSummary()).isEqualTo(DEFAULT_IME_TEXT);
     }
@@ -85,14 +75,12 @@
     @Test
     public void testIsAvailable() {
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.getImeLabelIfOwnerSet())
-            .thenReturn(null);
+                .thenReturn(null);
         assertThat(mController.isAvailable()).isFalse();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_INPUT_METHOD, false);
 
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.getImeLabelIfOwnerSet())
-            .thenReturn(DEFAULT_IME_LABEL);
+                .thenReturn(DEFAULT_IME_LABEL);
         assertThat(mController.isAvailable()).isTrue();
-        verify(mObserver).onPreferenceAvailabilityUpdated(KEY_INPUT_METHOD, true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java
index 61b6909..df438c1 100644
--- a/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java
@@ -16,14 +16,21 @@
 
 package com.android.settings.gestures;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.os.Bundle;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 import android.support.v7.preference.TwoStatePreference;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.widget.VideoPreference;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
@@ -33,19 +40,11 @@
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class GesturePreferenceControllerTest {
 
 
@@ -55,12 +54,17 @@
     private PreferenceScreen mScreen;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Lifecycle mLifecycle;
+
     private TestPrefController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mController = new TestPrefController(mContext, mLifecycle);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
@@ -70,20 +74,16 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
     public void display_configIsFalse_shouldNotDisplay() {
         mController.mIsPrefAvailable = false;
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java
index e4b3cf2..88a3bcb 100644
--- a/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java
@@ -16,14 +16,18 @@
 
 package com.android.settings.language;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.TtsEngines;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -31,19 +35,14 @@
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class TtsPreferenceControllerTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -54,21 +53,28 @@
     private PreferenceScreen mScreen;
 
     private TtsPreferenceController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
         mController = new TtsPreferenceController(mContext, mTtsEngines);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
     public void testIsAvailable_ttsEngineEmpty_shouldReturnFalse() {
-
         // Not available when there is no engine.
         when(mTtsEngines.getEngines()).thenReturn(new ArrayList<>());
 
         assertThat(mController.isAvailable()).isFalse();
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -78,21 +84,9 @@
         when(mTtsEngines.getEngines()).thenReturn(infolist);
 
         assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Test
-    public void displayPreference_notAvailable_shouldRemoveCategory() {
-        final Preference preference = mock(Preference.class);
-        final Preference category = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(2);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(mScreen.getPreference(1)).thenReturn(category);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
-        when(category.getKey()).thenReturn("voice_category");
 
         mController.displayPreference(mScreen);
 
-        // Remove preference.
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
new file mode 100644
index 0000000..1ee52ca
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.localepicker;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.TextView;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION,
+        shadows = { ShadowSettingsPreferenceFragment.class })
+public class LocaleListEditorTest {
+
+    private LocaleListEditor mLocaleListEditor;
+
+    @Before
+    public void setUp() {
+        mLocaleListEditor = new LocaleListEditor();
+        ReflectionHelpers.setField(mLocaleListEditor, "mEmptyTextView",
+                new TextView(RuntimeEnvironment.application));
+        ReflectionHelpers.setField(mLocaleListEditor, "mRestrictionsManager",
+                RuntimeEnvironment.application.getSystemService(Context.RESTRICTIONS_SERVICE));
+        ReflectionHelpers.setField(mLocaleListEditor, "mUserManager",
+                RuntimeEnvironment.application.getSystemService(Context.USER_SERVICE));
+    }
+
+    @Test
+    public void testDisallowConfigLocale_unrestrict() {
+        ReflectionHelpers.setField(mLocaleListEditor, "mIsUiRestricted", true);
+        mLocaleListEditor.onResume();
+        Assert.assertEquals(View.GONE, mLocaleListEditor.getEmptyTextView().getVisibility());
+    }
+
+    @Test
+    public void testDisallowConfigLocale_restrict() {
+        ReflectionHelpers.setField(mLocaleListEditor, "mIsUiRestricted", false);
+        mLocaleListEditor.onResume();
+        Assert.assertEquals(View.VISIBLE, mLocaleListEditor.getEmptyTextView().getVisibility());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/location/AppLocationPermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/AppLocationPermissionPreferenceControllerTest.java
index 602bbd1..6041007 100644
--- a/tests/robotests/src/com/android/settings/location/AppLocationPermissionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/AppLocationPermissionPreferenceControllerTest.java
@@ -1,46 +1,46 @@
 package com.android.settings.location;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-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)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class AppLocationPermissionPreferenceControllerTest {
-    @Mock
-    private Preference mPreference;
+
     @Mock(answer = RETURNS_DEEP_STUBS)
     private PreferenceScreen mScreen;
 
     private AppLocationPermissionPreferenceController mController;
 
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    @Mock
     private Context mContext;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+        mContext = RuntimeEnvironment.application;
         mController = new AppLocationPermissionPreferenceController(mContext);
+        mPreference = new Preference(mContext);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
@@ -48,13 +48,10 @@
         Settings.System.putInt(mContext.getContentResolver(),
                 android.provider.Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                 0);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
-        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -64,7 +61,6 @@
                 1);
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
-
 }
diff --git a/tests/robotests/src/com/android/settings/notification/AllowSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/AllowSoundPreferenceControllerTest.java
new file mode 100644
index 0000000..9ba8706
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/AllowSoundPreferenceControllerTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class AllowSoundPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+
+    @Mock
+    NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+    private AllowSoundPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController =
+                spy(new AllowSoundPreferenceController(mContext, mImportanceListener, mBackend));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(RestrictedSwitchPreference.class));
+        mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+    }
+
+    @Test
+    public void testIsAvailable_notIfNull() throws Exception {
+        mController.onResume(null, mock(NotificationChannel.class), null, null);
+        assertFalse(mController.isAvailable());
+
+        mController.onResume(mock(NotificationBackend.AppRow.class), null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfAppBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfAppCreatedChannel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something new");
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_notConfigurable() throws Exception {
+        String lockedId = "locked";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = lockedId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(lockedId);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_configurable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_checkedForHighImportanceChannel() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertTrue(pref.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_checkedForUnspecifiedImportanceChannel() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_UNSPECIFIED);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertTrue(pref.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_notCheckedForLowImportanceChannel() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertFalse(pref.isChecked());
+    }
+
+    @Test
+    public void testOnPreferenceChange_on() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+        pref.setChecked(true);
+        mController.onPreferenceChange(pref, true);
+
+        assertEquals(IMPORTANCE_UNSPECIFIED, mController.mChannel.getImportance());
+        verify(mImportanceListener, times(1)).onImportanceChanged();
+    }
+
+    @Test
+    public void testOnPreferenceChange_off() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        pref.setChecked(false);
+        mController.onPreferenceChange(pref, false);
+
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+        assertEquals(IMPORTANCE_LOW, mController.mChannel.getImportance());
+        verify(mImportanceListener, times(1)).onImportanceChanged();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/AppLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/AppLinkPreferenceControllerTest.java
new file mode 100644
index 0000000..b440704
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/AppLinkPreferenceControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class AppLinkPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    private AppLinkPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new AppLinkPreferenceController(mContext));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(Preference.class));
+    }
+
+    @Test
+    public void testIsAvailable_notIfNull() throws Exception {
+        mController.onResume(null, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfAppBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfChannelBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notNoIntent() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.settingsIntent = new Intent("test");
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        Intent intent = new Intent("action");
+        appRow.settingsIntent = intent;
+        mController.onResume(appRow, null, null, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertEquals(intent, pref.getIntent());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BadgePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BadgePreferenceControllerTest.java
new file mode 100644
index 0000000..6052478
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BadgePreferenceControllerTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.provider.Settings.Secure.NOTIFICATION_BADGING;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+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.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class BadgePreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+
+    private BadgePreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new BadgePreferenceController(mContext, mBackend));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(RestrictedSwitchPreference.class));
+        mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+    }
+
+    @Test
+    public void testIsAvailable_notIfAppBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfChannelBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_channel_notIfAppOff() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.showBadge = false;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfOffGlobally() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+        Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BADGING, 0);
+
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_app() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        mController.onResume(appRow, null, null, null);
+        Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BADGING, 1);
+
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_channel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.showBadge = true;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+        Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BADGING, 1);
+
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_channelNotConfigurable() throws Exception {
+        String lockedId = "locked";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = lockedId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(lockedId);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_channel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = "a";
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.canShowBadge()).thenReturn(true);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.isChecked());
+
+        when(channel.canShowBadge()).thenReturn(false);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(pref);
+
+        assertFalse(pref.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_app() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.showBadge = true;
+        mController.onResume(appRow, null, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertTrue(pref.isChecked());
+
+        appRow.showBadge = false;
+        mController.onResume(appRow, null, null, null);
+
+        mController.updateState(pref);
+        assertFalse(pref.isChecked());
+    }
+
+    @Test
+    public void testOnPreferenceChange_on_channel() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.showBadge = true;
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
+        channel.setShowBadge(false);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, true);
+        assertTrue(channel.canShowBadge());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testOnPreferenceChange_off_channel() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.showBadge = true;
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+        channel.setShowBadge(true);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, false);
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+        assertFalse(channel.canShowBadge());
+    }
+
+    @Test
+    public void testOnPreferenceChange_on_app() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.showBadge = false;
+        mController.onResume(appRow, null, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, true);
+
+        assertTrue(appRow.showBadge);
+        verify(mBackend, times(1)).setShowBadge(any(), anyInt(), eq(true));
+    }
+
+    @Test
+    public void testOnPreferenceChange_off_app() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.showBadge = true;
+        mController.onResume(appRow, null, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, false);
+
+        assertFalse(appRow.showBadge);
+        verify(mBackend, times(1)).setShowBadge(any(), anyInt(), eq(false));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java
index ac158b6..d294122 100644
--- a/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java
@@ -18,9 +18,7 @@
 
 import static android.provider.Settings.Secure.NOTIFICATION_BADGING;
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -43,11 +41,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class BadgingNotificationPreferenceControllerTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -56,11 +55,15 @@
     private PreferenceScreen mScreen;
 
     private BadgingNotificationPreferenceController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mController = new BadgingNotificationPreferenceController(mContext);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
@@ -70,7 +73,7 @@
                 .thenReturn(true);
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
@@ -78,14 +81,10 @@
         when(mContext.getResources().
                 getBoolean(com.android.internal.R.bool.config_notificationBadging))
                 .thenReturn(false);
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
new file mode 100644
index 0000000..9014f4e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.SwitchBar;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+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;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class BlockPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    @Mock
+    NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+    private BlockPreferenceController mController;
+    @Mock
+    private LayoutPreference mPreference;
+    private SwitchBar mSwitch;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new BlockPreferenceController(mContext, mImportanceListener, mBackend));
+        mSwitch = new SwitchBar(mContext);
+        when(mPreference.findViewById(R.id.switch_bar)).thenReturn(mSwitch);
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(LayoutPreference.class));
+        mController.onSwitchChanged(null, false);
+    }
+
+    @Test
+    public void testIsAvailable_notIfNull() throws Exception {
+        mController.onResume(null, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfChannelNotBlockable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfGroupNotBlockable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        mController.onResume(appRow, null, mock(NotificationChannelGroupWrapper.class), null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfAppNotBlockable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        mController.onResume(appRow, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_systemApp() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_nonSystemApp() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = false;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_app() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, null, null, null);
+        mController.updateState(mPreference);
+
+        assertNotNull(mPreference.findViewById(R.id.switch_bar));
+
+        assertFalse(mSwitch.isChecked());
+
+        appRow.banned = false;
+        mController.onResume(appRow, null, null, null);
+        mController.updateState(mPreference);
+
+        assertTrue(mSwitch.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_group() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+        when(group.isBlocked()).thenReturn(true);
+        mController.onResume(appRow, null, group, null);
+        mController.updateState(mPreference);
+
+        assertFalse(mSwitch.isChecked());
+
+        appRow.banned = true;
+        mController.onResume(appRow, null, group, null);
+        when(group.isBlocked()).thenReturn(true);
+        mController.updateState(mPreference);
+
+        assertFalse(mSwitch.isChecked());
+
+        appRow.banned = false;
+        mController.onResume(appRow, null, group, null);
+        when(group.isBlocked()).thenReturn(false);
+        mController.updateState(mPreference);
+
+        assertTrue(mSwitch.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_channelBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(mPreference);
+
+        assertFalse(mSwitch.isChecked());
+
+        appRow.banned = true;
+        channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(mPreference);
+
+        assertFalse(mSwitch.isChecked());
+
+        appRow.banned = false;
+        channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(mPreference);
+
+        assertTrue(mSwitch.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_noCrashIfCalledTwice() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(mPreference);
+        mController.updateState(mPreference);
+    }
+
+    @Test
+    public void testUpdateState_doesNotResetImportance() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(mPreference);
+
+        assertEquals(IMPORTANCE_LOW, channel.getImportance());
+    }
+
+    @Test
+    public void testOnSwitchChanged_channel_default() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_UNSPECIFIED);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(mPreference);
+
+        mController.onSwitchChanged(null, false);
+        assertEquals(IMPORTANCE_NONE, channel.getImportance());
+
+        mController.onSwitchChanged(null, true);
+        assertEquals(IMPORTANCE_UNSPECIFIED, channel.getImportance());
+
+        verify(mBackend, times(2)).updateChannel(any(), anyInt(), any());
+
+    }
+
+    @Test
+    public void testOnSwitchChanged_channel_nonDefault() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+        mController.updateState(mPreference);
+
+        mController.onSwitchChanged(null, false);
+        assertEquals(IMPORTANCE_NONE, channel.getImportance());
+
+        mController.onSwitchChanged(null, true);
+        assertEquals(IMPORTANCE_DEFAULT, channel.getImportance());
+
+        verify(mBackend, times(2)).updateChannel(any(), anyInt(), any());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DeletedChannelsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DeletedChannelsPreferenceControllerTest.java
new file mode 100644
index 0000000..fd903f9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DeletedChannelsPreferenceControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.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;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DeletedChannelsPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    private DeletedChannelsPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = new DeletedChannelsPreferenceController(mContext, mBackend);
+    }
+
+    @Test
+    public void noCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(Preference.class));
+    }
+
+    @Test
+    public void isAvailable_appScreen_notIfAppBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_groupScreen_never() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        mController.onResume(appRow, null, mock(NotificationChannelGroupWrapper.class), null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_channelScreen_never() throws Exception {
+        mController.onResume(
+                new NotificationBackend.AppRow(), mock(NotificationChannel.class), null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_appScreen_notIfNoDeletedChannels() throws Exception {
+        when(mBackend.getDeletedChannelCount(any(), anyInt())).thenReturn(0);
+        mController.onResume(new NotificationBackend.AppRow(), null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_appScreen() throws Exception {
+        when(mBackend.getDeletedChannelCount(any(), anyInt())).thenReturn(1);
+        mController.onResume(new NotificationBackend.AppRow(), null, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void updateState() throws Exception {
+        when(mBackend.getDeletedChannelCount(any(), anyInt())).thenReturn(1);
+        mController.onResume(new NotificationBackend.AppRow(), null, null, null);
+
+        Preference pref = mock(Preference.class);
+        mController.updateState(pref);
+
+        verify(pref, times(1)).setEnabled(false);
+        verify(pref, times(1)).setSelectable(false);
+        verify(mBackend, times(1)).getDeletedChannelCount(any(), anyInt());
+        ArgumentCaptor<CharSequence> argumentCaptor = ArgumentCaptor.forClass(CharSequence.class);
+        verify(pref, times(1)).setTitle(argumentCaptor.capture());
+        assertTrue(argumentCaptor.getValue().toString().contains("1"));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DescriptionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DescriptionPreferenceControllerTest.java
new file mode 100644
index 0000000..1776a9b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DescriptionPreferenceControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class DescriptionPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    private DescriptionPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new DescriptionPreferenceController(mContext));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(Preference.class));
+    }
+
+    @Test
+    public void testIsAvailable_notIfNull() throws Exception {
+        mController.onResume(null, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfChannelGroupBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.isBlocked()).thenReturn(true);
+        mController.onResume(appRow, null, group, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfChannelBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfNoChannelDesc() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfNoChannelGroupDesc() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+        mController.onResume(appRow, null, group, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_channel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        when(channel.getDescription()).thenReturn("AAA");
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_channelGroup() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+        when(group.getDescription()).thenReturn("something");
+        when(group.isBlocked()).thenReturn(false);
+        mController.onResume(appRow, null, group, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_channel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        when(channel.getDescription()).thenReturn("AAA");
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertEquals("AAA", pref.getTitle());
+        assertFalse(pref.isEnabled());
+        assertFalse(pref.isSelectable());
+    }
+
+    @Test
+    public void testUpdateState_channelGroup() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+        when(group.getDescription()).thenReturn("something");
+        mController.onResume(appRow, null, group, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertEquals("something", pref.getTitle());
+        assertFalse(pref.isEnabled());
+        assertFalse(pref.isSelectable());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DndPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DndPreferenceControllerTest.java
new file mode 100644
index 0000000..241e279
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DndPreferenceControllerTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class DndPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock
+    private Lifecycle mLifecycle;
+
+    private DndPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new DndPreferenceController(mContext, mLifecycle, mBackend));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(RestrictedSwitchPreference.class));
+        mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+        mController.onResume();
+    }
+
+    @Test
+    public void testIsAvailable_notIfNotImportant_noVisEffects() throws Exception {
+        when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 0));
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+        mController.onResume();
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfNotImportant_visEffects() throws Exception {
+        when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 1));
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_MIN);
+        mController.onResume();
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_importance_noVisEffects() throws Exception {
+        when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 0));
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume();
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_important_visEffects() throws Exception {
+        when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 1));
+        assertTrue(mNm.getNotificationPolicy().suppressedVisualEffects != 0);
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+        mController.onResume();
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_notConfigurable() throws Exception {
+        String lockedId = "locked";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = lockedId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(lockedId);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_configurable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_bypassDnd() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.canBypassDnd()).thenReturn(true);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertTrue(pref.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_noBypassDnd() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.canBypassDnd()).thenReturn(false);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertFalse(pref.isChecked());
+    }
+
+    @Test
+    public void testOnPreferenceChange_on() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, true);
+
+        assertTrue(channel.canBypassDnd());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testOnPreferenceChange_off() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, false);
+
+        assertFalse(channel.canBypassDnd());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/HeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/HeaderPreferenceControllerTest.java
new file mode 100644
index 0000000..385376f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/HeaderPreferenceControllerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v14.preference.PreferenceFragment;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+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;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class HeaderPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    private HeaderPreferenceController mController;
+    @Mock
+    private LayoutPreference mPreference;
+    @Mock
+    private View mView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        PreferenceFragment fragment = mock(PreferenceFragment.class);
+        when(fragment.getContext()).thenReturn(mContext);
+        Activity activity = mock(Activity.class);
+        when(activity.getApplicationContext()).thenReturn(mContext);
+        when(fragment.getActivity()).thenReturn(activity);
+        mController = spy(new HeaderPreferenceController(mContext, fragment));
+        when(mPreference.findViewById(anyInt())).thenReturn(mView);
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(LayoutPreference.class));
+    }
+
+    @Test
+    public void testIsAvailable_notIfNull() throws Exception {
+        mController.onResume(null, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, null, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testGetLabel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.label = "bananas";
+        mController.onResume(appRow, null, null, null);
+        assertEquals(appRow.label, mController.getLabel());
+
+        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+        NotificationChannelGroupWrapper gWrapper = new NotificationChannelGroupWrapper(group);
+        mController.onResume(appRow, null, gWrapper, null);
+        assertEquals(group.getName(), mController.getLabel());
+
+        NotificationChannel channel = new NotificationChannel("cid", "cname", IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, gWrapper, null);
+        assertEquals(channel.getName(), mController.getLabel());
+    }
+
+    @Test
+    public void testGetSummary() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.label = "bananas";
+        mController.onResume(appRow, null, null, null);
+        assertEquals("", mController.getSummary());
+
+        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+        NotificationChannelGroupWrapper gWrapper = new NotificationChannelGroupWrapper(group);
+        mController.onResume(appRow, null, gWrapper, null);
+        assertEquals(appRow.label, mController.getSummary());
+
+        NotificationChannel channel = new NotificationChannel("cid", "cname", IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, gWrapper, null);
+        assertTrue(mController.getSummary().toString().contains(group.getName()));
+        assertTrue(mController.getSummary().toString().contains(appRow.label));
+
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.getSummary().toString().contains(group.getName()));
+        assertTrue(mController.getSummary().toString().contains(appRow.label));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java
new file mode 100644
index 0000000..aebd6c9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class ImportancePreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    private ImportancePreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new ImportancePreferenceController(mContext));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(Preference.class));
+    }
+
+    @Test
+    public void testIsAvailable_notIfNull() throws Exception {
+        mController.onResume(null, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfAppBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfChannelBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notForDefaultChannel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+        assertNull(pref.getIntent());
+    }
+
+    @Test
+    public void testUpdateState_notConfigurable() throws Exception {
+        String lockedId = "locked";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = lockedId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(lockedId);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+        assertNull(pref.getIntent());
+    }
+
+    @Test
+    public void testUpdateState() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.isEnabled());
+        assertNotNull(pref.getIntent());
+        assertFalse(TextUtils.isEmpty(pref.getSummary()));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/LightsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/LightsPreferenceControllerTest.java
new file mode 100644
index 0000000..017cb88
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/LightsPreferenceControllerTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.provider.Settings.System.NOTIFICATION_LIGHT_PULSE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O, shadows = {
+        SettingsShadowResources.class,
+})
+public class LightsPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+
+    private LightsPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new LightsPreferenceController(mContext, mBackend));
+
+        // By default allow lights
+        SettingsShadowResources.overrideResource(
+                com.android.internal.R.bool.config_intrusiveNotificationLed, true);
+        Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_LIGHT_PULSE, 1);
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(RestrictedSwitchPreference.class));
+        mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+    }
+
+    @Test
+    public void testIsAvailable_notIfConfigNotAllowed() throws Exception {
+        SettingsShadowResources.overrideResource(
+                com.android.internal.R.bool.config_intrusiveNotificationLed, false);
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfSettingNotAllowed() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_LIGHT_PULSE, 0);
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfNotImportant() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfDefaultChannel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_notConfigurable() throws Exception {
+        String lockedId = "locked";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = lockedId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(lockedId);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_configurable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_lightsOn() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.shouldShowLights()).thenReturn(true);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertTrue(pref.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_lightsOff() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.shouldShowLights()).thenReturn(false);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertFalse(pref.isChecked());
+    }
+
+    @Test
+    public void testOnPreferenceChange_on() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_DEFAULT);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, true);
+
+        assertTrue(channel.shouldShowLights());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testOnPreferenceChange_off() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, false);
+
+        assertFalse(channel.shouldShowLights());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java
new file mode 100644
index 0000000..d685740
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.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;
+
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+import com.android.settingslib.RestrictedLockUtils;
+
+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 org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class NotificationPreferenceControllerTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    private TestPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = new TestPreferenceController(mContext, mBackend);
+    }
+
+    @Test
+    public void noCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(Preference.class));
+        assertFalse(mController.checkCanBeVisible(IMPORTANCE_UNSPECIFIED));
+        mController.saveChannel();
+        assertFalse(mController.isChannelConfigurable());
+        assertFalse(mController.isChannelBlockable());
+        assertFalse(mController.isChannelGroupBlockable());
+    }
+
+    @Test
+    public void isAvailable_notIfNull() throws Exception {
+        mController.onResume(null, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_notIfAppBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, mock(NotificationChannel.class),
+                mock(NotificationChannelGroupWrapper.class), null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_notIfChannelBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_notIfChannelGroupBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+
+        mController.onResume(appRow, channel, group, null);
+        when(group.isBlocked()).thenReturn(true);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.isBlocked()).thenReturn(false);
+
+        mController.onResume(appRow, channel, group, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testOnResume() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        RestrictedLockUtils.EnforcedAdmin admin = mock(RestrictedLockUtils.EnforcedAdmin.class);
+
+        mController.onResume(appRow, channel, group, admin);
+
+        assertEquals(appRow, mController.mAppRow);
+        assertEquals(channel, mController.mChannel);
+        assertEquals(group, mController.mChannelGroup);
+        assertEquals(admin, mController.mAdmin);
+    }
+
+    @Test
+    public void testCanBeVisible_unspecified() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_UNSPECIFIED);
+
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.checkCanBeVisible(IMPORTANCE_MIN));
+    }
+
+    @Test
+    public void testCanBeVisible_sameImportance() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.checkCanBeVisible(IMPORTANCE_LOW));
+    }
+
+    @Test
+    public void testCanBeVisible_greaterImportance() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.checkCanBeVisible(IMPORTANCE_MIN));
+    }
+
+    @Test
+    public void testCanBeVisible_lesserImportance() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.checkCanBeVisible(IMPORTANCE_DEFAULT));
+    }
+
+    @Test
+    public void testSaveImportance() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
+
+        mController.onResume(appRow, channel, null, null);
+        mController.saveChannel();
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testIsConfigurable() {
+        String sameId = "bananas";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = sameId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(sameId);
+
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isChannelConfigurable());
+
+        when(channel.getId()).thenReturn("something new");
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isChannelConfigurable());
+    }
+
+    @Test
+    public void testIsChannelBlockable_nonSystemAppsBlockable() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = false;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.isBlockableSystem()).thenReturn(false);
+
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isChannelBlockable());
+    }
+
+    @Test
+    public void testIsChannelBlockable_mostSystemAppsNotBlockable() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.isBlockableSystem()).thenReturn(false);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isChannelBlockable());
+    }
+
+    @Test
+    public void testIsChannelBlockable_someSystemAppsAreBlockable() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.isBlockableSystem()).thenReturn(true);
+
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isChannelBlockable());
+    }
+
+    @Test
+    public void testIsChannelBlockable_canUndoSystemBlock() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.isBlockableSystem()).thenReturn(false);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isChannelBlockable());
+    }
+
+    @Test
+    public void testIsChannelGroupBlockable_nonSystemBlockable() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = false;
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.isBlocked()).thenReturn(false);
+
+        mController.onResume(appRow, null, group, null);
+        assertTrue(mController.isChannelGroupBlockable());
+    }
+
+    @Test
+    public void testIsChannelGroupBlockable_SystemNotBlockable() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.isBlocked()).thenReturn(false);
+
+        mController.onResume(appRow, null, group, null);
+        assertFalse(mController.isChannelGroupBlockable());
+    }
+
+    @Test
+    public void testIsChannelGroupBlockable_canUndoSystemBlock() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.systemApp = true;
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.isBlocked()).thenReturn(true);
+
+        mController.onResume(appRow, null, group, null);
+        assertTrue(mController.isChannelGroupBlockable());
+    }
+
+    private final class TestPreferenceController extends NotificationPreferenceController {
+
+        public TestPreferenceController(Context context,
+                NotificationBackend backend) {
+            super(context, backend);
+        }
+
+        @Override
+        public String getPreferenceKey() {
+            return null;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationsOffPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationsOffPreferenceControllerTest.java
new file mode 100644
index 0000000..e1f9eb7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationsOffPreferenceControllerTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class NotificationsOffPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+
+    private NotificationsOffPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new NotificationsOffPreferenceController(mContext));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(Preference.class));
+    }
+
+    @Test
+    public void testIsAvailable_yesIfAppBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, null, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_yesIfChannelGroupBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.isBlocked()).thenReturn(true);
+        mController.onResume(appRow, null, group, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_yesIfChannelBlocked() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_channel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.getTitle().toString().contains("category"));
+        assertFalse(pref.isEnabled());
+        assertFalse(pref.isSelectable());
+    }
+
+    @Test
+    public void testUpdateState_channelGroup() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+        when(group.isBlocked()).thenReturn(true);
+        mController.onResume(appRow, null, group, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.getTitle().toString().contains("group"));
+        assertFalse(pref.isEnabled());
+        assertFalse(pref.isSelectable());
+    }
+
+    @Test
+    public void testUpdateState_app() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        mController.onResume(appRow, null, null, null);
+
+        Preference pref = new Preference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.getTitle().toString().contains("app"));
+        assertFalse(pref.isEnabled());
+        assertFalse(pref.isSelectable());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/PulseNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/PulseNotificationPreferenceControllerTest.java
index f6c6bf2..4ee5f5a 100644
--- a/tests/robotests/src/com/android/settings/notification/PulseNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/PulseNotificationPreferenceControllerTest.java
@@ -16,6 +16,12 @@
 
 package com.android.settings.notification;
 
+import static android.provider.Settings.System.NOTIFICATION_LIGHT_PULSE;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
@@ -31,18 +37,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
-import static android.provider.Settings.System.NOTIFICATION_LIGHT_PULSE;
-import static org.mockito.Matchers.any;
-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(RobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class PulseNotificationPreferenceControllerTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -51,11 +51,15 @@
     private PreferenceScreen mScreen;
 
     private PulseNotificationPreferenceController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mController = new PulseNotificationPreferenceController(mContext);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
@@ -65,7 +69,7 @@
                 .thenReturn(true);
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
@@ -73,14 +77,10 @@
         when(mContext.getResources().
                 getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed))
                 .thenReturn(false);
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/SoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/SoundPreferenceControllerTest.java
new file mode 100644
index 0000000..1d5a791
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/SoundPreferenceControllerTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+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.Fragment;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.AttributeSet;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class SoundPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private UserManager mUm;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock
+    private SettingsPreferenceFragment mFragment;
+    @Mock
+    private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+    private SoundPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new SoundPreferenceController(
+                mContext, mFragment, mImportanceListener, mBackend));
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(NotificationSoundPreference.class));
+        mController.onPreferenceChange(mock(NotificationSoundPreference.class), Uri.EMPTY);
+        mController.handlePreferenceTreeClick(mock(NotificationSoundPreference.class));
+        mController.onActivityResult(1, 1, null);
+        mController.hasValidSound(null);
+    }
+
+    @Test
+    public void testIsAvailable_notIfChannelNull() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        mController.onResume(appRow, null, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfNotImportant() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfDefaultChannel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testDisplayPreference_savesPreference() throws Exception {
+        NotificationSoundPreference pref = mock(NotificationSoundPreference.class);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+
+        mController.onActivityResult(SoundPreferenceController.CODE, 1, new Intent());
+        verify(pref, times(1)).onActivityResult(anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        Preference pref = new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_notConfigurable() throws Exception {
+        String lockedId = "locked";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = lockedId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(lockedId);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_configurable() throws Exception {
+        Uri sound = Settings.System.DEFAULT_ALARM_ALERT_URI;
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        when(channel.getSound()).thenReturn(sound);
+        mController.onResume(appRow, channel, null, null);
+
+        NotificationSoundPreference pref =
+                new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+        mController.updateState(pref);
+
+        assertEquals(sound, pref.onRestoreRingtone());
+        assertTrue(pref.isEnabled());
+    }
+
+    @Test
+    public void testOnPreferenceChange() throws Exception {
+        Uri sound = Settings.System.DEFAULT_ALARM_ALERT_URI;
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
+        channel.setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+
+        NotificationSoundPreference pref =
+                new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, Uri.EMPTY);
+        assertEquals(Uri.EMPTY, channel.getSound());
+        assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, channel.getAudioAttributes());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testOnPreferenceTreeClick_incorrectPref() throws Exception {
+        NotificationSoundPreference pref = mock(NotificationSoundPreference.class);
+        mController.handlePreferenceTreeClick(pref);
+
+        verify(pref, never()).onPrepareRingtonePickerIntent(any());
+        verify(mFragment, never()).startActivityForResult(any(), anyInt());
+    }
+
+
+    @Test
+    public void testOnPreferenceTreeClick_correctPref() throws Exception {
+        NotificationSoundPreference pref =
+                spy(new NotificationSoundPreference(mContext, mock(AttributeSet.class)));
+        pref.setKey(mController.getPreferenceKey());
+        mController.handlePreferenceTreeClick(pref);
+
+        verify(pref, times(1)).onPrepareRingtonePickerIntent(any());
+        verify(mFragment, times(1)).startActivityForResult(any(), anyInt());
+    }
+
+    @Test
+    public void testOnActivityResult() {
+        NotificationSoundPreference pref = mock(NotificationSoundPreference.class);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+
+        mController.onActivityResult(SoundPreferenceController.CODE, 1, new Intent("hi"));
+        verify(pref, times(1)).onActivityResult(anyInt(), anyInt(), any());
+        verify(mImportanceListener, times(1)).onImportanceChanged();
+    }
+
+    @Test
+    public void testHasValidSound() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+        assertTrue(mController.hasValidSound(channel));
+
+        channel.setSound(Uri.EMPTY, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        assertFalse(mController.hasValidSound(channel));
+
+        channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        assertFalse(mController.hasValidSound(channel));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java
index 054309c..19cf649 100644
--- a/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VibrateWhenRingPreferenceControllerTest.java
@@ -16,6 +16,12 @@
 
 package com.android.settings.notification;
 
+import static android.provider.Settings.System.VIBRATE_WHEN_RINGING;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
@@ -23,26 +29,20 @@
 import android.support.v7.preference.TwoStatePreference;
 import android.telephony.TelephonyManager;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
-import static android.provider.Settings.System.VIBRATE_WHEN_RINGING;
-import static org.mockito.Matchers.any;
-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)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class VibrateWhenRingPreferenceControllerTest {
 
     @Mock
@@ -53,36 +53,34 @@
     private TelephonyManager mTelephonyManager;
 
     private VibrateWhenRingPreferenceController mController;
+    private Preference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
         mController = new VibrateWhenRingPreferenceController(mContext);
+        mPreference = new Preference(RuntimeEnvironment.application);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
     public void display_voiceCapable_shouldDisplay() {
         when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
-        when(mScreen.findPreference(mController.getPreferenceKey()))
-            .thenReturn(mock(Preference.class));
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
     public void display_notVoiceCapable_shouldNotDisplay() {
         when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/VibrationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VibrationPreferenceControllerTest.java
new file mode 100644
index 0000000..4695590
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/VibrationPreferenceControllerTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.os.Vibrator;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class VibrationPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    Vibrator mVibrator;
+    @Mock
+    private UserManager mUm;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+
+    private VibrationPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        shadowApplication.setSystemService(Context.VIBRATOR_SERVICE, mVibrator);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new VibrationPreferenceController(mContext, mBackend));
+
+        // by default allow vibration
+        when(mVibrator.hasVibrator()).thenReturn(true);
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(RestrictedSwitchPreference.class));
+        mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+    }
+
+    @Test
+    public void testIsAvailable_notSystemDoesNotHave() throws Exception {
+        when(mVibrator.hasVibrator()).thenReturn(false);
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfNotImportant() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfDefaultChannel() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_notConfigurable() throws Exception {
+        String lockedId = "locked";
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.lockedChannelId = lockedId;
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn(lockedId);
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertFalse(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_configurable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(appRow, channel, null, null);
+
+        Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        assertTrue(pref.isEnabled());
+    }
+
+    @Test
+    public void testUpdateState_vibrateOn() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.shouldVibrate()).thenReturn(true);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertTrue(pref.isChecked());
+    }
+
+    @Test
+    public void testUpdateState_vibrateOff() throws Exception {
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.shouldVibrate()).thenReturn(false);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+        assertFalse(pref.isChecked());
+    }
+
+    @Test
+    public void testOnPreferenceChange_on() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_DEFAULT);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, true);
+
+        assertTrue(channel.shouldVibrate());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testOnPreferenceChange_off() {
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+        RestrictedSwitchPreference pref =
+                new RestrictedSwitchPreference(RuntimeEnvironment.application);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+        mController.displayPreference(mScreen);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, false);
+
+        assertFalse(channel.shouldVibrate());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java
new file mode 100644
index 0000000..ed658fe
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowRestrictionUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O, shadows = {
+        ShadowRestrictionUtils.class,
+})
+public class VisibilityPreferenceControllerTest {
+
+    private Context mContext;
+    @Mock
+    private NotificationBackend mBackend;
+    @Mock
+    private NotificationManager mNm;
+    @Mock
+    private LockPatternUtils mLockUtils;
+    @Mock
+    private UserManager mUm;
+    @Mock
+    private DevicePolicyManager mDm;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+
+    private VisibilityPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+        shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+        shadowApplication.setSystemService(Context.DEVICE_POLICY_SERVICE, mDm);
+        mContext = shadowApplication.getApplicationContext();
+        mController = spy(new VisibilityPreferenceController(mContext, mLockUtils, mBackend));
+
+        // by default the lockscreen is secure
+        when(mLockUtils.isSecure(anyInt())).thenReturn(true);
+        // and notifications are visible in redacted form
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+        // and not restricted
+        ShadowRestrictionUtils.setRestricted(false);
+        // with no managed profile
+        UserInfo userInfo = new UserInfo();
+        when(mUm.getUserInfo(anyInt())).thenReturn(userInfo);
+    }
+
+    @Test
+    public void testNoCrashIfNoOnResume() throws Exception {
+        mController.isAvailable();
+        mController.updateState(mock(RestrictedDropDownPreference.class));
+        mController.onPreferenceChange(mock(RestrictedDropDownPreference.class), true);
+    }
+
+    @Test
+    public void testIsAvailable_notSecure() throws Exception {
+        when(mLockUtils.isSecure(anyInt())).thenReturn(false);
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_notIfNotImportant() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_MIN);
+        mController.onResume(appRow, channel, null, null);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel =
+                new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+
+        channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+        mController.onResume(appRow, channel, null, null);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin_disableSecure() throws Exception {
+        ShadowRestrictionUtils.setRestricted(true);
+        UserInfo userInfo = new UserInfo(2, "user 2", UserInfo.FLAG_MANAGED_PROFILE);
+        when(mUm.getUserInfo(anyInt())).thenReturn(userInfo);
+        List<ComponentName> components = new ArrayList<>();
+        components.add(new ComponentName("", ""));
+        when(mDm.getActiveAdminsAsUser(anyInt())).thenReturn(components);
+        when(mDm.getKeyguardDisabledFeatures(any(), anyInt()))
+                .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        verify(pref, times(2)).addRestrictedItem(any());
+    }
+
+    @Test
+    public void testUpdateState_disabledByAdmin_disableUnredacted() throws Exception {
+        ShadowRestrictionUtils.setRestricted(true);
+        UserInfo userInfo = new UserInfo(2, "user 2", UserInfo.FLAG_MANAGED_PROFILE);
+        when(mUm.getUserInfo(anyInt())).thenReturn(userInfo);
+        List<ComponentName> components = new ArrayList<>();
+        components.add(new ComponentName("", ""));
+        when(mDm.getActiveAdminsAsUser(anyInt())).thenReturn(components);
+        when(mDm.getKeyguardDisabledFeatures(any(), anyInt()))
+                .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getId()).thenReturn("something");
+        mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+                RestrictedLockUtils.EnforcedAdmin.class));
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        verify(pref, times(1)).addRestrictedItem(any());
+    }
+
+    @Test
+    public void testUpdateState_noLockScreenNotificationsGlobally() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
+        verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
+        assertFalse(Arrays.asList(argumentCaptor.getValue())
+                .contains(VISIBILITY_NO_OVERRIDE));
+    }
+
+    @Test
+    public void testUpdateState_noPrivateLockScreenNotificationsGlobally() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
+        verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
+        assertFalse(Arrays.asList(argumentCaptor.getValue())
+                .contains(VISIBILITY_NO_OVERRIDE));
+    }
+
+    @Test
+    public void testUpdateState_noGlobalRestriction() throws Exception {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
+        verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
+        List<String> values = Arrays.asList(argumentCaptor.getValue());
+        assertEquals(3, values.size());
+        assertTrue(values.contains(String.valueOf(VISIBILITY_NO_OVERRIDE)));
+        assertTrue(values.contains(String.valueOf(Notification.VISIBILITY_PRIVATE)));
+        assertTrue(values.contains(String.valueOf(Notification.VISIBILITY_SECRET)));
+    }
+
+    @Test
+    public void testUpdateState_noChannelOverride() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getLockscreenVisibility()).thenReturn(VISIBILITY_NO_OVERRIDE);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
+        verify(pref, times(1)).setValue(argumentCaptor.capture());
+
+        assertEquals(String.valueOf(Notification.VISIBILITY_PRIVATE), argumentCaptor.getValue());
+    }
+
+    @Test
+    public void testUpdateState_channelOverride() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = mock(NotificationChannel.class);
+        when(channel.getLockscreenVisibility()).thenReturn(Notification.VISIBILITY_SECRET);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
+        verify(pref, times(1)).setValue(argumentCaptor.capture());
+
+        assertEquals(String.valueOf(Notification.VISIBILITY_SECRET), argumentCaptor.getValue());
+    }
+
+    @Test
+    public void testOnPreferenceChange_noOverride() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", 4);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, String.valueOf(Notification.VISIBILITY_PRIVATE));
+
+        assertEquals(VISIBILITY_NO_OVERRIDE, channel.getLockscreenVisibility());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+
+    @Test
+    public void testOnPreferenceChange_override() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        NotificationChannel channel = new NotificationChannel("", "", 4);
+        channel.setLockscreenVisibility(VISIBILITY_NO_OVERRIDE);
+        mController.onResume(appRow, channel, null, null);
+
+        RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+        mController.updateState(pref);
+
+        mController.onPreferenceChange(pref, String.valueOf(Notification.VISIBILITY_SECRET));
+
+        assertEquals(Notification.VISIBILITY_SECRET, channel.getLockscreenVisibility());
+        verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
index a529b0b..5b6c7ee 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
@@ -18,13 +18,24 @@
 package com.android.settings.search;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.app.Activity;
 import android.content.ComponentName;
+import android.content.Intent;
+import android.util.FeatureFlagUtils;
+import android.widget.Toolbar;
+
 import com.android.settings.TestConfig;
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.dashboard.SiteMapManager;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,13 +43,12 @@
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
 
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O, shadows = {
+        SettingsShadowSystemProperties.class
+})
 public class SearchFeatureProviderImplTest {
+
     private SearchFeatureProviderImpl mProvider;
     private Activity mActivity;
 
@@ -49,6 +59,11 @@
         mProvider = spy(new SearchFeatureProviderImpl());
     }
 
+    @After
+    public void tearDown() {
+        SettingsShadowSystemProperties.clear();
+    }
+
     @Test
     public void getSiteMapManager_shouldCacheInstance() {
         final SiteMapManager manager1 = mProvider.getSiteMapManager();
@@ -75,13 +90,51 @@
         verify(mProvider).cleanQuery(eq(query));
     }
 
+    @Test
+    public void initSearchToolbar_searchV2_shouldInitWithOnClickListener() {
+        mProvider.initSearchToolbar(mActivity, null);
+        // Should not crash.
+
+        SettingsShadowSystemProperties.set(
+                FeatureFlagUtils.FFLAG_PREFIX + FeatureFlags.SEARCH_V2,
+                "true");
+        final Toolbar toolbar = new Toolbar(mActivity);
+        mProvider.initSearchToolbar(mActivity, toolbar);
+
+        toolbar.performClick();
+
+        final Intent launchIntent = shadowOf(mActivity).getNextStartedActivity();
+
+        assertThat(launchIntent.getAction())
+                .isEqualTo("com.android.settings.action.SETTINGS_SEARCH");
+    }
+
+    @Test
+    public void initSearchToolbar_searchV1_shouldInitWithOnClickListener() {
+        mProvider.initSearchToolbar(mActivity, null);
+        // Should not crash.
+
+        SettingsShadowSystemProperties.set(
+                FeatureFlagUtils.FFLAG_PREFIX + FeatureFlags.SEARCH_V2,
+                "false");
+        final Toolbar toolbar = new Toolbar(mActivity);
+        mProvider.initSearchToolbar(mActivity, toolbar);
+
+        toolbar.performClick();
+
+        final Intent launchIntent = shadowOf(mActivity).getNextStartedActivity();
+
+        assertThat(launchIntent.getComponent().getClassName())
+                .isEqualTo(SearchActivity.class.getName());
+    }
+
     @Test(expected = IllegalArgumentException.class)
     public void verifyLaunchSearchResultPageCaller_nullCaller_shouldCrash() {
         mProvider.verifyLaunchSearchResultPageCaller(mActivity, null /* caller */);
     }
 
     @Test(expected = SecurityException.class)
-    public void everifyLaunchSearchResultPageCaller_badCaller_shouldCrash() {
+    public void verifyLaunchSearchResultPageCaller_badCaller_shouldCrash() {
         final ComponentName cn = new ComponentName("pkg", "class");
         mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
     }
diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
index b4a91c5..faf4280 100644
--- a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
+++ b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.search;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -28,8 +30,6 @@
 import java.util.List;
 import java.util.Set;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 /**
  * {@link CodeInspector} to ensure fragments implement search components correctly.
  */
@@ -47,7 +47,7 @@
                     + " these are not: \n";
     private static final String NOT_IN_INDEXABLE_PROVIDER_REGISTRY =
             "Class containing " + DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER
-                    + " must be added to " + SearchIndexableResources.class.getName()
+                    + " must be added to " + SettingsSearchIndexablesProvider.class.getName()
                     + " but these are not: \n";
 
     private final List<String> notImplementingIndexableGrandfatherList;
@@ -114,11 +114,10 @@
                 continue;
             }
             // Must be in SearchProviderRegistry
-            if (SearchIndexableResources.getResourceByName(className) == null) {
+            if (!SettingsSearchIndexablesProvider.INDEXABLES.contains(clazz)) {
                 if (!notInSearchIndexableRegistryGrandfatherList.remove(className)) {
                     notInSearchProviderRegistry.add(className);
                 }
-                continue;
             }
         }
 
diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
index 0e3ce50..bb9d0ca 100644
--- a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java
@@ -17,12 +17,11 @@
 package com.android.settings.search;
 
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
-import static com.android.settings.search.SearchIndexableResources.NO_RES_ID;
 import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.fail;
 import static org.mockito.Mockito.spy;
 
 import android.database.Cursor;
-import android.provider.SearchIndexableResource;
 import android.text.TextUtils;
 
 import com.android.settings.TestConfig;
@@ -35,61 +34,49 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
+import java.util.Set;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class SearchIndexableResourcesTest {
 
-    Map<String, SearchIndexableResource> sResMapCopy;
+    private Set<Class> mProviderClassCopy;
 
     @Before
     public void setUp() {
-        sResMapCopy = new HashMap<>(SearchIndexableResources.sResMap);
+        mProviderClassCopy = new HashSet<>(SettingsSearchIndexablesProvider.INDEXABLES);
     }
 
     @After
     public void cleanUp() {
-        SearchIndexableResources.sResMap.clear();
-        for (String key : sResMapCopy.keySet()) {
-            SearchIndexableResources.sResMap.put(key, sResMapCopy.get(key));
-        }
+        SettingsSearchIndexablesProvider.INDEXABLES.clear();
+        SettingsSearchIndexablesProvider.INDEXABLES.addAll(mProviderClassCopy);
     }
 
     @Test
     public void testAddIndex() {
+        final Class stringClass = java.lang.String.class;
         // Confirms that String.class isn't contained in SearchIndexableResources.
-        assertThat(SearchIndexableResources.getResourceByName("java.lang.String")).isNull();
-        final int beforeCount = SearchIndexableResources.values().size();
+        assertThat(SettingsSearchIndexablesProvider.INDEXABLES).doesNotContain(stringClass);
+        final int beforeCount = SettingsSearchIndexablesProvider.INDEXABLES.size();
 
-        SearchIndexableResources.addIndex(java.lang.String.class);
-        final SearchIndexableResource index = SearchIndexableResources
-                .getResourceByName("java.lang.String");
+        SettingsSearchIndexablesProvider.addIndex(java.lang.String.class);
 
-        assertThat(index).isNotNull();
-        assertThat(index.className).isEqualTo("java.lang.String");
-        assertThat(index.xmlResId).isEqualTo(NO_RES_ID);
-        assertThat(index.iconResId).isEqualTo(NO_RES_ID);
-        final int afterCount = SearchIndexableResources.values().size();
+        assertThat(SettingsSearchIndexablesProvider.INDEXABLES).contains(stringClass);
+        final int afterCount = SettingsSearchIndexablesProvider.INDEXABLES.size();
         assertThat(afterCount).isEqualTo(beforeCount + 1);
     }
 
     @Test
     public void testIndexHasWifiSettings() {
-        final SearchIndexableResource index = SearchIndexableResources
-                .getResourceByName(WifiSettings.class.getName());
-
-        assertThat(index).isNotNull();
-        assertThat(index.className).isEqualTo(WifiSettings.class.getName());
-        assertThat(index.xmlResId).isEqualTo(NO_RES_ID);
-        assertThat(index.iconResId).isEqualTo(NO_RES_ID);
+        assertThat(mProviderClassCopy).contains(WifiSettings.class);
     }
 
     @Test
     public void testNonIndexableKeys_GetsKeyFromProvider() {
-        SearchIndexableResources.sResMap.clear();
-        SearchIndexableResources.addIndex(FakeIndexProvider.class);
+        SettingsSearchIndexablesProvider.INDEXABLES.clear();
+        SettingsSearchIndexablesProvider.addIndex(FakeIndexProvider.class);
 
         SettingsSearchIndexablesProvider provider = spy(new SettingsSearchIndexablesProvider());
 
@@ -105,4 +92,13 @@
 
         assertThat(hasTestKey).isTrue();
     }
+
+    @Test
+    public void testAllClassNamesHaveProviders() {
+        for (Class clazz : mProviderClassCopy) {
+            if (DatabaseIndexingUtils.getSearchIndexProvider(clazz) == null) {
+                fail(clazz.getName() + "is not an index provider");
+            }
+        }
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java b/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
new file mode 100644
index 0000000..921dcb6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java
@@ -0,0 +1,117 @@
+package com.android.settings.search;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.SearchIndexablesContract;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.search.indexing.FakeSettingsFragment;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class SettingsSearchIndexablesProviderTest {
+
+    private final String BASE_AUTHORITY = "com.android.settings";
+
+    private SettingsSearchIndexablesProvider mProvider;
+
+    private Set<Class> mProviderClasses;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+
+        mProvider = new SettingsSearchIndexablesProvider();
+        ProviderInfo info = new ProviderInfo();
+        info.exported = true;
+        info.grantUriPermissions = true;
+        info.authority = BASE_AUTHORITY;
+        info.readPermission = Manifest.permission.READ_SEARCH_INDEXABLES;
+        mProvider.attachInfo(mContext, info);
+
+        mProviderClasses = new HashSet<>(SettingsSearchIndexablesProvider.INDEXABLES);
+        SettingsSearchIndexablesProvider.INDEXABLES.clear();
+        SettingsSearchIndexablesProvider.INDEXABLES.add(FakeSettingsFragment.class);
+    }
+
+    @After
+    public void cleanUp() {
+        SettingsSearchIndexablesProvider.INDEXABLES.clear();
+        SettingsSearchIndexablesProvider.INDEXABLES.addAll(mProviderClasses);
+    }
+
+    @Test
+    public void testRawColumnFetched() {
+        Uri rawUri = Uri.parse("content://" + BASE_AUTHORITY + "/" +
+                SearchIndexablesContract.INDEXABLES_RAW_PATH);
+
+        final Cursor cursor = mProvider.query(rawUri,
+                SearchIndexablesContract.INDEXABLES_RAW_COLUMNS, null, null, null);
+
+        cursor.moveToFirst();
+        assertThat(cursor.getString(1)).isEqualTo(FakeSettingsFragment.TITLE);
+        assertThat(cursor.getString(2)).isEqualTo(FakeSettingsFragment.SUMMARY_ON);
+        assertThat(cursor.getString(3)).isEqualTo(FakeSettingsFragment.SUMMARY_OFF);
+        assertThat(cursor.getString(4)).isEqualTo(FakeSettingsFragment.ENTRIES);
+        assertThat(cursor.getString(5)).isEqualTo(FakeSettingsFragment.KEYWORDS);
+        assertThat(cursor.getString(6)).isEqualTo(FakeSettingsFragment.SCREEN_TITLE);
+        assertThat(cursor.getString(7)).isEqualTo(FakeSettingsFragment.CLASS_NAME);
+        assertThat(cursor.getInt(8)).isEqualTo(FakeSettingsFragment.ICON);
+        assertThat(cursor.getString(9)).isEqualTo(FakeSettingsFragment.INTENT_ACTION);
+        assertThat(cursor.getString(10)).isEqualTo(FakeSettingsFragment.TARGET_PACKAGE);
+        assertThat(cursor.getString(11)).isEqualTo(FakeSettingsFragment.TARGET_CLASS);
+        assertThat(cursor.getString(12)).isEqualTo(FakeSettingsFragment.KEY);
+    }
+
+    @Test
+    public void testResourcesColumnFetched() {
+        Uri rawUri = Uri.parse("content://" + BASE_AUTHORITY + "/" +
+                SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
+
+        final Cursor cursor = mProvider.query(rawUri,
+                SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS, null, null, null);
+
+        cursor.moveToFirst();
+        assertThat(cursor.getCount()).isEqualTo(1);
+        assertThat(cursor.getInt(1)).isEqualTo(R.xml.display_settings);
+        assertThat(cursor.getString(2)).isEqualTo(FakeSettingsFragment.CLASS_NAME);
+        assertThat(cursor.getInt(3)).isEqualTo(0);
+        assertThat(cursor.getString(4)).isNull();
+        assertThat(cursor.getString(5)).isNull();
+        assertThat(cursor.getString(6)).isNull();
+    }
+
+    @Test
+    public void testNonIndexablesColumnFetched() {
+        Uri rawUri = Uri.parse("content://" + BASE_AUTHORITY + "/" +
+                SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
+        //final ContentResolver resolver = mContext.getContentResolver();
+
+        final Cursor cursor = mProvider.query(rawUri,
+                SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS, null, null, null);
+
+        cursor.moveToFirst();
+        assertThat(cursor.getCount()).isEqualTo(2);
+        assertThat(cursor.getString(0)).isEqualTo("pref_key_1");
+        cursor.moveToNext();
+        assertThat(cursor.getString(0)).isEqualTo("pref_key_3");
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/search/indexing/IndexDataConverterTest.java b/tests/robotests/src/com/android/settings/search/indexing/IndexDataConverterTest.java
index b95bfe8..0f2ab56 100644
--- a/tests/robotests/src/com/android/settings/search/indexing/IndexDataConverterTest.java
+++ b/tests/robotests/src/com/android/settings/search/indexing/IndexDataConverterTest.java
@@ -266,55 +266,6 @@
         assertThat(row.iconResId).isGreaterThan(0);
     }
 
-    // Tests for the flow: IndexOneResource -> IndexFromProvider -> IndexFromResource ->
-    //                     UpdateOneRowWithFilteredData -> UpdateOneRow
-
-    @Test
-    public void testAddProviderWithResource_rowInserted() {
-        final SearchIndexableResource resource = getFakeResource(0 /* xml */);
-        resource.className = FAKE_CLASS_NAME;
-        final PreIndexData preIndexData = new PreIndexData();
-        preIndexData.dataToUpdate.add(resource);
-
-        List<IndexData> indexData = mConverter.convertPreIndexDataToIndexData(preIndexData);
-
-        assertThat(indexData.size()).isEqualTo(NUM_FAKE_FRAGMENT_ENTRIES);
-        assertThat(findIndexDataForTitle(indexData, PAGE_TITLE)).isNotNull();
-        assertThat(findIndexDataForTitle(indexData, TITLE_ONE)).isNotNull();
-        assertThat(findIndexDataForTitle(indexData, TITLE_TWO)).isNotNull();
-        assertThat(findIndexDataForTitle(indexData, TITLE_THREE)).isNotNull();
-        assertThat(findIndexDataForTitle(indexData, TITLE_FOUR)).isNotNull();
-        assertThat(findIndexDataForTitle(indexData, TITLE_FIVE)).isNotNull();
-        assertThat(findIndexDataForTitle(indexData, FakeSettingsFragment.TITLE)).isNotNull();
-    }
-
-    @Test
-    public void testAddProviderWithRaw_rowInserted() {
-        final SearchIndexableResource resource = getFakeResource(0 /* xml */);
-        resource.className = FAKE_CLASS_NAME;
-        final PreIndexData preIndexData = new PreIndexData();
-        preIndexData.dataToUpdate.add(resource);
-
-        List<IndexData> indexData = mConverter.convertPreIndexDataToIndexData(preIndexData);
-
-        final IndexData data = findIndexDataForTitle(indexData, FakeSettingsFragment.TITLE);
-        assertFakeFragment(data);
-    }
-
-    @Test
-    public void testAddProvider_disabledRows() {
-        // Note that in FakeSettingsFragment, preferences 1 and 3 are disabled.
-        final SearchIndexableResource resource = getFakeResource(0 /* xml */);
-        resource.className = FAKE_CLASS_NAME;
-
-        final PreIndexData preIndexData = new PreIndexData();
-        preIndexData.dataToUpdate.add(resource);
-
-        List<IndexData> indexData = mConverter.convertPreIndexDataToIndexData(preIndexData);
-
-        assertThat(getEnabledResultCount(indexData)).isEqualTo(NUM_ENABLED_FAKE_FRAGMENT_ENTRIES);
-    }
-
     @Test
     public void testResource_sameTitleForSettingAndPage_titleNotInserted() {
         final SearchIndexableResource resource = getFakeResource(R.xml.about_legal);
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSettingsPreferenceFragment.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSettingsPreferenceFragment.java
new file mode 100644
index 0000000..586ff48
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSettingsPreferenceFragment.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.testutils.shadow;
+
+import android.os.Bundle;
+
+import com.android.settings.SettingsPreferenceFragment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/**
+ * Shadow of {@link SettingsPreferenceFragment}.
+ *
+ * Override the {@link #onCreate(Bundle)} to skip a null pointer exception in
+ * {@link android.content.res.Resources.Theme}, much the same as {@link ShadowDashboardFragment}.
+ */
+@Implements(SettingsPreferenceFragment.class)
+public class ShadowSettingsPreferenceFragment {
+
+    @Implementation
+    public void onCreate(Bundle savedInstanceState) {
+        // do nothing
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
index a578364..56f3949 100644
--- a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
@@ -16,27 +16,28 @@
 
 package com.android.settings.users;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.app.Activity;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
 
 import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.dashboard.SummaryLoader;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 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.Robolectric;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -70,4 +71,9 @@
             mActivity.getString(R.string.users_summary, name));
     }
 
+    @Test
+    public void testAssignDefaultPhoto_ContextNull_ReturnFalseAndNotCrash() {
+        // Should not crash here
+        assertThat(UserSettings.assignDefaultPhoto(null, 0)).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java b/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java
index 49a678f..b8fe81e 100644
--- a/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java
@@ -42,7 +42,7 @@
 import org.robolectric.shadows.ShadowActivity;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O,
         shadows = {
                 WallpaperSuggestionActivityTest.ShadowWallpaperManagerWrapper.class
         })
@@ -67,6 +67,8 @@
         final Intent intent = activity.getNextStartedActivity();
 
         assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
+        assertThat(intent.getFlags()).isEqualTo(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+        assertThat(activity.isFinishing()).isTrue();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/wallpaper/WallpaperTypeSettingsTest.java b/tests/robotests/src/com/android/settings/wallpaper/WallpaperTypeSettingsTest.java
new file mode 100644
index 0000000..7e15f7a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wallpaper/WallpaperTypeSettingsTest.java
@@ -0,0 +1,67 @@
+package com.android.settings.wallpaper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class WallpaperTypeSettingsTest {
+
+    private Preference mPreference;
+
+    private Intent mIntent;
+
+    @Before
+    public void setUp() {
+        mIntent = new Intent();
+        mPreference = new Preference(application);
+    }
+
+    @Test
+    public void testOnPreferenceTreeClick_intentNull_shouldDoNothing() {
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        WallpaperTypeSettings fragment = spy(new WallpaperTypeSettings());
+        doReturn(activity).when(fragment).getActivity();
+
+        boolean handled = fragment.onPreferenceTreeClick(mPreference);
+
+        assertThat(handled).isFalse();
+    }
+
+    @Test
+    public void testOnPreferenceTreeClick_shouldLaunchIntentAndFinish() {
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        WallpaperTypeSettings fragment = spy(new WallpaperTypeSettings());
+        doReturn(activity).when(fragment).getActivity();
+        mPreference.setIntent(mIntent);
+        doNothing().when(fragment).finish();
+        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+        doNothing().when(fragment).startActivity(intent.capture());
+
+        boolean handled = fragment.onPreferenceTreeClick(mPreference);
+
+        assertThat(handled).isTrue();
+        verify(fragment, times(1)).finish();
+        assertThat(intent.getValue()).isSameAs(mIntent);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/ConnectedAccessPointPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/ConnectedAccessPointPreferenceTest.java
new file mode 100644
index 0000000..2eaa587
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/ConnectedAccessPointPreferenceTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.wifi.AccessPoint;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ConnectedAccessPointPreferenceTest {
+    @Mock
+    private AccessPoint mAccessPoint;
+    @Mock
+    private View mView;
+    @Mock
+    private ConnectedAccessPointPreference.OnGearClickListener mOnGearClickListener;
+    private Context mContext;
+    private ConnectedAccessPointPreference mConnectedAccessPointPreference;
+
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mConnectedAccessPointPreference = new ConnectedAccessPointPreference(mAccessPoint, mContext,
+                null, 0 /* iconResId */, false /* forSavedNetworks */);
+        mConnectedAccessPointPreference.setOnGearClickListener(mOnGearClickListener);
+    }
+
+    @Test
+    public void testOnClick_gearClicked_listenerInvoked() {
+        doReturn(R.id.settings_button).when(mView).getId();
+
+        mConnectedAccessPointPreference.onClick(mView);
+
+        verify(mOnGearClickListener).onGearClick(mConnectedAccessPointPreference);
+    }
+
+    @Test
+    public void testOnClick_gearNotClicked_listenerNotInvoked() {
+        mConnectedAccessPointPreference.onClick(mView);
+
+        verify(mOnGearClickListener, never()).onGearClick(mConnectedAccessPointPreference);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/p2p/P2pCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/p2p/P2pCategoryPreferenceControllerTest.java
index dd2ef52..e53fb87 100644
--- a/tests/robotests/src/com/android/settings/wifi/p2p/P2pCategoryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/p2p/P2pCategoryPreferenceControllerTest.java
@@ -16,12 +16,19 @@
 
 package com.android.settings.wifi.p2p;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -31,13 +38,8 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class P2pCategoryPreferenceControllerTest {
 
     @Mock
@@ -81,7 +83,8 @@
         mController.addChild(pref);
 
         verify(mCategory).addPreference(pref);
-        verify(mCategory).setVisible(true);
+        verify(mCategory, atLeastOnce()).setVisible(true);
+        verify(mCategory, never()).setVisible(false);
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
index c75ca13..5296c36 100644
--- a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
+++ b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
@@ -20,7 +20,6 @@
 
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
-import android.provider.SearchIndexableResource;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -28,7 +27,7 @@
 
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableResources;
+import com.android.settings.search.SettingsSearchIndexablesProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import org.junit.Before;
@@ -54,8 +53,7 @@
     public void controllersInSearchShouldImplementPreferenceControllerMixin() {
         final Set<String> errorClasses = new ArraySet<>();
 
-        for (SearchIndexableResource page : SearchIndexableResources.values()) {
-            final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(page.className);
+        for (Class clazz: SettingsSearchIndexablesProvider.INDEXABLES) {
 
             final Indexable.SearchIndexProvider provider =
                     DatabaseIndexingUtils.getSearchIndexProvider(clazz);
diff --git a/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
index 4c51772..87b1a32 100644
--- a/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
+++ b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
@@ -33,7 +33,7 @@
 
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableResources;
+import com.android.settings.search.SettingsSearchIndexablesProvider;
 import com.android.settings.search.XmlParserUtils;
 
 import org.junit.Before;
@@ -90,8 +90,8 @@
         final Set<String> uniqueKeys = new HashSet<>();
         final Set<String> nullKeyClasses = new HashSet<>();
         final Set<String> duplicatedKeys = new HashSet<>();
-        for (SearchIndexableResource sir : SearchIndexableResources.values()) {
-            verifyPreferenceIdInXml(uniqueKeys, duplicatedKeys, nullKeyClasses, sir);
+        for (Class<?> clazz : SettingsSearchIndexablesProvider.INDEXABLES) {
+            verifyPreferenceIdInXml(uniqueKeys, duplicatedKeys, nullKeyClasses, clazz);
         }
 
         if (!nullKeyClasses.isEmpty()) {
@@ -115,22 +115,24 @@
     }
 
     private void verifyPreferenceIdInXml(Set<String> uniqueKeys, Set<String> duplicatedKeys,
-            Set<String> nullKeyClasses, SearchIndexableResource page)
+            Set<String> nullKeyClasses, Class<?> clazz)
             throws IOException, XmlPullParserException, Resources.NotFoundException {
-        final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(page.className);
-
+        if (clazz == null) {
+            return;
+        }
+        final String className = clazz.getName();
         final Indexable.SearchIndexProvider provider =
                 DatabaseIndexingUtils.getSearchIndexProvider(clazz);
         final List<SearchIndexableResource> resourcesToIndex =
                 provider.getXmlResourcesToIndex(mContext, true);
         if (resourcesToIndex == null) {
-            Log.d(TAG, page.className + "is not providing SearchIndexableResource, skipping");
+            Log.d(TAG, className + "is not providing SearchIndexableResource, skipping");
             return;
         }
 
         for (SearchIndexableResource sir : resourcesToIndex) {
             if (sir.xmlResId <= 0) {
-                Log.d(TAG, page.className + " doesn't have a valid xml to index.");
+                Log.d(TAG, className + " doesn't have a valid xml to index.");
                 continue;
             }
             final XmlResourceParser parser = mContext.getResources().getXml(sir.xmlResId);
@@ -154,14 +156,14 @@
                 final String key = XmlParserUtils.getDataKey(mContext, attrs);
                 if (TextUtils.isEmpty(key)) {
                     Log.e(TAG, "Every preference must have an key; found null key"
-                            + " in " + page.className
+                            + " in " + className
                             + " at " + parser.getPositionDescription());
-                    nullKeyClasses.add(page.className);
+                    nullKeyClasses.add(className);
                     continue;
                 }
                 if (uniqueKeys.contains(key) && !WHITELISTED_DUPLICATE_KEYS.contains(key)) {
                     Log.e(TAG, "Every preference key must unique; found " + nodeName
-                            + " in " + page.className
+                            + " in " + className
                             + " at " + parser.getPositionDescription());
                     duplicatedKeys.add(key);
                 }
diff --git a/tests/unit/src/com/android/settings/search/SearchIndexablesContractTest.java b/tests/unit/src/com/android/settings/search/SearchIndexablesContractTest.java
index 4de5fb7..2e779e8 100644
--- a/tests/unit/src/com/android/settings/search/SearchIndexablesContractTest.java
+++ b/tests/unit/src/com/android/settings/search/SearchIndexablesContractTest.java
@@ -17,105 +17,52 @@
 
 package com.android.settings.search;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.provider.SearchIndexablesContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class SearchIndexablesContractTest extends AndroidTestCase {
-        @SmallTest
-        public void testRawColumns_IncludesRank() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_RANK,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[0]);
-        }
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SearchIndexablesContractTest {
 
-        @SmallTest
-        public void testRawColumns_IncludesTitle() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_TITLE,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[1]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesSummaryOn() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_SUMMARY_ON,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[2]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesSummaryOff() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_SUMMARY_OFF,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[3]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesEntries() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_ENTRIES,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[4]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesKeywords() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_KEYWORDS,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[5]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesScreenTitle() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_SCREEN_TITLE,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[6]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesClassName() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_CLASS_NAME,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[7]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesIcon() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_ICON_RESID,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[8]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesIntentAction() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_INTENT_ACTION,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[9]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesIntentTargetPackage() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_INTENT_TARGET_PACKAGE,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[10]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesTargetClass() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_INTENT_TARGET_CLASS,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[11]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesKey() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_KEY,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[12]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesUserId() {
-            assertEquals(SearchIndexablesContract.RawData.COLUMN_USER_ID,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[13]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesPayloadType() {
-            assertEquals(SearchIndexablesContract.RawData.PAYLOAD_TYPE,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[14]);
-        }
-
-        @SmallTest
-        public void testRawColumns_IncludesPayload() {
-            assertEquals(SearchIndexablesContract.RawData.PAYLOAD,
-                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[15]);
-        }
+    @Test
+    public void testRawColumns_matchContractIndexing() {
+        assertThat(SearchIndexablesContract.RawData.COLUMN_RANK)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[0]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_TITLE)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[1]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_SUMMARY_ON)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[2]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_SUMMARY_OFF)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[3]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_ENTRIES)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[4]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_KEYWORDS)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[5]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_SCREEN_TITLE)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[6]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_CLASS_NAME)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[7]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_ICON_RESID)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[8]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_INTENT_ACTION)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[9]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_INTENT_TARGET_PACKAGE)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[10]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_INTENT_TARGET_CLASS)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[11]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_KEY)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[12]);
+        assertThat(SearchIndexablesContract.RawData.COLUMN_USER_ID)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[13]);
+        assertThat(SearchIndexablesContract.RawData.PAYLOAD_TYPE)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[14]);
+        assertThat(SearchIndexablesContract.RawData.PAYLOAD)
+                .isEqualTo(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS[15]);
+    }
 }
diff --git a/tests/unit/src/com/android/settings/search/SearchResultTrampolineTest.java b/tests/unit/src/com/android/settings/search/SearchResultTrampolineTest.java
new file mode 100644
index 0000000..974518f
--- /dev/null
+++ b/tests/unit/src/com/android/settings/search/SearchResultTrampolineTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.search;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SearchResultTrampolineTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void canLaunchSettingsTrampolineWithIntentAction() {
+        final PackageManager pm = mContext.getPackageManager();
+        final ResolveInfo info =
+                pm.resolveActivity(new Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE"), 0);
+
+        assertThat(info.activityInfo.name)
+                .isEqualTo(SearchResultTrampoline.class.getName());
+
+    }
+}