Merge "Rootless GPU Debug"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d65d92d..0a8bcb6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2817,6 +2817,8 @@
             </intent-filter>
             <meta-data android:name="com.android.settings.category"
                 android:value="com.android.settings.category.ia.wireless" />
+            <meta-data android:name="com.android.settings.summary"
+                android:resource="@string/summary_empty"/>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.sim.SimSettings" />
         </activity>
diff --git a/proguard.flags b/proguard.flags
index d644f47..7a403a4 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -39,3 +39,21 @@
     public static ** SEARCH_INDEX_DATA_PROVIDER;
     public static ** SUMMARY_PROVIDER_FACTORY;
 }
+
+# Keep classes, annotations and members used by Lifecycle
+-keepattributes *Annotation*
+
+-keepclassmembers enum android.arch.lifecycle.Lifecycle$Event {
+    <fields>;
+}
+
+-keep class * implements android.arch.lifecycle.LifecycleObserver {
+}
+
+-keep class * implements android.arch.lifecycle.GeneratedAdapter {
+    <init>(...);
+}
+
+-keepclassmembers class ** {
+    @android.arch.lifecycle.OnLifecycleEvent *;
+}
diff --git a/res/layout/zen_mode_button.xml b/res/layout/zen_mode_button.xml
new file mode 100644
index 0000000..af24fce
--- /dev/null
+++ b/res/layout/zen_mode_button.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:gravity="bottom"
+    android:paddingTop="4dp"
+    android:paddingStart="72dp"
+    android:paddingEnd="72dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/zen_mode_button"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:paddingEnd="8dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/zen_mode_settings_button.xml b/res/layout/zen_mode_settings_button.xml
new file mode 100644
index 0000000..4d4b7d6
--- /dev/null
+++ b/res/layout/zen_mode_settings_button.xml
@@ -0,0 +1,48 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:gravity="bottom"
+    android:paddingTop="4dp"
+    android:paddingStart="72dp"
+    android:paddingEnd="72dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/zen_mode_settings_turn_on_button"
+        style="@style/ActionPrimaryButton"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/zen_mode_button_turn_on"
+        android:paddingEnd="8dp" />
+
+    <Button
+        android:id="@+id/zen_mode_settings_turn_off_button"
+        style="@style/ActionSecondaryButton"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/zen_mode_button_turn_off"
+        android:paddingEnd="8dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 84f3722..aa8a586 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1031,4 +1031,18 @@
         <item>never</item>
     </string-array>
 
+    <string-array name="zen_mode_contacts_entries" translatable="false">
+        <item>@string/zen_mode_from_anyone</item>
+        <item>@string/zen_mode_from_contacts</item>
+        <item>@string/zen_mode_from_starred</item>
+        <item>@string/zen_mode_from_none</item>
+    </string-array>
+
+    <string-array name="zen_mode_contacts_values" translatable="false">
+        <item>zen_mode_from_anyone</item>
+        <item>zen_mode_from_contacts</item>
+        <item>zen_mode_from_starred</item>
+        <item>zen_mode_from_none</item>
+    </string-array>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bc43892..43827e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6475,6 +6475,8 @@
     <string name="sim_calls_ask_first_prefs_title">Ask every time</string>
     <!-- When a SIM preference hasn't been selected yet, this string is displayed as the pref summary until the user chooses a SIM subscription from the preference list [CHAR LIMIT=50] -->
     <string name="sim_selection_required_pref">Selection required</string>
+    <!-- Title for SIM selection notification channel -->
+    <string name="sim_selection_channel_title">SIM selection</string>
 
     <!--Dashboard strings-->
     <!-- Text to describe the dashboard fragment title [CHAR LIMIT=16] -->
@@ -6573,7 +6575,10 @@
     <string name="keywords_payment_settings">pay, tap, payments</string>
     <string name="keywords_backup">backup, back up</string>
     <string name="keywords_assist_gesture_launch">gesture</string>
-    <string name="keywords_imei_info">imei, meid</string>
+    <string name="keywords_imei_info">imei, meid, min, prl version, imei sv</string>
+    <string name="keywords_sim_status">network, mobile network state, service state, signal strength, mobile network type, roaming, iccid</string>
+    <string name="keywords_model_and_hardware">serial number, hardware version</string>
+    <string name="keywords_android_version">android security patch level, baseband version, kernel version, </string>
 
     <!-- NFC Wi-Fi pairing/setup strings-->
 
@@ -6752,6 +6757,27 @@
     <!--  Do not disturb: Button to add new automatic rule to DND. [CHAR LIMIT=30] -->
     <string name="zen_mode_add">Add</string>
 
+    <!--  Do not disturb: Label for button that will turn on zen mode. [CHAR LIMIT=30] -->
+    <string name="zen_mode_button_turn_on">TURN ON NOW</string>
+
+    <!--  Do not disturb: Label for button that will turn off zen mode. [CHAR LIMIT=30] -->
+    <string name="zen_mode_button_turn_off">TURN OFF NOW</string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer showing end time of DND -->
+    <string name="zen_mode_settings_dnd_manual_end_time_next_day">Do Not Disturb is on until <xliff:g id="formatted_time" example="7:00 AM">%s</xliff:g></string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer showing length of DND -->
+    <string name="zen_mode_settings_dnd_manual_indefinite">Do Not Disturb will stay on until you turn it off.</string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer showing how DND was triggered by an automatic DND rule -->
+    <string name="zen_mode_settings_dnd_automatic_rule">Do Not Disturb was automatically turned on by a rule <xliff:g id="rule_name" example="Weeknights">%s</xliff:g></string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer how DND was triggered by an app -->
+    <string name="zen_mode_settings_dnd_automatic_rule_app">Do Not Disturb was automatically turned on by an app <xliff:g id="app_name" example="Pixel Services">%s</xliff:g></string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer how DND was triggered by multiple rules and/or apps -->
+    <string name="zen_mode_settings_dnd_automatic_rule_multiple">Do Not Disturb was automatically turned on by a rule or app</string>
+
     <!-- Work Sounds: Work sound settings section header.  [CHAR LIMIT=50] -->
     <string name="sound_work_settings">Work profile sounds</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index f3b67e2..e2b6d44 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -218,6 +218,7 @@
         <item name="android:paddingStart">4dip</item>
         <item name="android:layout_marginStart">4dip</item>
         <item name="android:textSize">18sp</item>
+        <item name="android:textAlignment">viewStart</item>
     </style>
 
     <style name="wifi_section">
diff --git a/res/xml/data_usage_metered_prefs.xml b/res/xml/data_usage_metered_prefs.xml
index ef0faf2..9764ad3 100644
--- a/res/xml/data_usage_metered_prefs.xml
+++ b/res/xml/data_usage_metered_prefs.xml
@@ -14,8 +14,10 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:title="@string/network_restrictions">
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="data_usage_metered_prefs"
+    android:title="@string/network_restrictions">
 
     <PreferenceCategory
         android:key="mobile"
@@ -37,6 +39,7 @@
 
     <com.android.settingslib.widget.FooterPreference
         android:title="@string/data_usage_metered_body"
+        android:key="footer_preference"
         android:selectable="false" />
 
 </PreferenceScreen>
diff --git a/res/xml/device_info_settings_v2.xml b/res/xml/device_info_settings_v2.xml
index 9a48e96..bf4b47b 100644
--- a/res/xml/device_info_settings_v2.xml
+++ b/res/xml/device_info_settings_v2.xml
@@ -19,8 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
     android:key="device_info_pref_screen"
-    android:title="@string/about_settings"
-    settings:initialExpandedChildrenCount="6">
+    android:title="@string/about_settings">
 
     <!-- Phone number -->
     <Preference
@@ -34,6 +33,7 @@
         android:key="sim_status"
         android:order="10"
         android:title="@string/sim_status_title"
+        settings:keywords="@string/keywords_sim_status"
         android:summary="@string/summary_placeholder"/>
 
     <!-- Model & hardware -->
@@ -41,6 +41,7 @@
         android:key="device_model"
         android:order="21"
         android:title="@string/hardware_info"
+        settings:keywords="@string/keywords_model_and_hardware"
         android:summary="@string/summary_placeholder"/>
 
     <!-- IMEI -->
@@ -56,12 +57,20 @@
         android:key="firmware_version"
         android:order="32"
         android:title="@string/firmware_version"
+        settings:keywords="@string/keywords_android_version"
+        android:summary="@string/summary_placeholder"/>
+
+    <!-- IMS registration -->
+    <Preference
+        android:key="ims_reg_state"
+        android:order="33"
+        android:title="@string/ims_reg_title"
         android:summary="@string/summary_placeholder"/>
 
     <!--IP address -->
     <Preference
         android:key="wifi_ip_address"
-        android:order="33"
+        android:order="34"
         android:title="@string/wifi_ip_address"
         android:summary="@string/summary_placeholder"
         settings:allowDividerAbove="true"/>
@@ -69,14 +78,14 @@
     <!-- Wi-Fi MAC address -->
     <Preference
         android:key="wifi_mac_address"
-        android:order="34"
+        android:order="35"
         android:title="@string/status_wifi_mac_address"
         android:summary="@string/summary_placeholder"/>
 
     <!-- Bluetooth address -->
     <Preference
         android:key="bt_address"
-        android:order="35"
+        android:order="36"
         android:title="@string/status_bt_address"
         android:summary="@string/summary_placeholder"/>
 
@@ -84,7 +93,7 @@
     <!-- Legal information -->
     <Preference
         android:key="legal_container"
-        android:order="36"
+        android:order="37"
         android:title="@string/legal_information"
         android:fragment="com.android.settings.LegalSettings"
         settings:allowDividerAbove="true"/>
@@ -92,7 +101,7 @@
     <!-- Regulatory labels -->
     <Preference
         android:key="regulatory_info"
-        android:order="37"
+        android:order="38"
         android:title="@string/regulatory_labels">
         <intent android:action="android.settings.SHOW_REGULATORY_INFO"/>
     </Preference>
@@ -100,15 +109,36 @@
     <!-- Safety & regulatory manual -->
     <Preference
         android:key="safety_info"
-        android:order="38"
+        android:order="39"
         android:title="@string/safety_and_regulatory_info">
         <intent android:action="android.settings.SHOW_SAFETY_AND_REGULATORY_INFO"/>
     </Preference>
 
+    <!-- Manual -->
+    <Preference
+        android:key="manual"
+        android:order="40"
+        android:title="@string/manual">
+        <intent android:action="android.settings.SHOW_MANUAL"/>
+    </Preference>
+
+    <!-- Feedback on the device -->
+    <Preference
+        android:key="device_feedback"
+        android:order="41"
+        android:title="@string/device_feedback"/>
+
+    <!-- Device FCC equipment id -->
+    <Preference
+        android:key="fcc_equipment_id"
+        android:order="42"
+        android:title="@string/fcc_equipment_id"
+        android:summary="@string/summary_placeholder"/>
+
     <!-- Build number -->
     <Preference
         android:key="build_number"
-        android:order="39"
+        android:order="43"
         android:title="@string/build_number"
         android:summary="@string/summary_placeholder"
         settings:allowDividerAbove="true"/>
diff --git a/res/xml/location_mode.xml b/res/xml/location_mode.xml
index e6dc067..ac8e584 100644
--- a/res/xml/location_mode.xml
+++ b/res/xml/location_mode.xml
@@ -14,20 +14,22 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-        android:title="@string/location_mode_screen_title">
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="location_mode_settings"
+    android:title="@string/location_mode_screen_title">
 
-        <com.android.settings.widget.RadioButtonPreference
-            android:key="high_accuracy"
-            android:title="@string/location_mode_high_accuracy_title"
-            android:summary="@string/location_mode_high_accuracy_description" />
-        <com.android.settings.widget.RadioButtonPreference
-            android:key="battery_saving"
-            android:title="@string/location_mode_battery_saving_title"
-            android:summary="@string/location_mode_battery_saving_description" />
-        <com.android.settings.widget.RadioButtonPreference
-            android:key="sensors_only"
-            android:title="@string/location_mode_sensors_only_title"
-            android:summary="@string/location_mode_sensors_only_description" />
+    <com.android.settings.widget.RadioButtonPreference
+        android:key="high_accuracy"
+        android:title="@string/location_mode_high_accuracy_title"
+        android:summary="@string/location_mode_high_accuracy_description" />
+    <com.android.settings.widget.RadioButtonPreference
+        android:key="battery_saving"
+        android:title="@string/location_mode_battery_saving_title"
+        android:summary="@string/location_mode_battery_saving_description" />
+    <com.android.settings.widget.RadioButtonPreference
+        android:key="sensors_only"
+        android:title="@string/location_mode_sensors_only_title"
+        android:summary="@string/location_mode_sensors_only_description" />
 
 </PreferenceScreen>
diff --git a/res/xml/screen_pinning_settings.xml b/res/xml/screen_pinning_settings.xml
index 229c4ea..f27e4aa 100644
--- a/res/xml/screen_pinning_settings.xml
+++ b/res/xml/screen_pinning_settings.xml
@@ -14,11 +14,13 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-        android:title="@string/screen_pinning_title">
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="screen_pinning_settings_screen"
+    android:title="@string/screen_pinning_title">
 
-        <SwitchPreference
-                android:key="use_screen_lock"
-                android:title="@string/screen_pinning_unlock_none" />
+    <SwitchPreference
+        android:key="use_screen_lock"
+        android:title="@string/screen_pinning_unlock_none" />
 
 </PreferenceScreen>
diff --git a/res/xml/security_settings_misc.xml b/res/xml/security_settings_misc.xml
index bc737b0..730686c 100644
--- a/res/xml/security_settings_misc.xml
+++ b/res/xml/security_settings_misc.xml
@@ -75,7 +75,7 @@
         android:key="screen_pinning_settings"
         android:title="@string/screen_pinning_title"
         android:summary="@string/switch_off_text"
-        android:fragment="com.android.settings.ScreenPinningSettings"/>
+        android:fragment="com.android.settings.security.ScreenPinningSettings"/>
 
     <Preference android:key="security_misc_usage_access"
         android:title="@string/usage_access_title"
diff --git a/res/xml/zen_mode_automation_settings.xml b/res/xml/zen_mode_automation_settings.xml
index 33842ec..99826ea 100644
--- a/res/xml/zen_mode_automation_settings.xml
+++ b/res/xml/zen_mode_automation_settings.xml
@@ -19,5 +19,15 @@
                   android:key="zen_mode_automation_settings_page"
                   android:title="@string/zen_mode_automation_settings_page_title" >
 
-    <!-- Rules added at runtime -->
+
+    <PreferenceCategory
+        android:key="zen_mode_automatic_rules">
+        <!-- Rules added at runtime -->
+    </PreferenceCategory>
+
+    <Preference
+        android:key="zen_mode_add_automatic_rule"
+        android:icon="@drawable/ic_menu_add"
+        android:title="@string/zen_mode_add_rule"/>
+
 </PreferenceScreen>
diff --git a/res/xml/zen_mode_behavior_settings.xml b/res/xml/zen_mode_behavior_settings.xml
index 999d6b4..7b5ed91 100644
--- a/res/xml/zen_mode_behavior_settings.xml
+++ b/res/xml/zen_mode_behavior_settings.xml
@@ -46,17 +46,16 @@
            android:key="zen_mode_events"
            android:title="@string/zen_mode_events"/>
 
-       <!-- Messages -->
-       <DropDownPreference
+       <Preference
            android:key="zen_mode_messages"
            android:title="@string/zen_mode_messages"
-           android:summary="%s" />
+           android:fragment="com.android.settings.notification.ZenModeMessagesSettings" />
 
        <!-- Calls -->
-       <DropDownPreference
+       <Preference
            android:key="zen_mode_calls"
            android:title="@string/zen_mode_calls"
-           android:summary="%s" />
+           android:fragment="com.android.settings.notification.ZenModeCallsSettings" />
 
        <!-- Repeat callers -->
        <SwitchPreference
diff --git a/res/xml/zen_mode_calls_settings.xml b/res/xml/zen_mode_calls_settings.xml
new file mode 100644
index 0000000..aa84216
--- /dev/null
+++ b/res/xml/zen_mode_calls_settings.xml
@@ -0,0 +1,21 @@
+<?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"
+    android:key="zen_mode_calls_settings"
+    android:title="@string/zen_mode_calls" />
diff --git a/res/xml/zen_mode_messages_settings.xml b/res/xml/zen_mode_messages_settings.xml
new file mode 100644
index 0000000..4b4a1e2
--- /dev/null
+++ b/res/xml/zen_mode_messages_settings.xml
@@ -0,0 +1,21 @@
+<?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"
+    android:key="zen_mode_messages_settings"
+    android:title="@string/zen_mode_messages" />
diff --git a/res/xml/zen_mode_settings.xml b/res/xml/zen_mode_settings.xml
index 92e2b7f..82bef4d 100644
--- a/res/xml/zen_mode_settings.xml
+++ b/res/xml/zen_mode_settings.xml
@@ -19,6 +19,7 @@
     android:key="zen_mode_settings"
     android:title="@string/zen_mode_settings_title">
 
+    <!-- Priority behavior settings -->
     <Preference
             android:key="zen_mode_behavior_settings"
             android:title="@string/zen_mode_behavior_settings_title"
@@ -29,4 +30,14 @@
         android:key="zen_mode_automation_settings"
         android:title="@string/zen_mode_automation_settings_title"
         android:fragment="com.android.settings.notification.ZenModeAutomationSettings" />
+
+    <!-- Turn on DND button -->
+    <!-- Layout preference doesn't obey allowDividerAbove, so put it in a PreferenceCategory -->
+    <PreferenceCategory>
+        <com.android.settings.applications.LayoutPreference
+            android:key="zen_mode_settings_button_container"
+            android:selectable="false"
+            android:layout="@layout/zen_mode_settings_button" />
+    </PreferenceCategory>
+
 </PreferenceScreen>
diff --git a/src/com/android/settings/DeviceInfoSettings.java b/src/com/android/settings/DeviceInfoSettings.java
index 6c6c9e9..e5c3e06 100644
--- a/src/com/android/settings/DeviceInfoSettings.java
+++ b/src/com/android/settings/DeviceInfoSettings.java
@@ -22,8 +22,11 @@
 import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 import android.provider.SearchIndexableResource;
+import android.telephony.TelephonyManager;
 import android.util.FeatureFlagUtils;
+import android.support.annotation.VisibleForTesting;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.dashboard.DashboardFragment;
@@ -35,6 +38,7 @@
 import com.android.settings.deviceinfo.FccEquipmentIdPreferenceController;
 import com.android.settings.deviceinfo.FeedbackPreferenceController;
 import com.android.settings.deviceinfo.FirmwareVersionPreferenceController;
+import com.android.settings.deviceinfo.ImsStatusPreferenceController;
 import com.android.settings.deviceinfo.IpAddressPreferenceController;
 import com.android.settings.deviceinfo.KernelVersionPreferenceController;
 import com.android.settings.deviceinfo.ManualPreferenceController;
@@ -61,6 +65,11 @@
 
     private static final String KEY_LEGAL_CONTAINER = "legal_container";
 
+    @VisibleForTesting
+    static final int SIM_PREFERENCES_COUNT = 3;
+    @VisibleForTesting
+    static final int NON_SIM_PREFERENCES_COUNT = 2;
+
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.DEVICEINFO;
@@ -72,6 +81,21 @@
     }
 
     @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        if (FeatureFlagUtils.isEnabled(getContext(), DEVICE_INFO_V2)) {
+            // Increase the number of children when the device contains more than 1 sim.
+            final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(
+                    Context.TELEPHONY_SERVICE);
+            final int numberOfChildren = Math.max(SIM_PREFERENCES_COUNT,
+                    SIM_PREFERENCES_COUNT * telephonyManager.getPhoneCount())
+                    + NON_SIM_PREFERENCES_COUNT;
+            getPreferenceScreen().setInitialExpandedChildrenCount(numberOfChildren);
+        }
+    }
+
+    @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         final BuildNumberPreferenceController buildNumberPreferenceController =
                 getPreferenceController(BuildNumberPreferenceController.class);
@@ -139,6 +163,8 @@
 
             controllers.add(new FirmwareVersionPreferenceControllerV2(context, fragment));
 
+            controllers.add(new ImsStatusPreferenceController(context, lifecycle));
+
             controllers.add(new IpAddressPreferenceController(context, lifecycle));
 
             controllers.add(new WifiMacAddressPreferenceController(context, lifecycle));
@@ -149,6 +175,12 @@
 
             controllers.add(new SafetyInfoPreferenceController(context));
 
+            controllers.add(new ManualPreferenceController(context));
+
+            controllers.add(new FeedbackPreferenceController(fragment, context));
+
+            controllers.add(new FccEquipmentIdPreferenceController(context));
+
             controllers.add(
                     new BuildNumberPreferenceController(context, activity, fragment, lifecycle));
 
diff --git a/src/com/android/settings/datausage/DataUsageMeteredSettings.java b/src/com/android/settings/datausage/DataUsageMeteredSettings.java
index 0afb894..8bc7e04 100644
--- a/src/com/android/settings/datausage/DataUsageMeteredSettings.java
+++ b/src/com/android/settings/datausage/DataUsageMeteredSettings.java
@@ -14,27 +14,26 @@
 
 package com.android.settings.datausage;
 
-import static android.net.wifi.WifiInfo.removeDoubleQuotes;
-
 import android.app.backup.BackupManager;
 import android.content.Context;
-import android.content.res.Resources;
 import android.net.NetworkPolicyManager;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
+import android.provider.SearchIndexableResource;
 import android.support.v7.preference.DropDownPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
 import android.text.TextUtils;
+
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.NetworkPolicyEditor;
-import java.util.ArrayList;
+
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -134,49 +133,11 @@
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
         new BaseSearchIndexProvider() {
             @Override
-            public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
-                final Resources res = context.getResources();
-
-                // Add fragment title
-                SearchIndexableRaw data = new SearchIndexableRaw(context);
-                data.title = res.getString(R.string.data_usage_menu_metered);
-                data.screenTitle = res.getString(R.string.data_usage_menu_metered);
-                result.add(data);
-
-                // Body
-                data = new SearchIndexableRaw(context);
-                data.title = res.getString(R.string.data_usage_metered_body);
-                data.screenTitle = res.getString(R.string.data_usage_menu_metered);
-                result.add(data);
-
-                // Wi-Fi networks category
-                data = new SearchIndexableRaw(context);
-                data.title = res.getString(R.string.data_usage_metered_wifi);
-                data.screenTitle = res.getString(R.string.data_usage_menu_metered);
-                result.add(data);
-
-                final WifiManager wifiManager =
-                        (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-                if (DataUsageUtils.hasWifiRadio(context) && wifiManager.isWifiEnabled()) {
-                    for (WifiConfiguration config : wifiManager.getConfiguredNetworks()) {
-                        if (config.SSID != null) {
-                            final String networkId = config.SSID;
-
-                            data = new SearchIndexableRaw(context);
-                            data.title = removeDoubleQuotes(networkId);
-                            data.screenTitle = res.getString(R.string.data_usage_menu_metered);
-                            result.add(data);
-                        }
-                    }
-                } else {
-                    data = new SearchIndexableRaw(context);
-                    data.title = res.getString(R.string.data_usage_metered_wifi_disabled);
-                    data.screenTitle = res.getString(R.string.data_usage_menu_metered);
-                    result.add(data);
-                }
-
-                return result;
+            public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                    boolean enabled) {
+                final SearchIndexableResource sir = new SearchIndexableResource(context);
+                sir.xmlResId = R.xml.data_usage_metered_prefs;
+                return Arrays.asList(sir);
             }
 
             @Override
diff --git a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java
index 5edac0f..802d774 100644
--- a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java
@@ -26,13 +26,15 @@
 import android.text.TextUtils;
 
 import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.DeviceInfoUtils;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class PhoneNumberPreferenceController extends AbstractPreferenceController {
+public class PhoneNumberPreferenceController extends AbstractPreferenceController implements
+        PreferenceControllerMixin {
 
     private final static String KEY_PHONE_NUMBER = "phone_number";
 
diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java
index a3ed940..e9d3c85 100644
--- a/src/com/android/settings/deviceinfo/StorageSettings.java
+++ b/src/com/android/settings/deviceinfo/StorageSettings.java
@@ -16,7 +16,8 @@
 
 package com.android.settings.deviceinfo;
 
-import android.app.Activity;
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.Fragment;
@@ -63,8 +64,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
 /**
  * Panel showing both internal storage (both built-in storage and private
  * volumes) and removable storage (public volumes).
@@ -545,13 +544,7 @@
 
 
     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
-            = new SummaryLoader.SummaryProviderFactory() {
-        @Override
-        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
-                                                                   SummaryLoader summaryLoader) {
-            return new SummaryProvider(activity, summaryLoader);
-        }
-    };
+            = (activity, summaryLoader) -> new SummaryProvider(activity, summaryLoader);
 
     /** Enable indexing of searchable data */
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
@@ -559,15 +552,17 @@
                 @Override
                 public List<SearchIndexableRaw> getRawDataToIndex(
                         Context context, boolean enabled) {
-                    final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
+                    final List<SearchIndexableRaw> result = new ArrayList<>();
 
                     SearchIndexableRaw data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.storage_settings);
+                    data.key = "storage_settings";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.internal_storage);
+                    data.key = "storage_settings_internal_storage";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
@@ -577,6 +572,7 @@
                     for (VolumeInfo vol : vols) {
                         if (isInteresting(vol)) {
                             data.title = storage.getBestVolumeDescription(vol);
+                            data.key = "storage_settings_volume_" +vol.id;
                             data.screenTitle = context.getString(R.string.storage_settings);
                             result.add(data);
                         }
@@ -584,36 +580,43 @@
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.memory_size);
+                    data.key = "storage_settings_memory_size";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.memory_available);
+                    data.key = "storage_settings_memory_available";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.memory_apps_usage);
+                    data.key = "storage_settings_apps_space";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.memory_dcim_usage);
+                    data.key = "storage_settings_dcim_space";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.memory_music_usage);
+                    data.key = "storage_settings_music_space";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.memory_media_misc_usage);
+                    data.key = "storage_settings_misc_space";
                     data.screenTitle = context.getString(R.string.storage_settings);
                     result.add(data);
 
                     data = new SearchIndexableRaw(context);
                     data.title = context.getString(R.string.storage_menu_free);
+                    data.key = "storage_settings_free_space";
                     data.screenTitle = context.getString(R.string.storage_menu_free);
                     // We need to define all three in order for this to trigger properly.
                     data.intentAction = StorageManager.ACTION_MANAGE_STORAGE;
diff --git a/src/com/android/settings/display/ScreenZoomSettings.java b/src/com/android/settings/display/ScreenZoomSettings.java
index a77782f..6b5216e 100644
--- a/src/com/android/settings/display/ScreenZoomSettings.java
+++ b/src/com/android/settings/display/ScreenZoomSettings.java
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.view.Display;
+
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.PreviewSeekBarPreferenceFragment;
 import com.android.settings.R;
@@ -48,7 +49,7 @@
         mActivityLayoutResId = R.layout.screen_zoom_activity;
 
         // This should be replaced once the final preview sample screen is in place.
-        mPreviewSampleResIds = new int[]{R.layout.screen_zoom_preview_1,
+        mPreviewSampleResIds = new int[] {R.layout.screen_zoom_preview_1,
                 R.layout.screen_zoom_preview_2,
                 R.layout.screen_zoom_preview_settings};
 
@@ -60,8 +61,8 @@
             // connect to the window manager service. Just use the current
             // density and don't let the user change anything.
             final int densityDpi = getResources().getDisplayMetrics().densityDpi;
-            mValues = new int[] { densityDpi };
-            mEntries = new String[] { getString(DisplayDensityUtils.SUMMARY_DEFAULT) };
+            mValues = new int[] {densityDpi};
+            mEntries = new String[] {getString(DisplayDensityUtils.SUMMARY_DEFAULT)};
             mInitialIndex = 0;
             mDefaultDensity = densityDpi;
         } else {
@@ -109,10 +110,12 @@
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
                 @Override
-                public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
+                public List<SearchIndexableRaw> getRawDataToIndex(Context context,
+                        boolean enabled) {
                     final Resources res = context.getResources();
                     final SearchIndexableRaw data = new SearchIndexableRaw(context);
                     data.title = res.getString(R.string.screen_zoom_title);
+                    data.key = "screen_zoom_settings";
                     data.screenTitle = res.getString(R.string.screen_zoom_title);
                     data.keywords = res.getString(R.string.screen_zoom_keywords);
 
diff --git a/src/com/android/settings/network/PrivateDnsModeDialogFragment.java b/src/com/android/settings/network/PrivateDnsModeDialogFragment.java
index cb3079e..5704fb9 100644
--- a/src/com/android/settings/network/PrivateDnsModeDialogFragment.java
+++ b/src/com/android/settings/network/PrivateDnsModeDialogFragment.java
@@ -32,6 +32,7 @@
 import android.text.TextWatcher;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.Button;
 import android.widget.EditText;
 import android.widget.RadioGroup;
 
@@ -68,6 +69,8 @@
     @VisibleForTesting
     RadioGroup mRadioGroup;
     @VisibleForTesting
+    Button mSaveButton;
+    @VisibleForTesting
     String mMode;
 
     public static void show(FragmentManager fragmentManager) {
@@ -81,17 +84,23 @@
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         final Context context = getContext();
 
-        return new AlertDialog.Builder(context)
+        final AlertDialog dialog = new AlertDialog.Builder(context)
                 .setTitle(R.string.select_private_dns_configuration_title)
                 .setView(buildPrivateDnsView(context))
                 .setPositiveButton(R.string.save, this)
                 .setNegativeButton(R.string.dlg_cancel, null)
                 .create();
+
+        dialog.setOnShowListener(dialogInterface -> {
+            mSaveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+            updateDialogInfo();
+        });
+        return dialog;
     }
 
     private View buildPrivateDnsView(final Context context) {
         final ContentResolver contentResolver = context.getContentResolver();
-        final String mode = Settings.Global.getString(contentResolver, MODE_KEY);
+        mMode = Settings.Global.getString(contentResolver, MODE_KEY);
         final View view = LayoutInflater.from(context).inflate(R.layout.private_dns_mode_dialog,
                 null);
 
@@ -101,7 +110,7 @@
 
         mRadioGroup = view.findViewById(R.id.private_dns_radio_group);
         mRadioGroup.setOnCheckedChangeListener(this);
-        mRadioGroup.check(PRIVATE_DNS_MAP.getOrDefault(mode, R.id.private_dns_mode_opportunistic));
+        mRadioGroup.check(PRIVATE_DNS_MAP.getOrDefault(mMode, R.id.private_dns_mode_opportunistic));
 
         return view;
     }
@@ -129,17 +138,15 @@
         switch (checkedId) {
             case R.id.private_dns_mode_off:
                 mMode = PRIVATE_DNS_MODE_OFF;
-                mEditText.setEnabled(false);
                 break;
             case R.id.private_dns_mode_opportunistic:
                 mMode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
-                mEditText.setEnabled(false);
                 break;
             case R.id.private_dns_mode_provider:
                 mMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
-                mEditText.setEnabled(true);
                 break;
         }
+        updateDialogInfo();
     }
 
     @Override
@@ -152,8 +159,9 @@
 
     @Override
     public void afterTextChanged(Editable s) {
-        // TODO(b/68030013): Disable the "positive button" ("Save") when appearsValid is false.
-        final boolean valid = isWeaklyValidatedHostname(s.toString());
+        if (mSaveButton != null) {
+            mSaveButton.setEnabled(isWeaklyValidatedHostname(mEditText.getText().toString()));
+        }
     }
 
     private boolean isWeaklyValidatedHostname(String hostname) {
@@ -165,4 +173,17 @@
         return hostname.matches(WEAK_HOSTNAME_REGEX);
     }
 
+    private void updateDialogInfo() {
+        final boolean modeProvider = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mMode);
+        if (mEditText != null) {
+            mEditText.setEnabled(modeProvider);
+        }
+        if (mSaveButton != null) {
+            mSaveButton.setEnabled(
+                    modeProvider
+                            ? isWeaklyValidatedHostname(mEditText.getText().toString())
+                            : true);
+        }
+    }
+
 }
diff --git a/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java b/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java
new file mode 100644
index 0000000..ec9cf2a
--- /dev/null
+++ b/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java
@@ -0,0 +1,160 @@
+/*
+ * 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.AutomaticZenRule;
+import android.app.Fragment;
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.provider.Settings;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.ZenModeConfig;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Set;
+
+abstract public class AbstractZenModeAutomaticRulePreferenceController extends
+        AbstractPreferenceController implements PreferenceControllerMixin {
+
+    private static final String TAG = "ZenModeAutomaticRule";
+    protected ZenModeBackend mBackend;
+    protected Fragment mParent;
+    protected Set<Map.Entry<String, AutomaticZenRule>> mRules;
+    protected PackageManager mPm;
+
+    public AbstractZenModeAutomaticRulePreferenceController(Context context, Fragment parent) {
+        super(context);
+        mBackend = ZenModeBackend.getInstance(context);
+        mParent = parent;
+        mPm = mContext.getPackageManager();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        mRules = getZenModeRules();
+    }
+
+    private Set<Map.Entry<String, AutomaticZenRule>> getZenModeRules() {
+        Map<String, AutomaticZenRule> ruleMap =
+                NotificationManager.from(mContext).getAutomaticZenRules();
+        return ruleMap.entrySet();
+    }
+
+    protected void showNameRuleDialog(final ZenRuleInfo ri) {
+        new ZenRuleNameDialog(mContext, null, ri.defaultConditionId) {
+            @Override
+            public void onOk(String ruleName) {
+                AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent,
+                        ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY,
+                        true);
+                String savedRuleId = mBackend.addZenRule(rule);
+                if (savedRuleId != null) {
+                    mParent.startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId));
+                }
+            }
+        }.show();
+    }
+
+    protected Map.Entry<String, AutomaticZenRule>[] sortedRules() {
+        if (mRules == null) {
+            mRules = getZenModeRules();
+        }
+        final Map.Entry<String, AutomaticZenRule>[] rt =
+                mRules.toArray(new Map.Entry[mRules.size()]);
+        Arrays.sort(rt, RULE_COMPARATOR);
+        return rt;
+    }
+
+    protected static Intent getRuleIntent(String settingsAction,
+            ComponentName configurationActivity, String ruleId) {
+        final Intent intent = new Intent()
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId);
+        if (configurationActivity != null) {
+            intent.setComponent(configurationActivity);
+        } else {
+            intent.setAction(settingsAction);
+        }
+        return intent;
+    }
+
+    private static final Comparator<Map.Entry<String, AutomaticZenRule>> RULE_COMPARATOR =
+            new Comparator<Map.Entry<String, AutomaticZenRule>>() {
+                @Override
+                public int compare(Map.Entry<String, AutomaticZenRule> lhs,
+                        Map.Entry<String, AutomaticZenRule> rhs) {
+                    int byDate = Long.compare(lhs.getValue().getCreationTime(),
+                            rhs.getValue().getCreationTime());
+                    if (byDate != 0) {
+                        return byDate;
+                    } else {
+                        return key(lhs.getValue()).compareTo(key(rhs.getValue()));
+                    }
+                }
+
+                private String key(AutomaticZenRule rule) {
+                    final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
+                            ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
+                            ? 2 : 3;
+                    return type + rule.getName().toString();
+                }
+            };
+
+    public static ZenRuleInfo getRuleInfo(PackageManager pm, ServiceInfo si) {
+        if (si == null || si.metaData == null) {
+            return null;
+        }
+        final String ruleType = si.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE);
+        final ComponentName configurationActivity = getSettingsActivity(si);
+        if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) {
+            final ZenRuleInfo ri = new ZenRuleInfo();
+            ri.serviceComponent = new ComponentName(si.packageName, si.name);
+            ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS;
+            ri.title = ruleType;
+            ri.packageName = si.packageName;
+            ri.configurationActivity = getSettingsActivity(si);
+            ri.packageLabel = si.applicationInfo.loadLabel(pm);
+            ri.ruleInstanceLimit =
+                    si.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
+            return ri;
+        }
+        return null;
+    }
+
+    protected static ComponentName getSettingsActivity(ServiceInfo si) {
+        if (si == null || si.metaData == null) {
+            return null;
+        }
+        final String configurationActivity =
+                si.metaData.getString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
+        if (configurationActivity != null) {
+            return ComponentName.unflattenFromString(configurationActivity);
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/settings/notification/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/AbstractZenModePreferenceController.java
new file mode 100644
index 0000000..ec275b2
--- /dev/null
+++ b/src/com/android/settings/notification/AbstractZenModePreferenceController.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.notification;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceControllerMixin;
+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.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+abstract public class AbstractZenModePreferenceController extends
+        AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver,
+        OnResume, OnPause {
+
+    private SettingObserver mSettingObserver;
+    private final String KEY;
+    final private NotificationManager mNotificationManager;
+
+    public AbstractZenModePreferenceController(Context context, String key,
+            Lifecycle lifecycle) {
+        super(context);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+        KEY = key;
+        mNotificationManager = (NotificationManager) context.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mSettingObserver = new SettingObserver(screen.findPreference(KEY));
+    }
+
+    @Override
+    public void onResume() {
+        if (mSettingObserver != null) {
+            mSettingObserver.register(mContext.getContentResolver());
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mSettingObserver != null) {
+            mSettingObserver.unregister(mContext.getContentResolver());
+        }
+    }
+
+    protected NotificationManager.Policy getPolicy() {
+        return mNotificationManager.getNotificationPolicy();
+    }
+
+    protected int getZenMode() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, 0);
+    }
+
+    class SettingObserver extends ContentObserver {
+        private final Uri ZEN_MODE_URI = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
+        private final Uri ZEN_MODE_CONFIG_ETAG_URI = Settings.Global.getUriFor(
+                Settings.Global.ZEN_MODE_CONFIG_ETAG);
+
+        private final Preference mPreference;
+
+        public SettingObserver(Preference preference) {
+            super(new Handler());
+            mPreference = preference;
+        }
+
+        public void register(ContentResolver cr) {
+            cr.registerContentObserver(ZEN_MODE_URI, false, this);
+            cr.registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this);
+        }
+
+        public void unregister(ContentResolver cr) {
+            cr.unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            if (ZEN_MODE_URI.equals(uri)) {
+                updateState(mPreference);
+            }
+
+            if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) {
+                updateState(mPreference);
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java b/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java
new file mode 100644
index 0000000..a15536c
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java
@@ -0,0 +1,75 @@
+/*
+ * 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.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.utils.ZenServiceListing;
+
+public class ZenModeAddAutomaticRulePreferenceController extends
+        AbstractZenModeAutomaticRulePreferenceController implements
+        Preference.OnPreferenceClickListener {
+
+    private final String KEY_ADD_RULE;
+    private final ZenServiceListing mZenServiceListing;
+
+    public ZenModeAddAutomaticRulePreferenceController(Context context, String key,
+            Fragment parent, ZenServiceListing serviceListing) {
+        super(context, parent);
+        KEY_ADD_RULE = key;
+        mZenServiceListing = serviceListing;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ADD_RULE;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        Preference pref = screen.findPreference(KEY_ADD_RULE);
+        pref.setPersistent(false);
+        pref.setOnPreferenceClickListener(this);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        new ZenRuleSelectionDialog(mContext, mZenServiceListing) {
+            @Override
+            public void onSystemRuleSelected(ZenRuleInfo ri) {
+                showNameRuleDialog(ri);
+            }
+
+            @Override
+            public void onExternalRuleSelected(ZenRuleInfo ri) {
+                Intent intent = new Intent().setComponent(ri.configurationActivity);
+                mParent.startActivity(intent);
+            }
+        }.show();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java b/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java
new file mode 100644
index 0000000..ef8d026
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * 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.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeAlarmsPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
+
+    protected static final String KEY = "zen_mode_alarms";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeAlarmsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(true);
+                break;
+            default:
+                pref.setEnabled(true);
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_ALARMS));
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowAlarms = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) {
+            Log.d(TAG, "onPrefChange allowAlarms=" + allowAlarms);
+        }
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_ALARMS, allowAlarms);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java b/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java
new file mode 100644
index 0000000..f91bdd6
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java
@@ -0,0 +1,99 @@
+/*
+ * 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.AutomaticZenRule;
+import android.app.Fragment;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceScreen;
+import java.util.Map;
+
+public class ZenModeAutomaticRulesPreferenceController extends
+        AbstractZenModeAutomaticRulePreferenceController {
+
+    private final String KEY_AUTOMATIC_RULES;
+    private PreferenceCategory mPreferenceCategory;
+    Map.Entry<String, AutomaticZenRule>[] mSortedRules;
+
+    public ZenModeAutomaticRulesPreferenceController(Context context, String key,
+            Fragment parent) {
+        super(context, parent);
+        KEY_AUTOMATIC_RULES = key;
+        mSortedRules = sortedRules();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_AUTOMATIC_RULES;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreferenceCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey());
+        mPreferenceCategory.setPersistent(false);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        // no need to update AutomaticRule if a rule was deleted
+        // (on rule deletion, the preference removes itself from its parent)
+        int oldRuleLength = mSortedRules.length;
+        mSortedRules = sortedRules();
+        if  (!wasRuleDeleted(oldRuleLength)) {
+            updateAutomaticRules();
+        }
+    }
+
+    private boolean wasRuleDeleted(int oldRuleLength) {
+        int newRuleLength = mSortedRules.length;
+        int prefCount =  mPreferenceCategory.getPreferenceCount();
+
+        return (prefCount == oldRuleLength -1) && (prefCount == newRuleLength);
+    }
+
+    private void updateAutomaticRules() {
+        for (Map.Entry<String, AutomaticZenRule> sortedRule : mSortedRules) {
+            ZenRulePreference currPref = (ZenRulePreference)
+                    mPreferenceCategory.findPreference(sortedRule.getKey());
+            if (currPref != null && currPref.appExists) {
+                // rule already exists in preferences, update it
+                currPref.setAttributes(sortedRule.getValue());
+            } else {
+                // rule doesn't exist in preferences, add it
+                ZenRulePreference pref = new ZenRulePreference(mPreferenceCategory.getContext(),
+                        sortedRule, mPreferenceCategory);
+                if (pref.appExists) {
+                    mPreferenceCategory.addPreference(pref);
+                }
+            }
+        }
+
+    }
+}
+
+
+
diff --git a/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java b/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java
new file mode 100644
index 0000000..aa46d4e
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java
@@ -0,0 +1,34 @@
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public class ZenModeAutomationPreferenceController extends
+        AbstractPreferenceController implements PreferenceControllerMixin {
+
+    protected static final String KEY_ZEN_MODE_AUTOMATION = "zen_mode_automation_settings";
+    private final ZenModeSettings.SummaryBuilder mSummaryBuilder;
+
+    public ZenModeAutomationPreferenceController(Context context) {
+        super(context);
+        mSummaryBuilder = new ZenModeSettings.SummaryBuilder(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ZEN_MODE_AUTOMATION;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        preference.setSummary(mSummaryBuilder.getAutomaticRulesSummary());
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAutomationSettings.java b/src/com/android/settings/notification/ZenModeAutomationSettings.java
index 07e9228..d096e8c 100644
--- a/src/com/android/settings/notification/ZenModeAutomationSettings.java
+++ b/src/com/android/settings/notification/ZenModeAutomationSettings.java
@@ -16,167 +16,47 @@
 
 package com.android.settings.notification;
 
-import android.app.AlertDialog;
-import android.app.AutomaticZenRule;
-import android.app.NotificationManager;
-import android.content.ComponentName;
+import android.app.Fragment;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.provider.SearchIndexableResource;
-import android.provider.Settings;
 import android.service.notification.ConditionProviderService;
-import android.service.notification.ZenModeConfig;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import android.support.v7.preference.PreferenceScreen;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.view.View;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.utils.ManagedServiceSettings.Config;
+import com.android.settings.utils.ManagedServiceSettings;
 import com.android.settings.utils.ZenServiceListing;
-import com.android.settingslib.TwoTargetPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 
-import java.util.Arrays;
-import java.util.Comparator;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
-public class ZenModeAutomationSettings extends ZenModeSettingsBase implements Indexable {
-
-    static final Config CONFIG = getConditionProviderConfig();
-
-    private PackageManager mPm;
-    private ZenServiceListing mServiceListing;
+public class ZenModeAutomationSettings extends ZenModeSettingsBase {
+    private static final String KEY_ADD_RULE = "zen_mode_add_automatic_rule";
+    private static final String KEY_AUTOMATIC_RULES = "zen_mode_automatic_rules";
+    protected static final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig();
 
     @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.zen_mode_automation_settings);
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        ZenServiceListing serviceListing = new ZenServiceListing(getContext(), CONFIG);
+        serviceListing.reloadApprovedServices();
+        return buildPreferenceControllers(context, this, serviceListing);
+    }
 
-        mPm = mContext.getPackageManager();
-        mServiceListing = new ZenServiceListing(mContext, CONFIG);
-        mServiceListing.reloadApprovedServices();
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Fragment parent, ZenServiceListing serviceListing) {
+        List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new ZenModeAddAutomaticRulePreferenceController(context, KEY_ADD_RULE,
+                parent, serviceListing));
+        controllers.add(new ZenModeAutomaticRulesPreferenceController(context,
+                KEY_AUTOMATIC_RULES, parent));
+
+        return controllers;
     }
 
     @Override
-    protected void onZenModeChanged() {
-        // don't care
-    }
-
-    @Override
-    protected void onZenModeConfigChanged() {
-        updateControls();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (isUiRestricted()) {
-            return;
-        }
-        updateControls();
-    }
-
-    private void showAddRuleDialog() {
-        new ZenRuleSelectionDialog(mContext, mServiceListing) {
-            @Override
-            public void onSystemRuleSelected(ZenRuleInfo ri) {
-                showNameRuleDialog(ri);
-            }
-
-            @Override
-            public void onExternalRuleSelected(ZenRuleInfo ri) {
-                Intent intent = new Intent().setComponent(ri.configurationActivity);
-                startActivity(intent);
-            }
-        }.show();
-    }
-
-    private void showNameRuleDialog(final ZenRuleInfo ri) {
-        new ZenRuleNameDialog(mContext, null, ri.defaultConditionId) {
-            @Override
-            public void onOk(String ruleName) {
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE_OK);
-                AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent,
-                        ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY,
-                        true);
-                String savedRuleId = addZenRule(rule);
-                if (savedRuleId != null) {
-                    startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId));
-                }
-            }
-        }.show();
-    }
-
-    private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName) {
-        new AlertDialog.Builder(mContext)
-                .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, ruleName))
-                .setNegativeButton(R.string.cancel, null)
-                .setPositiveButton(R.string.zen_mode_delete_rule_button,
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                mMetricsFeatureProvider.action(mContext,
-                                        MetricsEvent.ACTION_ZEN_DELETE_RULE_OK);
-                                removeZenRule(ruleId);
-                            }
-                        })
-                .show();
-    }
-
-    private Intent getRuleIntent(String settingsAction, ComponentName configurationActivity,
-            String ruleId) {
-        final Intent intent = new Intent()
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
-                .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId);
-        if (configurationActivity != null) {
-            intent.setComponent(configurationActivity);
-        } else {
-            intent.setAction(settingsAction);
-        }
-        return intent;
-    }
-
-    private Map.Entry<String,AutomaticZenRule>[] sortedRules() {
-        final Map.Entry<String,AutomaticZenRule>[] rt =
-                mRules.toArray(new Map.Entry[mRules.size()]);
-        Arrays.sort(rt, RULE_COMPARATOR);
-        return rt;
-    }
-
-    private void updateControls() {
-        final PreferenceScreen root = getPreferenceScreen();
-        root.removeAll();
-        final Map.Entry<String,AutomaticZenRule>[] sortedRules = sortedRules();
-        for (Map.Entry<String,AutomaticZenRule> sortedRule : sortedRules) {
-            ZenRulePreference pref = new ZenRulePreference(getPrefContext(), sortedRule);
-            if (pref.appExists) {
-                root.addPreference(pref);
-            }
-        }
-        final Preference p = new Preference(getPrefContext());
-        p.setIcon(R.drawable.ic_menu_add);
-        p.setTitle(R.string.zen_mode_add_rule);
-        p.setPersistent(false);
-        p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(Preference preference) {
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE);
-                showAddRuleDialog();
-                return true;
-            }
-        });
-        root.addPreference(p);
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_automation_settings;
     }
 
     @Override
@@ -184,18 +64,8 @@
         return MetricsEvent.NOTIFICATION_ZEN_MODE_AUTOMATION;
     }
 
-    private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule,
-            CharSequence providerLabel) {
-        final String mode = computeZenModeCaption(getResources(), rule.getInterruptionFilter());
-        final String ruleState = (rule == null || !rule.isEnabled())
-                ? getString(R.string.switch_off_text)
-                : getString(R.string.zen_mode_rule_summary_enabled_combination, mode);
-
-        return ruleState;
-    }
-
-    private static Config getConditionProviderConfig() {
-        final Config c = new Config();
+    protected static ManagedServiceSettings.Config getConditionProviderConfig() {
+        final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config();
         c.tag = TAG;
         c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
         c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
@@ -203,146 +73,22 @@
         return c;
     }
 
-    private static String computeZenModeCaption(Resources res, int zenMode) {
-        switch (zenMode) {
-            case NotificationManager.INTERRUPTION_FILTER_ALARMS:
-                return res.getString(R.string.zen_mode_option_alarms);
-            case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
-                return res.getString(R.string.zen_mode_option_important_interruptions);
-            case NotificationManager.INTERRUPTION_FILTER_NONE:
-                return res.getString(R.string.zen_mode_option_no_interruptions);
-            default:
-                return null;
-        }
-    }
-
-    public static ZenRuleInfo getRuleInfo(PackageManager pm, ServiceInfo si) {
-        if (si == null || si.metaData == null) {
-            return null;
-        }
-        final String ruleType = si.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE);
-        final ComponentName configurationActivity = getSettingsActivity(si);
-        if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) {
-            final ZenRuleInfo ri = new ZenRuleInfo();
-            ri.serviceComponent = new ComponentName(si.packageName, si.name);
-            ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS;
-            ri.title = ruleType;
-            ri.packageName = si.packageName;
-            ri.configurationActivity = getSettingsActivity(si);
-            ri.packageLabel = si.applicationInfo.loadLabel(pm);
-            ri.ruleInstanceLimit =
-                    si.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
-            return ri;
-        }
-        return null;
-    }
-
-    private static ComponentName getSettingsActivity(ServiceInfo si) {
-        if (si == null || si.metaData == null) {
-            return null;
-        }
-        final String configurationActivity =
-                si.metaData.getString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
-        if (configurationActivity != null) {
-            return ComponentName.unflattenFromString(configurationActivity);
-        }
-        return null;
-    }
-
-    private static final Comparator<Map.Entry<String,AutomaticZenRule>> RULE_COMPARATOR =
-            new Comparator<Map.Entry<String,AutomaticZenRule>>() {
-                @Override
-                public int compare(Map.Entry<String,AutomaticZenRule> lhs,
-                        Map.Entry<String,AutomaticZenRule> rhs) {
-                    int byDate = Long.compare(lhs.getValue().getCreationTime(),
-                            rhs.getValue().getCreationTime());
-                    if (byDate != 0) {
-                        return byDate;
-                    } else {
-                        return key(lhs.getValue()).compareTo(key(rhs.getValue()));
-                    }
-                }
-
-                private String key(AutomaticZenRule rule) {
-                    final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
-                            ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
-                            ? 2 : 3;
-                    return type + rule.getName().toString();
-                }
-            };
-
-    private class ZenRulePreference extends TwoTargetPreference {
-        final CharSequence mName;
-        final String mId;
-        final boolean appExists;
-
-        public ZenRulePreference(Context context,
-                final Map.Entry<String, AutomaticZenRule> ruleEntry) {
-            super(context);
-
-            final AutomaticZenRule rule = ruleEntry.getValue();
-            mName = rule.getName();
-            mId = ruleEntry.getKey();
-
-            final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
-                    rule.getConditionId());
-            final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId());
-            final boolean isSystemRule = isSchedule || isEvent;
-
-            try {
-                ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0);
-                setSummary(computeRuleSummary(rule, isSystemRule, info.loadLabel(mPm)));
-            } catch (PackageManager.NameNotFoundException e) {
-                appExists = false;
-                return;
-            }
-
-            appExists = true;
-            setTitle(rule.getName());
-            setPersistent(false);
-
-            final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
-                    : isEvent ? ZenModeEventRuleSettings.ACTION : "";
-            ServiceInfo si = mServiceListing.findService(rule.getOwner());
-            ComponentName settingsActivity = getSettingsActivity(si);
-            setIntent(getRuleIntent(action, settingsActivity, mId));
-            setSelectable(settingsActivity != null || isSystemRule);
-        }
-
-        @Override
-        protected int getSecondTargetResId() {
-            return R.layout.zen_rule_widget;
-        }
-
-        @Override
-        public void onBindViewHolder(PreferenceViewHolder view) {
-            super.onBindViewHolder(view);
-
-            View v = view.findViewById(R.id.delete_zen_rule);
-            if (v != null) {
-                v.setOnClickListener(mDeleteListener);
-            }
-        }
-
-        private final View.OnClickListener mDeleteListener = new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                showDeleteRuleDialog(mId, mName);
-            }
-        };
-    }
-
     /**
      * For Search.
      */
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
-                @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.zen_mode_automation_settings;
-                    return Arrays.asList(sir);
-                }
-            };
+        @Override
+        public List<String> getNonIndexableKeys(Context context) {
+            final List<String> keys = super.getNonIndexableKeys(context);
+            keys.add(KEY_ADD_RULE);
+            keys.add(KEY_AUTOMATIC_RULES);
+            return keys;
+        }
+
+        @Override
+        public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+            return buildPreferenceControllers(context, null, null);
+        }
+    };
 }
diff --git a/src/com/android/settings/notification/ZenModeBackend.java b/src/com/android/settings/notification/ZenModeBackend.java
new file mode 100644
index 0000000..945da0b
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeBackend.java
@@ -0,0 +1,274 @@
+/*
+ * 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.AutomaticZenRule;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.service.notification.ZenModeConfig;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.settings.R;
+
+public class ZenModeBackend {
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_ANYONE = "zen_mode_from_anyone";
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_CONTACTS = "zen_mode_from_contacts";
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_STARRED = "zen_mode_from_starred";
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_NONE = "zen_mode_from_none";
+    protected static final int SOURCE_NONE = -1;
+
+    private static ZenModeBackend sInstance;
+
+    protected int mZenMode;
+    /** gets policy last set by updatePolicy **/
+    protected NotificationManager.Policy mPolicy;
+    private final NotificationManager mNotificationManager;
+
+    private String TAG = "ZenModeSettingsBackend";
+    private final Context mContext;
+
+    public static ZenModeBackend getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new ZenModeBackend(context);
+        }
+        return sInstance;
+    }
+
+    public ZenModeBackend(Context context) {
+        mContext = context;
+        mNotificationManager = (NotificationManager) context.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+        updateZenMode();
+        updatePolicy();
+    }
+
+    protected void updatePolicy() {
+        if (mNotificationManager != null) {
+            mPolicy = mNotificationManager.getNotificationPolicy();
+        }
+    }
+
+    protected void updateZenMode() {
+        mZenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, mZenMode);
+    }
+
+    protected boolean setZenRule(String id, AutomaticZenRule rule) {
+        return NotificationManager.from(mContext).updateAutomaticZenRule(id, rule);
+    }
+
+    protected void setZenMode(int zenMode) {
+        NotificationManager.from(mContext).setZenMode(zenMode, null, TAG);
+        mZenMode = zenMode;
+    }
+
+    /** gets last zen mode set by setZenMode or updateZenMode **/
+    protected int getZenMode() {
+        mZenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, mZenMode);
+        return mZenMode;
+    }
+
+    protected boolean isPriorityCategoryEnabled(int categoryType) {
+        return (mPolicy.priorityCategories & categoryType) != 0;
+    }
+
+    protected int getNewPriorityCategories(boolean allow, int categoryType) {
+        int priorityCategories = mPolicy.priorityCategories;
+        if (allow) {
+            priorityCategories |= categoryType;
+        } else {
+            priorityCategories &= ~categoryType;
+        }
+        return priorityCategories;
+    }
+
+    protected int getPriorityCallSenders() {
+        return mPolicy.priorityCallSenders;
+    }
+
+    protected int getPriorityMessageSenders() {
+        return mPolicy.priorityMessageSenders;
+    }
+
+    protected void saveVisualEffectsPolicy(int category, boolean canBypass) {
+        int suppressedEffects = getNewSuppressedEffects(!canBypass, category);
+        savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders,
+                mPolicy.priorityMessageSenders, suppressedEffects);
+    }
+
+    protected void saveSoundPolicy(int category, boolean allow) {
+        int priorityCategories = getNewPriorityCategories(allow, category);
+        savePolicy(priorityCategories, mPolicy.priorityCallSenders,
+                mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects);
+    }
+
+    protected void savePolicy(int priorityCategories, int priorityCallSenders,
+            int priorityMessageSenders, int suppressedVisualEffects) {
+        mPolicy = new NotificationManager.Policy(priorityCategories, priorityCallSenders,
+                priorityMessageSenders,
+                suppressedVisualEffects);
+        mNotificationManager.setNotificationPolicy(mPolicy);
+    }
+
+    protected int getNewSuppressedEffects(boolean suppress, int effectType) {
+        int effects = mPolicy.suppressedVisualEffects;
+        if (suppress) {
+            effects |= effectType;
+        } else {
+            effects &= ~effectType;
+        }
+        return effects;
+    }
+
+    protected boolean isEffectAllowed(int effect) {
+        return (mPolicy.suppressedVisualEffects & effect) == 0;
+    }
+
+    protected void saveSenders(int category, int val) {
+        int priorityCallSenders = getPriorityCallSenders();
+        int priorityMessagesSenders = getPriorityMessageSenders();
+        int categorySenders = getPrioritySenders(category);
+
+        final boolean allowSenders = val != SOURCE_NONE;
+        final int allowSendersFrom = val == SOURCE_NONE ? categorySenders : val;
+
+        String stringCategory = "";
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
+            stringCategory = "Calls";
+            priorityCallSenders = allowSendersFrom;
+        }
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
+            stringCategory = "Messages";
+            priorityMessagesSenders = allowSendersFrom;
+        }
+
+        savePolicy(getNewPriorityCategories(allowSenders, category),
+            priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects);
+
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allow=" +
+                stringCategory + allowSenders + " allow" + stringCategory + "From="
+                + ZenModeConfig.sourceToString(allowSendersFrom));
+    }
+
+    protected String getSendersKey(int category) {
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                return getKeyFromSetting(SOURCE_NONE);
+            default:
+                int prioritySenders = getPrioritySenders(category);
+                return getKeyFromSetting(isPriorityCategoryEnabled(category)
+                        ? prioritySenders : SOURCE_NONE);
+            }
+    }
+
+    private int getPrioritySenders(int category) {
+        int categorySenders = -1;
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
+            return getPriorityCallSenders();
+        }
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
+            return getPriorityMessageSenders();
+        }
+
+        return categorySenders;
+    }
+
+    protected static String getKeyFromSetting(int contactType) {
+        switch (contactType) {
+            case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
+                return ZEN_MODE_FROM_ANYONE;
+            case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
+                return ZEN_MODE_FROM_CONTACTS;
+            case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
+                return ZEN_MODE_FROM_STARRED;
+            case SOURCE_NONE:
+            default:
+                return ZEN_MODE_FROM_NONE;
+        }
+    }
+
+    protected int getContactsSummary(int category) {
+        int contactType = -1;
+
+        // SOURCE_NONE can be used when in total silence or alarms only
+        // (policy is based on user's preferences but the UI displayed is based on zenMode)
+        if (category == SOURCE_NONE) {
+            return R.string.zen_mode_from_none;
+        }
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
+            if (isPriorityCategoryEnabled(category)) {
+                contactType = getPriorityMessageSenders();
+            }
+        } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
+            if (isPriorityCategoryEnabled(category)) {
+                contactType = getPriorityCallSenders();
+            }
+        }
+
+        switch (contactType) {
+            case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
+                return R.string.zen_mode_from_anyone;
+            case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
+                return  R.string.zen_mode_from_contacts;
+            case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
+                return  R.string.zen_mode_from_starred;
+            case SOURCE_NONE:
+            default:
+                return R.string.zen_mode_from_none;
+        }
+    }
+
+    protected static int getSettingFromPrefKey(String key) {
+        switch (key) {
+            case ZEN_MODE_FROM_ANYONE:
+                return NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+            case ZEN_MODE_FROM_CONTACTS:
+                return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
+            case ZEN_MODE_FROM_STARRED:
+                return NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+            case ZEN_MODE_FROM_NONE:
+            default:
+                return SOURCE_NONE;
+        }
+    }
+
+    public boolean removeZenRule(String ruleId) {
+        return NotificationManager.from(mContext).removeAutomaticZenRule(ruleId);
+    }
+
+    protected String addZenRule(AutomaticZenRule rule) {
+        try {
+            String id = NotificationManager.from(mContext).addAutomaticZenRule(rule);
+            NotificationManager.from(mContext).getAutomaticZenRule(id);
+            return id;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java b/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java
new file mode 100644
index 0000000..0e1f066
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java
@@ -0,0 +1,37 @@
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeBehaviorPreferenceController extends
+        AbstractZenModePreferenceController implements PreferenceControllerMixin {
+
+    protected static final String KEY_BEHAVIOR_SETTINGS = "zen_mode_behavior_settings";
+    private final ZenModeSettings.SummaryBuilder mSummaryBuilder;
+
+    public ZenModeBehaviorPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY_BEHAVIOR_SETTINGS, lifecycle);
+        mSummaryBuilder = new ZenModeSettings.SummaryBuilder(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BEHAVIOR_SETTINGS;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        preference.setSummary(mSummaryBuilder.getBehaviorSettingSummary(getPolicy(),
+                getZenMode()));
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeBehaviorSettings.java b/src/com/android/settings/notification/ZenModeBehaviorSettings.java
index 6fc97cf..dfe6786 100644
--- a/src/com/android/settings/notification/ZenModeBehaviorSettings.java
+++ b/src/com/android/settings/notification/ZenModeBehaviorSettings.java
@@ -16,303 +16,43 @@
 
 package com.android.settings.notification;
 
-import android.app.NotificationManager;
-import android.app.NotificationManager.Policy;
 import android.content.Context;
-import android.os.Bundle;
-import android.provider.SearchIndexableResource;
-import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.DropDownPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.support.v7.preference.PreferenceScreen;
-import android.util.Log;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 
 public class ZenModeBehaviorSettings extends ZenModeSettingsBase implements Indexable {
-    private static final String KEY_ALARMS = "zen_mode_alarms";
-    private static final String KEY_MEDIA = "zen_mode_media";
-    private static final String KEY_REMINDERS = "zen_mode_reminders";
-    private static final String KEY_EVENTS = "zen_mode_events";
-    private static final String KEY_MESSAGES = "zen_mode_messages";
-    private static final String KEY_CALLS = "zen_mode_calls";
-    private static final String KEY_REPEAT_CALLERS = "zen_mode_repeat_callers";
-    private static final String KEY_SCREEN_OFF = "zen_mode_screen_off";
-    private static final String KEY_SCREEN_ON = "zen_mode_screen_on";
-
-    private SwitchPreference mScreenOff;
-    private SwitchPreference mScreenOn;
-
-    private static final int SOURCE_NONE = -1;
-
-    private boolean mDisableListeners;
-    private SwitchPreference mReminders;
-    private SwitchPreference mEvents;
-    private DropDownPreference mMessages;
-    private DropDownPreference mCalls;
-    private SwitchPreference mRepeatCallers;
-    private SwitchPreference mAlarms;
-    private SwitchPreference mMediaSystemOther;
-
-    private Policy mPolicy;
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        addPreferencesFromResource(R.xml.zen_mode_behavior_settings);
-        final PreferenceScreen root = getPreferenceScreen();
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return buildPreferenceControllers(context, getLifecycle());
+    }
 
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-
-        mReminders = (SwitchPreference) root.findPreference(KEY_REMINDERS);
-        mReminders.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_REMINDERS,
-                        val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowReminders=" + val);
-                savePolicy(getNewPriorityCategories(val, Policy.PRIORITY_CATEGORY_REMINDERS),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mEvents = (SwitchPreference) root.findPreference(KEY_EVENTS);
-        mEvents.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_EVENTS, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowEvents=" + val);
-                savePolicy(getNewPriorityCategories(val, Policy.PRIORITY_CATEGORY_EVENTS),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mMessages = (DropDownPreference) root.findPreference(KEY_MESSAGES);
-        addSources(mMessages);
-        mMessages.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return false;
-                final int val = Integer.parseInt((String) newValue);
-                final boolean allowMessages = val != SOURCE_NONE;
-                final int allowMessagesFrom =
-                        val == SOURCE_NONE ? mPolicy.priorityMessageSenders : val;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_MESSAGES, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowMessages=" + allowMessages
-                        + " allowMessagesFrom=" + ZenModeConfig.sourceToString(allowMessagesFrom));
-                savePolicy(
-                        getNewPriorityCategories(allowMessages, Policy.PRIORITY_CATEGORY_MESSAGES),
-                        mPolicy.priorityCallSenders, allowMessagesFrom,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mCalls = (DropDownPreference) root.findPreference(KEY_CALLS);
-        addSources(mCalls);
-        mCalls.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return false;
-                final int val = Integer.parseInt((String) newValue);
-                final boolean allowCalls = val != SOURCE_NONE;
-                final int allowCallsFrom = val == SOURCE_NONE ? mPolicy.priorityCallSenders : val;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_CALLS, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowCalls=" + allowCalls
-                        + " allowCallsFrom=" + ZenModeConfig.sourceToString(allowCallsFrom));
-                savePolicy(getNewPriorityCategories(allowCalls, Policy.PRIORITY_CATEGORY_CALLS),
-                        allowCallsFrom, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mRepeatCallers = (SwitchPreference) root.findPreference(KEY_REPEAT_CALLERS);
-        mRepeatCallers.setSummary(mContext.getString(R.string.zen_mode_repeat_callers_summary,
-                mContext.getResources().getInteger(com.android.internal.R.integer
-                        .config_zen_repeat_callers_threshold)));
-        mRepeatCallers.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_REPEAT_CALLS,
-                        val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowRepeatCallers=" + val);
-                int priorityCategories = getNewPriorityCategories(val,
-                        Policy.PRIORITY_CATEGORY_REPEAT_CALLERS);
-                savePolicy(priorityCategories, mPolicy.priorityCallSenders,
-                        mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mAlarms = (SwitchPreference) root.findPreference(KEY_ALARMS);
-        mAlarms.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_ALARMS, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowAlarms=" + val);
-                savePolicy(getNewPriorityCategories(val, Policy.PRIORITY_CATEGORY_ALARMS),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mMediaSystemOther = (SwitchPreference) root.findPreference(KEY_MEDIA);
-        mMediaSystemOther.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_MEDIA, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowMediaSystemOther=" + val);
-                savePolicy(getNewPriorityCategories(val,
-                        Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mScreenOff = (SwitchPreference) root.findPreference(KEY_SCREEN_OFF);
-        if (!getResources()
-                .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
-            mScreenOff.setSummary(R.string.zen_mode_screen_off_summary_no_led);
-        }
-
-        mScreenOff.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean bypass = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext,
-                        MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_OFF, !bypass);
-                if (DEBUG) Log.d(TAG, "onPrefChange suppressWhenScreenOff=" + !bypass);
-                savePolicy(mPolicy.priorityCategories,
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        getNewSuppressedEffects(!bypass, Policy.SUPPRESSED_EFFECT_SCREEN_OFF));
-                return true;
-            }
-        });
-
-        mScreenOn = (SwitchPreference) root.findPreference(KEY_SCREEN_ON);
-        mScreenOn.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean bypass = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext,
-                        MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_ON, bypass);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOn=" + !bypass);
-                savePolicy(mPolicy.priorityCategories,
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        getNewSuppressedEffects(!bypass, Policy.SUPPRESSED_EFFECT_SCREEN_ON));
-                return true;
-            }
-        });
-
-        updateControls();
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Lifecycle lifecycle) {
+        List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new ZenModeAlarmsPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeMediaSystemOtherPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeEventsPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeRemindersPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeMessagesPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeCallsPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeRepeatCallersPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeScreenOnPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeScreenOffPreferenceController(context, lifecycle));
+        return controllers;
     }
 
     @Override
-    protected void onZenModeChanged() {
-        updateControls();
-    }
-
-    @Override
-    protected void onZenModeConfigChanged() {
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-        updateControls();
-    }
-
-    private void updateControlsPolicy() {
-        if (mCalls != null) {
-            mCalls.setValue(Integer.toString(
-                    isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS)
-                            ? mPolicy.priorityCallSenders : SOURCE_NONE));
-        }
-        mMessages.setValue(Integer.toString(
-                isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES)
-                        ? mPolicy.priorityMessageSenders : SOURCE_NONE));
-        mAlarms.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS));
-        mMediaSystemOther.setChecked(isPriorityCategoryEnabled(
-                Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER));
-        mReminders.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS));
-        mEvents.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS));
-        mRepeatCallers.setChecked(
-                isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS));
-        mRepeatCallers.setVisible(!isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS)
-                || mPolicy.priorityCallSenders != Policy.PRIORITY_SENDERS_ANY);
-
-    }
-
-    private void updateControls() {
-        mDisableListeners = true;
-        switch(mZenMode) {
-            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
-                toggleBasicNoInterruptions();
-                mAlarms.setChecked(false);
-                mMediaSystemOther.setChecked(false);
-                setTogglesEnabled(false);
-                break;
-            case Settings.Global.ZEN_MODE_ALARMS:
-                toggleBasicNoInterruptions();
-                mAlarms.setChecked(true);
-                mMediaSystemOther.setChecked(true);
-                setTogglesEnabled(false);
-                break;
-            default:
-                updateControlsPolicy();
-                setTogglesEnabled(true);
-        }
-
-        mScreenOff.setChecked(isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_OFF));
-        mScreenOn.setChecked(isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_ON));
-
-        mDisableListeners = false;
-    }
-
-    private void toggleBasicNoInterruptions() {
-        if (mCalls != null) {
-            mCalls.setValue(Integer.toString(SOURCE_NONE));
-        }
-        mMessages.setValue(Integer.toString(SOURCE_NONE));
-        mReminders.setChecked(false);
-        mEvents.setChecked(false);
-        mRepeatCallers.setChecked(false);
-    }
-
-    private void setTogglesEnabled(boolean enable) {
-        if (mCalls != null) {
-            mCalls.setEnabled(enable);
-        }
-        mMessages.setEnabled(enable);
-        mReminders.setEnabled(enable);
-        mEvents.setEnabled(enable);
-        mRepeatCallers.setEnabled(enable);
-        mAlarms.setEnabled(enable);
-        mMediaSystemOther.setEnabled(enable);
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_behavior_settings;
     }
 
     @Override
@@ -320,67 +60,29 @@
         return MetricsEvent.NOTIFICATION_ZEN_MODE_PRIORITY;
     }
 
-    private static void addSources(DropDownPreference pref) {
-        pref.setEntries(new CharSequence[]{
-                pref.getContext().getString(R.string.zen_mode_from_anyone),
-                pref.getContext().getString(R.string.zen_mode_from_contacts),
-                pref.getContext().getString(R.string.zen_mode_from_starred),
-                pref.getContext().getString(R.string.zen_mode_from_none),
-        });
-        pref.setEntryValues(new CharSequence[] {
-                Integer.toString(Policy.PRIORITY_SENDERS_ANY),
-                Integer.toString(Policy.PRIORITY_SENDERS_CONTACTS),
-                Integer.toString(Policy.PRIORITY_SENDERS_STARRED),
-                Integer.toString(SOURCE_NONE),
-        });
-    }
-
-    private boolean isPriorityCategoryEnabled(int categoryType) {
-        return (mPolicy.priorityCategories & categoryType) != 0;
-    }
-
-    private int getNewPriorityCategories(boolean allow, int categoryType) {
-        int priorityCategories = mPolicy.priorityCategories;
-        if (allow) {
-            priorityCategories |= categoryType;
-        } else {
-            priorityCategories &= ~categoryType;
-        }
-        return priorityCategories;
-    }
-
-    private void savePolicy(int priorityCategories, int priorityCallSenders,
-            int priorityMessageSenders, int suppressedVisualEffects) {
-        mPolicy = new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
-                suppressedVisualEffects);
-        NotificationManager.from(mContext).setNotificationPolicy(mPolicy);
-    }
-
     /**
      * For Search.
      */
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
                 @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.zen_mode_behavior_settings;
-                    return Arrays.asList(sir);
+                public List<String> getNonIndexableKeys(Context context) {
+                    final List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add(ZenModeAlarmsPreferenceController.KEY);
+                    keys.add(ZenModeMediaSystemOtherPreferenceController.KEY);
+                    keys.add(ZenModeEventsPreferenceController.KEY);
+                    keys.add(ZenModeRemindersPreferenceController.KEY);
+                    keys.add(ZenModeMessagesPreferenceController.KEY);
+                    keys.add(ZenModeCallsPreferenceController.KEY);
+                    keys.add(ZenModeRepeatCallersPreferenceController.KEY);
+                    keys.add(ZenModeScreenOnPreferenceController.KEY);
+                    keys.add(ZenModeScreenOffPreferenceController.KEY);
+                    return keys;
                 }
-            };
 
-    private int getNewSuppressedEffects(boolean suppress, int effectType) {
-        int effects = mPolicy.suppressedVisualEffects;
-        if (suppress) {
-            effects |= effectType;
-        } else {
-            effects &= ~effectType;
-        }
-        return effects;
-    }
-
-    private boolean isEffectAllowed(int effect) {
-        return (mPolicy.suppressedVisualEffects & effect) == 0;
-    }
+            @Override
+            public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+                return buildPreferenceControllers(context, null);
+            }
+        };
 }
diff --git a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
new file mode 100644
index 0000000..79115f2
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
@@ -0,0 +1,88 @@
+/*
+ * 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.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController
+        implements PreferenceControllerMixin {
+
+    protected static final String KEY = "zen_mode_settings_button_container";
+    private Button mZenButtonOn;
+    private Button mZenButtonOff;
+    private ZenModeBackend mBackend;
+
+    public ZenModeButtonPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        if (null == mZenButtonOn) {
+            mZenButtonOn = (Button) ((LayoutPreference) preference)
+                    .findViewById(R.id.zen_mode_settings_turn_on_button);
+            mZenButtonOn.setOnClickListener(v ->
+                    mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+        }
+
+        if (null == mZenButtonOff) {
+            mZenButtonOff = (Button) ((LayoutPreference) preference)
+                    .findViewById(R.id.zen_mode_settings_turn_off_button);
+            mZenButtonOff.setOnClickListener(v ->
+                    mBackend.setZenMode(Settings.Global.ZEN_MODE_OFF));
+        }
+
+        updateButtons();
+    }
+
+    private void updateButtons() {
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_ALARMS:
+            case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+                mZenButtonOff.setVisibility(View.VISIBLE);
+                mZenButtonOn.setVisibility(View.GONE);
+                break;
+            case Settings.Global.ZEN_MODE_OFF:
+            default:
+                mZenButtonOff.setVisibility(View.GONE);
+                mZenButtonOn.setVisibility(View.VISIBLE);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/ZenModeCallsPreferenceController.java b/src/com/android/settings/notification/ZenModeCallsPreferenceController.java
new file mode 100644
index 0000000..d952c11
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeCallsPreferenceController.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.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeCallsPreferenceController extends AbstractZenModePreferenceController {
+
+    protected static final String KEY = "zen_mode_calls";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeCallsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                preference.setEnabled(false);
+                preference.setSummary(mBackend.getContactsSummary(mBackend.SOURCE_NONE));
+                break;
+            default:
+                preference.setEnabled(true);
+                preference.setSummary(mBackend.getContactsSummary(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_CALLS));
+        }
+
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeCallsSettings.java b/src/com/android/settings/notification/ZenModeCallsSettings.java
new file mode 100644
index 0000000..6874dc0
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeCallsSettings.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 android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.widget.RadioButtonPickerFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ZenModeCallsSettings extends RadioButtonPickerFragment {
+    private ZenModeBackend mBackend;
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_CALLS;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_calls_settings;
+    }
+
+    @Override
+    protected List<? extends RadioButtonPickerFragment.CandidateInfo> getCandidates() {
+        final String[] entries = entries();
+        final String[] values = keys();
+        final List<CallsCandidateInfo> candidates = new ArrayList<>();
+
+        if (entries == null || entries.length <= 0) return null;
+        if (values == null || values.length != entries.length) {
+            throw new IllegalArgumentException("Entries and values must be of the same length.");
+        }
+
+        for (int i = 0; i < entries.length; i++) {
+            candidates.add(new CallsCandidateInfo(entries[i], values[i]));
+        }
+
+        return candidates;
+    }
+
+    private String[] entries() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_entries);
+    }
+
+    private String[] keys() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_values);
+    }
+
+    @Override
+    protected String getDefaultKey() {
+        return mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS);
+    }
+
+    @Override
+    protected boolean setDefaultKey(String key) {
+        mBackend.saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+        return true;
+    }
+
+    private static final class CallsCandidateInfo extends RadioButtonPickerFragment.CandidateInfo {
+        private final String name;
+        private final String key;
+
+        CallsCandidateInfo(String title, String value) {
+            super(true);
+
+            name = title;
+            key = value;
+        }
+
+        @Override
+        public CharSequence loadLabel() {
+            return name;
+        }
+
+        @Override
+        public Drawable loadIcon() {
+            return null;
+        }
+
+        @Override
+        public String getKey() {
+            return key;
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
index 3361734..aa2cc3f 100644
--- a/src/com/android/settings/notification/ZenModeEventRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -81,6 +82,16 @@
         mCreate = false;
     }
 
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_event_rule_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
+
     private void reloadCalendar() {
         mCalendars = getCalendars(mContext);
         ArrayList<CharSequence> entries = new ArrayList<>();
@@ -107,7 +118,6 @@
     @Override
     protected void onCreateInternal() {
         mCreate = true;
-        addPreferencesFromResource(R.xml.zen_mode_event_rule_settings);
         final PreferenceScreen root = getPreferenceScreen();
 
         mCalendar = (DropDownPreference) root.findPreference(KEY_CALENDAR);
@@ -243,5 +253,4 @@
         public String name;
         public int userId;
     }
-
 }
diff --git a/src/com/android/settings/notification/ZenModeEventsPreferenceController.java b/src/com/android/settings/notification/ZenModeEventsPreferenceController.java
new file mode 100644
index 0000000..3763fed
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeEventsPreferenceController.java
@@ -0,0 +1,77 @@
+/*
+ * 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.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeEventsPreferenceController extends AbstractZenModePreferenceController
+        implements Preference.OnPreferenceChangeListener {
+
+    protected static final String KEY = "zen_mode_events";
+        private final ZenModeBackend mBackend;
+
+    public ZenModeEventsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            default:
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_EVENTS));
+                pref.setEnabled(true);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowEvents = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) {
+            Log.d(TAG, "onPrefChange allowEvents=" + allowEvents);
+        }
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_EVENTS, allowEvents);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeMediaSystemOtherPreferenceController.java b/src/com/android/settings/notification/ZenModeMediaSystemOtherPreferenceController.java
new file mode 100644
index 0000000..8afe881
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeMediaSystemOtherPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * 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.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeMediaSystemOtherPreferenceController extends AbstractZenModePreferenceController
+        implements Preference.OnPreferenceChangeListener {
+
+    protected static final String KEY = "zen_mode_media";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeMediaSystemOtherPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(true);
+                break;
+            default:
+                pref.setEnabled(true);
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER));
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowMedia = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) {
+            Log.d(TAG, "onPrefChange allowMediaSystemOther=" + allowMedia);
+        }
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allowMedia);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeMessagesPreferenceController.java b/src/com/android/settings/notification/ZenModeMessagesPreferenceController.java
new file mode 100644
index 0000000..dad6cf1
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeMessagesPreferenceController.java
@@ -0,0 +1,46 @@
+package com.android.settings.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeMessagesPreferenceController extends AbstractZenModePreferenceController {
+
+    protected static final String KEY = "zen_mode_messages";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeMessagesPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                preference.setEnabled(false);
+                preference.setSummary(mBackend.getContactsSummary(mBackend.SOURCE_NONE));
+                break;
+            default:
+                preference.setEnabled(true);
+                preference.setSummary(mBackend.getContactsSummary(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES));
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeMessagesSettings.java b/src/com/android/settings/notification/ZenModeMessagesSettings.java
new file mode 100644
index 0000000..9cbf248
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeMessagesSettings.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.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.widget.RadioButtonPickerFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ZenModeMessagesSettings extends RadioButtonPickerFragment {
+    private ZenModeBackend mBackend;
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_MESSAGES;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_messages_settings;
+    }
+
+    @Override
+    protected List<? extends RadioButtonPickerFragment.CandidateInfo> getCandidates() {
+        final String[] entries = entries();
+        final String[] values = keys();
+        final List<MessagesCandidateInfo> candidates = new ArrayList<>();
+
+        if (entries == null || entries.length <= 0) return null;
+        if (values == null || values.length != entries.length) {
+            throw new IllegalArgumentException("Entries and values must be of the same length.");
+        }
+
+        for (int i = 0; i < entries.length; i++) {
+            candidates.add(new MessagesCandidateInfo(entries[i], values[i]));
+        }
+
+        return candidates;
+    }
+
+    private String[] entries() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_entries);
+    }
+
+    private String[] keys() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_values);
+    }
+
+    @Override
+    protected String getDefaultKey() {
+        return mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES);
+    }
+
+    @Override
+    protected boolean setDefaultKey(String key) {
+        mBackend.saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+        return true;
+    }
+
+    private final class MessagesCandidateInfo extends RadioButtonPickerFragment.CandidateInfo {
+        private final String name;
+        private final String key;
+
+        MessagesCandidateInfo(String title, String value) {
+            super(true);
+
+            name = title;
+            key = value;
+        }
+
+        @Override
+        public CharSequence loadLabel() {
+            return name;
+        }
+
+        @Override
+        public Drawable loadIcon() {
+            return null;
+        }
+
+        @Override
+        public String getKey() {
+            return key;
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java b/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java
new file mode 100644
index 0000000..edc7cf9
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java
@@ -0,0 +1,75 @@
+/*
+ * 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.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeRemindersPreferenceController extends AbstractZenModePreferenceController
+        implements Preference.OnPreferenceChangeListener {
+
+    protected static final String KEY = "zen_mode_reminders";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeRemindersPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            default:
+                pref.setEnabled(true);
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS));
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowReminders = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowReminders=" + allowReminders);
+        mBackend.saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS,
+                allowReminders);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java b/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java
new file mode 100644
index 0000000..1d18409
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.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.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeRepeatCallersPreferenceController extends AbstractZenModePreferenceController
+        implements Preference.OnPreferenceChangeListener {
+
+    protected static final String KEY = "zen_mode_repeat_callers";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeRepeatCallersPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            default:
+                boolean anyCallersCanBypassDnd = (mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_CALLS)
+                        && mBackend.getPriorityCallSenders() == Policy.PRIORITY_SENDERS_ANY);
+                // if any caller can bypass dnd then repeat callers preference is disabled
+                if (anyCallersCanBypassDnd) {
+                    pref.setEnabled(false);
+                    pref.setChecked(true);
+                } else {
+                    pref.setEnabled(true);
+                    pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                            Policy.PRIORITY_CATEGORY_REPEAT_CALLERS));
+                }
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowRepeatCallers = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowRepeatCallers="
+                + allowRepeatCallers);
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, allowRepeatCallers);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
index 83c6753..9eccfdc 100644
--- a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
+++ b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.SearchIndexableResource;
 import android.service.notification.ConditionProviderService;
 import android.support.v7.preference.DropDownPreference;
 import android.support.v7.preference.Preference;
@@ -43,7 +44,16 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
 import com.android.settings.widget.SwitchBar;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.Arrays;
+import java.util.List;
+
+import java.util.Arrays;
+import java.util.List;
 
 public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase
         implements SwitchBar.OnSwitchChangeListener {
@@ -52,6 +62,8 @@
 
     private static final String KEY_RULE_NAME = "rule_name";
     private static final String KEY_ZEN_MODE = "zen_mode";
+    private static final String KEY_EVENT_RULE_SETTINGS = "zen_mode_event_rule_settings";
+    private static final String KEY_SCHEDULE_RULE_SETTINGS = "zen_mode_schedule_rule_settings";
 
     protected Context mContext;
     protected boolean mDisableListeners;
@@ -72,8 +84,6 @@
 
     @Override
     public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
         mContext = getActivity();
 
         final Intent intent = getActivity().getIntent();
@@ -96,6 +106,8 @@
             return;
         }
 
+        super.onCreate(icicle);
+
         setHasOptionsMenu(true);
 
         onCreateInternal();
@@ -129,7 +141,7 @@
                 if (zenMode == mRule.getInterruptionFilter()) return false;
                 if (DEBUG) Log.d(TAG, "onPrefChange zenMode=" + zenMode);
                 mRule.setInterruptionFilter(zenMode);
-                setZenRule(mId, mRule);
+                mBackend.setZenRule(mId, mRule);
                 return true;
             }
         });
@@ -138,6 +150,11 @@
     }
 
     @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         if (isUiRestricted()) {
@@ -172,7 +189,7 @@
         mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ENABLE_RULE, enabled);
         if (DEBUG) Log.d(TAG, "onSwitchChanged enabled=" + enabled);
         mRule.setEnabled(enabled);
-        setZenRule(mId, mRule);
+        mBackend.setZenRule(mId, mRule);
         if (enabled) {
             final int toastText = getEnabledToastText();
             if (toastText != 0) {
@@ -188,16 +205,12 @@
 
     protected void updateRule(Uri newConditionId) {
         mRule.setConditionId(newConditionId);
-        setZenRule(mId, mRule);
-    }
-
-    @Override
-    protected void onZenModeChanged() {
-        // noop
+        mBackend.setZenRule(mId, mRule);
     }
 
     @Override
     protected void onZenModeConfigChanged() {
+        super.onZenModeConfigChanged();
         if (!refreshRuleOrFinish()) {
             updateControls();
         }
@@ -225,7 +238,7 @@
             @Override
             public void onOk(String ruleName) {
                 mRule.setName(ruleName);
-                setZenRule(mId, mRule);
+                mBackend.setZenRule(mId, mRule);
             }
         }.show();
     }
@@ -250,7 +263,7 @@
                         mMetricsFeatureProvider.action(mContext,
                                 MetricsEvent.ACTION_ZEN_DELETE_RULE_OK);
                         mDeleting = true;
-                        removeZenRule(mId);
+                        mBackend.removeZenRule(mId);
                     }
                 })
                 .show();
@@ -294,4 +307,25 @@
         mDisableListeners = false;
     }
 
+    /**
+     * For Search.
+     */
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    // not indexable
+                    return Arrays.asList(sir);
+                }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    final List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add(KEY_SCHEDULE_RULE_SETTINGS);
+                    keys.add(KEY_EVENT_RULE_SETTINGS);
+                    return keys;
+                }
+            };
 }
diff --git a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
index 72f6567..ab0349e 100644
--- a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
@@ -19,7 +19,6 @@
 import android.app.AlertDialog;
 import android.app.AutomaticZenRule;
 import android.app.Dialog;
-import android.app.DialogFragment;
 import android.app.FragmentManager;
 import android.app.TimePickerDialog;
 import android.content.Context;
@@ -40,10 +39,12 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.List;
 
 public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase {
     private static final String KEY_DAYS = "days";
@@ -71,6 +72,16 @@
     }
 
     @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_schedule_rule_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
+
+    @Override
     protected String getZenModeDependency() {
         return mDays.getKey();
     }
@@ -82,7 +93,6 @@
 
     @Override
     protected void onCreateInternal() {
-        addPreferencesFromResource(R.xml.zen_mode_schedule_rule_settings);
         final PreferenceScreen root = getPreferenceScreen();
 
         mDays = root.findPreference(KEY_DAYS);
@@ -306,5 +316,4 @@
             boolean onSetTime(int hour, int minute);
         }
     }
-
 }
diff --git a/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java b/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java
new file mode 100644
index 0000000..2b70706
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.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.notification;
+
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeScreenOffPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
+
+    protected static final String KEY = "zen_mode_screen_off";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeScreenOffPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        pref.setChecked(mBackend.isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_OFF));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean bypass = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOff="
+                + !bypass);
+        mBackend.saveVisualEffectsPolicy(Policy.SUPPRESSED_EFFECT_SCREEN_OFF, bypass);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java b/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java
new file mode 100644
index 0000000..8e0b348
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java
@@ -0,0 +1,65 @@
+/*
+ * 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.NotificationManager.Policy;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeScreenOnPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
+
+    protected static final String KEY = "zen_mode_screen_on";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeScreenOnPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        pref.setChecked(mBackend.isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_ON));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean bypass = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOn="
+                + !bypass);
+
+        mBackend.saveVisualEffectsPolicy(Policy.SUPPRESSED_EFFECT_SCREEN_ON, bypass);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
index 2699cfd..fbc9f7d 100644
--- a/src/com/android/settings/notification/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -20,56 +20,27 @@
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.Context;
-import android.os.Bundle;
 import android.provider.SearchIndexableResource;
 import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
 import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-public class ZenModeSettings extends ZenModeSettingsBase implements Indexable {
-
-    private static final String KEY_BEHAVIOR_SETTINGS = "zen_mode_behavior_settings";
-    private static final String KEY_AUTOMATION_SETTINGS = "zen_mode_automation_settings";
-
-    private Preference mBehaviorSettings;
-    private Preference mAutomationSettings;
-    private Policy mPolicy;
-    private SummaryBuilder mSummaryBuilder;
-
+public class ZenModeSettings extends ZenModeSettingsBase {
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        addPreferencesFromResource(R.xml.zen_mode_settings);
-        final PreferenceScreen root = getPreferenceScreen();
-
-        mBehaviorSettings = root.findPreference(KEY_BEHAVIOR_SETTINGS);
-        mAutomationSettings = root.findPreference(KEY_AUTOMATION_SETTINGS);
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-        mSummaryBuilder = new SummaryBuilder(getContext());
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (isUiRestricted()) {
-            return;
-        }
-        updateControls();
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_settings;
     }
 
     @Override
@@ -78,27 +49,8 @@
     }
 
     @Override
-    protected void onZenModeChanged() {
-        updateControls();
-    }
-
-    @Override
-    protected void onZenModeConfigChanged() {
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-        updateControls();
-    }
-
-    private void updateControls() {
-        updateBehaviorSettingsSummary();
-        updateAutomationSettingsSummary();
-    }
-
-    private void updateBehaviorSettingsSummary() {
-        mBehaviorSettings.setSummary(mSummaryBuilder.getBehaviorSettingSummary(mPolicy, mZenMode));
-    }
-
-    private void updateAutomationSettingsSummary() {
-        mAutomationSettings.setSummary(mSummaryBuilder.getAutomaticRulesSummary());
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return buildPreferenceControllers(context, getLifecycle());
     }
 
     @Override
@@ -106,6 +58,15 @@
         return R.string.help_uri_interruptions;
     }
 
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Lifecycle lifecycle) {
+        List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new ZenModeBehaviorPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeAutomationPreferenceController(context));
+        controllers.add(new ZenModeButtonPreferenceController(context, lifecycle));
+        return controllers;
+    }
+
     public static class SummaryBuilder {
 
         private Context mContext;
@@ -227,42 +188,31 @@
         }
     }
 
-    private static final Comparator<Entry<String,AutomaticZenRule>> RULE_COMPARATOR =
-            new Comparator<Map.Entry<String,AutomaticZenRule>>() {
-                @Override
-                public int compare(Map.Entry<String,AutomaticZenRule> lhs,
-                        Map.Entry<String,AutomaticZenRule> rhs) {
-                    int byDate = Long.compare(lhs.getValue().getCreationTime(),
-                            rhs.getValue().getCreationTime());
-                    if (byDate != 0) {
-                        return byDate;
-                    } else {
-                        return key(lhs.getValue()).compareTo(key(rhs.getValue()));
-                    }
-                }
-
-                private String key(AutomaticZenRule rule) {
-                    final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
-                            ? 1
-                            : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
-                                    ? 2
-                                    : 3;
-                    return type + rule.getName().toString();
-                }
-            };
-
     /**
      * For Search.
      */
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
+
                 @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
                     final SearchIndexableResource sir = new SearchIndexableResource(context);
                     sir.xmlResId = R.xml.zen_mode_settings;
                     return Arrays.asList(sir);
                 }
-            };
 
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add(ZenModeButtonPreferenceController.KEY);
+                    return keys;
+                }
+
+                @Override
+                public List<AbstractPreferenceController> getPreferenceControllers(Context
+                        context) {
+                    return buildPreferenceControllers(context, null);
+                }
+            };
 }
diff --git a/src/com/android/settings/notification/ZenModeSettingsBase.java b/src/com/android/settings/notification/ZenModeSettingsBase.java
index 6a9431e..2aecae4 100644
--- a/src/com/android/settings/notification/ZenModeSettingsBase.java
+++ b/src/com/android/settings/notification/ZenModeSettingsBase.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.notification;
 
-import android.app.AutomaticZenRule;
-import android.app.NotificationManager;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -26,17 +24,11 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.service.notification.ZenModeConfig;
 import android.util.Log;
 
-import com.android.settings.RestrictedSettingsFragment;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-abstract public class ZenModeSettingsBase extends RestrictedSettingsFragment {
+abstract public class ZenModeSettingsBase extends RestrictedDashboardFragment {
     protected static final String TAG = "ZenModeSettings";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -44,30 +36,33 @@
     private final SettingsObserver mSettingsObserver = new SettingsObserver();
 
     protected Context mContext;
-    protected Set<Map.Entry<String, AutomaticZenRule>> mRules;
     protected int mZenMode;
 
-    abstract protected void onZenModeChanged();
-    abstract protected void onZenModeConfigChanged();
+    protected ZenModeBackend mBackend;
+
+    protected void onZenModeConfigChanged() {};
 
     public ZenModeSettingsBase() {
         super(UserManager.DISALLOW_ADJUST_VOLUME);
     }
 
     @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
     public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
         mContext = getActivity();
+        mBackend = ZenModeBackend.getInstance(mContext);
+        super.onCreate(icicle);
         updateZenMode(false /*fireChanged*/);
-        maybeRefreshRules(true, false /*fireChanged*/);
-        if (DEBUG) Log.d(TAG, "Loaded mRules=" + mRules);
     }
 
     @Override
     public void onResume() {
         super.onResume();
         updateZenMode(true /*fireChanged*/);
-        maybeRefreshRules(true, true /*fireChanged*/);
         mSettingsObserver.register();
         if (isUiRestricted()) {
             if (isUiRestrictedByOnlyAdmin()) {
@@ -89,56 +84,7 @@
         final int zenMode = Settings.Global.getInt(getContentResolver(), Global.ZEN_MODE, mZenMode);
         if (zenMode == mZenMode) return;
         mZenMode = zenMode;
-        if (DEBUG) Log.d(TAG, "updateZenMode mZenMode=" + mZenMode);
-        if (fireChanged) {
-            onZenModeChanged();
-        }
-    }
-
-    protected String addZenRule(AutomaticZenRule rule) {
-        try {
-            String id = NotificationManager.from(mContext).addAutomaticZenRule(rule);
-            final AutomaticZenRule savedRule =
-                    NotificationManager.from(mContext).getAutomaticZenRule(id);
-            maybeRefreshRules(savedRule != null, true);
-            return id;
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    protected boolean setZenRule(String id, AutomaticZenRule rule) {
-        final boolean success =
-                NotificationManager.from(mContext).updateAutomaticZenRule(id, rule);
-        maybeRefreshRules(success, true);
-        return success;
-    }
-
-    protected boolean removeZenRule(String id) {
-        final boolean success =
-                NotificationManager.from(mContext).removeAutomaticZenRule(id);
-        maybeRefreshRules(success, true);
-        return success;
-    }
-
-    protected void maybeRefreshRules(boolean success, boolean fireChanged) {
-        if (success) {
-            mRules = getZenModeRules();
-            if (DEBUG) Log.d(TAG, "Refreshed mRules=" + mRules);
-            if (fireChanged) {
-                onZenModeConfigChanged();
-            }
-        }
-    }
-
-    protected void setZenMode(int zenMode, Uri conditionId) {
-        NotificationManager.from(mContext).setZenMode(zenMode, conditionId, TAG);
-    }
-
-    private Set<Map.Entry<String, AutomaticZenRule>> getZenModeRules() {
-        Map<String, AutomaticZenRule> ruleMap
-                = NotificationManager.from(mContext).getAutomaticZenRules();
-        return ruleMap.entrySet();
+        if (DEBUG) Log.d(TAG, "updateZenMode mZenMode=" + mZenMode + " " + fireChanged);
     }
 
     private final class SettingsObserver extends ContentObserver {
@@ -165,7 +111,8 @@
                 updateZenMode(true /*fireChanged*/);
             }
             if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) {
-                maybeRefreshRules(true, true /*fireChanged*/);
+                mBackend.updatePolicy();
+                onZenModeConfigChanged();
             }
         }
     }
diff --git a/src/com/android/settings/notification/ZenRulePreference.java b/src/com/android/settings/notification/ZenRulePreference.java
new file mode 100644
index 0000000..4d7181a
--- /dev/null
+++ b/src/com/android/settings/notification/ZenRulePreference.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 android.app.AlertDialog;
+import android.app.AutomaticZenRule;
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.service.notification.ZenModeConfig;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.utils.ManagedServiceSettings;
+import com.android.settings.utils.ZenServiceListing;
+import com.android.settingslib.TwoTargetPreference;
+
+import java.util.Map;
+
+public class ZenRulePreference extends TwoTargetPreference {
+    private static final ManagedServiceSettings.Config CONFIG =
+            ZenModeAutomationSettings.getConditionProviderConfig();
+    final CharSequence mName;
+    final String mId;
+    boolean appExists;
+    final PreferenceCategory mParent;
+    final Preference mPref;
+    final Context mContext;
+    final ZenModeBackend mBackend;
+    final ZenServiceListing mServiceListing;
+    final PackageManager mPm;
+
+    public ZenRulePreference(Context context,
+            final Map.Entry<String, AutomaticZenRule> ruleEntry,
+            PreferenceCategory prefCategory) {
+        super(context);
+
+        mBackend = ZenModeBackend.getInstance(context);
+        mContext = context;
+        final AutomaticZenRule rule = ruleEntry.getValue();
+        mName = rule.getName();
+        mId = ruleEntry.getKey();
+        mParent = prefCategory;
+        mPm = mContext.getPackageManager();
+        mServiceListing = new ZenServiceListing(mContext, CONFIG);
+        mServiceListing.reloadApprovedServices();
+        mPref = this;
+
+        setAttributes(rule);
+    }
+
+    @Override
+    protected int getSecondTargetResId() {
+        return R.layout.zen_rule_widget;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+
+        View v = view.findViewById(R.id.delete_zen_rule);
+        if (v != null) {
+            v.setOnClickListener(mDeleteListener);
+        }
+    }
+
+    private final View.OnClickListener mDeleteListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            showDeleteRuleDialog(mId, mName, mParent, mPref);
+        }
+    };
+
+    private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName,
+            PreferenceCategory parent, Preference pref) {
+        new AlertDialog.Builder(mContext)
+                .setMessage(mContext.getResources().getString(
+                        R.string.zen_mode_delete_rule_confirmation, ruleName))
+                .setNegativeButton(R.string.cancel, null)
+                .setPositiveButton(R.string.zen_mode_delete_rule_button,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                mBackend.removeZenRule(ruleId);
+                                parent.removePreference(pref);
+                            }
+                        })
+                .show();
+    }
+
+    protected void setAttributes(AutomaticZenRule rule) {
+        final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
+                rule.getConditionId());
+        final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId());
+        final boolean isSystemRule = isSchedule || isEvent;
+
+        try {
+            ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0);
+            setSummary(computeRuleSummary(rule, isSystemRule, info.loadLabel(mPm)));
+        } catch (PackageManager.NameNotFoundException e) {
+            appExists = false;
+            return;
+        }
+
+        appExists = true;
+        setTitle(rule.getName());
+        setPersistent(false);
+
+        final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
+                : isEvent ? ZenModeEventRuleSettings.ACTION : "";
+        ServiceInfo si = mServiceListing.findService(rule.getOwner());
+        ComponentName settingsActivity = AbstractZenModeAutomaticRulePreferenceController.
+                getSettingsActivity(si);
+        setIntent(AbstractZenModeAutomaticRulePreferenceController.getRuleIntent(action,
+                settingsActivity, mId));
+        setSelectable(settingsActivity != null || isSystemRule);
+        setKey(mId);
+    }
+
+    private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule,
+            CharSequence providerLabel) {
+        final String mode = computeZenModeCaption(mContext.getResources(),
+                rule.getInterruptionFilter());
+        final String ruleState = (rule == null || !rule.isEnabled())
+                ? mContext.getResources().getString(R.string.switch_off_text)
+                : mContext.getResources().getString(
+                        R.string.zen_mode_rule_summary_enabled_combination, mode);
+
+        return ruleState;
+    }
+
+    private static String computeZenModeCaption(Resources res, int zenMode) {
+        switch (zenMode) {
+            case NotificationManager.INTERRUPTION_FILTER_ALARMS:
+                return res.getString(R.string.zen_mode_option_alarms);
+            case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
+                return res.getString(R.string.zen_mode_option_important_interruptions);
+            case NotificationManager.INTERRUPTION_FILTER_NONE:
+                return res.getString(R.string.zen_mode_option_no_interruptions);
+            default:
+                return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/ZenRuleSelectionDialog.java b/src/com/android/settings/notification/ZenRuleSelectionDialog.java
index 3c49dcb..0c725ed 100644
--- a/src/com/android/settings/notification/ZenRuleSelectionDialog.java
+++ b/src/com/android/settings/notification/ZenRuleSelectionDialog.java
@@ -164,7 +164,8 @@
             if (DEBUG) Log.d(TAG, "Services reloaded: count=" + services.size());
             Set<ZenRuleInfo> externalRuleTypes = new TreeSet<>(RULE_TYPE_COMPARATOR);
             for (ServiceInfo serviceInfo : services) {
-                final ZenRuleInfo ri = ZenModeAutomationSettings.getRuleInfo(mPm, serviceInfo);
+                final ZenRuleInfo ri = AbstractZenModeAutomaticRulePreferenceController.
+                        getRuleInfo(mPm, serviceInfo);
                 if (ri != null && ri.configurationActivity != null
                         && mNm.isNotificationPolicyAccessGrantedForPackage(ri.packageName)
                         && (ri.ruleInstanceLimit <= 0 || ri.ruleInstanceLimit
diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java
index 3d63000..6bdce48 100644
--- a/src/com/android/settings/print/PrintSettingsFragment.java
+++ b/src/com/android/settings/print/PrintSettingsFragment.java
@@ -55,7 +55,6 @@
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.utils.ProfileSettingsPreferenceFragment;
 
 import java.text.DateFormat;
@@ -616,49 +615,11 @@
 
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
-        @Override
-        public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-            List<SearchIndexableRaw> indexables = new ArrayList<SearchIndexableRaw>();
-
-            PackageManager packageManager = context.getPackageManager();
-            PrintManager printManager = (PrintManager) context.getSystemService(
-                    Context.PRINT_SERVICE);
-
-            String screenTitle = context.getResources().getString(R.string.print_settings);
-            SearchIndexableRaw data = new SearchIndexableRaw(context);
-            data.title = screenTitle;
-            data.screenTitle = screenTitle;
-            indexables.add(data);
-
-            // Indexing all services, regardless if enabled. Please note that the index will not be
-            // updated until this function is called again
-            List<PrintServiceInfo> services =
-                    printManager.getPrintServices(PrintManager.ALL_SERVICES);
-
-            if (services != null) {
-                final int serviceCount = services.size();
-                for (int i = 0; i < serviceCount; i++) {
-                    PrintServiceInfo service = services.get(i);
-
-                    ComponentName componentName = new ComponentName(
-                            service.getResolveInfo().serviceInfo.packageName,
-                            service.getResolveInfo().serviceInfo.name);
-
-                    data = new SearchIndexableRaw(context);
-                    data.key = componentName.flattenToString();
-                    data.title = service.getResolveInfo().loadLabel(packageManager).toString();
-                    data.screenTitle = screenTitle;
-                    indexables.add(data);
-                }
-            }
-
-            return indexables;
-        }
 
         @Override
         public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
                 boolean enabled) {
-            List<SearchIndexableResource> indexables = new ArrayList<SearchIndexableResource>();
+            List<SearchIndexableResource> indexables = new ArrayList<>();
             SearchIndexableResource indexable = new SearchIndexableResource(context);
             indexable.xmlResId = R.xml.print_settings;
             indexables.add(indexable);
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 46e693d..c0cfc46 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -22,7 +22,6 @@
 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;
@@ -69,10 +68,13 @@
 import com.android.settings.notification.SoundSettings;
 import com.android.settings.notification.ZenModeAutomationSettings;
 import com.android.settings.notification.ZenModeBehaviorSettings;
+import com.android.settings.notification.ZenModeEventRuleSettings;
+import com.android.settings.notification.ZenModeScheduleRuleSettings;
 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.ScreenPinningSettings;
 import com.android.settings.security.SecuritySettings;
 import com.android.settings.security.screenlock.ScreenLockSettings;
 import com.android.settings.sim.SimSettings;
@@ -168,6 +170,8 @@
         addIndex(LockscreenDashboardFragment.class);
         addIndex(ZenModeBehaviorSettings.class);
         addIndex(ZenModeAutomationSettings.class);
+        addIndex(ZenModeEventRuleSettings.class);
+        addIndex(ZenModeScheduleRuleSettings.class);
     }
 
     private SearchIndexableResources() {
diff --git a/src/com/android/settings/ScreenPinningSettings.java b/src/com/android/settings/security/ScreenPinningSettings.java
similarity index 84%
rename from src/com/android/settings/ScreenPinningSettings.java
rename to src/com/android/settings/security/ScreenPinningSettings.java
index b3b6868..488e0c8 100644
--- a/src/com/android/settings/ScreenPinningSettings.java
+++ b/src/com/android/settings/security/ScreenPinningSettings.java
@@ -13,14 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.settings;
+package com.android.settings.security;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.provider.SearchIndexableResource;
 import android.provider.Settings;
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
@@ -33,13 +33,15 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.password.ChooseLockGeneric;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.widget.SwitchBar;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -210,33 +212,13 @@
      */
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
         new BaseSearchIndexProvider() {
+
             @Override
-            public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
-
-                final Resources res = context.getResources();
-
-                // Add fragment title
-                SearchIndexableRaw data = new SearchIndexableRaw(context);
-                data.title = res.getString(R.string.screen_pinning_title);
-                data.screenTitle = res.getString(R.string.screen_pinning_title);
-                result.add(data);
-
-                if (isLockToAppEnabled(context)) {
-                    // Screen lock option
-                    data = new SearchIndexableRaw(context);
-                    data.title = res.getString(R.string.screen_pinning_unlock_none);
-                    data.screenTitle = res.getString(R.string.screen_pinning_title);
-                    result.add(data);
-                } else {
-                    // Screen pinning description.
-                    data = new SearchIndexableRaw(context);
-                    data.title = res.getString(R.string.screen_pinning_description);
-                    data.screenTitle = res.getString(R.string.screen_pinning_title);
-                    result.add(data);
-                }
-
-                return result;
+            public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                    boolean enabled) {
+                final SearchIndexableResource sir = new SearchIndexableResource(context);
+                sir.xmlResId = R.xml.screen_pinning_settings;
+                return Arrays.asList(sir);
             }
         };
 }
diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java
index 35ce909..01c138a 100644
--- a/src/com/android/settings/security/SecuritySettings.java
+++ b/src/com/android/settings/security/SecuritySettings.java
@@ -823,6 +823,7 @@
 
             SearchIndexableRaw data = new SearchIndexableRaw(context);
             data.title = screenTitle;
+            data.key = "security_settings_screen";
             data.screenTitle = screenTitle;
             result.add(data);
 
@@ -834,11 +835,13 @@
                 // This catches the title which can be overloaded in an overlay
                 data = new SearchIndexableRaw(context);
                 data.title = res.getString(R.string.security_settings_fingerprint_preference_title);
+                data.key = "security_fingerprint";
                 data.screenTitle = screenTitle;
                 result.add(data);
                 // Fallback for when the above doesn't contain "fingerprint"
                 data = new SearchIndexableRaw(context);
                 data.title = res.getString(R.string.fingerprint_manage_category_title);
+                data.key = "security_managed_fingerprint";
                 data.screenTitle = screenTitle;
                 result.add(data);
             }
@@ -853,22 +856,7 @@
                                 profileUserId)) {
                     data = new SearchIndexableRaw(context);
                     data.title = res.getString(R.string.lock_settings_profile_unification_title);
-                    data.screenTitle = screenTitle;
-                    result.add(data);
-                }
-            }
-
-            // Advanced
-            if (lockPatternUtils.isSecure(MY_USER_ID)) {
-                final TrustAgentManager trustAgentManager =
-                    FeatureFactory.getFactory(context).getSecurityFeatureProvider()
-                        .getTrustAgentManager();
-                final List<TrustAgentComponentInfo> agents =
-                        trustAgentManager.getActiveTrustAgents(context, lockPatternUtils);
-                for (int i = 0; i < agents.size(); i++) {
-                    final TrustAgentComponentInfo agent = agents.get(i);
-                    data = new SearchIndexableRaw(context);
-                    data.title = agent.title;
+                    data.key = "security_use_one_lock";
                     data.screenTitle = screenTitle;
                     result.add(data);
                 }
diff --git a/src/com/android/settings/sim/SimSelectNotification.java b/src/com/android/settings/sim/SimSelectNotification.java
index 67e423b..2dd2dcf 100644
--- a/src/com/android/settings/sim/SimSelectNotification.java
+++ b/src/com/android/settings/sim/SimSelectNotification.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.sim;
 
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -40,6 +41,9 @@
     private static final String TAG = "SimSelectNotification";
     private static final int NOTIFICATION_ID = 1;
 
+    private static final String SIM_SELECT_NOTIFICATION_CHANNEL =
+            "sim_select_notification_channel";
+
     @Override
     public void onReceive(Context context, Intent intent) {
         final TelephonyManager telephonyManager = (TelephonyManager)
@@ -117,8 +121,14 @@
 
     private void createNotification(Context context){
         final Resources resources = context.getResources();
+
+        NotificationChannel notificationChannel = new NotificationChannel(
+                SIM_SELECT_NOTIFICATION_CHANNEL,
+                resources.getString(R.string.sim_selection_channel_title),
+                NotificationManager.IMPORTANCE_LOW);
+
         NotificationCompat.Builder builder =
-                new NotificationCompat.Builder(context)
+                new NotificationCompat.Builder(context, SIM_SELECT_NOTIFICATION_CHANNEL)
                 .setSmallIcon(R.drawable.ic_sim_card_alert_white_48dp)
                 .setColor(context.getColor(R.color.sim_noitification))
                 .setContentTitle(resources.getString(R.string.sim_notification_title))
@@ -130,6 +140,7 @@
         builder.setContentIntent(resultPendingIntent);
         NotificationManager notificationManager =
                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.createNotificationChannel(notificationChannel);
         notificationManager.notify(NOTIFICATION_ID, builder.build());
     }
 
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 906c9d4..9f510d2 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -1136,6 +1136,7 @@
                     final Resources res = context.getResources();
                     SearchIndexableRaw data = new SearchIndexableRaw(context);
                     data.title = res.getString(R.string.user_settings_title);
+                    data.key = "users_settings";
                     data.screenTitle = res.getString(R.string.user_settings_title);
                     result.add(data);
 
@@ -1145,6 +1146,7 @@
                                 R.string.user_add_user_or_profile_menu
                                 : R.string.user_add_user_menu);
                         data.screenTitle = res.getString(R.string.user_settings_title);
+                        data.key = "user_settings_add_users";
                         result.add(data);
                     }
                     return result;
diff --git a/src/com/android/settings/wallpaper/WallpaperTypeSettings.java b/src/com/android/settings/wallpaper/WallpaperTypeSettings.java
index 3c95785..a87249e 100644
--- a/src/com/android/settings/wallpaper/WallpaperTypeSettings.java
+++ b/src/com/android/settings/wallpaper/WallpaperTypeSettings.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.wallpaper;
 
-import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -113,6 +112,7 @@
 
                     SearchIndexableRaw data = new SearchIndexableRaw(context);
                     data.title = label.toString();
+                    data.key = "wallpaper_type_settings";
                     data.screenTitle = context.getResources().getString(
                             R.string.wallpaper_settings_fragment_title);
                     data.intentAction = Intent.ACTION_SET_WALLPAPER;
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 14db3d3..4c87bac 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -68,7 +68,6 @@
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.settingslib.wifi.AccessPoint.AccessPointListener;
 import com.android.settingslib.wifi.AccessPointPreference;
-import com.android.settingslib.wifi.WifiSavedConfigUtils;
 import com.android.settingslib.wifi.WifiTracker;
 import com.android.settingslib.wifi.WifiTrackerFactory;
 
@@ -1117,18 +1116,6 @@
                 data.key = DATA_KEY_REFERENCE;
                 result.add(data);
 
-                // Add saved Wi-Fi access points
-                final List<AccessPoint> accessPoints =
-                        WifiSavedConfigUtils.getAllConfigs(context,
-                                context.getSystemService(WifiManager.class));
-                for (AccessPoint accessPoint : accessPoints) {
-                    data = new SearchIndexableRaw(context);
-                    data.title = accessPoint.getSsidStr();
-                    data.screenTitle = res.getString(R.string.wifi_settings);
-                    data.enabled = enabled;
-                    result.add(data);
-                }
-
                 return result;
             }
         };
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index 1569bfc..d06ad4a 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.logging.nano.MetricsProto;
@@ -40,6 +41,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 
 public class WifiTetherSettings extends RestrictedDashboardFragment
         implements WifiTetherBasePreferenceController.OnTetherConfigUpdateListener {
@@ -156,6 +158,7 @@
 
         config.SSID = mSSIDPreferenceController.getSSID();
         config.preSharedKey = mPasswordPreferenceController.getPassword();
+        ensureWifiConfigHasPassword(config);
         config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
         config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
         config.apBand = mApBandPreferenceController.getBandIndex();
@@ -177,6 +180,15 @@
     }
 
     @VisibleForTesting
+    static void ensureWifiConfigHasPassword(WifiConfiguration config) {
+        if (TextUtils.isEmpty(config.preSharedKey)) {
+            String randomUUID = UUID.randomUUID().toString();
+            //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+            config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
+        }
+    }
+
+    @VisibleForTesting
     class TetherChangeReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context content, Intent intent) {
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 8c44112..46414c7 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -3,7 +3,6 @@
 com.android.settings.deviceinfo.PrivateVolumeForget
 com.android.settings.inputmethod.SpellCheckersSettings
 com.android.settings.inputmethod.KeyboardLayoutPickerFragment
-com.android.settings.notification.ZenModeEventRuleSettings
 com.android.settings.fuelgauge.InactiveApps
 com.android.settings.accessibility.CaptionPropertiesFragment
 com.android.settings.accessibility.AccessibilitySettingsForSetupWizard
@@ -30,7 +29,6 @@
 com.android.settings.accessibility.ToggleAutoclickPreferenceFragment
 com.android.settings.applications.AppLaunchSettings
 com.android.settings.applications.ProcessStatsUi
-com.android.settings.notification.ZenModeScheduleRuleSettings
 com.android.settings.datausage.BillingCycleSettings
 com.android.settings.notification.NotificationStation
 com.android.settings.print.PrintJobSettingsFragment
diff --git a/tests/robotests/src/com/android/settings/DeviceInfoSettingsTest.java b/tests/robotests/src/com/android/settings/DeviceInfoSettingsTest.java
index 8cedaf1..9f2415b 100644
--- a/tests/robotests/src/com/android/settings/DeviceInfoSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/DeviceInfoSettingsTest.java
@@ -16,19 +16,32 @@
 
 package com.android.settings;
 
+import static com.android.settings.DeviceInfoSettings.NON_SIM_PREFERENCES_COUNT;
+import static com.android.settings.DeviceInfoSettings.SIM_PREFERENCES_COUNT;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.app.Activity;
 import android.content.Context;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.support.v7.preference.PreferenceScreen;
+import android.telephony.TelephonyManager;
+import android.util.FeatureFlagUtils;
+import android.view.View;
 
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
 import com.android.settings.testutils.shadow.ShadowConnectivityManager;
 import com.android.settings.testutils.shadow.ShadowUtils;
 import com.android.settingslib.DeviceInfoUtils;
@@ -40,29 +53,43 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
 
 import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(
-    manifest = TestConfig.MANIFEST_PATH,
-    sdk = TestConfig.SDK_VERSION_O,
-    shadows = {ShadowUtils.class, ShadowConnectivityManager.class}
+        manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION_O,
+        shadows = {ShadowUtils.class, ShadowConnectivityManager.class}
 )
 public class DeviceInfoSettingsTest {
 
     @Mock
+    private Activity mActivity;
+    @Mock
     private PreferenceScreen mScreen;
     @Mock
     private SummaryLoader mSummaryLoader;
+    @Mock
+    private TelephonyManager mTelephonyManager;
 
+    private Context mContext;
     private DeviceInfoSettings mSettings;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
         mSettings = spy(new DeviceInfoSettings());
+
+        doReturn(mActivity).when(mSettings).getActivity();
+        doReturn(mContext.getTheme()).when(mActivity).getTheme();
+        doReturn(mContext.getResources()).when(mSettings).getResources();
+        doNothing().when(mSettings).onCreatePreferences(any(), any());
+
         doReturn(mScreen).when(mSettings).getPreferenceScreen();
+        doReturn(mTelephonyManager).when(mSettings).getSystemService(Context.TELEPHONY_SERVICE);
     }
 
     @Test
@@ -91,4 +118,32 @@
 
         assertThat(keys).containsAllIn(niks);
     }
+
+    @Test
+    @Config(shadows = {SettingsShadowResources.SettingsShadowTheme.class,
+            SettingsShadowSystemProperties.class})
+    public void onCreate_singleSim_shouldAddSingleSimCount() {
+        SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + FeatureFlags.DEVICE_INFO_V2,
+                "true");
+        doReturn(1).when(mTelephonyManager).getPhoneCount();
+
+        mSettings.onCreate(null /* icicle */);
+
+        verify(mScreen).setInitialExpandedChildrenCount(
+                SIM_PREFERENCES_COUNT + NON_SIM_PREFERENCES_COUNT);
+    }
+
+    @Test
+    @Config(shadows = {SettingsShadowResources.SettingsShadowTheme.class,
+            SettingsShadowSystemProperties.class})
+    public void onCreate_dualeSim_shouldAddDualSimCount() {
+        SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + FeatureFlags.DEVICE_INFO_V2,
+                "true");
+        doReturn(2).when(mTelephonyManager).getPhoneCount();
+
+        mSettings.onCreate(null /* icicle */);
+
+        verify(mScreen).setInitialExpandedChildrenCount(
+                2 * SIM_PREFERENCES_COUNT + NON_SIM_PREFERENCES_COUNT);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogFragmentTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogFragmentTest.java
index 3468702..f1d7a73 100644
--- a/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogFragmentTest.java
@@ -27,6 +27,7 @@
 
 import android.content.Context;
 import android.provider.Settings;
+import android.widget.Button;
 
 import com.android.settings.R;
 import com.android.settings.TestConfig;
@@ -43,9 +44,11 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class PrivateDnsModeDialogFragmentTest {
     private static final String HOST_NAME = "192.168.1.1";
+    private static final String INVALID_HOST_NAME = "...,";
 
     private Context mContext;
     private PrivateDnsModeDialogFragment mFragment;
+    private Button mSaveButton;
 
 
     @Before
@@ -53,9 +56,12 @@
         MockitoAnnotations.initMocks(this);
 
         mContext = RuntimeEnvironment.application;
+        mSaveButton = new Button(mContext);
+
         mFragment = spy(new PrivateDnsModeDialogFragment());
         doReturn(mContext).when(mFragment).getContext();
         mFragment.onCreateDialog(null);
+        mFragment.mSaveButton = mSaveButton;
     }
 
     @Test
@@ -96,4 +102,19 @@
                 R.id.private_dns_mode_opportunistic);
     }
 
+    @Test
+    public void testOnCheckedChanged_switchMode_saveButtonHasCorrectState() {
+        // Set invalid hostname
+        mFragment.mEditText.setText(INVALID_HOST_NAME);
+
+        mFragment.onCheckedChanged(null, R.id.private_dns_mode_opportunistic);
+        assertThat(mSaveButton.isEnabled()).isTrue();
+
+        mFragment.onCheckedChanged(null, R.id.private_dns_mode_provider);
+        assertThat(mSaveButton.isEnabled()).isFalse();
+
+        mFragment.onCheckedChanged(null, R.id.private_dns_mode_off);
+        assertThat(mSaveButton.isEnabled()).isTrue();
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeAlarmsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeAlarmsPreferenceControllerTest.java
new file mode 100644
index 0000000..06ca70a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeAlarmsPreferenceControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+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.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeAlarmsPreferenceControllerTest {
+    private ZenModeAlarmsPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private Context mContext;
+    private ContentResolver mContentResolver;
+
+    private final boolean ALARMS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+        mController = new ZenModeAlarmsPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(true);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS)).thenReturn(ALARMS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(ALARMS_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableAlarms() {
+        boolean allowAlarms = true;
+        mController.onPreferenceChange(mockPref, allowAlarms);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS,
+                allowAlarms);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableAlarms() {
+        boolean allowAlarms = false;
+        mController.onPreferenceChange(mockPref, allowAlarms);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS,
+                allowAlarms);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeButtonPreferenceControllerTest.java
new file mode 100644
index 0000000..a1b4dab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeButtonPreferenceControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeButtonPreferenceControllerTest {
+    private ZenModeButtonPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private Preference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private Button mZenButtonOn;
+    @Mock
+    private Button mZenButtonOff;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        mController = new ZenModeButtonPreferenceController(mContext, mock(Lifecycle.class));
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+        ReflectionHelpers.setField(mController, "mZenButtonOn", mZenButtonOn);
+        ReflectionHelpers.setField(mController, "mZenButtonOff", mZenButtonOff);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.GONE);
+        verify(mZenButtonOff).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.GONE);
+        verify(mZenButtonOff).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.GONE);
+        verify(mZenButtonOff).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void updateState_ZenOff() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_OFF);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.VISIBLE);
+        verify(mZenButtonOff).setVisibility(View.GONE);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeCallsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeCallsPreferenceControllerTest.java
new file mode 100644
index 0000000..ea7e9f5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeCallsPreferenceControllerTest.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.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+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.testutils.SettingsRobolectricTestRunner;
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeCallsPreferenceControllerTest {
+    private ZenModeCallsPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private Preference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean CALLS_SETTINGS = true;
+    private final int MOCK_CALLS_SENDERS = NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+    private final int SUMMARY_ID_MOCK_CALLS_SENDERS = R.string.zen_mode_from_starred;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+        when(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE))
+                .thenCallRealMethod();
+        when(mBackend.getContactsSummary(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenCallRealMethod();
+
+        mController = new ZenModeCallsPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(false);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(CALLS_SETTINGS);
+        when(mBackend.getPriorityCallSenders()).thenReturn(MOCK_CALLS_SENDERS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setSummary(SUMMARY_ID_MOCK_CALLS_SENDERS);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeCallsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeCallsTest.java
new file mode 100644
index 0000000..3cc87a8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeCallsTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+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.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ZenModeCallsTest {
+    private ZenModeCallsSettings mCalls;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ZenModeBackend mBackend;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private NotificationManager mNotificationManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mActivity.getSystemService(Context.NOTIFICATION_SERVICE))
+                .thenReturn(mNotificationManager);
+        FakeFeatureFactory.setupForTest(mActivity);
+
+        mCalls = new ZenModeCallsSettings();
+        mCalls.onAttach((Context)mActivity);
+
+        ReflectionHelpers.setField(mCalls, "mBackend", mBackend);
+    }
+
+    @Test
+    public void getDefaultKeyReturnsBasedOnZen() {
+        when(mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenCallRealMethod();
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+        assertThat(mCalls.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_ALARMS);
+        assertThat(mCalls.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(true);
+        when(mBackend.getPriorityMessageSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        assertThat(mCalls.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(
+                        NotificationManager.Policy.PRIORITY_SENDERS_ANY));
+    }
+
+    @Test
+    public void setAnySender() {
+        String key = mBackend.getKeyFromSetting(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setNoSender() {
+        String key = mBackend.getKeyFromSetting(ZenModeBackend.SOURCE_NONE);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setStarredSenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setContactsOnlySenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeEventsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeEventsPreferenceControllerTest.java
new file mode 100644
index 0000000..b527abf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeEventsPreferenceControllerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+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.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.shadows.ShadowApplication;
+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 ZenModeEventsPreferenceControllerTest {
+    private ZenModeEventsPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean EVENTS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeEventsPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS)).thenReturn(EVENTS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(EVENTS_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableEvents() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS,
+                allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableEvents() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS,
+                allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeMediaPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeMediaPreferenceControllerTest.java
new file mode 100644
index 0000000..976d6d4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeMediaPreferenceControllerTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+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.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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeMediaPreferenceControllerTest {
+    private ZenModeMediaSystemOtherPreferenceController mController;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+
+    private final boolean MEDIA_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeMediaSystemOtherPreferenceController(mContext,
+                mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(true);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER)).
+                thenReturn(MEDIA_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(MEDIA_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableEvents() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableEvents() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeMessagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesPreferenceControllerTest.java
new file mode 100644
index 0000000..c06f93f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesPreferenceControllerTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.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.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+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.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeMessagesPreferenceControllerTest {
+    private ZenModeMessagesPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private Preference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean MESSAGES_SETTINGS = true;
+    private final int MOCK_MESSAGES_SENDERS = NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+    private final int SUMMARY_ID_MOCK_MESSAGES_SENDERS = R.string.zen_mode_from_starred;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        when(mBackend.getPriorityMessageSenders()).thenReturn(MOCK_MESSAGES_SENDERS);
+        when(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE))
+                .thenCallRealMethod();
+        when(mBackend.getContactsSummary(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenCallRealMethod();
+
+        mController = new ZenModeMessagesPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(false);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(MESSAGES_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setSummary(SUMMARY_ID_MOCK_MESSAGES_SENDERS);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeMessagesTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesTest.java
new file mode 100644
index 0000000..fe92570
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+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.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ZenModeMessagesTest {
+    private ZenModeMessagesSettings mMessages;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ZenModeBackend mBackend;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private NotificationManager mNotificationManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mActivity.getSystemService(Context.NOTIFICATION_SERVICE))
+                .thenReturn(mNotificationManager);
+        FakeFeatureFactory.setupForTest(mActivity);
+
+        mMessages = new ZenModeMessagesSettings();
+        mMessages.onAttach((Context)mActivity);
+
+        ReflectionHelpers.setField(mMessages, "mBackend", mBackend);
+    }
+
+    @Test
+    public void getDefaultKeyReturnsBasedOnZen() {
+        when(mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenCallRealMethod();
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+        assertThat(mMessages.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_ALARMS);
+        assertThat(mMessages.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(true);
+        when(mBackend.getPriorityMessageSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        assertThat(mMessages.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(
+                        NotificationManager.Policy.PRIORITY_SENDERS_ANY));
+    }
+
+    @Test
+    public void setAnySender() {
+        String key = mBackend.getKeyFromSetting(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setNoSender() {
+        String key = mBackend.getKeyFromSetting(ZenModeBackend.SOURCE_NONE);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setStarredSenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setContactsOnlySenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
index 1d71a8a..0a28673 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
@@ -43,7 +43,7 @@
 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 ZenModePreferenceControllerTest {
 
     @Mock
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeRemindersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeRemindersPreferenceControllerTest.java
new file mode 100644
index 0000000..9d8b011
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeRemindersPreferenceControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+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.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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeRemindersPreferenceControllerTest {
+    private ZenModeRemindersPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private ContentResolver mContentResolver;
+    private Context mContext;
+    private final boolean REMINDERS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeRemindersPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS)).
+                thenReturn(REMINDERS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(REMINDERS_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableReminders() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS,
+                allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableReminders() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS,
+                allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceControllerTest.java
new file mode 100644
index 0000000..d4ee9bc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceControllerTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+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.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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeRepeatCallersPreferenceControllerTest {
+    private ZenModeRepeatCallersPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean REPEAT_CALLERS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeRepeatCallersPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)).
+                thenReturn(REPEAT_CALLERS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(REPEAT_CALLERS_SETTINGS);
+    }
+
+    @Test
+    public void updateState_Priority_anyCallers() {
+        boolean mockPriorityState = false;
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(true);
+        when(mBackend.getPriorityCallSenders()).thenReturn(
+                NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS))
+                .thenReturn(mockPriorityState);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(true);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableRepeatCallers() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableRepeatCallers() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
index f8e5775..89b3f2a 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
@@ -105,11 +105,6 @@
         protected Object getSystemService(final String name) {
             return null;
         }
-
-        @Override
-        protected void maybeRefreshRules(boolean success, boolean fireChanged) {
-            //do nothing
-        }
     }
 
 }
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java
new file mode 100644
index 0000000..3fe1eab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeScreenOffPreferenceControllerTest {
+    private ZenModeScreenOffPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+
+    private Context mContext;
+    private final boolean MOCK_PRIORITY_SCREEN_OFF_SETTING = false;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeScreenOffPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+    }
+
+    @Test
+    public void updateState() {
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        when(mBackend.isEffectAllowed(NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF))
+                .thenReturn(MOCK_PRIORITY_SCREEN_OFF_SETTING);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(MOCK_PRIORITY_SCREEN_OFF_SETTING);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableScreenOff() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableScreenOff() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF, allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScreenOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOnPreferenceControllerTest.java
new file mode 100644
index 0000000..24e3ce3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOnPreferenceControllerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeScreenOnPreferenceControllerTest {
+    private ZenModeScreenOnPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+
+    private Context mContext;
+    private final boolean MOCK_PRIORITY_SCREEN_ON_SETTING = false;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeScreenOnPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+    }
+
+    @Test
+    public void updateState() {
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        when(mBackend.isEffectAllowed(NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON))
+                .thenReturn(MOCK_PRIORITY_SCREEN_ON_SETTING);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(MOCK_PRIORITY_SCREEN_ON_SETTING);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableScreenOn() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableScreenOn() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON, allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java
index 6832ca8..bb33f93 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java
@@ -42,7 +42,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)
 public class WifiTetherApBandPreferenceControllerTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
index a6d536d..044efad 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
@@ -43,7 +43,7 @@
 
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class WifiTetherPasswordPreferenceControllerTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
index 9d24e78..44c70f6 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
@@ -63,7 +63,7 @@
 import java.util.ArrayList;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O,
         shadows = {
                 WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class,
                 WifiTetherPreferenceControllerTest.ShadowWifiTetherSwitchBarController.class,
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
index 1cba30e..e058eed 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
@@ -42,7 +42,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)
 public class WifiTetherSSIDPreferenceControllerTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
new file mode 100644
index 0000000..76a8e23
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.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.wifi.tether;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.wifi.WifiConfiguration;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class WifiTetherSettingsTest {
+
+    @Test
+    public void ensureWifiConfigHasPassword_shouldGeneratePassword() {
+        WifiConfiguration config = new WifiConfiguration();
+        WifiTetherSettings.ensureWifiConfigHasPassword(config);
+
+        assertThat(config.preSharedKey).isNotEmpty();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
index 5f3e512..ce3fe01 100644
--- a/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
+++ b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
@@ -33,6 +33,7 @@
 
 import com.android.settings.search.DatabaseIndexingUtils;
 import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.search.SearchIndexableResources;
 import com.android.settings.search.XmlParserUtils;
 
@@ -64,7 +65,7 @@
 
             "dashboard_tile_placeholder"    // This is the placeholder pref for injecting dynamic
                                             // tiles.
-            );
+    );
 
     private Context mContext;
 
@@ -91,13 +92,13 @@
         final Set<String> nullKeyClasses = new HashSet<>();
         final Set<String> duplicatedKeys = new HashSet<>();
         for (Class<?> clazz : SearchIndexableResources.providerValues()) {
-            verifyPreferenceIdInXml(uniqueKeys, duplicatedKeys, nullKeyClasses, clazz);
+            verifyPreferenceKeys(uniqueKeys, duplicatedKeys, nullKeyClasses, clazz);
         }
 
         if (!nullKeyClasses.isEmpty()) {
             final StringBuilder nullKeyErrors = new StringBuilder()
-                    .append("Each preference must have a key, ")
-                    .append("the following classes have pref without keys:\n");
+                    .append("Each preference/SearchIndexableData must have a key, ")
+                    .append("the following classes have null keys:\n");
             for (String c : nullKeyClasses) {
                 nullKeyErrors.append(c).append("\n");
             }
@@ -114,7 +115,7 @@
         }
     }
 
-    private void verifyPreferenceIdInXml(Set<String> uniqueKeys, Set<String> duplicatedKeys,
+    private void verifyPreferenceKeys(Set<String> uniqueKeys, Set<String> duplicatedKeys,
             Set<String> nullKeyClasses, Class<?> clazz)
             throws IOException, XmlPullParserException, Resources.NotFoundException {
         if (clazz == null) {
@@ -123,8 +124,16 @@
         final String className = clazz.getName();
         final Indexable.SearchIndexProvider provider =
                 DatabaseIndexingUtils.getSearchIndexProvider(clazz);
+        final List<SearchIndexableRaw> rawsToIndex = provider.getRawDataToIndex(mContext, true);
         final List<SearchIndexableResource> resourcesToIndex =
                 provider.getXmlResourcesToIndex(mContext, true);
+        verifyResources(className, resourcesToIndex, uniqueKeys, duplicatedKeys, nullKeyClasses);
+        verifyRaws(className, rawsToIndex, uniqueKeys, duplicatedKeys, nullKeyClasses);
+    }
+
+    private void verifyResources(String className, List<SearchIndexableResource> resourcesToIndex,
+            Set<String> uniqueKeys, Set<String> duplicatedKeys, Set<String> nullKeyClasses)
+            throws IOException, XmlPullParserException, Resources.NotFoundException {
         if (resourcesToIndex == null) {
             Log.d(TAG, className + "is not providing SearchIndexableResource, skipping");
             return;
@@ -172,4 +181,25 @@
                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth));
         }
     }
+
+    private void verifyRaws(String className, List<SearchIndexableRaw> rawsToIndex,
+            Set<String> uniqueKeys, Set<String> duplicatedKeys, Set<String> nullKeyClasses) {
+        if (rawsToIndex == null) {
+            Log.d(TAG, className + "is not providing SearchIndexableRaw, skipping");
+            return;
+        }
+        for (SearchIndexableRaw raw : rawsToIndex) {
+            if (TextUtils.isEmpty(raw.key)) {
+                Log.e(TAG, "Every SearchIndexableRaw must have an key; found null key"
+                        + " in " + className);
+                nullKeyClasses.add(className);
+                continue;
+            }
+            if (uniqueKeys.contains(raw.key) && !WHITELISTED_DUPLICATE_KEYS.contains(raw.key)) {
+                Log.e(TAG, "Every SearchIndexableRaw key must unique; found " + raw.key
+                        + " in " + className);
+                duplicatedKeys.add(raw.key);
+            }
+        }
+    }
 }