Merge "Align the search view with the setting items" into pi-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index feb1b05..c9045d8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -739,6 +739,19 @@
         </activity>
 
         <activity
+            android:name="Settings$ZenModeBlockedEffectsSettingsActivity"
+            android:label="@string/zen_mode_what_to_block_title"
+            android:icon="@drawable/ic_settings_notifications"
+            android:exported="true"
+            android:taskAffinity="com.android.settings"
+            android:parentActivityName="Settings">
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.notification.ZenModeBlockedEffectsSetting" />
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                       android:value="true" />
+        </activity>
+
+        <activity
             android:name="Settings$ZenModeBehaviorSettingsActivity"
             android:label="@string/zen_mode_behavior_settings_title"
             android:icon="@drawable/ic_settings_notifications"
@@ -3218,7 +3231,7 @@
         </activity>
 
         <provider android:name=".slices.SettingsSliceProvider"
-                  android:authorities="com.android.settings.slices"
+                  android:authorities="com.android.settings.slices;android.settings.slices"
                   android:exported="true">
         </provider>
 
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index d37091c..d0ddab6 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -96,8 +96,12 @@
 
     <!-- For Search -->
     <declare-styleable name="Preference">
+        <!-- Synonyms for search results -->
         <attr name="keywords" format="string" />
+        <!-- Classname of a PreferenceController corresponding to the preference -->
         <attr name="controller" format="string" />
+        <!-- {@code true} when the controller declared represents a slice from {@link android.app.SettingsSliceContract} -->
+        <attr name="platform_slice" format="boolean" />
     </declare-styleable>
 
     <!-- For DotsPageIndicator -->
diff --git a/res/values/bools.xml b/res/values/bools.xml
index 4665f2e..61a3c75 100644
--- a/res/values/bools.xml
+++ b/res/values/bools.xml
@@ -96,6 +96,9 @@
     <!-- Whether device_administrators should be shown or not. -->
     <bool name="config_show_device_administrators">true</bool>
 
+    <!-- Whether encryption_and_credentials_encryption_status should be shown or not. -->
+    <bool name="config_show_encryption_and_credentials_encryption_status">true</bool>
+
     <!-- Whether premium_sms should be shown or not. -->
     <bool name="config_show_premium_sms">true</bool>
 
@@ -153,6 +156,9 @@
     <!-- Whether assist_and_voice_input should be shown or not. -->
     <bool name="config_show_assist_and_voice_input">true</bool>
 
+    <!-- Whether reset_dashboard should be shown or not. -->
+    <bool name="config_show_reset_dashboard">true</bool>
+
     <!-- Whether system_update_settings should be shown or not. -->
     <bool name="config_show_system_update_settings">true</bool>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 39f641c..185732d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1676,10 +1676,10 @@
     <string name="bluetooth_max_connected_audio_devices_string">Maximum connected Bluetooth audio devices</string>
     <!-- Bluetooth developer settings: Maximum number of connected audio devices -->
     <string name="bluetooth_max_connected_audio_devices_dialog_title">Select maximum number of connected Bluetooth audio devices</string>
-    <!-- Bluetooth developer settings: Checkbox title for enabling Bluetooth receiving AVDTP delay reports -->
-    <string name="bluetooth_enable_avdtp_delay_reports">Enable Bluetooth AVDTP delay reports</string>
-    <!-- Bluetooth developer settings: Summary of checkbox for enabling Bluetooth receiving AVDTP delay reports -->
-    <string name="bluetooth_enable_avdtp_delay_reports_summary">Allow receiving Bluetooth AVDTP delay reports</string>
+    <!-- Bluetooth developer settings: Checkbox title for disabling Bluetooth receiving AVDTP delay reports -->
+    <string name="bluetooth_disable_avdtp_delay_reports">Disable Bluetooth AVDTP delay reports</string>
+    <!-- Bluetooth developer settings: Summary of checkbox for disabling Bluetooth receiving AVDTP delay reports -->
+    <string name="bluetooth_disable_avdtp_delay_reports_summary">Disallow receiving Bluetooth AVDTP delay reports</string>
 
     <!-- Wifi Display settings. The title of the screen. [CHAR LIMIT=40] -->
     <string name="wifi_display_settings_title">Cast</string>
@@ -4177,10 +4177,6 @@
     <string name="keyboard_shortcuts_helper">Keyboard shortcuts helper</string>
     <!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] -->
     <string name="keyboard_shortcuts_helper_summary">Display available shortcuts</string>
-    <!--
-        Format string for a physical device in the form: InputMethodSubtype - InputMethodEditor.
-        e.g. English (US) - X Keyboard -->
-    <string name="physical_device_title" translatable="false"><xliff:g id="input_method_editor" example="X Keyboard">%1$s</xliff:g> - <xliff:g id="input_method_subtype" example="English (US)">%2$s</xliff:g></string>
 
     <!-- Summary text for keyboards when no layout has been selected. [CHAR LIMIT=35] -->
     <string name="default_keyboard_layout">Default</string>
@@ -6918,8 +6914,11 @@
     <!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
     <string name="zen_mode_settings_turn_on_dialog_title">Turn on Do Not Disturb</string>
 
-    <!-- Do not disturb: Title for the behaviors option and associated settings page. [CHAR LIMIT=30] -->
-    <string name="zen_mode_behavior_settings_title">Behavior</string>
+    <!-- Do not disturb: Title for the page describing what can bypass DND. [CHAR LIMIT=30] -->
+    <string name="zen_mode_behavior_settings_title">Exceptions</string>
+
+    <!-- Do not disturb: Title for the dnd duration setting (user can specify how long dnd will last when toggling dnd on from qs or settings) [CHAR LIMIT=30] -->
+    <string name="zen_mode_duration_settings_title">Duration</string>
 
     <!-- Do not disturb: Instructions indicating what types of sounds can bypass DND. [CHAR LIMIT=52] -->
     <string name="zen_mode_behavior_allow_title">Allow sounds and vibrations from</string>
@@ -6972,6 +6971,34 @@
     <!--  Do not disturb: Subtitle for the Visual signals option to toggle on/off visual signals/alerts when the screen is on/when screen is off. [CHAR LIMIT=30] -->
     <string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string>
 
+    <!-- Do not disturb: what to block title [CHAR LIMIT = 60] -->
+    <string name="zen_mode_what_to_block_title">What to block</string>
+    <!-- Do not disturb: what to block > effects title [CHAR LIMIT = 60] -->
+    <string name="zen_mode_block_effects_title">When notifications arrive</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_sound">Mute sound and vibration</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_intent">Don\'t turn on screen</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_light">Don\'t blink light</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_peek">Don\'t pop notifications on screen</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_status">Hide status bar icons</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_badge">Hide notification dots</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_ambient">Hide from ambient display</string>
+    <!-- Do not disturb: what to block option [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_block_effect_list">Hide from notification list</string>
+
+    <!-- Do not disturb: what to block summary, only sound and vibration -->
+    <string name="zen_mode_block_effect_summary_sound">Sound and vibration</string>
+    <!-- Do not disturb: what to block summary, sound vibration and some visual signals-->
+    <string name="zen_mode_block_effect_summary_some">Sound, vibration, and some visual signs of notifications</string>
+    <!-- Do not disturb: what to block summary, all effects -->
+    <string name="zen_mode_block_effect_summary_all">Sound, vibration, and visual signs of notifications</string>
+
     <!--  Do not disturb: Button to add new automatic rule to DND. [CHAR LIMIT=30] -->
     <string name="zen_mode_add">Add</string>
 
@@ -7002,8 +7029,8 @@
     <!-- [CHAR LIMIT=20] Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description.  -->
     <string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
 
-    <!-- Sound settings screen, summary format of do not disturb when on. [CHAR LIMIT=NONE] -->
-    <string name="zen_mode_sound_summary_on">On / <xliff:g name="dnd_summary" example="No sound except alarms and media">%1$s</xliff:g></string>
+    <!-- Sound settings screen, summary format of do not disturb when on with extra info. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_sound_summary_on_with_info">On / <xliff:g name="dnd_summary" example="No sound except alarms and media">%1$s</xliff:g></string>
 
     <!-- Sound settings screen, summary format of do not disturb when off with extra information. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_sound_summary_off_with_info">Off / <xliff:g name="dnd_summary" example="1 rule can turn on automatically">%1$s</xliff:g></string>
@@ -7011,6 +7038,24 @@
     <!-- Sound settings screen, summary format of do not disturb when off with no extra information. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_sound_summary_off">Off</string>
 
+    <!-- Sound settings screen, summary format of do not disturb when on with no extra information. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_sound_summary_on">On</string>
+
+    <!--  Do not disturb: Summary for zen mode duration setting indicating user will be prompted to set dnd duration whenever dnd is manually toggled on [CHAR LIMIT=NONE]-->
+    <string name="zen_mode_duration_summary_always_prompt">Ask every time (unless turned on automatically)</string>
+
+    <!--  Do not disturb: Summary for zen mode duration setting indicating how long dnd will last when dnd is manually toggled on [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_duration_summary_forever">Until you turn off (unless turned on automatically)</string>
+
+    <!--  Do not disturb: Summary for zen mode duration setting indicating how long dnd will last when dnd is manually toggled on [CHAR LIMIT=NONE] -->
+    <plurals name="zen_mode_duration_summary_time_hours">
+        <item quantity="one">1 hour (unless turned on automatically)</item>
+        <item quantity="other"><xliff:g id="num_hours" example="3">%d</xliff:g> hours (unless turned on automatically)</item>
+    </plurals>
+
+    <!--  Do not disturb: Summary for zen mode duration setting indicating how long dnd will last when toggled on -->
+    <string name="zen_mode_duration_summary_time_minutes"><xliff:g id="num_minutes" example="5">%d</xliff:g> minutes (unless turned on automatically)</string>
+
     <!-- Summary for the Sound Do not Disturb option when at least one automatic rules is enabled. [CHAR LIMIT=NONE]-->
     <plurals name="zen_mode_sound_summary_summary_off_info">
         <item quantity="one">1 rule can turn on automatically</item>
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index 36eab22..dda5655 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -26,7 +26,8 @@
       android:icon="@drawable/ic_settings_bluetooth"
       android:summary="@string/bluetooth_pref_summary"
       android:order="-7"
-      settings:controller="com.android.settings.bluetooth.BluetoothSwitchPreferenceController"/>
+      settings:controller="com.android.settings.bluetooth.BluetoothSwitchPreferenceController"
+      settings:platform_slice="true"/>
 
     <SwitchPreference
         android:key="toggle_nfc"
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 571f38e..282a5121 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -264,9 +264,9 @@
             android:summary="@string/bluetooth_disable_inband_ringing_summary" />
 
         <SwitchPreference
-            android:key="bluetooth_enable_avdtp_delay_reports"
-            android:title="@string/bluetooth_enable_avdtp_delay_reports"
-            android:summary="@string/bluetooth_enable_avdtp_delay_reports_summary"/>
+            android:key="bluetooth_disable_avdtp_delay_reports"
+            android:title="@string/bluetooth_disable_avdtp_delay_reports"
+            android:summary="@string/bluetooth_disable_avdtp_delay_reports_summary"/>
 
         <ListPreference
             android:key="bluetooth_select_avrcp_version"
diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml
index d8459dd..c3f15b9 100644
--- a/res/xml/system_dashboard_fragment.xml
+++ b/res/xml/system_dashboard_fragment.xml
@@ -46,7 +46,8 @@
         android:summary="@string/reset_dashboard_summary"
         android:icon="@drawable/ic_restore"
         android:order="-50"
-        android:fragment="com.android.settings.system.ResetDashboardFragment" />
+        android:fragment="com.android.settings.system.ResetDashboardFragment"
+        settings:controller="com.android.settings.system.ResetPreferenceController"/>
 
     <!-- System updates -->
     <Preference
diff --git a/res/xml/zen_mode_behavior_settings.xml b/res/xml/zen_mode_behavior_settings.xml
index 084b78d..8426d91 100644
--- a/res/xml/zen_mode_behavior_settings.xml
+++ b/res/xml/zen_mode_behavior_settings.xml
@@ -19,71 +19,51 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:key="zen_mode_behavior_settings_page"
-    android:title="@string/zen_mode_behavior_settings_title"
-    settings:initialExpandedChildrenCount="8">
+    android:title="@string/zen_mode_behavior_settings_title" >
 
-   <PreferenceCategory
-       android:title="@string/zen_mode_behavior_allow_title"
-       android:key="zen_mode_behavior_allow_preferences">
+   <!-- Alarms -->
+   <SwitchPreference
+       android:key="zen_mode_alarms"
+       android:title="@string/zen_mode_alarms"/>
 
-       <!-- Alarms -->
-       <SwitchPreference
-           android:key="zen_mode_alarms"
-           android:title="@string/zen_mode_alarms"/>
+   <!-- Media -->
+   <SwitchPreference
+       android:key="zen_mode_media"
+       android:title="@string/zen_mode_media"/>
 
-       <!-- Media -->
-       <SwitchPreference
-           android:key="zen_mode_media"
-           android:title="@string/zen_mode_media"/>
+   <!-- System -->
+   <SwitchPreference
+       android:key="zen_mode_system"
+       android:title="@string/zen_mode_system"/>
 
-       <!-- System -->
-       <SwitchPreference
-           android:key="zen_mode_system"
-           android:title="@string/zen_mode_system"/>
+   <!-- Reminders -->
+   <SwitchPreference
+       android:key="zen_mode_reminders"
+       android:title="@string/zen_mode_reminders"/>
 
-       <!-- Reminders -->
-       <SwitchPreference
-           android:key="zen_mode_reminders"
-           android:title="@string/zen_mode_reminders"/>
+   <!-- Events -->
+   <SwitchPreference
+       android:key="zen_mode_events"
+       android:title="@string/zen_mode_events"/>
 
-       <!-- Events -->
-       <SwitchPreference
-           android:key="zen_mode_events"
-           android:title="@string/zen_mode_events"/>
+   <!-- Messages -->
+   <ListPreference
+       android:key="zen_mode_messages"
+       android:title="@string/zen_mode_messages"
+       android:entries="@array/zen_mode_contacts_entries"
+       android:entryValues="@array/zen_mode_contacts_values"/>
 
-       <!-- Messages -->
-       <ListPreference
-           android:key="zen_mode_messages"
-           android:title="@string/zen_mode_messages"
-           android:entries="@array/zen_mode_contacts_entries"
-           android:entryValues="@array/zen_mode_contacts_values"/>
+   <!-- Calls -->
+   <ListPreference
+       android:key="zen_mode_calls"
+       android:title="@string/zen_mode_calls"
+       android:entries="@array/zen_mode_contacts_entries"
+       android:entryValues="@array/zen_mode_contacts_values"/>
 
-       <!-- Calls -->
-       <ListPreference
-           android:key="zen_mode_calls"
-           android:title="@string/zen_mode_calls"
-           android:entries="@array/zen_mode_contacts_entries"
-           android:entryValues="@array/zen_mode_contacts_values"/>
-
-       <!-- Repeat callers -->
-       <SwitchPreference
-           android:key="zen_mode_repeat_callers"
-           android:title="@string/zen_mode_repeat_callers" />
-
-   </PreferenceCategory>
-
-    <PreferenceCategory
-        android:title="@string/zen_mode_visual_signals_settings_subtitle"
-        android:key="zen_mode_visual_signals_preferences">
-
-        <SwitchPreference android:key="zen_mode_screen_on"
-                          android:title="@string/zen_mode_screen_on"
-                          android:summary="@string/zen_mode_screen_on_summary" />
-
-        <SwitchPreference android:key="zen_mode_screen_off"
-                          android:title="@string/zen_mode_screen_off"
-                          android:summary="@string/zen_mode_screen_off_summary" />
-    </PreferenceCategory>
+   <!-- Repeat callers -->
+   <SwitchPreference
+       android:key="zen_mode_repeat_callers"
+       android:title="@string/zen_mode_repeat_callers" />
 
     <com.android.settingslib.widget.FooterPreference />
 
diff --git a/res/xml/zen_mode_block_settings.xml b/res/xml/zen_mode_block_settings.xml
new file mode 100644
index 0000000..ce33e97
--- /dev/null
+++ b/res/xml/zen_mode_block_settings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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_block_settings_page">
+
+    <!-- sound vibration -->
+    <CheckBoxPreference
+        android:key="zen_effect_sound"
+        android:title="@string/zen_mode_block_effect_sound"
+        android:enabled="false" />
+
+   <PreferenceCategory
+       android:title="@string/zen_mode_block_effects_title"
+       android:key="zen_mode_block_vis_effects">
+
+       <CheckBoxPreference
+           android:key="zen_effect_intent"
+           android:title="@string/zen_mode_block_effect_intent" />
+
+       <CheckBoxPreference
+           android:key="zen_effect_light"
+           android:title="@string/zen_mode_block_effect_light" />
+
+       <CheckBoxPreference
+           android:key="zen_effect_peek"
+           android:title="@string/zen_mode_block_effect_peek" />
+
+       <CheckBoxPreference
+           android:key="zen_effect_status"
+           android:title="@string/zen_mode_block_effect_status" />
+
+       <CheckBoxPreference
+           android:key="zen_effect_badge"
+           android:title="@string/zen_mode_block_effect_badge" />
+
+       <CheckBoxPreference
+           android:key="zen_effect_ambient"
+           android:title="@string/zen_mode_block_effect_ambient" />
+
+       <CheckBoxPreference
+           android:key="zen_effect_list"
+           android:title="@string/zen_mode_block_effect_list" />
+   </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/res/xml/zen_mode_settings.xml b/res/xml/zen_mode_settings.xml
index ca8ad10..65fb7ab 100644
--- a/res/xml/zen_mode_settings.xml
+++ b/res/xml/zen_mode_settings.xml
@@ -19,12 +19,23 @@
     android:key="zen_mode_settings"
     android:title="@string/zen_mode_settings_title">
 
-    <!-- Priority behavior settings -->
+    <!-- What to block (effects) -->
+    <Preference
+        android:key="zen_mode_block_effects_settings"
+        android:title="@string/zen_mode_what_to_block_title"
+        android:fragment="com.android.settings.notification.ZenModeBlockedEffectsSettings" />
+
+    <!-- Behavior settings (exceptions) -->
     <Preference
             android:key="zen_mode_behavior_settings"
             android:title="@string/zen_mode_behavior_settings_title"
             android:fragment="com.android.settings.notification.ZenModeBehaviorSettings" />
 
+    <!-- DND duration settings -->
+    <Preference
+        android:key="zen_mode_duration_settings"
+        android:title="@string/zen_mode_duration_settings_title" />
+
     <!-- Automatic rules -->
     <Preference
         android:key="zen_mode_automation_settings"
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 4baa77b..03853d1 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -100,6 +100,7 @@
     public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeBehaviorSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class ZenModeBlockedEffectsSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeAutomationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeScheduleRuleSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeEventRuleSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
index 1ecfed4..6fbb84a 100644
--- a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
@@ -16,12 +16,14 @@
 
 package com.android.settings.bluetooth;
 
+import android.content.pm.PackageManager;
 import android.content.Context;
 import android.content.Intent;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -30,7 +32,7 @@
 /**
  * Controller that shows received files
  */
-public class BluetoothFilesPreferenceController extends AbstractPreferenceController
+public class BluetoothFilesPreferenceController extends BasePreferenceController
         implements PreferenceControllerMixin {
     private static final String TAG = "BluetoothFilesPrefCtrl";
 
@@ -47,13 +49,15 @@
     private MetricsFeatureProvider mMetricsFeatureProvider;
 
     public BluetoothFilesPreferenceController(Context context) {
-        super(context);
+        super(context, KEY_RECEIVED_FILES);
         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 
     @Override
-    public boolean isAvailable() {
-        return true;
+    public int getAvailabilityStatus() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
index 0e556c4..1a64f26 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
+import android.util.Log;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
@@ -71,7 +72,10 @@
     @Override
     public void onStart() {
         super.onStart();
-
+        if (mLocalManager == null){
+            Log.e(TAG, "Bluetooth is not supported on this device");
+            return;
+        }
         updateBluetooth();
         mAvailableDevicesCategory.setProgress(mLocalAdapter.isDiscovering());
     }
@@ -89,7 +93,10 @@
     @Override
     public void onStop() {
         super.onStop();
-
+        if (mLocalManager == null){
+            Log.e(TAG, "Bluetooth is not supported on this device");
+            return;
+        }
         // Make the device only visible to connected devices.
         mAlwaysDiscoverable.stop();
         disableScanning();
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
index 3d5d0e5..c77a735 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
@@ -15,15 +15,18 @@
  */
 package com.android.settings.connecteddevice;
 
+import android.content.pm.PackageManager;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
 import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
+import com.android.settings.search.ResultPayload;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -35,7 +38,7 @@
  * Controller to maintain the {@link android.support.v7.preference.PreferenceGroup} for all
  * connected devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference}
  */
-public class ConnectedDeviceGroupController extends AbstractPreferenceController
+public class ConnectedDeviceGroupController extends BasePreferenceController
         implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop,
         DevicePreferenceCallback {
 
@@ -47,7 +50,7 @@
     private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater;
 
     public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) {
-        super(fragment.getContext());
+        super(fragment.getContext(), KEY);
         init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this),
                 new ConnectedUsbDeviceUpdater(fragment, this));
     }
@@ -56,7 +59,7 @@
     ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle,
             BluetoothDeviceUpdater bluetoothDeviceUpdater,
             ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater) {
-        super(fragment.getContext());
+        super(fragment.getContext(), KEY);
         init(lifecycle, bluetoothDeviceUpdater, connectedUsbDeviceUpdater);
     }
 
@@ -75,17 +78,21 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
-        mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY);
-        mPreferenceGroup.setVisible(false);
+        if (isAvailable()) {
+            mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY);
+            mPreferenceGroup.setVisible(false);
 
-        mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
-        mBluetoothDeviceUpdater.forceUpdate();
-        mConnectedUsbDeviceUpdater.initUsbPreference(screen.getContext());
+            mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
+            mBluetoothDeviceUpdater.forceUpdate();
+            mConnectedUsbDeviceUpdater.initUsbPreference(screen.getContext());
+        }
     }
 
     @Override
-    public boolean isAvailable() {
-        return true;
+    public int getAvailabilityStatus() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
     }
 
     @Override
@@ -111,7 +118,7 @@
 
     private void init(Lifecycle lifecycle, BluetoothDeviceUpdater bluetoothDeviceUpdater,
             ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater) {
-        if (lifecycle != null) {
+        if (lifecycle != null && isAvailable()) {
             lifecycle.addObserver(this);
         }
         mBluetoothDeviceUpdater = bluetoothDeviceUpdater;
diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
index 7445047..da38d9f 100644
--- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
@@ -15,6 +15,7 @@
  */
 package com.android.settings.connecteddevice;
 
+import android.content.pm.PackageManager;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
@@ -22,6 +23,7 @@
 
 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
 import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -34,7 +36,7 @@
  * Controller to maintain the {@link PreferenceGroup} for all
  * saved devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference}
  */
-public class SavedDeviceGroupController extends AbstractPreferenceController
+public class SavedDeviceGroupController extends BasePreferenceController
         implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop,
         DevicePreferenceCallback {
 
@@ -45,14 +47,14 @@
     private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
 
     public SavedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) {
-        super(fragment.getContext());
+        super(fragment.getContext(), KEY);
         init(lifecycle, new SavedBluetoothDeviceUpdater(fragment, SavedDeviceGroupController.this));
     }
 
     @VisibleForTesting
     SavedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle,
             BluetoothDeviceUpdater bluetoothDeviceUpdater) {
-        super(fragment.getContext());
+        super(fragment.getContext(), KEY);
         init(lifecycle, bluetoothDeviceUpdater);
     }
 
@@ -68,15 +70,19 @@
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
-        mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY);
-        mPreferenceGroup.setVisible(false);
-        mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
-        mBluetoothDeviceUpdater.forceUpdate();
+        if (isAvailable()) {
+            mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY);
+            mPreferenceGroup.setVisible(false);
+            mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
+            mBluetoothDeviceUpdater.forceUpdate();
+        }
     }
 
     @Override
-    public boolean isAvailable() {
-        return true;
+    public int getAvailabilityStatus() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
     }
 
     @Override
@@ -101,7 +107,7 @@
     }
 
     private void init(Lifecycle lifecycle, BluetoothDeviceUpdater bluetoothDeviceUpdater) {
-        if (lifecycle != null) {
+        if (lifecycle != null && isAvailable()) {
             lifecycle.addObserver(this);
         }
         mBluetoothDeviceUpdater = bluetoothDeviceUpdater;
diff --git a/src/com/android/settings/core/PreferenceXmlParserUtils.java b/src/com/android/settings/core/PreferenceXmlParserUtils.java
index 7afe8d8..8620dce 100644
--- a/src/com/android/settings/core/PreferenceXmlParserUtils.java
+++ b/src/com/android/settings/core/PreferenceXmlParserUtils.java
@@ -76,6 +76,7 @@
         int FLAG_NEED_PREF_TITLE = 1 << 4;
         int FLAG_NEED_PREF_SUMMARY = 1 << 5;
         int FLAG_NEED_PREF_ICON = 1 << 6;
+        int FLAG_NEED_PLATFORM_SLICE_FLAG = 1 << 7;
     }
 
     public static final String METADATA_PREF_TYPE = "type";
@@ -84,35 +85,48 @@
     public static final String METADATA_TITLE = "title";
     public static final String METADATA_SUMMARY = "summary";
     public static final String METADATA_ICON = "icon";
+    public static final String METADATA_PLATFORM_SLICE_FLAG = "platform_slice";
 
     private static final String ENTRIES_SEPARATOR = "|";
 
+    /**
+     * Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_KEY} instead.
+     */
+    @Deprecated
     public static String getDataKey(Context context, AttributeSet attrs) {
-        return getData(context, attrs,
+        return getStringData(context, attrs,
                 com.android.internal.R.styleable.Preference,
                 com.android.internal.R.styleable.Preference_key);
     }
 
+    /**
+     * Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_TITLE} instead.
+     */
+    @Deprecated
     public static String getDataTitle(Context context, AttributeSet attrs) {
-        return getData(context, attrs,
+        return getStringData(context, attrs,
                 com.android.internal.R.styleable.Preference,
                 com.android.internal.R.styleable.Preference_title);
     }
 
+    /**
+     * Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_SUMMARY} instead.
+     */
+    @Deprecated
     public static String getDataSummary(Context context, AttributeSet attrs) {
-        return getData(context, attrs,
+        return getStringData(context, attrs,
                 com.android.internal.R.styleable.Preference,
                 com.android.internal.R.styleable.Preference_summary);
     }
 
     public static String getDataSummaryOn(Context context, AttributeSet attrs) {
-        return getData(context, attrs,
+        return getStringData(context, attrs,
                 com.android.internal.R.styleable.CheckBoxPreference,
                 com.android.internal.R.styleable.CheckBoxPreference_summaryOn);
     }
 
     public static String getDataSummaryOff(Context context, AttributeSet attrs) {
-        return getData(context, attrs,
+        return getStringData(context, attrs,
                 com.android.internal.R.styleable.CheckBoxPreference,
                 com.android.internal.R.styleable.CheckBoxPreference_summaryOff);
     }
@@ -124,13 +138,23 @@
     }
 
     public static String getDataKeywords(Context context, AttributeSet attrs) {
-        return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords);
+        return getStringData(context, attrs, R.styleable.Preference,
+                R.styleable.Preference_keywords);
     }
 
+    /**
+     * Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_CONTROLLER} instead.
+     */
+    @Deprecated
     public static String getController(Context context, AttributeSet attrs) {
-        return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_controller);
+        return getStringData(context, attrs, R.styleable.Preference,
+                R.styleable.Preference_controller);
     }
 
+    /**
+     * Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_ICON} instead.
+     */
+    @Deprecated
     public static int getDataIcon(Context context, AttributeSet attrs) {
         final TypedArray ta = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.Preference);
@@ -176,25 +200,35 @@
             }
             final Bundle preferenceMetadata = new Bundle();
             final AttributeSet attrs = Xml.asAttributeSet(parser);
+            final TypedArray preferenceAttributes = context.obtainStyledAttributes(attrs,
+                    R.styleable.Preference);
+
             if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TYPE)) {
                 preferenceMetadata.putString(METADATA_PREF_TYPE, nodeName);
             }
             if (hasFlag(flags, MetadataFlag.FLAG_NEED_KEY)) {
-                preferenceMetadata.putString(METADATA_KEY, getDataKey(context, attrs));
+                preferenceMetadata.putString(METADATA_KEY, getKey(preferenceAttributes));
             }
             if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_CONTROLLER)) {
-                preferenceMetadata.putString(METADATA_CONTROLLER, getController(context, attrs));
+                preferenceMetadata.putString(METADATA_CONTROLLER,
+                        getController(preferenceAttributes));
             }
             if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TITLE)) {
-                preferenceMetadata.putString(METADATA_TITLE, getDataTitle(context, attrs));
+                preferenceMetadata.putString(METADATA_TITLE, getTitle(preferenceAttributes));
             }
             if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_SUMMARY)) {
-                preferenceMetadata.putString(METADATA_SUMMARY, getDataSummary(context, attrs));
+                preferenceMetadata.putString(METADATA_SUMMARY, getSummary(preferenceAttributes));
             }
             if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_ICON)) {
-                preferenceMetadata.putInt(METADATA_ICON, getDataIcon(context, attrs));
+                preferenceMetadata.putInt(METADATA_ICON, getIcon(preferenceAttributes));
+            }
+            if (hasFlag(flags, MetadataFlag.FLAG_NEED_PLATFORM_SLICE_FLAG)) {
+                preferenceMetadata.putBoolean(METADATA_PLATFORM_SLICE_FLAG,
+                        getPlatformSlice(preferenceAttributes));
             }
             metadata.add(preferenceMetadata);
+
+            preferenceAttributes.recycle();
         } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth));
         parser.close();
@@ -205,12 +239,16 @@
      * Returns the fragment name if this preference launches a child fragment.
      */
     public static String getDataChildFragment(Context context, AttributeSet attrs) {
-        return getData(context, attrs, R.styleable.Preference,
+        return getStringData(context, attrs, R.styleable.Preference,
                 R.styleable.Preference_android_fragment);
     }
 
+    /**
+     * Call {@link #extractMetadata(Context, int, int)} with a {@link MetadataFlag} instead.
+     */
+    @Deprecated
     @Nullable
-    private static String getData(Context context, AttributeSet set, int[] attrs, int resId) {
+    private static String getStringData(Context context, AttributeSet set, int[] attrs, int resId) {
         final TypedArray ta = context.obtainStyledAttributes(set, attrs);
         String data = ta.getString(resId);
         ta.recycle();
@@ -243,4 +281,28 @@
         }
         return result.toString();
     }
+
+    private static String getKey(TypedArray styledAttributes) {
+        return styledAttributes.getString(com.android.internal.R.styleable.Preference_key);
+    }
+
+    private static String getTitle(TypedArray styledAttributes) {
+        return styledAttributes.getString(com.android.internal.R.styleable.Preference_title);
+    }
+
+    private static String getSummary(TypedArray styledAttributes) {
+        return styledAttributes.getString(com.android.internal.R.styleable.Preference_summary);
+    }
+
+    private static String getController(TypedArray styledAttributes) {
+        return styledAttributes.getString(R.styleable.Preference_controller);
+    }
+
+    private static int getIcon(TypedArray styledAttributes) {
+        return styledAttributes.getResourceId(com.android.internal.R.styleable.Icon_icon, 0);
+    }
+
+    private static boolean getPlatformSlice(TypedArray styledAttributes) {
+        return styledAttributes.getBoolean(R.styleable.Preference_platform_slice, false /* def */);
+    }
 }
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index f9c7847..0cbc539 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -105,6 +105,7 @@
 import com.android.settings.notification.ZenAccessSettings;
 import com.android.settings.notification.ZenModeAutomationSettings;
 import com.android.settings.notification.ZenModeBehaviorSettings;
+import com.android.settings.notification.ZenModeBlockedEffectsSettings;
 import com.android.settings.notification.ZenModeEventRuleSettings;
 import com.android.settings.notification.ZenModeScheduleRuleSettings;
 import com.android.settings.notification.ZenModeSettings;
@@ -220,6 +221,7 @@
             ZenModeBehaviorSettings.class.getName(),
             ZenModeScheduleRuleSettings.class.getName(),
             ZenModeEventRuleSettings.class.getName(),
+            ZenModeBlockedEffectsSettings.class.getName(),
             ProcessStatsUi.class.getName(),
             AdvancedPowerUsageDetail.class.getName(),
             ProcessStatsSummary.class.getName(),
diff --git a/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceController.java b/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceController.java
index 16c1f19..234e7db 100644
--- a/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceController.java
+++ b/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceController.java
@@ -17,7 +17,8 @@
 package com.android.settings.datetime.timezone;
 
 import android.content.Context;
-import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
 
 public class FixedOffsetPreferenceController extends BaseTimeZonePreferenceController {
 
@@ -31,8 +32,18 @@
 
     @Override
     public CharSequence getSummary() {
-        // This is a Spannable object, which contains TTS span. It shouldn't be converted to String.
-        return mTimeZoneInfo == null ? "" : mTimeZoneInfo.getGmtOffset();
+        if (mTimeZoneInfo == null) {
+            return "";
+        }
+
+        String standardName = mTimeZoneInfo.getStandardName();
+        if (standardName == null) {
+            return mTimeZoneInfo.getGmtOffset();
+        } else {
+            // GmtOffset is Spannable, which contains TTS span. It shouldn't be converted to String.
+            return SpannableUtil.getResourcesText(mContext.getResources(),
+                    R.string.zone_info_offset_and_name, mTimeZoneInfo.getGmtOffset(), standardName);
+        }
     }
 
     public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
diff --git a/src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java b/src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java
index 0858555..6c7a7dd 100644
--- a/src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java
@@ -28,11 +28,11 @@
 public class BluetoothDelayReportsPreferenceController extends DeveloperOptionsPreferenceController
         implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
 
-    private static final String BLUETOOTH_ENABLE_AVDTP_DELAY_REPORT_KEY =
-            "bluetooth_enable_avdtp_delay_reports";
+    private static final String BLUETOOTH_DISABLE_AVDTP_DELAY_REPORT_KEY =
+            "bluetooth_disable_avdtp_delay_reports";
     @VisibleForTesting
-    static final String BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY =
-            "persist.bluetooth.enabledelayreports";
+    static final String BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY =
+            "persist.bluetooth.disabledelayreports";
 
     public BluetoothDelayReportsPreferenceController(Context context) {
         super(context);
@@ -40,22 +40,22 @@
 
     @Override
     public String getPreferenceKey() {
-        return BLUETOOTH_ENABLE_AVDTP_DELAY_REPORT_KEY;
+        return BLUETOOTH_DISABLE_AVDTP_DELAY_REPORT_KEY;
     }
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean isEnabled = (Boolean) newValue;
-        SystemProperties.set(BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY,
-                isEnabled ? "true" : "false");
+        final boolean isDisabled = (Boolean) newValue;
+        SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY,
+                isDisabled ? "true" : "false");
         return true;
     }
 
     @Override
     public void updateState(Preference preference) {
-        final boolean isEnabled = SystemProperties.getBoolean(
-                BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
-        ((SwitchPreference) mPreference).setChecked(isEnabled);
+        final boolean isDisabled = SystemProperties.getBoolean(
+                BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
+        ((SwitchPreference) mPreference).setChecked(isDisabled);
     }
 
     @Override
@@ -63,7 +63,7 @@
         super.onDeveloperOptionsSwitchDisabled();
         // the default setting for this preference is the disabled state
         ((SwitchPreference) mPreference).setChecked(false);
-        SystemProperties.set(BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY, "false");
+        SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, "false");
     }
 
 }
diff --git a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java
index 05b3503..34d354f 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java
@@ -25,6 +25,7 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
@@ -40,6 +41,7 @@
  */
 public class AutoBatterySeekBarPreferenceController extends BasePreferenceController implements
         LifecycleObserver, OnStart, OnStop, SeekBarPreference.OnPreferenceChangeListener {
+    private static final String TAG = "AutoBatterySeekBarPreferenceController";
     @VisibleForTesting
     static final String KEY_AUTO_BATTERY_SEEK_BAR = "battery_saver_seek_bar";
     private final int mDefWarnLevel;
@@ -61,6 +63,7 @@
         super.displayPreference(screen);
         mPreference = (SeekBarPreference) screen.findPreference(
                 KEY_AUTO_BATTERY_SEEK_BAR);
+        mPreference.setContinuousUpdates(true);
         updatePreference(mPreference);
     }
 
@@ -96,6 +99,24 @@
     @VisibleForTesting
     void updatePreference(Preference preference) {
         final ContentResolver contentResolver = mContext.getContentResolver();
+
+        // Override the max value with LOW_POWER_MODE_TRIGGER_LEVEL_MAX, if set.
+        final int maxLevel = Settings.Global.getInt(contentResolver,
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 0);
+        if (maxLevel > 0) {
+            if (!(preference instanceof SeekBarPreference)) {
+                Log.e(TAG, "Unexpected preference class: " + preference.getClass());
+            } else {
+                final SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
+                if (maxLevel < seekBarPreference.getMin()) {
+                    Log.e(TAG, "LOW_POWER_MODE_TRIGGER_LEVEL_MAX too low; ignored.");
+                } else {
+                    seekBarPreference.setMax(maxLevel);
+                }
+            }
+        }
+
+        // Set the current value.
         final int level = Settings.Global.getInt(contentResolver,
                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, mDefWarnLevel);
         if (level == 0) {
@@ -109,7 +130,7 @@
     }
 
     /**
-     * Observer that listens to change from {@link Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL}
+     * Observer that listens to change from {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL}
      */
     private final class AutoBatterySaverSettingObserver extends ContentObserver {
         private final Uri mUri = Settings.Global.getUriFor(
diff --git a/src/com/android/settings/fuelgauge/batterytip/AppInfo.java b/src/com/android/settings/fuelgauge/batterytip/AppInfo.java
index dc6ba25..9e8aba3 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AppInfo.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AppInfo.java
@@ -20,9 +20,12 @@
 import android.os.Parcelable;
 import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 
+import java.util.Objects;
+
 /**
  * Model class stores app info(e.g. package name, type..) that used in battery tip
  */
@@ -32,13 +35,13 @@
      * Anomaly type of the app
      * @see Anomaly.AnomalyType
      */
-    public final int anomalyType;
+    public final ArraySet<Integer> anomalyTypes;
     public final long screenOnTimeMs;
     public final int uid;
 
     private AppInfo(AppInfo.Builder builder) {
         packageName = builder.mPackageName;
-        anomalyType = builder.mAnomalyType;
+        anomalyTypes = builder.mAnomalyTypes;
         screenOnTimeMs = builder.mScreenOnTimeMs;
         uid = builder.mUid;
     }
@@ -46,7 +49,7 @@
     @VisibleForTesting
     AppInfo(Parcel in) {
         packageName = in.readString();
-        anomalyType = in.readInt();
+        anomalyTypes = (ArraySet<Integer>) in.readArraySet(null /* loader */);
         screenOnTimeMs = in.readLong();
         uid = in.readInt();
     }
@@ -64,14 +67,14 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(packageName);
-        dest.writeInt(anomalyType);
+        dest.writeArraySet(anomalyTypes);
         dest.writeLong(screenOnTimeMs);
         dest.writeInt(uid);
     }
 
     @Override
     public String toString() {
-        return "packageName=" + packageName + ",anomalyType=" + anomalyType + ",screenTime="
+        return "packageName=" + packageName + ",anomalyTypes=" + anomalyTypes + ",screenTime="
                 + screenOnTimeMs;
     }
 
@@ -85,7 +88,7 @@
         }
 
         AppInfo other = (AppInfo) obj;
-        return anomalyType == other.anomalyType
+        return Objects.equals(anomalyTypes, other.anomalyTypes)
                 && uid == other.uid
                 && screenOnTimeMs == other.screenOnTimeMs
                 && TextUtils.equals(packageName, other.packageName);
@@ -102,13 +105,13 @@
     };
 
     public static final class Builder {
-        private int mAnomalyType;
+        private ArraySet<Integer> mAnomalyTypes = new ArraySet<>();
         private String mPackageName;
         private long mScreenOnTimeMs;
         private int mUid;
 
-        public Builder setAnomalyType(int type) {
-            mAnomalyType = type;
+        public Builder addAnomalyType(int type) {
+            mAnomalyTypes.add(type);
             return this;
         }
 
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java b/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java
index 798c8c6..58e9d03 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java
@@ -32,10 +32,12 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Database manager for battery data. Now it only contains anomaly data stored in {@link AppInfo}.
@@ -88,20 +90,30 @@
         try (SQLiteDatabase db = mDatabaseHelper.getReadableDatabase()) {
             final String[] projection = {PACKAGE_NAME, ANOMALY_TYPE, UID};
             final String orderBy = AnomalyDatabaseHelper.AnomalyColumns.TIME_STAMP_MS + " DESC";
+            final Map<Integer, AppInfo.Builder> mAppInfoBuilders = new ArrayMap<>();
+            final String selection = TIME_STAMP_MS + " > ? AND " + ANOMALY_STATE + " = ? ";
+            final String[] selectionArgs = new String[]{String.valueOf(timestampMsAfter),
+                    String.valueOf(state)};
 
-            try (Cursor cursor = db.query(TABLE_ANOMALY, projection,
-                    TIME_STAMP_MS + " > ? AND " + ANOMALY_STATE + " = ? ",
-                    new String[]{String.valueOf(timestampMsAfter), String.valueOf(state)}, null,
-                    null, orderBy)) {
+            try (Cursor cursor = db.query(TABLE_ANOMALY, projection, selection, selectionArgs,
+                    null /* groupBy */, null /* having */, orderBy)) {
                 while (cursor.moveToNext()) {
-                    AppInfo appInfo = new AppInfo.Builder()
-                            .setPackageName(cursor.getString(cursor.getColumnIndex(PACKAGE_NAME)))
-                            .setAnomalyType(cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE)))
-                            .setUid(cursor.getInt(cursor.getColumnIndex(UID)))
-                            .build();
-                    appInfos.add(appInfo);
+                    final int uid = cursor.getInt(cursor.getColumnIndex(UID));
+                    if (!mAppInfoBuilders.containsKey(uid)) {
+                        final AppInfo.Builder builder = new AppInfo.Builder()
+                                .setUid(uid)
+                                .setPackageName(
+                                        cursor.getString(cursor.getColumnIndex(PACKAGE_NAME)));
+                        mAppInfoBuilders.put(uid, builder);
+                    }
+                    mAppInfoBuilders.get(uid).addAnomalyType(
+                            cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE)));
                 }
             }
+
+            for (Integer uid : mAppInfoBuilders.keySet()) {
+                appInfos.add(mAppInfoBuilders.get(uid).build());
+            }
         }
 
         return appInfos;
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
index 2702bb6..d2af589 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
@@ -104,9 +104,7 @@
                 final Preference preference = batteryTip.buildPreference(mPrefContext);
                 mBatteryTipMap.put(preference.getKey(), batteryTip);
                 mPreferenceGroup.addPreference(preference);
-                mMetricsFeatureProvider.action(mContext,
-                        MetricsProto.MetricsEvent.ACTION_BATTERY_TIP_SHOWN,
-                        batteryTip.getType());
+                batteryTip.log(mContext, mMetricsFeatureProvider);
                 break;
             }
         }
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index 5c0147a..5ff5430 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.fuelgauge.batterytip;
 
-import android.app.Fragment;
-
 import com.android.settings.SettingsActivity;
 import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index f252638..3c3a5c0 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -25,6 +25,8 @@
 import android.support.v7.preference.Preference;
 import android.util.SparseIntArray;
 
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -123,6 +125,11 @@
      */
     public abstract void updateState(BatteryTip tip);
 
+    /**
+     * Log the battery tip
+     */
+    public abstract void log(Context context, MetricsFeatureProvider metricsFeatureProvider);
+
     public Preference buildPreference(Context context) {
         Preference preference = new Preference(context);
 
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java
index 7b8f624..908b873 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java
@@ -19,7 +19,9 @@
 import android.content.Context;
 import android.os.Parcel;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Tip to show early warning if battery couldn't make to usual charging time
@@ -76,6 +78,12 @@
     }
 
     @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_EARLY_WARNING_TIP,
+                mState);
+    }
+
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeBoolean(mPowerSaveModeOn);
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java
index 475ea56..3ce7538 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java
@@ -21,9 +21,11 @@
 import android.os.Parcelable;
 import android.support.annotation.VisibleForTesting;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
 
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.StringUtil;
 import java.util.List;
 
@@ -78,6 +80,18 @@
         mState = tip.mState;
     }
 
+    @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_HIGH_USAGE_TIP,
+                mState);
+        for (int i = 0, size = mHighUsageAppList.size(); i < size; i++) {
+            final AppInfo appInfo = mHighUsageAppList.get(i);
+            metricsFeatureProvider.action(context,
+                    MetricsProto.MetricsEvent.ACTION_HIGH_USAGE_TIP_LIST,
+                    appInfo.packageName);
+        }
+    }
+
     public long getScreenTimeMs() {
         return mScreenTimeMs;
     }
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
index 32cbe02..86237dd 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
@@ -20,7 +20,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Tip to show current battery life is short
@@ -55,6 +57,12 @@
         mState = tip.mState;
     }
 
+    @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_LOW_BATTERY_TIP,
+                mState);
+    }
+
     public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
         public BatteryTip createFromParcel(Parcel in) {
             return new LowBatteryTip(in);
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
index 037457a..566cbfa 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
@@ -19,11 +19,14 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Parcel;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -93,6 +96,24 @@
         }
     }
 
+    @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_APP_RESTRICTION_TIP,
+                mState);
+        if (mState == StateType.NEW) {
+            for (int i = 0, size = mRestrictAppList.size(); i < size; i++) {
+                final AppInfo appInfo = mRestrictAppList.get(i);
+                for (Integer anomalyType : appInfo.anomalyTypes) {
+                    metricsFeatureProvider.action(context,
+                            MetricsProto.MetricsEvent.ACTION_APP_RESTRICTION_TIP_LIST,
+                            appInfo.packageName,
+                            Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, anomalyType));
+                }
+
+            }
+        }
+    }
+
     public List<AppInfo> getRestrictAppList() {
         return mRestrictAppList;
     }
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/SmartBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/SmartBatteryTip.java
index 7b408bb..68c7d70 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/SmartBatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/SmartBatteryTip.java
@@ -20,7 +20,9 @@
 import android.os.Parcel;
 import android.provider.Settings;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Tip to suggest turn on smart battery if it is not on
@@ -55,6 +57,12 @@
         mState = tip.mState;
     }
 
+    @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_SMART_BATTERY_TIP,
+                mState);
+    }
+
     public static final Creator CREATOR = new Creator() {
         public BatteryTip createFromParcel(Parcel in) {
             return new SmartBatteryTip(in);
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
index 458bd2e..8993754 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
@@ -20,7 +20,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Tip to show general summary about battery life
@@ -55,6 +57,12 @@
         mState = tip.mState;
     }
 
+    @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_SUMMARY_TIP,
+                mState);
+    }
+
     public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
         public BatteryTip createFromParcel(Parcel in) {
             return new SummaryTip(in);
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java
index ec67f6a..93de9b7 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java
@@ -21,6 +21,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Tip to suggest user to remove app restriction. This is the empty tip and it is only used in
@@ -67,6 +68,11 @@
     }
 
     @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        // Do nothing
+    }
+
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeParcelable(mAppInfo, flags);
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java
index 80d3531..4c9715c 100644
--- a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java
+++ b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java
@@ -16,43 +16,31 @@
 
 package com.android.settings.inputmethod;
 
-import android.annotation.Nullable;
-import android.app.Activity;
+import android.content.Context;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManager.InputDeviceListener;
 import android.hardware.input.KeyboardLayout;
 import android.os.Bundle;
+import android.support.v7.preference.CheckBoxPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 import android.view.InputDevice;
 
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.Preconditions;
-import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.inputmethod.PhysicalKeyboardFragment.KeyboardInfoPreference;
 
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
-public final class KeyboardLayoutPickerFragment extends SettingsPreferenceFragment
+public class KeyboardLayoutPickerFragment extends SettingsPreferenceFragment
         implements InputDeviceListener {
-
     private InputDeviceIdentifier mInputDeviceIdentifier;
     private int mInputDeviceId = -1;
     private InputManager mIm;
-    private InputMethodInfo mImi;
-    @Nullable
-    private InputMethodSubtype mSubtype;
     private KeyboardLayout[] mKeyboardLayouts;
-    private Map<Preference, KeyboardLayout> mPreferenceMap = new HashMap<>();
-
-    // TODO: Make these constants public API for b/25752827
+    private HashMap<CheckBoxPreference, KeyboardLayout> mPreferenceMap = new HashMap<>();
 
     /**
      * Intent extra: The input device descriptor of the keyboard whose keyboard
@@ -60,16 +48,6 @@
      */
     public static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier";
 
-    /**
-     * Intent extra: The associated {@link InputMethodInfo}.
-     */
-    public static final String EXTRA_INPUT_METHOD_INFO = "input_method_info";
-
-    /**
-     * Intent extra: The associated {@link InputMethodSubtype}.
-     */
-    public static final String EXTRA_INPUT_METHOD_SUBTYPE = "input_method_subtype";
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.INPUTMETHOD_KEYBOARD;
@@ -78,18 +56,14 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        Activity activity = Preconditions.checkNotNull(getActivity());
 
-        mInputDeviceIdentifier = activity.getIntent().getParcelableExtra(
+        mInputDeviceIdentifier = getActivity().getIntent().getParcelableExtra(
                 EXTRA_INPUT_DEVICE_IDENTIFIER);
-        mImi = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_INFO);
-        mSubtype = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_SUBTYPE);
-
-        if (mInputDeviceIdentifier == null || mImi == null) {
-            activity.finish();
+        if (mInputDeviceIdentifier == null) {
+            getActivity().finish();
         }
 
-        mIm = activity.getSystemService(InputManager.class);
+        mIm = (InputManager) getSystemService(Context.INPUT_SERVICE);
         mKeyboardLayouts = mIm.getKeyboardLayoutsForInputDevice(mInputDeviceIdentifier);
         Arrays.sort(mKeyboardLayouts);
         setPreferenceScreen(createPreferenceHierarchy());
@@ -108,6 +82,8 @@
             return;
         }
         mInputDeviceId = inputDevice.getId();
+
+        updateCheckedState();
     }
 
     @Override
@@ -120,21 +96,34 @@
 
     @Override
     public boolean onPreferenceTreeClick(Preference preference) {
-        KeyboardLayout layout = mPreferenceMap.get(preference);
-        if (layout != null) {
-            mIm.setKeyboardLayoutForInputDevice(mInputDeviceIdentifier, mImi, mSubtype,
-                    layout.getDescriptor());
-            getActivity().finish();
-            return true;
+        if (preference instanceof CheckBoxPreference) {
+            CheckBoxPreference checkboxPref = (CheckBoxPreference)preference;
+            KeyboardLayout layout = mPreferenceMap.get(checkboxPref);
+            if (layout != null) {
+                boolean checked = checkboxPref.isChecked();
+                if (checked) {
+                    mIm.addKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
+                            layout.getDescriptor());
+                } else {
+                    mIm.removeKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
+                            layout.getDescriptor());
+                }
+                return true;
+            }
         }
         return super.onPreferenceTreeClick(preference);
     }
 
     @Override
-    public void onInputDeviceAdded(int deviceId) {}
+    public void onInputDeviceAdded(int deviceId) {
+    }
 
     @Override
-    public void onInputDeviceChanged(int deviceId) {}
+    public void onInputDeviceChanged(int deviceId) {
+        if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
+            updateCheckedState();
+        }
+    }
 
     @Override
     public void onInputDeviceRemoved(int deviceId) {
@@ -147,14 +136,23 @@
         PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
 
         for (KeyboardLayout layout : mKeyboardLayouts) {
-            Preference pref = new Preference(getPrefContext());
+            CheckBoxPreference pref = new CheckBoxPreference(getPrefContext());
             pref.setTitle(layout.getLabel());
             pref.setSummary(layout.getCollection());
             root.addPreference(pref);
             mPreferenceMap.put(pref, layout);
         }
-
-        root.setTitle(KeyboardInfoPreference.getDisplayName(getContext(), mImi, mSubtype));
         return root;
     }
+
+    private void updateCheckedState() {
+        String[] enabledKeyboardLayouts = mIm.getEnabledKeyboardLayoutsForInputDevice(
+                mInputDeviceIdentifier);
+        Arrays.sort(enabledKeyboardLayouts);
+
+        for (Map.Entry<CheckBoxPreference, KeyboardLayout> entry : mPreferenceMap.entrySet()) {
+            entry.getKey().setChecked(Arrays.binarySearch(enabledKeyboardLayouts,
+                    entry.getValue().getDescriptor()) >= 0);
+        }
+    }
 }
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index 83d501d..a02a6d0 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -19,11 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
-import android.app.LoaderManager;
-import android.content.AsyncTaskLoader;
 import android.content.Context;
 import android.content.Intent;
-import android.content.Loader;
 import android.database.ContentObserver;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
@@ -40,9 +37,6 @@
 import android.support.v7.preference.PreferenceScreen;
 import android.text.TextUtils;
 import android.view.InputDevice;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.inputmethod.InputMethodUtils;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -52,33 +46,25 @@
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 
 public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
-        implements InputManager.InputDeviceListener, Indexable {
+        implements InputManager.InputDeviceListener,
+        KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable {
 
     private static final String KEYBOARD_ASSISTANCE_CATEGORY = "keyboard_assistance_category";
     private static final String SHOW_VIRTUAL_KEYBOARD_SWITCH = "show_virtual_keyboard_switch";
     private static final String KEYBOARD_SHORTCUTS_HELPER = "keyboard_shortcuts_helper";
-    private static final String IM_SUBTYPE_MODE_KEYBOARD = "keyboard";
 
     @NonNull
-    private final List<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
-    @NonNull
-    private final List<KeyboardInfoPreference> mTempKeyboardInfoList = new ArrayList<>();
-
-    @NonNull
-    private final HashSet<Integer> mLoaderIDs = new HashSet<>();
-    private int mNextLoaderId = 0;
+    private final ArrayList<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
 
     private InputManager mIm;
     @NonNull
@@ -88,6 +74,8 @@
     @NonNull
     private InputMethodUtils.InputMethodSettings mSettings;
 
+    private Intent mIntentWaitingForResult;
+
     @Override
     public void onCreatePreferences(Bundle bundle, String s) {
         Activity activity = Preconditions.checkNotNull(getActivity());
@@ -118,9 +106,8 @@
     @Override
     public void onResume() {
         super.onResume();
-        clearLoader();
         mLastHardKeyboards.clear();
-        updateHardKeyboards();
+        scheduleUpdateHardKeyboards();
         mIm.registerInputDeviceListener(this, null);
         mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(
                 mShowVirtualKeyboardSwitchPreferenceChangeListener);
@@ -130,67 +117,25 @@
     @Override
     public void onPause() {
         super.onPause();
-        clearLoader();
         mLastHardKeyboards.clear();
         mIm.unregisterInputDeviceListener(this);
         mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(null);
         unregisterShowVirtualKeyboardSettingsObserver();
     }
 
-    private void onLoadFinishedInternal(
-            final int loaderId, @NonNull final List<Keyboards> keyboardsList) {
-        if (!mLoaderIDs.remove(loaderId)) {
-            // Already destroyed loader.  Ignore.
-            return;
-        }
-
-        Collections.sort(keyboardsList);
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        preferenceScreen.removeAll();
-        for (Keyboards keyboards : keyboardsList) {
-            final PreferenceCategory category = new PreferenceCategory(getPrefContext(), null);
-            category.setTitle(keyboards.mDeviceInfo.mDeviceName);
-            category.setOrder(0);
-            preferenceScreen.addPreference(category);
-            for (Keyboards.KeyboardInfo info : keyboards.mKeyboardInfoList) {
-                mTempKeyboardInfoList.clear();
-                final InputMethodInfo imi = info.mImi;
-                final InputMethodSubtype imSubtype = info.mImSubtype;
-                if (imi != null) {
-                    KeyboardInfoPreference pref =
-                            new KeyboardInfoPreference(getPrefContext(), info);
-                    pref.setOnPreferenceClickListener(preference -> {
-                        showKeyboardLayoutScreen(
-                                keyboards.mDeviceInfo.mDeviceIdentifier, imi, imSubtype);
-                        return true;
-                    });
-                    mTempKeyboardInfoList.add(pref);
-                    Collections.sort(mTempKeyboardInfoList);
-                }
-                for (KeyboardInfoPreference pref : mTempKeyboardInfoList) {
-                    category.addPreference(pref);
-                }
-            }
-        }
-        mTempKeyboardInfoList.clear();
-        mKeyboardAssistanceCategory.setOrder(1);
-        preferenceScreen.addPreference(mKeyboardAssistanceCategory);
-        updateShowVirtualKeyboardSwitch();
-    }
-
     @Override
     public void onInputDeviceAdded(int deviceId) {
-        updateHardKeyboards();
+        scheduleUpdateHardKeyboards();
     }
 
     @Override
     public void onInputDeviceRemoved(int deviceId) {
-        updateHardKeyboards();
+        scheduleUpdateHardKeyboards();
     }
 
     @Override
     public void onInputDeviceChanged(int deviceId) {
-        updateHardKeyboards();
+        scheduleUpdateHardKeyboards();
     }
 
     @Override
@@ -198,50 +143,57 @@
         return MetricsEvent.PHYSICAL_KEYBOARDS;
     }
 
-    @NonNull
-    public static List<HardKeyboardDeviceInfo> getHardKeyboards() {
-        final List<HardKeyboardDeviceInfo> keyboards = new ArrayList<>();
-        final int[] devicesIds = InputDevice.getDeviceIds();
-        for (int deviceId : devicesIds) {
-            final InputDevice device = InputDevice.getDevice(deviceId);
-            if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
-                keyboards.add(new HardKeyboardDeviceInfo(device.getName(), device.getIdentifier()));
-            }
-        }
-        return keyboards;
+    private void scheduleUpdateHardKeyboards() {
+        final Context context = getContext();
+        ThreadUtils.postOnBackgroundThread(() -> {
+            final List<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards(context);
+            ThreadUtils.postOnMainThread(() -> updateHardKeyboards(newHardKeyboards));
+        });
     }
 
-    private void updateHardKeyboards() {
-        final List<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards();
-        if (!Objects.equals(newHardKeyboards, mLastHardKeyboards)) {
-            clearLoader();
-            mLastHardKeyboards.clear();
-            mLastHardKeyboards.addAll(newHardKeyboards);
-            mLoaderIDs.add(mNextLoaderId);
-            getLoaderManager().initLoader(mNextLoaderId, null,
-                    new Callbacks(getContext(), this, mLastHardKeyboards));
-            ++mNextLoaderId;
+    private void updateHardKeyboards(@NonNull List<HardKeyboardDeviceInfo> newHardKeyboards) {
+        if (Objects.equals(mLastHardKeyboards, newHardKeyboards)) {
+            // Nothing has changed.  Ignore.
+            return;
         }
+
+        // TODO(yukawa): Maybe we should follow the style used in ConnectedDeviceDashboardFragment.
+
+        mLastHardKeyboards.clear();
+        mLastHardKeyboards.addAll(newHardKeyboards);
+
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        preferenceScreen.removeAll();
+        final PreferenceCategory category = new PreferenceCategory(getPrefContext());
+        category.setTitle(R.string.builtin_keyboard_settings_title);
+        category.setOrder(0);
+        preferenceScreen.addPreference(category);
+
+        for (HardKeyboardDeviceInfo hardKeyboardDeviceInfo : newHardKeyboards) {
+            // TODO(yukawa): Consider using com.android.settings.widget.GearPreference
+            final Preference pref = new Preference(getPrefContext());
+            pref.setTitle(hardKeyboardDeviceInfo.mDeviceName);
+            pref.setSummary(hardKeyboardDeviceInfo.mLayoutLabel);
+            pref.setOnPreferenceClickListener(preference -> {
+                showKeyboardLayoutDialog(hardKeyboardDeviceInfo.mDeviceIdentifier);
+                return true;
+            });
+            category.addPreference(pref);
+        }
+
+        mKeyboardAssistanceCategory.setOrder(1);
+        preferenceScreen.addPreference(mKeyboardAssistanceCategory);
+        updateShowVirtualKeyboardSwitch();
     }
 
-    private void showKeyboardLayoutScreen(
-            @NonNull InputDeviceIdentifier inputDeviceIdentifier,
-            @NonNull InputMethodInfo imi,
-            @Nullable InputMethodSubtype imSubtype) {
-        final Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class);
-        intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
-                inputDeviceIdentifier);
-        intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_METHOD_INFO, imi);
-        intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_METHOD_SUBTYPE, imSubtype);
-        startActivity(intent);
-    }
-
-    private void clearLoader() {
-        for (final int loaderId : mLoaderIDs) {
-            getLoaderManager().destroyLoader(loaderId);
+    private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
+        KeyboardLayoutDialogFragment fragment = (KeyboardLayoutDialogFragment)
+                getFragmentManager().findFragmentByTag("keyboardLayout");
+        if (fragment == null) {
+            fragment = new KeyboardLayoutDialogFragment(inputDeviceIdentifier);
+            fragment.setTargetFragment(this, 0);
+            fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
         }
-        mLoaderIDs.clear();
     }
 
     private void registerShowVirtualKeyboardSettingsObserver() {
@@ -282,102 +234,75 @@
         }
     };
 
-    private static final class Callbacks implements LoaderManager.LoaderCallbacks<List<Keyboards>> {
-        @NonNull
-        final Context mContext;
-        @NonNull
-        final PhysicalKeyboardFragment mPhysicalKeyboardFragment;
-        @NonNull
-        final List<HardKeyboardDeviceInfo> mHardKeyboards;
+    @Override
+    public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class);
+        intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
+                inputDeviceIdentifier);
+        mIntentWaitingForResult = intent;
+        startActivityForResult(intent, 0);
+    }
 
-        public Callbacks(
-                @NonNull Context context,
-                @NonNull PhysicalKeyboardFragment physicalKeyboardFragment,
-                @NonNull List<HardKeyboardDeviceInfo> hardKeyboards) {
-            mContext = context;
-            mPhysicalKeyboardFragment = physicalKeyboardFragment;
-            mHardKeyboards = hardKeyboards;
-        }
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
 
-        @Override
-        public Loader<List<Keyboards>> onCreateLoader(int id, Bundle args) {
-            return new KeyboardLayoutLoader(mContext, mHardKeyboards);
-        }
-
-        @Override
-        public void onLoadFinished(Loader<List<Keyboards>> loader, List<Keyboards> data) {
-            mPhysicalKeyboardFragment.onLoadFinishedInternal(loader.getId(), data);
-        }
-
-        @Override
-        public void onLoaderReset(Loader<List<Keyboards>> loader) {
+        if (mIntentWaitingForResult != null) {
+            InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
+                    .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER);
+            mIntentWaitingForResult = null;
+            showKeyboardLayoutDialog(inputDeviceIdentifier);
         }
     }
 
-    private static final class KeyboardLayoutLoader extends AsyncTaskLoader<List<Keyboards>> {
-        @NonNull
-        private final List<HardKeyboardDeviceInfo> mHardKeyboards;
-
-        public KeyboardLayoutLoader(
-                @NonNull Context context,
-                @NonNull List<HardKeyboardDeviceInfo> hardKeyboards) {
-            super(context);
-            mHardKeyboards = Preconditions.checkNotNull(hardKeyboards);
+    private static String getLayoutLabel(@NonNull InputDevice device,
+            @NonNull Context context, @NonNull InputManager im) {
+        final String currentLayoutDesc =
+                im.getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
+        if (currentLayoutDesc == null) {
+            return context.getString(R.string.keyboard_layout_default_label);
         }
+        final KeyboardLayout currentLayout = im.getKeyboardLayout(currentLayoutDesc);
+        if (currentLayout == null) {
+            return context.getString(R.string.keyboard_layout_default_label);
+        }
+        // If current layout is specified but the layout is null, just return an empty string
+        // instead of falling back to R.string.keyboard_layout_default_label.
+        return TextUtils.emptyIfNull(currentLayout.getLabel());
+    }
 
-        private Keyboards loadInBackground(HardKeyboardDeviceInfo deviceInfo) {
-            final ArrayList<Keyboards.KeyboardInfo> keyboardInfoList = new ArrayList<>();
-            final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
-            final InputManager im = getContext().getSystemService(InputManager.class);
-            if (imm != null && im != null) {
-                for (InputMethodInfo imi : imm.getEnabledInputMethodList()) {
-                    final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(
-                            imi, true /* allowsImplicitlySelectedSubtypes */);
-                    if (subtypes.isEmpty()) {
-                        // Here we use null to indicate that this IME has no subtype.
-                        final InputMethodSubtype nullSubtype = null;
-                        final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
-                                deviceInfo.mDeviceIdentifier, imi, nullSubtype);
-                        keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, nullSubtype, layout));
-                        continue;
-                    }
-
-                    // If the IME supports subtypes, we pick up "keyboard" subtypes only.
-                    final int N = subtypes.size();
-                    for (int i = 0; i < N; ++i) {
-                        final InputMethodSubtype subtype = subtypes.get(i);
-                        if (!IM_SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
-                            continue;
-                        }
-                        final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
-                                deviceInfo.mDeviceIdentifier, imi, subtype);
-                        keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, subtype, layout));
-                    }
-                }
+    @NonNull
+    static List<HardKeyboardDeviceInfo> getHardKeyboards(@NonNull Context context) {
+        final List<HardKeyboardDeviceInfo> keyboards = new ArrayList<>();
+        final InputManager im = context.getSystemService(InputManager.class);
+        if (im == null) {
+            return new ArrayList<>();
+        }
+        for (int deviceId : InputDevice.getDeviceIds()) {
+            final InputDevice device = InputDevice.getDevice(deviceId);
+            if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
+                continue;
             }
-            return new Keyboards(deviceInfo, keyboardInfoList);
+            keyboards.add(new HardKeyboardDeviceInfo(
+                    device.getName(), device.getIdentifier(), getLayoutLabel(device, context, im)));
         }
 
-        @Override
-        public List<Keyboards> loadInBackground() {
-            List<Keyboards> keyboardsList = new ArrayList<>(mHardKeyboards.size());
-            for (HardKeyboardDeviceInfo deviceInfo : mHardKeyboards) {
-                keyboardsList.add(loadInBackground(deviceInfo));
+        // We intentionally don't reuse Comparator because Collator may not be thread-safe.
+        final Collator collator = Collator.getInstance();
+        keyboards.sort((a, b) -> {
+            int result = collator.compare(a.mDeviceName, b.mDeviceName);
+            if (result != 0) {
+                return result;
             }
-            return keyboardsList;
-        }
-
-        @Override
-        protected void onStartLoading() {
-            super.onStartLoading();
-            forceLoad();
-        }
-
-        @Override
-        protected void onStopLoading() {
-            super.onStopLoading();
-            cancelLoad();
-        }
+            result = a.mDeviceIdentifier.getDescriptor().compareTo(
+                    b.mDeviceIdentifier.getDescriptor());
+            if (result != 0) {
+                return result;
+            }
+            return collator.compare(a.mLayoutLabel, b.mLayoutLabel);
+        });
+        return keyboards;
     }
 
     public static final class HardKeyboardDeviceInfo {
@@ -385,12 +310,16 @@
         public final String mDeviceName;
         @NonNull
         public final InputDeviceIdentifier mDeviceIdentifier;
+        @NonNull
+        public final String mLayoutLabel;
 
         public HardKeyboardDeviceInfo(
-                @Nullable final String deviceName,
-                @NonNull final InputDeviceIdentifier deviceIdentifier) {
-            mDeviceName = deviceName != null ? deviceName : "";
+                @Nullable String deviceName,
+                @NonNull InputDeviceIdentifier deviceIdentifier,
+                @NonNull String layoutLabel) {
+            mDeviceName = TextUtils.emptyIfNull(deviceName);
             mDeviceIdentifier = deviceIdentifier;
+            mLayoutLabel = layoutLabel;
         }
 
         @Override
@@ -404,14 +333,10 @@
             if (!TextUtils.equals(mDeviceName, that.mDeviceName)) {
                 return false;
             }
-            if (mDeviceIdentifier.getVendorId() != that.mDeviceIdentifier.getVendorId()) {
+            if (!Objects.equals(mDeviceIdentifier, that.mDeviceIdentifier)) {
                 return false;
             }
-            if (mDeviceIdentifier.getProductId() != that.mDeviceIdentifier.getProductId()) {
-                return false;
-            }
-            if (!TextUtils.equals(mDeviceIdentifier.getDescriptor(),
-                    that.mDeviceIdentifier.getDescriptor())) {
+            if (!TextUtils.equals(mLayoutLabel, that.mLayoutLabel)) {
                 return false;
             }
 
@@ -419,121 +344,6 @@
         }
     }
 
-    public static final class Keyboards implements Comparable<Keyboards> {
-        @NonNull
-        public final HardKeyboardDeviceInfo mDeviceInfo;
-        @NonNull
-        public final ArrayList<KeyboardInfo> mKeyboardInfoList;
-        @NonNull
-        public final Collator mCollator = Collator.getInstance();
-
-        public Keyboards(
-                @NonNull final HardKeyboardDeviceInfo deviceInfo,
-                @NonNull final ArrayList<KeyboardInfo> keyboardInfoList) {
-            mDeviceInfo = deviceInfo;
-            mKeyboardInfoList = keyboardInfoList;
-        }
-
-        @Override
-        public int compareTo(@NonNull Keyboards another) {
-            return mCollator.compare(mDeviceInfo.mDeviceName, another.mDeviceInfo.mDeviceName);
-        }
-
-        public static final class KeyboardInfo {
-            @NonNull
-            public final InputMethodInfo mImi;
-            @Nullable
-            public final InputMethodSubtype mImSubtype;
-            @NonNull
-            public final KeyboardLayout mLayout;
-
-            public KeyboardInfo(
-                    @NonNull final InputMethodInfo imi,
-                    @Nullable final InputMethodSubtype imSubtype,
-                    @NonNull final KeyboardLayout layout) {
-                mImi = imi;
-                mImSubtype = imSubtype;
-                mLayout = layout;
-            }
-        }
-    }
-
-    static final class KeyboardInfoPreference extends Preference {
-
-        @NonNull
-        private final CharSequence mImeName;
-        @Nullable
-        private final CharSequence mImSubtypeName;
-        @NonNull
-        private final Collator collator = Collator.getInstance();
-
-        private KeyboardInfoPreference(
-                @NonNull Context context, @NonNull Keyboards.KeyboardInfo info) {
-            super(context);
-            mImeName = info.mImi.loadLabel(context.getPackageManager());
-            mImSubtypeName = getImSubtypeName(context, info.mImi, info.mImSubtype);
-            setTitle(formatDisplayName(context, mImeName, mImSubtypeName));
-            if (info.mLayout != null) {
-                setSummary(info.mLayout.getLabel());
-            }
-        }
-
-        @NonNull
-        static CharSequence getDisplayName(
-                @NonNull Context context, @NonNull InputMethodInfo imi,
-                @Nullable InputMethodSubtype imSubtype) {
-            final CharSequence imeName = imi.loadLabel(context.getPackageManager());
-            final CharSequence imSubtypeName = getImSubtypeName(context, imi, imSubtype);
-            return formatDisplayName(context, imeName, imSubtypeName);
-        }
-
-        private static CharSequence formatDisplayName(
-                @NonNull Context context,
-                @NonNull CharSequence imeName, @Nullable CharSequence imSubtypeName) {
-            if (imSubtypeName == null) {
-                return imeName;
-            }
-            return String.format(
-                    context.getString(R.string.physical_device_title), imeName, imSubtypeName);
-        }
-
-        @Nullable
-        private static CharSequence getImSubtypeName(
-                @NonNull Context context, @NonNull InputMethodInfo imi,
-                @Nullable InputMethodSubtype imSubtype) {
-            if (imSubtype != null) {
-                return InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(
-                        imSubtype, context, imi);
-            }
-            return null;
-        }
-
-        @Override
-        public int compareTo(@NonNull Preference object) {
-            if (!(object instanceof KeyboardInfoPreference)) {
-                return super.compareTo(object);
-            }
-            KeyboardInfoPreference another = (KeyboardInfoPreference) object;
-            int result = compare(mImeName, another.mImeName);
-            if (result == 0) {
-                result = compare(mImSubtypeName, another.mImSubtypeName);
-            }
-            return result;
-        }
-
-        private int compare(@Nullable CharSequence lhs, @Nullable CharSequence rhs) {
-            if (!TextUtils.isEmpty(lhs) && !TextUtils.isEmpty(rhs)) {
-                return collator.compare(lhs.toString(), rhs.toString());
-            } else if (TextUtils.isEmpty(lhs) && TextUtils.isEmpty(rhs)) {
-                return 0;
-            } else if (!TextUtils.isEmpty(lhs)) {
-                return -1;
-            } else {
-                return 1;
-            }
-        }
-    }
-
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
                 @Override
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java b/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java
index f80b68f..b9e95be 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java
@@ -94,7 +94,7 @@
             return;
         }
         final List<HardKeyboardDeviceInfo> keyboards =
-                PhysicalKeyboardFragment.getHardKeyboards();
+                PhysicalKeyboardFragment.getHardKeyboards(mContext);
         if (keyboards.isEmpty()) {
             mPreference.setSummary(R.string.disconnected);
             return;
diff --git a/src/com/android/settings/notification/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/AbstractZenModePreferenceController.java
index 9180791..c60299e 100644
--- a/src/com/android/settings/notification/AbstractZenModePreferenceController.java
+++ b/src/com/android/settings/notification/AbstractZenModePreferenceController.java
@@ -112,10 +112,17 @@
                 mBackend.mZenMode);
     }
 
+    protected int getZenDuration() {
+        return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ZEN_DURATION,
+                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 Uri ZEN_MODE_DURATION_URI = Settings.Global.getUriFor(
+                Settings.Global.ZEN_DURATION);
 
         private final Preference mPreference;
 
@@ -127,6 +134,7 @@
         public void register(ContentResolver cr) {
             cr.registerContentObserver(ZEN_MODE_URI, false, this, UserHandle.USER_ALL);
             cr.registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this, UserHandle.USER_ALL);
+            cr.registerContentObserver(ZEN_MODE_DURATION_URI, false, this, UserHandle.USER_ALL);
         }
 
         public void unregister(ContentResolver cr) {
@@ -136,11 +144,8 @@
         @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)) {
+            if (ZEN_MODE_URI.equals(uri) || ZEN_MODE_CONFIG_ETAG_URI.equals(uri)
+                    || ZEN_MODE_DURATION_URI.equals(uri)) {
                 updateState(mPreference);
             }
         }
diff --git a/src/com/android/settings/notification/SettingsZenDurationDialog.java b/src/com/android/settings/notification/SettingsZenDurationDialog.java
new file mode 100644
index 0000000..23bf1a9
--- /dev/null
+++ b/src/com/android/settings/notification/SettingsZenDurationDialog.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 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.Dialog;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class SettingsZenDurationDialog extends InstrumentedDialogFragment {
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        return new com.android.settingslib.notification.ZenDurationDialog(
+                getContext()).createDialog();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_DURATION_DIALOG;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java b/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java
index aa46d4e..792a272 100644
--- a/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java
+++ b/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java
@@ -1,3 +1,19 @@
+/*
+ * 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;
diff --git a/src/com/android/settings/notification/ZenModeBackend.java b/src/com/android/settings/notification/ZenModeBackend.java
index e8f103e..3c6a026 100644
--- a/src/com/android/settings/notification/ZenModeBackend.java
+++ b/src/com/android/settings/notification/ZenModeBackend.java
@@ -16,9 +16,11 @@
 
 package com.android.settings.notification;
 
+import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.content.Context;
+import android.net.Uri;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
 import android.support.annotation.VisibleForTesting;
@@ -79,7 +81,15 @@
 
     protected void setZenMode(int zenMode) {
         NotificationManager.from(mContext).setZenMode(zenMode, null, TAG);
-        mZenMode = zenMode;
+        mZenMode = getZenMode();
+    }
+
+    protected void setZenModeForDuration(int minutes) {
+        Uri conditionId = ZenModeConfig.toTimeCondition(mContext, minutes,
+                ActivityManager.getCurrentUser(), true).id;
+        mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                conditionId, TAG);
+        mZenMode = getZenMode();
     }
 
     protected int getZenMode() {
@@ -88,6 +98,10 @@
         return mZenMode;
     }
 
+    protected boolean isVisualEffectSuppressed(int visualEffect) {
+        return (mPolicy.suppressedVisualEffects & visualEffect) != 0;
+    }
+
     protected boolean isPriorityCategoryEnabled(int categoryType) {
         return (mPolicy.priorityCategories & categoryType) != 0;
     }
@@ -117,8 +131,8 @@
         return SOURCE_NONE;
     }
 
-    protected void saveVisualEffectsPolicy(int category, boolean canBypass) {
-        int suppressedEffects = getNewSuppressedEffects(!canBypass, category);
+    protected void saveVisualEffectsPolicy(int category, boolean suppress) {
+        int suppressedEffects = getNewSuppressedEffects(suppress, category);
         savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders,
                 mPolicy.priorityMessageSenders, suppressedEffects);
     }
diff --git a/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java b/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java
index 0e1f066..cf6218c 100644
--- a/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java
+++ b/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java
@@ -1,3 +1,19 @@
+/*
+ * 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;
diff --git a/src/com/android/settings/notification/ZenModeBehaviorSettings.java b/src/com/android/settings/notification/ZenModeBehaviorSettings.java
index 7b5fa04..31542c3 100644
--- a/src/com/android/settings/notification/ZenModeBehaviorSettings.java
+++ b/src/com/android/settings/notification/ZenModeBehaviorSettings.java
@@ -49,8 +49,6 @@
         controllers.add(new ZenModeRepeatCallersPreferenceController(context, lifecycle,
                 context.getResources().getInteger(com.android.internal.R.integer
                 .config_zen_repeat_callers_threshold)));
-        controllers.add(new ZenModeScreenOnPreferenceController(context, lifecycle));
-        controllers.add(new ZenModeScreenOffPreferenceController(context, lifecycle));
         controllers.add(new ZenModeBehaviorFooterPreferenceController(context, lifecycle));
         return controllers;
     }
@@ -85,15 +83,6 @@
                 @Override
                 public List<String> getNonIndexableKeys(Context context) {
                     final List<String> keys = super.getNonIndexableKeys(context);
-                    keys.add(ZenModeAlarmsPreferenceController.KEY);
-                    keys.add(ZenModeMediaPreferenceController.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;
                 }
 
diff --git a/src/com/android/settings/notification/ZenModeBlockedEffectsPreferenceController.java b/src/com/android/settings/notification/ZenModeBlockedEffectsPreferenceController.java
new file mode 100644
index 0000000..9500320
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeBlockedEffectsPreferenceController.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeBlockedEffectsPreferenceController extends
+        AbstractZenModePreferenceController implements PreferenceControllerMixin {
+
+    protected static final String KEY = "zen_mode_block_effects_settings";
+    private final ZenModeSettings.SummaryBuilder mSummaryBuilder;
+
+    public ZenModeBlockedEffectsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mSummaryBuilder = new ZenModeSettings.SummaryBuilder(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return mSummaryBuilder.getBlockedEffectsSummary(getPolicy());
+    }
+
+}
diff --git a/src/com/android/settings/notification/ZenModeBlockedEffectsSettings.java b/src/com/android/settings/notification/ZenModeBlockedEffectsSettings.java
new file mode 100644
index 0000000..0cac9cb
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeBlockedEffectsSettings.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+import android.support.v7.preference.CheckBoxPreference;
+
+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.List;
+
+public class ZenModeBlockedEffectsSettings extends ZenModeSettingsBase implements Indexable {
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        CheckBoxPreference soundPreference =
+                (CheckBoxPreference) getPreferenceScreen().findPreference("zen_effect_sound");
+        if (soundPreference != null) {
+            soundPreference.setChecked(true);
+        }
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        return buildPreferenceControllers(context, getLifecycle());
+    }
+
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Lifecycle lifecycle) {
+        List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new ZenModeVisEffectPreferenceController(context, lifecycle,
+                "zen_effect_intent", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
+                MetricsEvent.ACTION_ZEN_BLOCK_FULL_SCREEN_INTENTS, null));
+        controllers.add(new ZenModeVisEffectPreferenceController(context, lifecycle,
+                "zen_effect_light", SUPPRESSED_EFFECT_LIGHTS,
+                MetricsEvent.ACTION_ZEN_BLOCK_LIGHT, null));
+        controllers.add(new ZenModeVisEffectPreferenceController(context, lifecycle,
+                "zen_effect_peek", SUPPRESSED_EFFECT_PEEK,
+                MetricsEvent.ACTION_ZEN_BLOCK_PEEK, null));
+        controllers.add(new ZenModeVisEffectPreferenceController(context, lifecycle,
+                "zen_effect_status", SUPPRESSED_EFFECT_STATUS_BAR,
+                MetricsEvent.ACTION_ZEN_BLOCK_STATUS,
+                new int[] {SUPPRESSED_EFFECT_NOTIFICATION_LIST}));
+        controllers.add(new ZenModeVisEffectPreferenceController(context, lifecycle,
+                "zen_effect_badge", SUPPRESSED_EFFECT_BADGE,
+                MetricsEvent.ACTION_ZEN_BLOCK_BADGE, null));
+        controllers.add(new ZenModeVisEffectPreferenceController(context, lifecycle,
+                "zen_effect_ambient", SUPPRESSED_EFFECT_AMBIENT,
+                MetricsEvent.ACTION_ZEN_BLOCK_AMBIENT, null));
+        controllers.add(new ZenModeVisEffectPreferenceController(context, lifecycle,
+                "zen_effect_list", SUPPRESSED_EFFECT_NOTIFICATION_LIST,
+                MetricsEvent.ACTION_ZEN_BLOCK_NOTIFICATION_LIST, null));
+        return controllers;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_block_settings;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ZEN_WHAT_TO_BLOCK;
+    }
+
+    /**
+     * For Search.
+     */
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    final ArrayList<SearchIndexableResource> result = new ArrayList<>();
+
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.zen_mode_block_settings;
+                    result.add(sir);
+                    return result;
+                }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    final List<String> keys = super.getNonIndexableKeys(context);
+                    return keys;
+                }
+
+            @Override
+            public List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+                return buildPreferenceControllers(context, null);
+            }
+        };
+}
diff --git a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
index d9c9691..12008a1 100644
--- a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
+++ b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
@@ -61,8 +61,7 @@
         if (null == mZenButtonOn) {
             mZenButtonOn = (Button) ((LayoutPreference) preference)
                     .findViewById(R.id.zen_mode_settings_turn_on_button);
-            mZenButtonOn.setOnClickListener(v ->
-                    new SettingsEnableZenModeDialog().show(mFragment, TAG));
+            updateZenButtonOnClickListener();
         }
 
         if (null == mZenButtonOff) {
@@ -89,7 +88,34 @@
             case Settings.Global.ZEN_MODE_OFF:
             default:
                 mZenButtonOff.setVisibility(View.GONE);
+                updateZenButtonOnClickListener();
                 mZenButtonOn.setVisibility(View.VISIBLE);
         }
     }
+
+    private void updateZenButtonOnClickListener() {
+        int zenDuration = getZenDuration();
+        switch (zenDuration) {
+            case Settings.Global.ZEN_DURATION_PROMPT:
+                mZenButtonOn.setOnClickListener(v -> {
+                    mMetricsFeatureProvider.action(mContext,
+                            MetricsProto.MetricsEvent.ACTION_ZEN_TOGGLE_DND_BUTTON, false);
+                    new SettingsEnableZenModeDialog().show(mFragment, TAG);
+                });
+                break;
+            case Settings.Global.ZEN_DURATION_FOREVER:
+                mZenButtonOn.setOnClickListener(v -> {
+                    mMetricsFeatureProvider.action(mContext,
+                            MetricsProto.MetricsEvent.ACTION_ZEN_TOGGLE_DND_BUTTON, false);
+                    mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+                });
+                break;
+            default:
+                mZenButtonOn.setOnClickListener(v -> {
+                    mMetricsFeatureProvider.action(mContext,
+                            MetricsProto.MetricsEvent.ACTION_ZEN_TOGGLE_DND_BUTTON, false);
+                    mBackend.setZenModeForDuration(zenDuration);
+                });
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/notification/ZenModeDurationPreferenceController.java b/src/com/android/settings/notification/ZenModeDurationPreferenceController.java
new file mode 100644
index 0000000..0cd0eb3
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeDurationPreferenceController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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.FragmentManager;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeDurationPreferenceController extends AbstractZenModePreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceClickListener {
+
+    private static final String TAG = "ZenModeDurationDialog";
+    protected static final String KEY = "zen_mode_duration_settings";
+    private FragmentManager mFragment;
+
+    public ZenModeDurationPreferenceController(Context context, Lifecycle lifecycle, FragmentManager
+            fragment) {
+        super(context, KEY, lifecycle);
+        mFragment = fragment;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        screen.findPreference(KEY).setOnPreferenceClickListener(this);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        String summary = "";
+        int zenDuration = getZenDuration();
+        if (zenDuration < 0) {
+            summary = mContext.getString(R.string.zen_mode_duration_summary_always_prompt);
+        } else if (zenDuration == 0) {
+            summary = mContext.getString(R.string.zen_mode_duration_summary_forever);
+        } else {
+            if (zenDuration >= 60) {
+                int hours = zenDuration / 60;
+                summary = mContext.getResources().getQuantityString(
+                        R.plurals.zen_mode_duration_summary_time_hours, hours, hours);
+            } else {
+                summary = mContext.getResources().getString(
+                        R.string.zen_mode_duration_summary_time_minutes, zenDuration);
+            }
+        }
+
+        preference.setSummary(summary);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        new SettingsZenDurationDialog().show(mFragment, TAG);
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java b/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java
deleted file mode 100644
index 81c9b0d..0000000
--- a/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.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.internal.logging.nano.MetricsProto;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-public class ZenModeScreenOffPreferenceController extends
-        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
-
-    protected static final String KEY = "zen_mode_screen_off";
-
-    public ZenModeScreenOffPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, KEY, lifecycle);
-    }
-
-    @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);
-        }
-        mMetricsFeatureProvider.action(mContext,
-                MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_OFF, 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
deleted file mode 100644
index bab4dd1..0000000
--- a/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.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.internal.logging.nano.MetricsProto;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-public class ZenModeScreenOnPreferenceController extends
-        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
-
-    protected static final String KEY = "zen_mode_screen_on";
-
-    public ZenModeScreenOnPreferenceController(Context context, Lifecycle lifecycle) {
-        super(context, KEY, lifecycle);
-    }
-
-    @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);
-        mMetricsFeatureProvider.action(mContext,
-                MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_ON, 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 4e8793b..b143b42 100644
--- a/src/com/android/settings/notification/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.provider.SearchIndexableResource;
 import android.provider.Settings;
+import android.service.notification.ZenModeConfig;
 import android.support.annotation.VisibleForTesting;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -63,6 +64,9 @@
             Lifecycle lifecycle, FragmentManager fragmentManager) {
         List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new ZenModeBehaviorPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeBlockedEffectsPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeDurationPreferenceController(context, lifecycle,
+                fragmentManager));
         controllers.add(new ZenModeAutomationPreferenceController(context));
         controllers.add(new ZenModeButtonPreferenceController(context, lifecycle, fragmentManager));
         controllers.add(new ZenModeSettingsFooterPreferenceController(context, lifecycle));
@@ -121,9 +125,15 @@
             int zenMode = NotificationManager.from(mContext).getZenMode();
 
             if (zenMode != Settings.Global.ZEN_MODE_OFF) {
-                Policy policy = NotificationManager.from(mContext).getNotificationPolicy();
-                return mContext.getString(R.string.zen_mode_sound_summary_on,
-                        getBehaviorSettingSummary(policy, zenMode));
+                ZenModeConfig config = NotificationManager.from(mContext).getZenModeConfig();
+                String description = ZenModeConfig.getDescription(mContext, true, config);
+
+                if (description == null) {
+                    return mContext.getString(R.string.zen_mode_sound_summary_on);
+                } else {
+                    return mContext.getString(R.string.zen_mode_sound_summary_on_with_info,
+                            description);
+                }
             } else {
                 final int count = getEnabledAutomaticRulesCount();
                 if (count > 0) {
@@ -137,6 +147,18 @@
             }
         }
 
+        String getBlockedEffectsSummary(Policy policy) {
+            if (policy.suppressedVisualEffects == 0) {
+                return mContext.getResources().getString(
+                        R.string.zen_mode_block_effect_summary_sound);
+            } else if (Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)) {
+                return mContext.getResources().getString(
+                        R.string.zen_mode_block_effect_summary_all);
+            }
+            return mContext.getResources().getString(
+                    R.string.zen_mode_block_effect_summary_some);
+        }
+
         String getAutomaticRulesSummary() {
             final int count = getEnabledAutomaticRulesCount();
             return count == 0 ? mContext.getString(R.string.zen_mode_settings_summary_off)
@@ -230,6 +252,7 @@
                 @Override
                 public List<String> getNonIndexableKeys(Context context) {
                     List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add(ZenModeDurationPreferenceController.KEY);
                     keys.add(ZenModeButtonPreferenceController.KEY);
                     return keys;
                 }
diff --git a/src/com/android/settings/notification/ZenModeVisEffectPreferenceController.java b/src/com/android/settings/notification/ZenModeVisEffectPreferenceController.java
new file mode 100644
index 0000000..89b953d
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeVisEffectPreferenceController.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeVisEffectPreferenceController
+        extends AbstractZenModePreferenceController
+        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+    protected final String mKey;
+    protected final int mEffect;
+    protected final int mMetricsCategory;
+    // if any of these effects are suppressed, this effect must be too
+    protected final int[] mParentSuppressedEffects;
+    private PreferenceScreen mScreen;
+
+    public ZenModeVisEffectPreferenceController(Context context, Lifecycle lifecycle, String key,
+            int visualEffect, int metricsCategory, int[] parentSuppressedEffects) {
+        super(context, key, lifecycle);
+        mKey = key;
+        mEffect = visualEffect;
+        mMetricsCategory = metricsCategory;
+        mParentSuppressedEffects = parentSuppressedEffects;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return mKey;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mScreen = screen;
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        boolean suppressed = mBackend.isVisualEffectSuppressed(mEffect);
+        boolean parentSuppressed = false;
+        if (mParentSuppressedEffects != null) {
+            for (int parentEffect : mParentSuppressedEffects) {
+                parentSuppressed |= mBackend.isVisualEffectSuppressed(parentEffect);
+            }
+        }
+        if (parentSuppressed) {
+            ((CheckBoxPreference) preference).setChecked(parentSuppressed);
+            onPreferenceChange(preference, parentSuppressed);
+            preference.setEnabled(false);
+        } else {
+            preference.setEnabled(true);
+            ((CheckBoxPreference) preference).setChecked(suppressed);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean suppressEffect = (Boolean) newValue;
+
+        mMetricsFeatureProvider.action(mContext, mMetricsCategory, suppressEffect);
+        mBackend.saveVisualEffectsPolicy(mEffect, suppressEffect);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java
index 864c5c2..8853950 100644
--- a/src/com/android/settings/password/SetupChooseLockPattern.java
+++ b/src/com/android/settings/password/SetupChooseLockPattern.java
@@ -20,7 +20,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
 
@@ -54,16 +57,20 @@
     public static class SetupChooseLockPatternFragment extends ChooseLockPatternFragment
             implements ChooseLockTypeDialogFragment.OnLockTypeSelectedListener {
 
+        @Nullable
+        private Button mOptionsButton;
+
         @Override
-        public void onViewCreated(View view, Bundle savedInstanceState) {
-            super.onViewCreated(view, savedInstanceState);
+        public View onCreateView(
+                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+            View view = super.onCreateView(inflater, container, savedInstanceState);
             if (!getResources().getBoolean(R.bool.config_lock_pattern_minimal_ui)) {
-                Button optionsButton = view.findViewById(R.id.screen_lock_options);
-                optionsButton.setVisibility(View.VISIBLE);
-                optionsButton.setOnClickListener((btn) ->
+                mOptionsButton = view.findViewById(R.id.screen_lock_options);
+                mOptionsButton.setOnClickListener((btn) ->
                         ChooseLockTypeDialogFragment.newInstance(mUserId)
                                 .show(getChildFragmentManager(), null));
             }
+            return view;
         }
 
         @Override
@@ -85,6 +92,16 @@
         }
 
         @Override
+        protected void updateStage(Stage stage) {
+            super.updateStage(stage);
+            if (!getResources().getBoolean(R.bool.config_lock_pattern_minimal_ui)
+                    && mOptionsButton != null) {
+                mOptionsButton.setVisibility(
+                        stage == Stage.Introduction ? View.VISIBLE : View.INVISIBLE);
+            }
+        }
+
+        @Override
         public void handleLeftButton() {
             SetupSkipDialog dialog = SetupSkipDialog.newInstance(
                     getActivity().getIntent()
diff --git a/src/com/android/settings/search/SearchIndexableResourcesImpl.java b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
index 1798d34..61f5fec 100644
--- a/src/com/android/settings/search/SearchIndexableResourcesImpl.java
+++ b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
@@ -74,6 +74,7 @@
 import com.android.settings.notification.SoundSettings;
 import com.android.settings.notification.ZenModeAutomationSettings;
 import com.android.settings.notification.ZenModeBehaviorSettings;
+import com.android.settings.notification.ZenModeBlockedEffectsSettings;
 import com.android.settings.notification.ZenModeSettings;
 import com.android.settings.print.PrintSettingsFragment;
 import com.android.settings.security.EncryptionAndCredential;
@@ -175,6 +176,7 @@
         addIndex(UsbDetailsFragment.class);
         addIndex(WifiDisplaySettings.class);
         addIndex(ZenModeBehaviorSettings.class);
+        addIndex(ZenModeBlockedEffectsSettings.class);
         addIndex(ZenModeAutomationSettings.class);
         addIndex(NightDisplaySettings.class);
         addIndex(SmartBatterySettings.class);
diff --git a/src/com/android/settings/security/EncryptionStatusPreferenceController.java b/src/com/android/settings/security/EncryptionStatusPreferenceController.java
index 2341248..8125599 100644
--- a/src/com/android/settings/security/EncryptionStatusPreferenceController.java
+++ b/src/com/android/settings/security/EncryptionStatusPreferenceController.java
@@ -41,6 +41,12 @@
 
     @Override
     public int getAvailabilityStatus() {
+        if (TextUtils.equals(getPreferenceKey(), PREF_KEY_ENCRYPTION_DETAIL_PAGE) &&
+                !mContext.getResources().getBoolean(
+                R.bool.config_show_encryption_and_credentials_encryption_status)) {
+            return DISABLED_UNSUPPORTED;
+        }
+
         return mUserManager.isAdminUser() ? AVAILABLE : DISABLED_FOR_USER;
     }
 
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index d8ba991..68c9731 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
+import android.provider.SettingsSlicesContract;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
@@ -63,6 +64,10 @@
 
     private static final String TAG = "SettingsSliceProvider";
 
+    /**
+     * Authority for Settings slices not officially supported by the platform, but extensible for
+     * OEMs.
+     */
     public static final String SLICE_AUTHORITY = "com.android.settings.slices";
 
     public static final String PATH_WIFI = "wifi";
@@ -82,13 +87,6 @@
     @VisibleForTesting
     Map<Uri, SliceData> mSliceDataCache;
 
-    public static Uri getUri(String path) {
-        return new Uri.Builder()
-                .scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(SLICE_AUTHORITY)
-                .appendPath(path).build();
-    }
-
     @Override
     public boolean onCreateSliceProvider() {
         mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
@@ -176,7 +174,8 @@
                         .setSubtitle(state)
                         .addEndItem(new SliceAction(getBroadcastIntent(ACTION_WIFI_CHANGED),
                                 null, finalWifiEnabled))
-                        .setPrimaryAction(new SliceAction(getIntent(Intent.ACTION_MAIN), null, null)))
+                        .setPrimaryAction(
+                                new SliceAction(getIntent(Intent.ACTION_MAIN), null, null)))
                 .build();
     }
 
diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java
index a68ed19..4124df7 100644
--- a/src/com/android/settings/slices/SliceBroadcastReceiver.java
+++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java
@@ -59,7 +59,8 @@
                 // Wait a bit for wifi to update (TODO: is there a better way to do this?)
                 Handler h = new Handler();
                 h.postDelayed(() -> {
-                    Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI);
+                    Uri uri = SliceBuilderUtils.getUri(SettingsSliceProvider.PATH_WIFI,
+                            false /* isPlatformSlice */);
                     context.getContentResolver().notifyChange(uri, null);
                 }, 1000);
                 break;
diff --git a/src/com/android/settings/slices/SliceBuilderUtils.java b/src/com/android/settings/slices/SliceBuilderUtils.java
index e94ee56..e90ea8e 100644
--- a/src/com/android/settings/slices/SliceBuilderUtils.java
+++ b/src/com/android/settings/slices/SliceBuilderUtils.java
@@ -19,9 +19,12 @@
 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY;
 
 import android.app.PendingIntent;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.provider.SettingsSlicesContract;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -98,6 +101,17 @@
                 sliceData.getKey());
     }
 
+    public static Uri getUri(String path, boolean isPlatformSlice) {
+        final String authority = isPlatformSlice
+                ? SettingsSlicesContract.AUTHORITY
+                : SettingsSliceProvider.SLICE_AUTHORITY;
+        return new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority)
+                .appendPath(path)
+                .build();
+    }
+
     private static BasePreferenceController getPreferenceController(Context context,
             String controllerClassName, String controllerKey) {
         try {
diff --git a/src/com/android/settings/slices/SliceData.java b/src/com/android/settings/slices/SliceData.java
index a7c9a11..c02b113 100644
--- a/src/com/android/settings/slices/SliceData.java
+++ b/src/com/android/settings/slices/SliceData.java
@@ -70,6 +70,8 @@
     @SliceType
     private final int mSliceType;
 
+    private final boolean mIsPlatformDefined;
+
     public String getKey() {
         return mKey;
     }
@@ -106,6 +108,10 @@
         return mSliceType;
     }
 
+    public boolean isPlatformDefined() {
+        return mIsPlatformDefined;
+    }
+
     private SliceData(Builder builder) {
         mKey = builder.mKey;
         mTitle = builder.mTitle;
@@ -116,6 +122,7 @@
         mUri = builder.mUri;
         mPreferenceController = builder.mPrefControllerClassName;
         mSliceType = builder.mSliceType;
+        mIsPlatformDefined = builder.mIsPlatformDefined;
     }
 
     @Override
@@ -151,6 +158,8 @@
 
         private int mSliceType;
 
+        private boolean mIsPlatformDefined;
+
         public Builder setKey(String key) {
             mKey = key;
             return this;
@@ -196,6 +205,11 @@
             return this;
         }
 
+        public Builder setPlatformDefined(boolean isPlatformDefined) {
+            mIsPlatformDefined = isPlatformDefined;
+            return this;
+        }
+
         public SliceData build() {
             if (TextUtils.isEmpty(mKey)) {
                 throw new IllegalStateException("Key cannot be empty");
diff --git a/src/com/android/settings/slices/SliceDataConverter.java b/src/com/android/settings/slices/SliceDataConverter.java
index e7b53d0..7cf1994 100644
--- a/src/com/android/settings/slices/SliceDataConverter.java
+++ b/src/com/android/settings/slices/SliceDataConverter.java
@@ -19,6 +19,7 @@
 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_CONTROLLER;
 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_ICON;
 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY;
+import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_PLATFORM_SLICE_FLAG;
 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SUMMARY;
 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_TITLE;
 
@@ -164,7 +165,8 @@
                             | MetadataFlag.FLAG_NEED_PREF_TYPE
                             | MetadataFlag.FLAG_NEED_PREF_TITLE
                             | MetadataFlag.FLAG_NEED_PREF_ICON
-                            | MetadataFlag.FLAG_NEED_PREF_SUMMARY);
+                            | MetadataFlag.FLAG_NEED_PREF_SUMMARY
+                            | MetadataFlag.FLAG_NEED_PLATFORM_SLICE_FLAG);
 
             for (Bundle bundle : metadata) {
                 // TODO (b/67996923) Non-controller Slices should become intent-only slices.
@@ -179,6 +181,7 @@
                 final int iconResId = bundle.getInt(METADATA_ICON);
                 final int sliceType = SliceBuilderUtils.getSliceType(mContext, controllerClassName,
                         key);
+                final boolean isPlatformSlice = bundle.getBoolean(METADATA_PLATFORM_SLICE_FLAG);
 
                 final SliceData xmlSlice = new SliceData.Builder()
                         .setKey(key)
@@ -189,6 +192,7 @@
                         .setPreferenceControllerClassName(controllerClassName)
                         .setFragmentName(fragmentName)
                         .setSliceType(sliceType)
+                        .setPlatformDefined(isPlatformSlice)
                         .build();
 
                 xmlSliceData.add(xmlSlice);
diff --git a/src/com/android/settings/system/ResetPreferenceController.java b/src/com/android/settings/system/ResetPreferenceController.java
new file mode 100644
index 0000000..16f7ce3
--- /dev/null
+++ b/src/com/android/settings/system/ResetPreferenceController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.system;
+
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+public class ResetPreferenceController extends BasePreferenceController {
+
+    public ResetPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mContext.getResources().getBoolean(R.bool.config_show_reset_dashboard)
+                ? AVAILABLE
+                : DISABLED_UNSUPPORTED;
+    }
+}
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 788c593..39620d8 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -34,6 +34,7 @@
     <bool name="config_show_screen_locking_sounds">false</bool>
     <bool name="config_show_touch_sounds">false</bool>
     <bool name="config_show_device_administrators">false</bool>
+    <bool name="config_show_encryption_and_credentials_encryption_status">false</bool>
     <bool name="config_show_premium_sms">false</bool>
     <bool name="config_show_data_saver">false</bool>
     <bool name="config_show_enabled_vr_listeners">false</bool>
@@ -54,6 +55,7 @@
     <bool name="config_show_tts_settings_summary">false</bool>
     <bool name="config_show_pointer_speed">false</bool>
     <bool name="config_show_vibrate_input_devices">false</bool>
+    <bool name="config_show_reset_dashboard">false</bool>
     <bool name="config_show_system_update_settings">false</bool>
     <bool name="config_wifi_support_connected_mac_randomization">false</bool>
     <bool name="config_show_device_model">false</bool>
diff --git a/tests/robotests/res/xml-mcc999/location_settings.xml b/tests/robotests/res/xml-mcc999/location_settings.xml
index de77bfa..e04f1be 100644
--- a/tests/robotests/res/xml-mcc999/location_settings.xml
+++ b/tests/robotests/res/xml-mcc999/location_settings.xml
@@ -24,6 +24,7 @@
         android:title="title"
         android:icon="@drawable/ic_android"
         android:summary="summary"
-        settings:controller="com.android.settings.slices.FakePreferenceController"/>
+        settings:controller="com.android.settings.slices.FakePreferenceController"
+        settings:platform_slice="true"/>
 
 </PreferenceScreen>
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothFilesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothFilesPreferenceControllerTest.java
index 2c1e46d..4335f05 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothFilesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothFilesPreferenceControllerTest.java
@@ -18,6 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.pm.PackageManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
@@ -28,6 +32,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadows.ShadowApplication;
@@ -38,15 +43,18 @@
     private Context mContext;
     private BluetoothFilesPreferenceController mController;
     private Preference mPreference;
+    @Mock
+    private PackageManager mPackageManager;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(RuntimeEnvironment.application);
         mController = new BluetoothFilesPreferenceController(mContext);
         mPreference = new Preference(mContext);
         mPreference.setKey(BluetoothFilesPreferenceController.KEY_RECEIVED_FILES);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
index 3656b38..373c255 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.settings.connecteddevice;
 
+import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doReturn;
@@ -24,6 +26,7 @@
 
 import android.arch.lifecycle.LifecycleOwner;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
 import android.support.v7.preference.PreferenceManager;
@@ -58,6 +61,8 @@
     private PreferenceScreen mPreferenceScreen;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private PreferenceManager mPreferenceManager;
+    @Mock
+    private PackageManager mPackageManager;
 
     private PreferenceGroup mPreferenceGroup;
     private Context mContext;
@@ -70,7 +75,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(RuntimeEnvironment.application);
         mPreference = new Preference(mContext);
         mPreference.setKey(PREFERENCE_KEY_1);
         mLifecycleOwner = () -> mLifecycle;
@@ -78,6 +83,8 @@
         mPreferenceGroup = spy(new PreferenceScreen(mContext, null));
         when(mPreferenceGroup.getPreferenceManager()).thenReturn(mPreferenceManager);
         doReturn(mContext).when(mDashboardFragment).getContext();
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
 
         mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mDashboardFragment,
                 mLifecycle, mConnectedBluetoothDeviceUpdater, mConnectedUsbDeviceUpdater);
@@ -134,4 +141,12 @@
         verify(mConnectedBluetoothDeviceUpdater).unregisterCallback();
         verify(mConnectedUsbDeviceUpdater).unregisterCallback();
     }
+
+    @Test
+    public void testGetAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
+        doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+        assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
+                DISABLED_UNSUPPORTED);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
new file mode 100644
index 0000000..3d606dd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 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.connecteddevice;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.BluetoothDeviceUpdater;
+import com.android.settings.dashboard.DashboardFragment;
+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.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class SavedDeviceGroupControllerTest {
+    private static final String PREFERENCE_KEY_1 = "pref_key_1";
+
+    @Mock
+    private DashboardFragment mDashboardFragment;
+    @Mock
+    private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PreferenceManager mPreferenceManager;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private PreferenceGroup mPreferenceGroup;
+    private Context mContext;
+    private Preference mPreference;
+    private SavedDeviceGroupController mConnectedDeviceGroupController;
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+
+        doReturn(mContext).when(mDashboardFragment).getContext();
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+    }
+
+    @Test
+    public void constructor_noBluetoothFeature_shouldNotRegisterCallback() {
+        doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+        mConnectedDeviceGroupController = new SavedDeviceGroupController(mDashboardFragment,
+                mLifecycle, mBluetoothDeviceUpdater);
+
+        assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
+                DISABLED_UNSUPPORTED);
+
+        mLifecycle.handleLifecycleEvent(ON_START);
+        verify(mBluetoothDeviceUpdater, never()).registerCallback();
+    }
+
+
+    @Test
+    public void constructor_hasBluetoothFeature_shouldRegisterCallback() {
+        doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+        mConnectedDeviceGroupController = new SavedDeviceGroupController(mDashboardFragment,
+                mLifecycle, mBluetoothDeviceUpdater);
+
+        assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
+                AVAILABLE);
+
+        mLifecycle.handleLifecycleEvent(ON_START);
+        verify(mBluetoothDeviceUpdater).registerCallback();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
index 533d4e9..dc492a9 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
@@ -44,6 +44,7 @@
 import com.android.settings.dashboard.DashboardAdapter;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowCardView;
 import com.android.settingslib.suggestions.SuggestionControllerMixin;
 import com.android.settingslib.utils.IconCache;
 
@@ -54,12 +55,14 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = ShadowCardView.class)
 public class SuggestionAdapterTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceControllerTest.java
index 0ffb7d2..f657560 100644
--- a/tests/robotests/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/FixedOffsetPreferenceControllerTest.java
@@ -40,10 +40,9 @@
     }
 
     @Test
-    public void updateState_matchTimeZoneSummary() {
+    public void updateState_GmtMinus8_matchTimeZoneSummary() {
         TimeZoneInfo fixedOffsetZone = new TimeZoneInfo.Builder(
                     TimeZone.getFrozenTimeZone("Etc/GMT-8"))
-                    .setExemplarLocation("Los Angeles")
                     .setGmtOffset("GMT-08:00")
                     .setItemId(0)
                     .build();
@@ -52,6 +51,21 @@
         controller.setTimeZoneInfo(fixedOffsetZone);
         controller.updateState(preference);
         assertThat(preference.getSummary()).isEqualTo("GMT-08:00");
+    }
 
+    @Test
+    public void updateState_Utc_matchTimeZoneSummary() {
+        TimeZoneInfo fixedOffsetZone = new TimeZoneInfo.Builder(
+                    TimeZone.getFrozenTimeZone("Etc/UTC"))
+                    .setStandardName("Coordinated Universal Time")
+                    .setGmtOffset("GMT+00:00")
+                    .setItemId(0)
+                    .build();
+        Preference preference = new Preference(mActivity);
+        FixedOffsetPreferenceController controller = new FixedOffsetPreferenceController(mActivity);
+        controller.setTimeZoneInfo(fixedOffsetZone);
+        controller.updateState(preference);
+        assertThat(preference.getSummary().toString())
+                .isEqualTo("Coordinated Universal Time (GMT+00:00)");
     }
 }
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java
index d529fc1..bdaad0a 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java
@@ -16,7 +16,9 @@
 
 package com.android.settings.development;
 
-import static com.android.settings.development.BluetoothDelayReportsPreferenceController.BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY;
+import static com.android.settings.development.BluetoothDelayReportsPreferenceController
+        .BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY;
+
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -57,11 +59,11 @@
     }
 
     @Test
-    public void onPreferenceChanged_settingEnabled_turnOnDelayReports() {
+    public void onPreferenceChanged_settingDisabled_turnOnDelayReports() {
         mController.onPreferenceChange(mPreference, true /* new value */);
 
         final boolean mode = SystemProperties.getBoolean(
-                BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
+                BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
 
         assertThat(mode).isTrue();
     }
@@ -71,14 +73,14 @@
         mController.onPreferenceChange(mPreference, false /* new value */);
 
         final boolean mode = SystemProperties.getBoolean(
-                BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
+                BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
 
         assertThat(mode).isFalse();
     }
 
     @Test
-    public void updateState_settingEnabled_preferenceShouldBeChecked() {
-        SystemProperties.set(BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY,
+    public void updateState_settingDisabled_preferenceShouldBeChecked() {
+        SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY,
                 Boolean.toString(true));
         mController.updateState(mPreference);
 
@@ -87,7 +89,7 @@
 
     @Test
     public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
-        SystemProperties.set(BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY,
+        SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY,
                 Boolean.toString(false));
         mController.updateState(mPreference);
 
@@ -99,10 +101,10 @@
         mController.onDeveloperOptionsDisabled();
 
         final boolean mode = SystemProperties.getBoolean(
-                BLUETOOTH_ENABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
+                BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */);
 
         assertThat(mode).isFalse();
         assertThat(mPreference.isEnabled()).isFalse();
         assertThat(mPreference.isChecked()).isFalse();
     }
-}
\ No newline at end of file
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java
index 6360232..30999cb 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java
@@ -40,7 +40,6 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class BatteryDatabaseManagerTest {
-
     private static String PACKAGE_NAME_NEW = "com.android.app1";
     private static int UID_NEW = 345;
     private static int TYPE_NEW = 1;
@@ -53,6 +52,9 @@
 
     private Context mContext;
     private BatteryDatabaseManager mBatteryDatabaseManager;
+    private AppInfo mNewAppInfo;
+    private AppInfo mOldAppInfo;
+    private AppInfo mCombinedAppInfo;
 
     @Before
     public void setUp() {
@@ -60,6 +62,23 @@
 
         mContext = RuntimeEnvironment.application;
         mBatteryDatabaseManager = spy(BatteryDatabaseManager.getInstance(mContext));
+
+        mNewAppInfo = new AppInfo.Builder()
+                .setUid(UID_NEW)
+                .setPackageName(PACKAGE_NAME_NEW)
+                .addAnomalyType(TYPE_NEW)
+                .build();
+        mOldAppInfo = new AppInfo.Builder()
+                .setUid(UID_OLD)
+                .setPackageName(PACKAGE_NAME_OLD)
+                .addAnomalyType(TYPE_OLD)
+                .build();
+        mCombinedAppInfo = new AppInfo.Builder()
+                .setUid(UID_NEW)
+                .setPackageName(PACKAGE_NAME_NEW)
+                .addAnomalyType(TYPE_NEW)
+                .addAnomalyType(TYPE_OLD)
+                .build();
     }
 
     @After
@@ -77,23 +96,19 @@
         // In database, it contains two record
         List<AppInfo> totalAppInfos = mBatteryDatabaseManager.queryAllAnomalies(0 /* timeMsAfter */,
                 AnomalyDatabaseHelper.State.NEW);
-        assertThat(totalAppInfos).hasSize(2);
-        assertAppInfo(totalAppInfos.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
-        assertAppInfo(totalAppInfos.get(1), UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD);
+        assertThat(totalAppInfos).containsExactly(mNewAppInfo, mOldAppInfo);
 
         // Only one record shows up if we query by timestamp
         List<AppInfo> appInfos = mBatteryDatabaseManager.queryAllAnomalies(ONE_DAY_BEFORE,
                 AnomalyDatabaseHelper.State.NEW);
-        assertThat(appInfos).hasSize(1);
-        assertAppInfo(appInfos.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
+        assertThat(appInfos).containsExactly(mNewAppInfo);
 
         mBatteryDatabaseManager.deleteAllAnomaliesBeforeTimeStamp(ONE_DAY_BEFORE);
 
         // The obsolete record is removed from database
         List<AppInfo> appInfos1 = mBatteryDatabaseManager.queryAllAnomalies(0 /* timeMsAfter */,
                 AnomalyDatabaseHelper.State.NEW);
-        assertThat(appInfos1).hasSize(1);
-        assertAppInfo(appInfos1.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
+        assertThat(appInfos1).containsExactly(mNewAppInfo);
     }
 
     @Test
@@ -113,19 +128,24 @@
         // The state of PACKAGE_NAME_NEW is still new
         List<AppInfo> newAppInfos = mBatteryDatabaseManager.queryAllAnomalies(ONE_DAY_BEFORE,
                 AnomalyDatabaseHelper.State.NEW);
-        assertThat(newAppInfos).hasSize(1);
-        assertAppInfo(newAppInfos.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
+        assertThat(newAppInfos).containsExactly(mNewAppInfo);
 
         // The state of PACKAGE_NAME_OLD is changed to handled
         List<AppInfo> handledAppInfos = mBatteryDatabaseManager.queryAllAnomalies(ONE_DAY_BEFORE,
                 AnomalyDatabaseHelper.State.HANDLED);
-        assertThat(handledAppInfos).hasSize(1);
-        assertAppInfo(handledAppInfos.get(0), UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD);
+        assertThat(handledAppInfos).containsExactly(mOldAppInfo);
     }
 
-    private void assertAppInfo(final AppInfo appInfo, int uid, String packageName, int type) {
-        assertThat(appInfo.packageName).isEqualTo(packageName);
-        assertThat(appInfo.anomalyType).isEqualTo(type);
-        assertThat(appInfo.uid).isEqualTo(uid);
+    @Test
+    public void testQueryAnomalies_removeDuplicateByUid() {
+        mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW,
+                AnomalyDatabaseHelper.State.NEW, NOW);
+        mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_OLD,
+                AnomalyDatabaseHelper.State.NEW, NOW);
+
+        // Only contain one AppInfo with multiple types
+        List<AppInfo> newAppInfos = mBatteryDatabaseManager.queryAllAnomalies(ONE_DAY_BEFORE,
+                AnomalyDatabaseHelper.State.NEW);
+        assertThat(newAppInfos).containsExactly(mCombinedAppInfo);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java
index d2c052a..72dde96 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java
@@ -99,4 +99,24 @@
         assertThat(Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isEqualTo(TRIGGER_LEVEL);
     }
+
+    @Test
+    public void testOnPreferenceChange_changeMax() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 50);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.getMax()).isEqualTo(50);
+    }
+
+    @Test
+    public void testOnPreferenceChange_noChangeMax() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 0);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.getMax()).isEqualTo(100);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AppInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AppInfoTest.java
index b140c4c..74e8fe6 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AppInfoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AppInfoTest.java
@@ -36,7 +36,8 @@
 public class AppInfoTest {
 
     private static final String PACKAGE_NAME = "com.android.app";
-    private static final int ANOMALY_TYPE = Anomaly.AnomalyType.WAKE_LOCK;
+    private static final int TYPE_WAKELOCK = Anomaly.AnomalyType.WAKE_LOCK;
+    private static final int TYPE_WAKEUP = Anomaly.AnomalyType.WAKEUP_ALARM;
     private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS;
     private static final int UID = 3452;
 
@@ -46,7 +47,8 @@
     public void setUp() {
         mAppInfo = new AppInfo.Builder()
                 .setPackageName(PACKAGE_NAME)
-                .setAnomalyType(ANOMALY_TYPE)
+                .addAnomalyType(TYPE_WAKELOCK)
+                .addAnomalyType(TYPE_WAKEUP)
                 .setScreenOnTimeMs(SCREEN_TIME_MS)
                 .setUid(UID)
                 .build();
@@ -61,7 +63,7 @@
         final AppInfo appInfo = new AppInfo(parcel);
 
         assertThat(appInfo.packageName).isEqualTo(PACKAGE_NAME);
-        assertThat(appInfo.anomalyType).isEqualTo(ANOMALY_TYPE);
+        assertThat(appInfo.anomalyTypes).containsExactly(TYPE_WAKELOCK, TYPE_WAKEUP);
         assertThat(appInfo.screenOnTimeMs).isEqualTo(SCREEN_TIME_MS);
         assertThat(appInfo.uid).isEqualTo(UID);
     }
@@ -70,7 +72,7 @@
     public void testCompareTo_hasCorrectOrder() {
         final AppInfo appInfo = new AppInfo.Builder()
                 .setPackageName(PACKAGE_NAME)
-                .setAnomalyType(ANOMALY_TYPE)
+                .addAnomalyType(TYPE_WAKELOCK)
                 .setScreenOnTimeMs(SCREEN_TIME_MS + 100)
                 .build();
 
@@ -85,7 +87,7 @@
     @Test
     public void testBuilder() {
         assertThat(mAppInfo.packageName).isEqualTo(PACKAGE_NAME);
-        assertThat(mAppInfo.anomalyType).isEqualTo(ANOMALY_TYPE);
+        assertThat(mAppInfo.anomalyTypes).containsExactly(TYPE_WAKELOCK, TYPE_WAKEUP);
         assertThat(mAppInfo.screenOnTimeMs).isEqualTo(SCREEN_TIME_MS);
         assertThat(mAppInfo.uid).isEqualTo(UID);
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
index a3dabba..9f1bb31 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.fuelgauge.batterytip;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.spy;
 import static org.robolectric.Shadows.shadowOf;
 
@@ -29,7 +30,6 @@
 import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
 import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
 import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
-import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
@@ -67,7 +67,6 @@
         MockitoAnnotations.initMocks(this);
 
         mContext = spy(RuntimeEnvironment.application);
-        FakeFeatureFactory.setupForTest();
 
         List<AppInfo> highUsageTips = new ArrayList<>();
         final AppInfo appInfo = new AppInfo.Builder()
@@ -102,7 +101,7 @@
         ShadowAlertDialog shadowDialog = shadowOf(dialog);
 
         assertThat(shadowDialog.getMessage())
-            .isEqualTo(mContext.getString(R.string.battery_tip_dialog_message, "1h"));
+                .isEqualTo(mContext.getString(R.string.battery_tip_dialog_message, "1h"));
     }
 
     @Test
@@ -116,7 +115,7 @@
 
         assertThat(shadowDialog.getTitle()).isEqualTo("Restrict app?");
         assertThat(shadowDialog.getMessage())
-            .isEqualTo(mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
+                .isEqualTo(mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
     }
 
     @Test
@@ -134,7 +133,7 @@
 
         assertThat(shadowDialog.getTitle()).isEqualTo("Restrict 2 apps?");
         assertThat(shadowDialog.getMessage())
-            .isEqualTo(mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
+                .isEqualTo(mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
         assertThat(shadowDialog.getView()).isNotNull();
     }
 
@@ -150,6 +149,6 @@
 
         assertThat(shadowDialog.getTitle()).isEqualTo("Remove restriction for app?");
         assertThat(shadowDialog.getMessage())
-            .isEqualTo(mContext.getString(R.string.battery_tip_unrestrict_app_dialog_message));
+                .isEqualTo(mContext.getString(R.string.battery_tip_unrestrict_app_dialog_message));
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
index b5cb173..6f898b2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
@@ -119,9 +119,9 @@
     public void testUpdateBatteryTips_logBatteryTip() {
         mBatteryTipPreferenceController.updateBatteryTips(mOldBatteryTips);
 
-        verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
-                eq(MetricsProto.MetricsEvent.ACTION_BATTERY_TIP_SHOWN),
-                eq(BatteryTip.TipType.SUMMARY));
+        verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_SUMMARY_TIP,
+                BatteryTip.StateType.NEW);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
index 753fc48..cee647e 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
@@ -25,6 +25,7 @@
 
 import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -122,6 +123,11 @@
             // do nothing
         }
 
+        @Override
+        public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+            // do nothing
+        }
+
         public final Parcelable.Creator CREATOR = new Parcelable.Creator() {
             public BatteryTip createFromParcel(Parcel in) {
                 return new TestBatteryTip(in);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java
index c30563d..1cac495 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java
@@ -17,28 +17,38 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.verify;
+
 import android.content.Context;
 import android.os.Parcel;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class EarlyWarningTipTest {
 
+    @Mock
+    private MetricsFeatureProvider mMetricsFeatureProvider;
     private Context mContext;
     private EarlyWarningTip mEarlyWarningTip;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
         mContext = RuntimeEnvironment.application;
         mEarlyWarningTip =
-            new EarlyWarningTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */);
+                new EarlyWarningTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */);
     }
 
     @Test
@@ -55,7 +65,7 @@
     @Test
     public void testInfo_stateNew_displayPowerModeInfo() {
         final EarlyWarningTip tip =
-            new EarlyWarningTip(BatteryTip.StateType.NEW, false /* powerModeOn */);
+                new EarlyWarningTip(BatteryTip.StateType.NEW, false /* powerModeOn */);
 
         assertThat(tip.getTitle(mContext)).isEqualTo("Turn on Low Battery Mode");
         assertThat(tip.getSummary(mContext)).isEqualTo("Extend your battery life");
@@ -65,7 +75,7 @@
     @Test
     public void testInfo_stateHandled_displayPowerModeHandledInfo() {
         final EarlyWarningTip tip =
-            new EarlyWarningTip(BatteryTip.StateType.HANDLED, false /* powerModeOn */);
+                new EarlyWarningTip(BatteryTip.StateType.HANDLED, false /* powerModeOn */);
 
         assertThat(tip.getTitle(mContext)).isEqualTo("Low Battery Mode is on");
         assertThat(tip.getSummary(mContext)).isEqualTo("Some features are limited");
@@ -75,7 +85,7 @@
     @Test
     public void testUpdate_powerModeTurnedOn_typeBecomeHandled() {
         final EarlyWarningTip nextTip =
-            new EarlyWarningTip(BatteryTip.StateType.INVISIBLE, true /* powerModeOn */);
+                new EarlyWarningTip(BatteryTip.StateType.INVISIBLE, true /* powerModeOn */);
 
         mEarlyWarningTip.updateState(nextTip);
 
@@ -115,4 +125,12 @@
 
         assertThat(earlyWarningTip.getState()).isEqualTo(BatteryTip.StateType.INVISIBLE);
     }
+
+    @Test
+    public void testLog() {
+        mEarlyWarningTip.log(mContext, mMetricsFeatureProvider);
+
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_EARLY_WARNING_TIP, BatteryTip.StateType.NEW);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTipTest.java
index af32dc5..b038a91 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTipTest.java
@@ -17,16 +17,22 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.verify;
+
 import android.content.Context;
 import android.os.Parcel;
 import android.text.format.DateUtils;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
@@ -38,14 +44,17 @@
     private static final String PACKAGE_NAME = "com.android.app";
     private static final long SCREEN_TIME = 30 * DateUtils.MINUTE_IN_MILLIS;
 
+    @Mock
+    private MetricsFeatureProvider mMetricsFeatureProvider;
     private Context mContext;
     private HighUsageTip mBatteryTip;
     private List<AppInfo> mUsageAppList;
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application;
+        MockitoAnnotations.initMocks(this);
 
+        mContext = RuntimeEnvironment.application;
         mUsageAppList = new ArrayList<>();
         mUsageAppList.add(new AppInfo.Builder()
                 .setPackageName(PACKAGE_NAME)
@@ -77,6 +86,18 @@
     @Test
     public void toString_containsAppData() {
         assertThat(mBatteryTip.toString()).isEqualTo(
-                "type=2 state=0 { packageName=com.android.app,anomalyType=0,screenTime=1800000 }");
+                "type=2 state=0 { packageName=com.android.app,anomalyTypes={},screenTime=1800000 "
+                        + "}");
+    }
+
+    @Test
+    public void testLog_logAppInfo() {
+        mBatteryTip.log(mContext, mMetricsFeatureProvider);
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_HIGH_USAGE_TIP, BatteryTip.StateType.NEW);
+
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_HIGH_USAGE_TIP_LIST,
+                PACKAGE_NAME);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
index 3298ea8..9d32b2b 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
@@ -16,16 +16,25 @@
 package com.android.settings.fuelgauge.batterytip.tips;
 
 import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Parcel;
+import android.util.Pair;
 
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -39,9 +48,10 @@
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class RestrictAppTipTest {
-
     private static final String PACKAGE_NAME = "com.android.app";
     private static final String DISPLAY_NAME = "app";
+    private static final int ANOMALY_WAKEUP = 0;
+    private static final int ANOMALY_WAKELOCK = 1;
 
     private Context mContext;
     private RestrictAppTip mNewBatteryTip;
@@ -52,6 +62,8 @@
     private ApplicationInfo mApplicationInfo;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private MetricsFeatureProvider mMetricsFeatureProvider;
 
     @Before
     public void setUp() throws Exception {
@@ -64,7 +76,11 @@
         doReturn(DISPLAY_NAME).when(mApplicationInfo).loadLabel(mPackageManager);
 
         mUsageAppList = new ArrayList<>();
-        mUsageAppList.add(new AppInfo.Builder().setPackageName(PACKAGE_NAME).build());
+        mUsageAppList.add(new AppInfo.Builder()
+                .setPackageName(PACKAGE_NAME)
+                .addAnomalyType(ANOMALY_WAKEUP)
+                .addAnomalyType(ANOMALY_WAKELOCK)
+                .build());
         mNewBatteryTip = new RestrictAppTip(BatteryTip.StateType.NEW, mUsageAppList);
         mHandledBatteryTip = new RestrictAppTip(BatteryTip.StateType.HANDLED, mUsageAppList);
         mInvisibleBatteryTip = new RestrictAppTip(BatteryTip.StateType.INVISIBLE, mUsageAppList);
@@ -125,6 +141,32 @@
     @Test
     public void toString_containsAppData() {
         assertThat(mNewBatteryTip.toString()).isEqualTo(
-                "type=1 state=0 { packageName=com.android.app,anomalyType=0,screenTime=0 }");
+                "type=1 state=0 { packageName=com.android.app,anomalyTypes={0, 1},screenTime=0 }");
+    }
+
+    @Test
+    public void testLog_stateNew_logAppInfo() {
+        mNewBatteryTip.log(mContext, mMetricsFeatureProvider);
+
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_APP_RESTRICTION_TIP, BatteryTip.StateType.NEW);
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_APP_RESTRICTION_TIP_LIST,
+                PACKAGE_NAME,
+                Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, ANOMALY_WAKEUP));
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_APP_RESTRICTION_TIP_LIST,
+                PACKAGE_NAME,
+                Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, ANOMALY_WAKELOCK));
+    }
+
+    @Test
+    public void testLog_stateHandled_doNotLogAppInfo() {
+        mHandledBatteryTip.log(mContext, mMetricsFeatureProvider);
+
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_APP_RESTRICTION_TIP, BatteryTip.StateType.HANDLED);
+        verify(mMetricsFeatureProvider, never()).action(any(), anyInt(), anyString(), any());
+
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/SmartBatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/SmartBatteryTipTest.java
new file mode 100644
index 0000000..1e4aef8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/SmartBatteryTipTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.tips;
+
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class SmartBatteryTipTest {
+
+    @Mock
+    private MetricsFeatureProvider mMetricsFeatureProvider;
+    private Context mContext;
+    private SmartBatteryTip mSmartBatteryTip;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mSmartBatteryTip = new SmartBatteryTip(BatteryTip.StateType.NEW);
+    }
+
+    @Test
+    public void testLog() {
+        mSmartBatteryTip.log(mContext, mMetricsFeatureProvider);
+
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_SMART_BATTERY_TIP, BatteryTip.StateType.NEW);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java
index 13c99a6..3c8f3c4 100644
--- a/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java
@@ -55,7 +55,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mContext.getSystemService(Context.INPUT_SERVICE)).thenReturn(mIm);
+        when(mContext.getSystemService(InputManager.class)).thenReturn(mIm);
         mController = new PhysicalKeyboardPreferenceController(mContext, null /* lifecycle */);
     }
 
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeBlockedEffectsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeBlockedEffectsPreferenceControllerTest.java
new file mode 100644
index 0000000..4bf05ef
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeBlockedEffectsPreferenceControllerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 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.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.Context;
+
+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.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public final class ZenModeBlockedEffectsPreferenceControllerTest {
+
+    private ZenModeBlockedEffectsPreferenceController mController;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+
+    private Context mContext;
+    @Mock
+    private ZenModeBackend mBackend;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = RuntimeEnvironment.application;
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeBlockedEffectsPreferenceController(
+                mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+    }
+
+    @Test
+    public void testIsAvailable() {
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
+    public void testHasSummary() {
+        assertNotNull(mController.getSummary());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeDurationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeDurationPreferenceControllerTest.java
new file mode 100644
index 0000000..9a94e6c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeDurationPreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 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.FragmentManager;
+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.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.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ZenModeDurationPreferenceControllerTest {
+    private ZenModeDurationPreferenceController 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;
+
+    @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 ZenModeDurationPreferenceController(mContext, mock(Lifecycle.class),
+                mock(FragmentManager.class));
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_DurationForever() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
+                Settings.Global.ZEN_DURATION_FOREVER);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setSummary(mContext.getString(R.string.zen_mode_duration_summary_forever));
+    }
+
+    @Test
+    public void updateState_DurationPrompt() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
+                Settings.Global.ZEN_DURATION_PROMPT);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setSummary(mContext.getString(
+                R.string.zen_mode_duration_summary_always_prompt));
+    }
+
+    @Test
+    public void updateState_DurationCustom() {
+        int zenDuration = 45;
+        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
+                zenDuration);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setSummary(mContext.getResources().getString(
+                R.string.zen_mode_duration_summary_time_minutes, zenDuration));
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java
deleted file mode 100644
index 6dd3c178..0000000
--- a/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.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.support.v14.preference.SwitchPreference;
-
-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.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-public class ZenModeScreenOffPreferenceControllerTest {
-
-    private static final boolean MOCK_PRIORITY_SCREEN_OFF_SETTING = false;
-
-    private ZenModeScreenOffPreferenceController mController;
-
-    @Mock
-    private ZenModeBackend mBackend;
-    @Mock
-    private NotificationManager mNotificationManager;
-    @Mock
-    private SwitchPreference mockPref;
-    @Mock
-    private NotificationManager.Policy mPolicy;
-
-    private Context mContext;
-
-    @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
deleted file mode 100644
index 2fd1380..0000000
--- a/tests/robotests/src/com/android/settings/notification/ZenModeScreenOnPreferenceControllerTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.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.support.v14.preference.SwitchPreference;
-
-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.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-public class ZenModeScreenOnPreferenceControllerTest {
-
-    private static final boolean MOCK_PRIORITY_SCREEN_ON_SETTING = false;
-
-    private ZenModeScreenOnPreferenceController mController;
-
-    @Mock
-    private ZenModeBackend mBackend;
-    @Mock
-    private NotificationManager mNotificationManager;
-    @Mock
-    private SwitchPreference mockPref;
-    @Mock
-    private NotificationManager.Policy mPolicy;
-
-    private Context mContext;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        ShadowApplication shadowApplication = ShadowApplication.getInstance();
-        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
-
-        mContext = RuntimeEnvironment.application;
-        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/notification/ZenModeVisEffectPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectPreferenceControllerTest.java
new file mode 100644
index 0000000..a424491
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectPreferenceControllerTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+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.Context;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+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.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ZenModeVisEffectPreferenceControllerTest {
+    private ZenModeVisEffectPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private CheckBoxPreference mockPref;
+    private Context mContext;
+    private FakeFeatureFactory mFeatureFactory;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock NotificationManager mNotificationManager;
+
+    private static final String PREF_KEY = "main_pref";
+    private static final int PREF_METRICS = 1;
+    private static final int PARENT_EFFECT1 = SUPPRESSED_EFFECT_BADGE;
+    private static final int PARENT_EFFECT2 = SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        mContext = shadowApplication.getApplicationContext();
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(
+                mock(NotificationManager.Policy.class));
+        mController = new ZenModeVisEffectPreferenceController(mContext, mock(Lifecycle.class),
+                PREF_KEY, SUPPRESSED_EFFECT_PEEK, PREF_METRICS, null);
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mockPref);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void updateState_notChecked() {
+        when(mBackend.isVisualEffectSuppressed(SUPPRESSED_EFFECT_PEEK)).thenReturn(false);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(false);
+        verify(mockPref).setEnabled(true);
+    }
+
+    @Test
+    public void updateState_checked() {
+        when(mBackend.isVisualEffectSuppressed(SUPPRESSED_EFFECT_PEEK)).thenReturn(true);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(true);
+        verify(mockPref).setEnabled(true);
+    }
+
+    @Test
+    public void updateState_checkedFalse_parentChecked() {
+        mController = new ZenModeVisEffectPreferenceController(mContext, mock(Lifecycle.class),
+                PREF_KEY, SUPPRESSED_EFFECT_PEEK, PREF_METRICS,
+                new int[] {PARENT_EFFECT1, PARENT_EFFECT2});
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+        when(mBackend.isVisualEffectSuppressed(SUPPRESSED_EFFECT_PEEK)).thenReturn(false);
+        when(mBackend.isVisualEffectSuppressed(PARENT_EFFECT1)).thenReturn(false);
+        when(mBackend.isVisualEffectSuppressed(PARENT_EFFECT2)).thenReturn(true);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(true);
+        verify(mockPref).setEnabled(false);
+        verify(mBackend, times(1)).saveVisualEffectsPolicy(SUPPRESSED_EFFECT_PEEK, true);
+    }
+
+    @Test
+    public void updateState_checkedFalse_parentNotChecked() {
+        mController = new ZenModeVisEffectPreferenceController(mContext, mock(Lifecycle.class),
+                PREF_KEY, SUPPRESSED_EFFECT_PEEK, PREF_METRICS,
+                new int[] {PARENT_EFFECT1, PARENT_EFFECT2});
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+        when(mBackend.isVisualEffectSuppressed(SUPPRESSED_EFFECT_PEEK)).thenReturn(false);
+        when(mBackend.isVisualEffectSuppressed(PARENT_EFFECT1)).thenReturn(false);
+        when(mBackend.isVisualEffectSuppressed(PARENT_EFFECT2)).thenReturn(false);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(false);
+        verify(mockPref).setEnabled(true);
+        verify(mBackend, never()).saveVisualEffectsPolicy(SUPPRESSED_EFFECT_PEEK, true);
+    }
+
+    @Test
+    public void onPreferenceChanged_checkedFalse() {
+        mController.onPreferenceChange(mockPref, false);
+
+        verify(mBackend).saveVisualEffectsPolicy(SUPPRESSED_EFFECT_PEEK, false);
+        verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class),
+                eq(PREF_METRICS),
+                eq(false));
+    }
+
+    @Test
+    public void onPreferenceChanged_checkedTrue() {
+        mController.onPreferenceChange(mockPref, true);
+        verify(mBackend).saveVisualEffectsPolicy(SUPPRESSED_EFFECT_PEEK, true);
+        verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class),
+                eq(PREF_METRICS),
+                eq(true));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
index e0df818..ab08a90 100644
--- a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
@@ -27,6 +27,7 @@
 import android.view.View;
 import android.widget.Button;
 
+import com.android.internal.widget.LockPatternView;
 import com.android.settings.R;
 import com.android.settings.SetupRedactionInterstitial;
 import com.android.settings.password.ChooseLockPattern.ChooseLockPatternFragment;
@@ -44,6 +45,11 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowAlertDialog;
 import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
+
+import java.util.Collections;
+import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(shadows = {
@@ -85,6 +91,17 @@
 
     @Config(qualifiers = "sw400dp")
     @Test
+    public void selectPattern_shouldHideOptionsButton() {
+        Button button = mActivity.findViewById(R.id.screen_lock_options);
+        assertThat(button).isNotNull();
+        assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
+
+        LockPatternView lockPatternView = mActivity.findViewById(R.id.lockPattern);
+        ReflectionHelpers.callInstanceMethod(lockPatternView, "notifyPatternDetected");
+    }
+
+    @Config(qualifiers = "sw400dp")
+    @Test
     public void sw400dp_shouldShowScreenLockOptions() {
         Button button = mActivity.findViewById(R.id.screen_lock_options);
         assertThat(button).isNotNull();
diff --git a/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java
index ee6d33a..2e6b703 100644
--- a/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java
@@ -68,6 +68,23 @@
     }
 
     @Test
+    @Config(qualifiers = "mcc999")
+    public void isAvailable_notVisible_false() {
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    @Config(qualifiers = "mcc999")
+    public void isAvailable_notVisible_butNotDetailPage_true() {
+        mController = new EncryptionStatusPreferenceController(mContext,
+                PREF_KEY_ENCRYPTION_SECURITY_PAGE);
+
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        Shadows.shadowOf(userManager).setIsAdminUser(true);
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
     public void updateSummary_encrypted_shouldSayEncrypted() {
         ShadowLockPatternUtils.setDeviceEncryptionEnabled(true);
 
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 50f47ff..9edae7e 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -18,8 +18,7 @@
 package com.android.settings.slices;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -28,6 +27,7 @@
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
+import android.provider.SettingsSlicesContract;
 
 import com.android.settings.testutils.DatabaseTestUtils;
 import com.android.settings.testutils.FakeToggleController;
@@ -50,6 +50,8 @@
 public class SettingsSliceProviderTest {
 
     private final String KEY = "KEY";
+    private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
+    private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY;
     private final String TITLE = "title";
     private final String SUMMARY = "summary";
     private final String SCREEN_TITLE = "screen title";
@@ -67,6 +69,8 @@
         mProvider = spy(new SettingsSliceProvider());
         mProvider.mSliceDataCache = new HashMap<>();
         mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
+        when(mProvider.getContext()).thenReturn(mContext);
+
         mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
         SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
     }
@@ -78,12 +82,8 @@
 
     @Test
     public void testInitialSliceReturned_emptySlice() {
-        insertSpecialCase(KEY);
-        ContentResolver mockResolver = mock(ContentResolver.class);
-        doReturn(mockResolver).when(mContext).getContentResolver();
-        when(mProvider.getContext()).thenReturn(mContext);
-
-        Uri uri = SettingsSliceProvider.getUri(KEY);
+        insertSpecialCase(INTENT_PATH);
+        Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
         Slice slice = mProvider.onBindSlice(uri);
 
         assertThat(slice.getUri()).isEqualTo(uri);
@@ -91,21 +91,9 @@
     }
 
     @Test
-    public void testUriBuilder_returnsValidSliceUri() {
-        Uri uri = SettingsSliceProvider.getUri(KEY);
-
-        assertThat(uri.getScheme()).isEqualTo(ContentResolver.SCHEME_CONTENT);
-        assertThat(uri.getAuthority()).isEqualTo(SettingsSliceProvider.SLICE_AUTHORITY);
-        assertThat(uri.getLastPathSegment()).isEqualTo(KEY);
-    }
-
-    @Test
     public void testLoadSlice_returnsSliceFromAccessor() {
-        ContentResolver mockResolver = mock(ContentResolver.class);
-        doReturn(mockResolver).when(mContext).getContentResolver();
-        when(mProvider.getContext()).thenReturn(mContext);
         insertSpecialCase(KEY);
-        Uri uri = SettingsSliceProvider.getUri(KEY);
+        Uri uri = SliceBuilderUtils.getUri(KEY, false);
 
         mProvider.loadSlice(uri);
         SliceData data = mProvider.mSliceDataCache.get(uri);
@@ -116,7 +104,6 @@
 
     @Test
     public void testLoadSlice_cachedEntryRemovedOnBuild() {
-        when(mProvider.getContext()).thenReturn(mContext);
         SliceData data = getDummyData();
         mProvider.mSliceDataCache.put(data.getUri(), data);
         mProvider.onBindSlice(data.getUri());
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
index 5a2271a..0fcce5f 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
@@ -20,8 +20,10 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.net.Uri;
+import android.provider.SettingsSlicesContract;
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
@@ -48,6 +50,9 @@
     private final Class PREF_CONTROLLER = FakeToggleController.class;
     private final Class PREF_CONTROLLER2 = FakeContextOnlyPreferenceController.class;
 
+    private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
+    private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY;
+
     private Context mContext;
 
     @Before
@@ -63,6 +68,58 @@
     }
 
     @Test
+    public void testUriBuilder_oemAuthority_intentPath_returnsValidSliceUri() {
+        Uri expectedUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .appendPath(INTENT_PATH)
+                .build();
+
+        Uri actualUri = SliceBuilderUtils.getUri(INTENT_PATH, false);
+
+        assertThat(actualUri).isEqualTo(expectedUri);
+    }
+
+    @Test
+    public void testUriBuilder_oemAuthority_actionPath_returnsValidSliceUri() {
+        Uri expectedUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+                .appendPath(ACTION_PATH)
+                .build();
+
+        Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, false);
+
+        assertThat(actualUri).isEqualTo(expectedUri);
+    }
+
+    @Test
+    public void testUriBuilder_platformAuthority_intentPath_returnsValidSliceUri() {
+        Uri expectedUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .appendPath(ACTION_PATH)
+                .build();
+
+        Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, true);
+
+        assertThat(actualUri).isEqualTo(expectedUri);
+    }
+
+    @Test
+    public void testUriBuilder_platformAuthority_actionPath_returnsValidSliceUri() {
+        Uri expectedUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(SettingsSlicesContract.AUTHORITY)
+                .appendPath(ACTION_PATH)
+                .build();
+
+        Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, true);
+
+        assertThat(actualUri).isEqualTo(expectedUri);
+    }
+
+    @Test
     public void testGetPreferenceController_buildsMatchingController() {
         BasePreferenceController controller =
             SliceBuilderUtils.getPreferenceController(mContext, getDummyData());
diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
index 4d6c08d..36c2754 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
@@ -81,6 +81,7 @@
         assertThat(fakeSlice.getUri()).isNull();
         assertThat(fakeSlice.getFragmentClassName()).isEqualTo(fakeFragmentClassName);
         assertThat(fakeSlice.getPreferenceController()).isEqualTo(fakeControllerName);
-        assertThat(fakeSlice.getSliceType()).isEqualTo(SliceData.SliceType.SLIDER);
+        assertThat(fakeSlice.getSliceType()).isEqualTo(SliceData.SliceType.SLIDER); // from XML
+        assertThat(fakeSlice.isPlatformDefined()).isTrue(); // from XML
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
index 7cd19b5..c2ab0af 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
@@ -37,6 +37,7 @@
     private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
     private final String PREF_CONTROLLER = "com.android.settings.slices.tester";
     private final int SLICE_TYPE = SliceData.SliceType.SWITCH;
+    private final boolean IS_PLATFORM_DEFINED = true;
 
     @Test
     public void testBuilder_buildsMatchingObject() {
@@ -49,7 +50,8 @@
                 .setFragmentName(FRAGMENT_NAME)
                 .setUri(URI)
                 .setPreferenceControllerClassName(PREF_CONTROLLER)
-                .setSliceType(SLICE_TYPE);
+                .setSliceType(SLICE_TYPE)
+                .setPlatformDefined(IS_PLATFORM_DEFINED);
 
         SliceData data = builder.build();
 
@@ -62,6 +64,7 @@
         assertThat(data.getUri()).isEqualTo(URI);
         assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
         assertThat(data.getSliceType()).isEqualTo(SLICE_TYPE);
+        assertThat(data.isPlatformDefined()).isEqualTo(IS_PLATFORM_DEFINED);
     }
 
     @Test(expected = IllegalStateException.class)
diff --git a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java
index 8989de9..331058c 100644
--- a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseAccessorTest.java
@@ -90,7 +90,7 @@
     public void testGetSliceFromUri_validUri_validSliceReturned() {
         String key = "key";
         insertSpecialCase(key);
-        Uri uri = SettingsSliceProvider.getUri(key);
+        Uri uri = SliceBuilderUtils.getUri(key, false);
 
         SliceData data = mAccessor.getSliceDataFromUri(uri);
 
@@ -106,7 +106,7 @@
 
     @Test(expected = IllegalStateException.class)
     public void testGetSliceFromUri_invalidUri_errorThrown() {
-        Uri uri = SettingsSliceProvider.getUri("durr");
+        Uri uri = SliceBuilderUtils.getUri("durr", false);
 
         mAccessor.getSliceDataFromUri(uri);
     }
diff --git a/tests/robotests/src/com/android/settings/system/ResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/ResetPreferenceControllerTest.java
new file mode 100644
index 0000000..3869903
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/system/ResetPreferenceControllerTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.system;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ResetPreferenceControllerTest {
+
+    private static final String KEY_RESET_DASHBOARD = "reset_dashboard";
+
+    private Context mContext;
+    private ResetPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new ResetPreferenceController(mContext, KEY_RESET_DASHBOARD);
+    }
+
+    @Test
+    public void isAvailable_byDefault_true() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    @Config(qualifiers = "mcc999")
+    public void isAvailable_ifNotVisible_false() {
+        assertThat(mController.isAvailable()).isFalse();
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowCardView.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowCardView.java
new file mode 100644
index 0000000..e349ee3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowCardView.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settings.testutils.shadow;
+
+import android.content.Context;
+import android.support.v7.widget.CardView;
+import android.util.AttributeSet;
+
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowFrameLayout;
+
+/**
+ * Shadow for CardView to get around view not implemented error.
+ */
+@Implements(CardView.class)
+public class ShadowCardView extends ShadowFrameLayout {
+
+    public void __constructor__(Context context, AttributeSet attrs, int defStyleAttr) {
+        // do nothing
+    }
+
+}