Merge "Fix a typo in AndroidManifest.xml" into pi-dev
diff --git a/res/layout/two_state_button.xml b/res/layout/two_state_button.xml
index 22bf2bd..3ad1634 100644
--- a/res/layout/two_state_button.xml
+++ b/res/layout/two_state_button.xml
@@ -27,19 +27,15 @@
     <Button
         android:id="@+id/state_on_button"
         style="@style/ActionPrimaryButton"
-        android:layout_width="0dp"
-        android:layout_weight="1"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:paddingEnd="8dp" />
+        android:layout_gravity="center" />
 
     <Button
         android:id="@+id/state_off_button"
         style="@style/ActionSecondaryButton"
-        android:layout_width="0dp"
-        android:layout_weight="1"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:paddingEnd="8dp" />
+        android:layout_gravity="center" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index e3ec74f..f8a7acc 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -131,4 +131,7 @@
     <!-- List of a11y components on the device allowed to be enabled by Settings Slices -->
     <string-array name="config_settings_slices_accessibility_components" translatable="false"/>
 
+    <!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
+    <bool name="config_swipe_up_gesture_setting_available">false</bool>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b061e61..5c33e9c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7928,6 +7928,15 @@
     <!-- [CHAR LIMIT=20] Zen mode settings: Calls screen footer -->
     <string name="zen_mode_calls_footer">When Do Not Disturb is on, incoming calls are blocked. You can adjust settings to allow your friends, family, or other contacts to reach you.</string>
 
+    <!-- [CHAR LIMIT=50] Zen mode settings: Starred contacts preference title -->
+    <string name="zen_mode_starred_contacts_title">Starred contacts</string>
+
+    <!-- Zen mode settings: Starred contacts summary [CHAR LIMIT=NONE] -->
+    <plurals name="zen_mode_starred_contacts_summary_additional_contacts">
+        <item quantity="one">and 1 other</item>
+        <item quantity="other">and <xliff:g id="num_people" example="3">%d</xliff:g> others</item>
+    </plurals>
+
     <!-- [CHAR LIMIT=20] Zen mode settings: Messages option -->
     <string name="zen_mode_messages">Messages</string>
 
@@ -9199,7 +9208,7 @@
     <!-- Informational text about time left in billing cycle [CHAR LIMIT=60] -->
     <string name="billing_cycle_less_than_one_day_left">Less than 1 day left</string>
 
-    <!-- Informational text about carrier and update time [CHAR LIMIT=30] -->
+    <!-- Informational text about carrier and update time [CHAR LIMIT=32] -->
     <string name="carrier_and_update_text">Updated by <xliff:g name="carrier" example="T-mobile">^1</xliff:g> <xliff:g name="time" example="3m">^2</xliff:g> ago</string>
 
     <!-- Informational text about update time only, without carrier. First argument intentionally skipped. [CHAR LIMIT=30] -->
diff --git a/res/xml/zen_mode_calls_settings.xml b/res/xml/zen_mode_calls_settings.xml
index d9e47f3..62d9ef4 100644
--- a/res/xml/zen_mode_calls_settings.xml
+++ b/res/xml/zen_mode_calls_settings.xml
@@ -29,7 +29,9 @@
           android:entries="@array/zen_mode_contacts_entries"
           android:entryValues="@array/zen_mode_contacts_values"/>
 
-      <!-- TODO: setting that links to contacts or placeholder for external app -->
+      <Preference
+          android:key="zen_mode_starred_contacts"
+          android:title="@string/zen_mode_starred_contacts_title"/>
 
       <!-- Repeat callers -->
       <SwitchPreference
diff --git a/res/xml/zen_mode_msg_event_reminder_settings.xml b/res/xml/zen_mode_msg_event_reminder_settings.xml
index 866f333..2f065a6 100644
--- a/res/xml/zen_mode_msg_event_reminder_settings.xml
+++ b/res/xml/zen_mode_msg_event_reminder_settings.xml
@@ -29,6 +29,10 @@
           android:entries="@array/zen_mode_contacts_entries"
           android:entryValues="@array/zen_mode_contacts_values"/>
 
+      <Preference
+          android:key="zen_mode_starred_contacts"
+          android:title="@string/zen_mode_starred_contacts_title"/>
+
       <!-- Reminders -->
       <SwitchPreference
           android:key="zen_mode_reminders"
diff --git a/src/com/android/settings/accessibility/AccessibilitySlicePreferenceController.java b/src/com/android/settings/accessibility/AccessibilitySlicePreferenceController.java
index 6b9a480..ec988cd 100644
--- a/src/com/android/settings/accessibility/AccessibilitySlicePreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilitySlicePreferenceController.java
@@ -23,7 +23,6 @@
 import android.provider.Settings;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.core.TogglePreferenceController;
 import com.android.settingslib.accessibility.AccessibilityUtils;
 
@@ -88,7 +87,7 @@
     @Override
     public int getAvailabilityStatus() {
         // Return unsupported when the service is disabled or not installed.
-        return getAccessibilityServiceInfo() == null ? DISABLED_UNSUPPORTED : AVAILABLE;
+        return getAccessibilityServiceInfo() == null ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
     }
 
     private AccessibilityServiceInfo getAccessibilityServiceInfo() {
diff --git a/src/com/android/settings/accessibility/MagnificationNavbarPreferenceController.java b/src/com/android/settings/accessibility/MagnificationNavbarPreferenceController.java
index 947893d..84063a1 100644
--- a/src/com/android/settings/accessibility/MagnificationNavbarPreferenceController.java
+++ b/src/com/android/settings/accessibility/MagnificationNavbarPreferenceController.java
@@ -56,7 +56,7 @@
     public int getAvailabilityStatus() {
         return MagnificationPreferenceFragment.isApplicable(mContext.getResources())
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/DataSaverController.java b/src/com/android/settings/applications/DataSaverController.java
index d9710fc..87589a7 100644
--- a/src/com/android/settings/applications/DataSaverController.java
+++ b/src/com/android/settings/applications/DataSaverController.java
@@ -35,6 +35,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_data_saver)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/applications/DeviceAdministratorsController.java b/src/com/android/settings/applications/DeviceAdministratorsController.java
index c7ef8ce..79c078e 100644
--- a/src/com/android/settings/applications/DeviceAdministratorsController.java
+++ b/src/com/android/settings/applications/DeviceAdministratorsController.java
@@ -34,6 +34,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_device_administrators)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/applications/EnabledVrListenersController.java b/src/com/android/settings/applications/EnabledVrListenersController.java
index 94a70ef..0aae14f 100644
--- a/src/com/android/settings/applications/EnabledVrListenersController.java
+++ b/src/com/android/settings/applications/EnabledVrListenersController.java
@@ -34,6 +34,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_enabled_vr_listeners)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/applications/HighPowerAppsController.java b/src/com/android/settings/applications/HighPowerAppsController.java
index dc847b3..b7ec2fb 100644
--- a/src/com/android/settings/applications/HighPowerAppsController.java
+++ b/src/com/android/settings/applications/HighPowerAppsController.java
@@ -34,6 +34,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_high_power_apps)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/applications/PremiumSmsController.java b/src/com/android/settings/applications/PremiumSmsController.java
index 40911a5..ecf19ff 100644
--- a/src/com/android/settings/applications/PremiumSmsController.java
+++ b/src/com/android/settings/applications/PremiumSmsController.java
@@ -34,6 +34,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_premium_sms)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index afcf760..b0bb173 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -78,7 +78,7 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_app_info_settings_battery)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java
index a40562a..69be98d 100644
--- a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java
@@ -56,7 +56,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return isBandwidthControlEnabled() ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isBandwidthControlEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
index daf3992..4bc680c 100644
--- a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
@@ -105,11 +105,11 @@
     @Override
     public int getAvailabilityStatus() {
         if (!mContext.getResources().getBoolean(R.bool.config_show_app_info_settings_memory)) {
-            return DISABLED_UNSUPPORTED;
+            return UNSUPPORTED_ON_DEVICE;
         }
 
         return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java b/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java
index cf1731d..d5061d5 100644
--- a/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java
+++ b/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java
@@ -46,7 +46,7 @@
         if (UserManager.get(mContext).isManagedProfile()) {
             return DISABLED_FOR_USER;
         }
-        return hasAppCapability() ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return hasAppCapability() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java b/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java
index 0997e60..0c4044a 100644
--- a/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java
@@ -55,19 +55,19 @@
     @Override
     public int getAvailabilityStatus() {
         if (TextUtils.isEmpty(mPackageName)) {
-            return DISABLED_UNSUPPORTED;
+            return UNSUPPORTED_ON_DEVICE;
         }
         final List<ResolveInfo> resolved = mPackageManager.queryIntentActivities(mIntent,
                 0 /* flags */);
         if (resolved == null || resolved.isEmpty()) {
-            return DISABLED_UNSUPPORTED;
+            return UNSUPPORTED_ON_DEVICE;
         }
         for (ResolveInfo info : resolved) {
             if (isSystemApp(info)) {
                 return AVAILABLE;
             }
         }
-        return DISABLED_UNSUPPORTED;
+        return UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java
index 183d13f..f0439d6 100644
--- a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java
+++ b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java
@@ -41,7 +41,7 @@
     public int getAvailabilityStatus() {
         return mUm.isAdminUser()
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
index f2f220a..8827a71 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
@@ -99,7 +99,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return mLocalAdapter != null ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return mLocalAdapter != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
index 6fbb84a..cacfa0c 100644
--- a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
@@ -26,7 +26,6 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
@@ -57,7 +56,7 @@
     public int getAvailabilityStatus() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java b/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java
index 2de271c..da6d86f 100644
--- a/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java
@@ -75,7 +75,7 @@
     public int getAvailabilityStatus() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
index e4bf71c..6fa7d1b 100644
--- a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
@@ -84,7 +84,7 @@
     public int getAvailabilityStatus() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
index ec616c8..77b75c5 100644
--- a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
@@ -128,7 +128,7 @@
                         BluetoothAdapter adapter = manager.getAdapter();
                         final int status = adapter != null
                                 ? TogglePreferenceController.AVAILABLE
-                                : TogglePreferenceController.DISABLED_UNSUPPORTED;
+                                : TogglePreferenceController.UNSUPPORTED_ON_DEVICE;
                         if (status != TogglePreferenceController.AVAILABLE) {
                             keys.add(KEY_BLUETOOTH_SCREEN);
                         }
diff --git a/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceController.java b/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceController.java
index 072de75..6d4e8b8 100644
--- a/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceController.java
@@ -38,7 +38,7 @@
         if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.BLUETOOTH_WHILE_DRIVING)) {
             return AVAILABLE;
         }
-        return DISABLED_UNSUPPORTED;
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
index 47e3438..e97683d 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
@@ -33,7 +33,6 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 
 /**
@@ -88,7 +87,7 @@
     public int getAvailabilityStatus() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
index f80c877..1826be5 100644
--- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
@@ -30,7 +30,6 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.overlay.DockUpdaterFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -86,7 +85,7 @@
     public int getAvailabilityStatus() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java
index 44305dd..c3eb672 100644
--- a/src/com/android/settings/core/BasePreferenceController.java
+++ b/src/com/android/settings/core/BasePreferenceController.java
@@ -49,8 +49,8 @@
      * {@link #isSupported()}.
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({AVAILABLE, DISABLED_UNSUPPORTED, DISABLED_FOR_USER, DISABLED_DEPENDENT_SETTING,
-            UNAVAILABLE_UNKNOWN})
+    @IntDef({AVAILABLE, UNSUPPORTED_ON_DEVICE, DISABLED_FOR_USER, DISABLED_DEPENDENT_SETTING,
+            CONDITIONALLY_UNAVAILABLE})
     public @interface AvailabilityStatus {
     }
 
@@ -60,12 +60,20 @@
     public static final int AVAILABLE = 0;
 
     /**
-     * The setting is not supported by the device.
+     * A generic catch for settings which are currently unavailable, but may become available in
+     * the future. You should use {@link #DISABLED_FOR_USER} or {@link #DISABLED_DEPENDENT_SETTING}
+     * if they describe the condition more accurately.
+     */
+    public static final int CONDITIONALLY_UNAVAILABLE = 1;
+
+    /**
+     * The setting is not, and will not supported by this device.
      * <p>
      * There is no guarantee that the setting page exists, and any links to the Setting should take
      * you to the home page of Settings.
      */
-    public static final int DISABLED_UNSUPPORTED = 1;
+    public static final int UNSUPPORTED_ON_DEVICE = 2;
+
 
     /**
      * The setting cannot be changed by the current user.
@@ -73,7 +81,7 @@
      * Links to the Setting should take you to the page of the Setting, even if it cannot be
      * changed.
      */
-    public static final int DISABLED_FOR_USER = 2;
+    public static final int DISABLED_FOR_USER = 3;
 
     /**
      * The setting has a dependency in the Settings App which is currently blocking access.
@@ -90,15 +98,8 @@
      * Links to the Setting should take you to the page of the Setting, even if it cannot be
      * changed.
      */
-    public static final int DISABLED_DEPENDENT_SETTING = 3;
+    public static final int DISABLED_DEPENDENT_SETTING = 4;
 
-    /**
-     * A catch-all case for internal errors and inexplicable unavailability.
-     * <p>
-     * There is no guarantee that the setting page exists, and any links to the Setting should take
-     * you to the home page of Settings.
-     */
-    public static final int UNAVAILABLE_UNKNOWN = 4;
 
     protected final String mPreferenceKey;
 
@@ -181,8 +182,8 @@
     @Override
     public final boolean isAvailable() {
         final int availabilityStatus = getAvailabilityStatus();
-        return (availabilityStatus == AVAILABLE) ||
-                (availabilityStatus == DISABLED_DEPENDENT_SETTING);
+        return (availabilityStatus == AVAILABLE
+                || availabilityStatus == DISABLED_DEPENDENT_SETTING);
     }
 
     /**
@@ -193,7 +194,7 @@
      * Note that a return value of {@code true} does not mean that the setting is available.
      */
     public final boolean isSupported() {
-        return getAvailabilityStatus() != DISABLED_UNSUPPORTED;
+        return getAvailabilityStatus() != UNSUPPORTED_ON_DEVICE;
     }
 
     /**
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
index 58faac2..0767f7a 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -28,7 +28,6 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionPlan;
 import android.text.TextUtils;
-import android.text.format.Formatter;
 import android.util.Log;
 import android.util.RecurrenceRule;
 
@@ -182,7 +181,7 @@
     @Override
     public int getAvailabilityStatus() {
         return DataUsageUtils.hasSim(mActivity)
-                || DataUsageUtils.hasWifiRadio(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+                || DataUsageUtils.hasWifiRadio(mContext) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
index a6d5363..cb04ed4 100644
--- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
@@ -50,7 +50,7 @@
         if (mConfig == null) {
             mConfig = new AmbientDisplayConfiguration(mContext);
         }
-        return isAvailable(mConfig) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isAvailable(mConfig) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
index 15eb8b3..a44b294 100644
--- a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java
@@ -86,7 +86,7 @@
         if (mConfig == null) {
             mConfig = new AmbientDisplayConfiguration(mContext);
         }
-        return mConfig.pulseOnNotificationAvailable() ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return mConfig.pulseOnNotificationAvailable() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/AutoBrightnessPreferenceController.java b/src/com/android/settings/display/AutoBrightnessPreferenceController.java
index 28f9260..564e27a 100644
--- a/src/com/android/settings/display/AutoBrightnessPreferenceController.java
+++ b/src/com/android/settings/display/AutoBrightnessPreferenceController.java
@@ -57,7 +57,7 @@
         return mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_automatic_brightness_available)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/AutoRotatePreferenceController.java b/src/com/android/settings/display/AutoRotatePreferenceController.java
index 596e2c6..6580b03 100644
--- a/src/com/android/settings/display/AutoRotatePreferenceController.java
+++ b/src/com/android/settings/display/AutoRotatePreferenceController.java
@@ -71,7 +71,7 @@
     @Override
     public int getAvailabilityStatus() {
         return RotationPolicy.isRotationLockToggleVisible(mContext)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/NightDisplayActivationPreferenceController.java b/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
index 9634739..7f50d0a 100644
--- a/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
@@ -50,7 +50,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
index e7b7de2..4ae15f2 100644
--- a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
@@ -37,7 +37,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java b/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java
index 524e7db..f76e730 100644
--- a/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java
@@ -35,7 +35,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java b/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java
index 4dda8f4..da54d87 100644
--- a/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java
@@ -35,7 +35,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/NightDisplayFooterPreferenceController.java b/src/com/android/settings/display/NightDisplayFooterPreferenceController.java
index 228d271..15dd501 100644
--- a/src/com/android/settings/display/NightDisplayFooterPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayFooterPreferenceController.java
@@ -31,7 +31,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return ColorDisplayController.isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
index b039644..ec7e577 100644
--- a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
@@ -35,7 +35,7 @@
     @Override
     public int getAvailabilityStatus() {
         if (!ColorDisplayController.isAvailable(mContext)) {
-            return DISABLED_UNSUPPORTED;
+            return UNSUPPORTED_ON_DEVICE;
         } else if (!mController.isActivated()) {
             return DISABLED_DEPENDENT_SETTING;
         }
diff --git a/src/com/android/settings/fingerprint/FingerprintStatusPreferenceController.java b/src/com/android/settings/fingerprint/FingerprintStatusPreferenceController.java
index 19eb4bb..91af1ce 100644
--- a/src/com/android/settings/fingerprint/FingerprintStatusPreferenceController.java
+++ b/src/com/android/settings/fingerprint/FingerprintStatusPreferenceController.java
@@ -60,7 +60,7 @@
     @Override
     public int getAvailabilityStatus() {
         if (mFingerprintManager == null || !mFingerprintManager.isHardwareDetected()) {
-            return DISABLED_UNSUPPORTED;
+            return UNSUPPORTED_ON_DEVICE;
         }
         if (isUserSupported()) {
             return AVAILABLE;
diff --git a/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceController.java b/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceController.java
index e8e5ad9..d7bfbc7 100644
--- a/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceController.java
@@ -42,7 +42,7 @@
     @Override
     public int getAvailabilityStatus() {
         return mPowerUsageFeatureProvider.isSmartBatterySupported()
-                ? DISABLED_UNSUPPORTED
+                ? UNSUPPORTED_ON_DEVICE
                 : AVAILABLE;
     }
 
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 639c1fb..f78d236 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -512,37 +512,6 @@
     }
 
     /**
-     * Check if the app represented by {@code uid} has battery usage more than {@code threshold}
-     *
-     * @param batteryStatsHelper used to check the battery usage
-     * @param userManager        used to init the {@code batteryStatsHelper}
-     * @param uid                represent the app
-     * @param threshold          battery percentage threshold(e.g. 10 means 10% battery usage )
-     * @return {@code true} if battery drain is more than the threshold
-     */
-    public boolean isAppHeavilyUsed(BatteryStatsHelper batteryStatsHelper, UserManager userManager,
-            int uid, int threshold) {
-        initBatteryStatsHelper(batteryStatsHelper, null /* bundle */, userManager);
-        final int dischargeAmount = batteryStatsHelper.getStats().getDischargeAmount(
-                BatteryStats.STATS_SINCE_CHARGED);
-        List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList();
-        final double hiddenAmount = removeHiddenBatterySippers(batterySippers);
-
-        for (int i = 0, size = batterySippers.size(); i < size; i++) {
-            final BatterySipper batterySipper = batterySippers.get(i);
-            if (batterySipper.getUid() == uid) {
-                final int percent = (int) calculateBatteryPercent(
-                        batterySipper.totalPowerMah, batteryStatsHelper.getTotalPower(),
-                        hiddenAmount,
-                        dischargeAmount);
-                return percent >= threshold;
-            }
-        }
-
-        return false;
-    }
-
-    /**
      * Return {@code true} if we should hide anomaly app represented by {@code uid}
      */
     public boolean shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid) {
diff --git a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
index e222b91..3e80c35 100644
--- a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
@@ -45,7 +45,7 @@
     public int getAvailabilityStatus() {
         return mPowerUsageFeatureProvider.isSmartBatterySupported()
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
index bbeb435..2fac9b6 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
@@ -143,7 +143,6 @@
                 StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES);
         final AnomalyInfo anomalyInfo = new AnomalyInfo(
                 !ArrayUtils.isEmpty(cookies) ? cookies.get(0) : "");
-        final PackageManager packageManager = context.getPackageManager();
         Log.i(TAG, "Extra stats value: " + intentDimsValue.toString());
 
         try {
@@ -158,9 +157,7 @@
 
             final boolean anomalyDetected;
             if (isExcessiveBackgroundAnomaly(anomalyInfo)) {
-                anomalyDetected = batteryUtils.isPreOApp(packageName)
-                        && batteryUtils.isAppHeavilyUsed(batteryStatsHelper, userManager, uid,
-                        policy.excessiveBgDrainPercentage);
+                anomalyDetected = batteryUtils.isPreOApp(packageName);
             } else {
                 anomalyDetected = true;
             }
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index b9194b4..fc2bbdf 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -67,7 +67,7 @@
         final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(mBatteryStatsHelper, TAG);
         final Context context = getContext();
 
-        tips.add(new LowBatteryDetector(policy, batteryInfo).detect());
+        tips.add(new LowBatteryDetector(context, policy, batteryInfo).detect());
         tips.add(new HighUsageDetector(context, policy, mBatteryStatsHelper,
                 batteryInfo.discharging).detect());
         tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
@@ -87,7 +87,8 @@
         final List<BatteryTip> tips = new ArrayList<>();
         tips.add(new SummaryTip(BatteryTip.StateType.NEW,
                 Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN));
-        tips.add(new LowBatteryTip(BatteryTip.StateType.NEW));
+        tips.add(new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */,
+                "Fake data"));
 
         return tips;
     }
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
index 5520bf3..1c7c760 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -50,6 +50,7 @@
     private static final String KEY_TEST_BATTERY_SAVER_TIP = "test_battery_saver_tip";
     private static final String KEY_TEST_HIGH_USAGE_TIP = "test_high_usage_tip";
     private static final String KEY_TEST_SMART_BATTERY_TIP = "test_smart_battery_tip";
+    private static final String KEY_TEST_LOW_BATTERY_TIP = "test_low_battery_tip";
 
     /**
      * {@code true} if general battery tip is enabled
@@ -192,6 +193,14 @@
      */
     public final boolean testSmartBatteryTip;
 
+    /**
+     * {@code true} if we want to test low battery tip.
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_TEST_LOW_BATTERY_TIP
+     */
+    public final boolean testLowBatteryTip;
+
     private final KeyValueListParser mParser;
 
     public BatteryTipPolicy(Context context) {
@@ -222,13 +231,14 @@
         reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, false);
         reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50);
         lowBatteryEnabled = mParser.getBoolean(KEY_LOW_BATTERY_ENABLED, false);
-        lowBatteryHour = mParser.getInt(KEY_LOW_BATTERY_HOUR, 16);
+        lowBatteryHour = mParser.getInt(KEY_LOW_BATTERY_HOUR, 3);
         dataHistoryRetainDay = mParser.getInt(KEY_DATA_HISTORY_RETAIN_DAY, 30);
         excessiveBgDrainPercentage = mParser.getInt(KEY_EXCESSIVE_BG_DRAIN_PERCENTAGE, 10);
 
         testBatterySaverTip = mParser.getBoolean(KEY_TEST_BATTERY_SAVER_TIP, false);
         testHighUsageTip = mParser.getBoolean(KEY_TEST_HIGH_USAGE_TIP, false);
         testSmartBatteryTip = mParser.getBoolean(KEY_TEST_SMART_BATTERY_TIP, false);
+        testLowBatteryTip = mParser.getBoolean(KEY_TEST_LOW_BATTERY_TIP, false);
     }
 
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
index 2a6302e..c3f9b07 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
@@ -16,31 +16,52 @@
 
 package com.android.settings.fuelgauge.batterytip.detectors;
 
-import android.text.format.DateUtils;
+import android.content.Context;
+import android.os.PowerManager;
 
 import com.android.settings.fuelgauge.BatteryInfo;
 import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Detect whether the battery is too low
  */
 public class LowBatteryDetector implements BatteryTipDetector {
     private BatteryInfo mBatteryInfo;
     private BatteryTipPolicy mPolicy;
+    private PowerManager mPowerManager;
+    private int mWarningLevel;
 
-    public LowBatteryDetector(BatteryTipPolicy policy, BatteryInfo batteryInfo) {
+    public LowBatteryDetector(Context context, BatteryTipPolicy policy, BatteryInfo batteryInfo) {
         mPolicy = policy;
         mBatteryInfo = batteryInfo;
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWarningLevel = context.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
     }
 
     @Override
     public BatteryTip detect() {
-        // Show it if battery life is less than mPolicy.lowBatteryHour
-        final boolean isShown = mPolicy.lowBatteryEnabled && mBatteryInfo.discharging
-                && mBatteryInfo.remainingTimeUs < mPolicy.lowBatteryHour * DateUtils.HOUR_IN_MILLIS;
+        final boolean powerSaveModeOn = mPowerManager.isPowerSaveMode();
+        final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel
+                || (mBatteryInfo.discharging
+                && mBatteryInfo.remainingTimeUs < TimeUnit.HOURS.toMicros(mPolicy.lowBatteryHour));
+
+        int state = BatteryTip.StateType.INVISIBLE;
+        if (mPolicy.lowBatteryEnabled) {
+            if (powerSaveModeOn) {
+                // Show it is handled if battery saver is on
+                state = BatteryTip.StateType.HANDLED;
+            } else if (mPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery)) {
+                // Show it is new if in test or in discharging low battery state
+                state = BatteryTip.StateType.NEW;
+            }
+        }
+
         return new LowBatteryTip(
-                isShown ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE);
+                state, powerSaveModeOn, mBatteryInfo.remainingLabel);
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index f02dd72..2afdc81 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -74,10 +74,10 @@
         TIP_ORDER.append(TipType.APP_RESTRICTION, 0);
         TIP_ORDER.append(TipType.BATTERY_SAVER, 1);
         TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 2);
-        TIP_ORDER.append(TipType.SUMMARY, 3);
-        TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 4);
-        TIP_ORDER.append(TipType.REDUCED_BATTERY, 5);
-        TIP_ORDER.append(TipType.LOW_BATTERY, 6);
+        TIP_ORDER.append(TipType.LOW_BATTERY, 3);
+        TIP_ORDER.append(TipType.SUMMARY, 4);
+        TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 5);
+        TIP_ORDER.append(TipType.REDUCED_BATTERY, 6);
         TIP_ORDER.append(TipType.REMOVE_APP_RESTRICTION, 7);
     }
 
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
index 86237dd..b48a7dd 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
@@ -25,36 +25,32 @@
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
- * Tip to show current battery life is short
+ * Tip to show current battery level is low or remaining time is less than a certain period
  */
-public class LowBatteryTip extends BatteryTip {
+public class LowBatteryTip extends EarlyWarningTip {
+    private CharSequence mSummary;
 
-    public LowBatteryTip(@StateType int state) {
-        super(TipType.LOW_BATTERY, state, false /* showDialog */);
+    public LowBatteryTip(@StateType int state, boolean powerSaveModeOn, CharSequence summary) {
+        super(state, powerSaveModeOn);
+        mType = TipType.LOW_BATTERY;
+        mSummary = summary;
     }
 
-    private LowBatteryTip(Parcel in) {
+    public LowBatteryTip(Parcel in) {
         super(in);
-    }
-
-    @Override
-    public CharSequence getTitle(Context context) {
-        return context.getString(R.string.battery_tip_low_battery_title);
+        mSummary = in.readCharSequence();
     }
 
     @Override
     public CharSequence getSummary(Context context) {
-        return context.getString(R.string.battery_tip_low_battery_summary);
+        return mState == StateType.HANDLED ? context.getString(
+                R.string.battery_tip_early_heads_up_done_summary) : mSummary;
     }
 
     @Override
-    public int getIconId() {
-        return R.drawable.ic_perm_device_information_red_24dp;
-    }
-
-    @Override
-    public void updateState(BatteryTip tip) {
-        mState = tip.mState;
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeCharSequence(mSummary);
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java b/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java
index fd94f9f..8a8e099 100644
--- a/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java
+++ b/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java
@@ -63,7 +63,7 @@
     public int getAvailabilityStatus() {
         final boolean isAvailable = mAssistOnly ? mFeatureProvider.isSupported(mContext)
                 : mFeatureProvider.isSensorAvailable(mContext);
-        return isAvailable ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isAvailable ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/DoubleTapPowerPreferenceController.java b/src/com/android/settings/gestures/DoubleTapPowerPreferenceController.java
index aebda18..c400cbd 100644
--- a/src/com/android/settings/gestures/DoubleTapPowerPreferenceController.java
+++ b/src/com/android/settings/gestures/DoubleTapPowerPreferenceController.java
@@ -58,7 +58,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return isGestureAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java b/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java
index 00fb956..94ef978 100644
--- a/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java
+++ b/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java
@@ -74,7 +74,7 @@
         if (mAmbientConfig == null) {
             mAmbientConfig = new AmbientDisplayConfiguration(mContext);
         }
-        return mAmbientConfig.pulseOnDoubleTapAvailable() ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return mAmbientConfig.pulseOnDoubleTapAvailable() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/DoubleTwistPreferenceController.java b/src/com/android/settings/gestures/DoubleTwistPreferenceController.java
index f819508..2dd8635 100644
--- a/src/com/android/settings/gestures/DoubleTwistPreferenceController.java
+++ b/src/com/android/settings/gestures/DoubleTwistPreferenceController.java
@@ -68,7 +68,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return isGestureAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
index 1eb188a..0e00abb 100644
--- a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
+++ b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
@@ -51,7 +51,7 @@
         for (AbstractPreferenceController controller : mGestureControllers) {
             isAvailable = isAvailable || controller.isAvailable();
         }
-        return isAvailable ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isAvailable ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     /**
diff --git a/src/com/android/settings/gestures/PickupGesturePreferenceController.java b/src/com/android/settings/gestures/PickupGesturePreferenceController.java
index 46318f5..d50df19 100644
--- a/src/com/android/settings/gestures/PickupGesturePreferenceController.java
+++ b/src/com/android/settings/gestures/PickupGesturePreferenceController.java
@@ -68,7 +68,7 @@
         if (mAmbientConfig == null) {
             mAmbientConfig = new AmbientDisplayConfiguration(mContext);
         }
-        return mAmbientConfig.pulseOnPickupAvailable() ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return mAmbientConfig.pulseOnPickupAvailable() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/PreventRingingPreferenceController.java b/src/com/android/settings/gestures/PreventRingingPreferenceController.java
index 493755f..66aef60 100644
--- a/src/com/android/settings/gestures/PreventRingingPreferenceController.java
+++ b/src/com/android/settings/gestures/PreventRingingPreferenceController.java
@@ -61,7 +61,7 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_volumeHushGestureEnabled)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java b/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java
index d755d72..bb6a26d 100644
--- a/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java
+++ b/src/com/android/settings/gestures/SwipeToNotificationPreferenceController.java
@@ -56,7 +56,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return isAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/SwipeUpPreferenceController.java b/src/com/android/settings/gestures/SwipeUpPreferenceController.java
index c3abd46..27c7241 100644
--- a/src/com/android/settings/gestures/SwipeUpPreferenceController.java
+++ b/src/com/android/settings/gestures/SwipeUpPreferenceController.java
@@ -19,17 +19,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
 
 import com.android.settings.R;
-import com.android.settings.Utils;
 
 public class SwipeUpPreferenceController extends GesturePreferenceController {
 
@@ -46,6 +41,10 @@
     }
 
     static boolean isGestureAvailable(Context context) {
+        if (!context.getResources().getBoolean(R.bool.config_swipe_up_gesture_setting_available)) {
+            return false;
+        }
+
         final ComponentName recentsComponentName = ComponentName.unflattenFromString(
                 context.getString(com.android.internal.R.string.config_recentsComponentName));
         final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP)
@@ -59,7 +58,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return isGestureAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
@@ -81,8 +80,10 @@
 
     @Override
     public boolean isChecked() {
+        final int defaultValue = mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_default) ? ON : OFF;
         final int swipeUpEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, OFF);
+                Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, defaultValue);
         return swipeUpEnabled != OFF;
     }
 }
diff --git a/src/com/android/settings/inputmethod/GameControllerPreferenceController.java b/src/com/android/settings/inputmethod/GameControllerPreferenceController.java
index 85aef63..4fc29ec 100644
--- a/src/com/android/settings/inputmethod/GameControllerPreferenceController.java
+++ b/src/com/android/settings/inputmethod/GameControllerPreferenceController.java
@@ -64,7 +64,7 @@
     public int getAvailabilityStatus() {
         // If device explicitly wants to hide this, return early.
         if (!mContext.getResources().getBoolean(R.bool.config_show_vibrate_input_devices)) {
-            return DISABLED_UNSUPPORTED;
+            return UNSUPPORTED_ON_DEVICE;
         }
 
         final int[] devices = mIm.getInputDeviceIds();
@@ -74,7 +74,7 @@
                 return AVAILABLE;
             }
         }
-        return DISABLED_UNSUPPORTED;
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/language/PointerSpeedController.java b/src/com/android/settings/language/PointerSpeedController.java
index 857751c..c7275e1 100644
--- a/src/com/android/settings/language/PointerSpeedController.java
+++ b/src/com/android/settings/language/PointerSpeedController.java
@@ -36,6 +36,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_pointer_speed)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
diff --git a/src/com/android/settings/location/LocationScanningPreferenceController.java b/src/com/android/settings/location/LocationScanningPreferenceController.java
index ec487e4..6c63010 100644
--- a/src/com/android/settings/location/LocationScanningPreferenceController.java
+++ b/src/com/android/settings/location/LocationScanningPreferenceController.java
@@ -36,6 +36,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_location_scanning)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java
index 56c305d..b6246e3 100644
--- a/src/com/android/settings/network/AirplaneModePreferenceController.java
+++ b/src/com/android/settings/network/AirplaneModePreferenceController.java
@@ -93,7 +93,7 @@
     @Override
     @AvailabilityStatus
     public int getAvailabilityStatus() {
-        return isAvailable(mContext) ? AVAILABLE : DISABLED_UNSUPPORTED;
+        return isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/AlarmVolumePreferenceController.java b/src/com/android/settings/notification/AlarmVolumePreferenceController.java
index 0900e3c..3c238aa 100644
--- a/src/com/android/settings/notification/AlarmVolumePreferenceController.java
+++ b/src/com/android/settings/notification/AlarmVolumePreferenceController.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.media.AudioManager;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 
 public class AlarmVolumePreferenceController extends
@@ -34,7 +33,7 @@
     @Override
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_alarm_volume)
-                && !mHelper.isSingleVolume() ? AVAILABLE : DISABLED_UNSUPPORTED;
+                && !mHelper.isSingleVolume() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/BadgingNotificationPreferenceController.java b/src/com/android/settings/notification/BadgingNotificationPreferenceController.java
index 37e3db0..260a415 100644
--- a/src/com/android/settings/notification/BadgingNotificationPreferenceController.java
+++ b/src/com/android/settings/notification/BadgingNotificationPreferenceController.java
@@ -84,7 +84,7 @@
     public int getAvailabilityStatus() {
         return mContext.getResources()
                 .getBoolean(com.android.internal.R.bool.config_notificationBadging)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java
index 8f0a7f9..f3bffe0 100644
--- a/src/com/android/settings/notification/MediaVolumePreferenceController.java
+++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java
@@ -18,9 +18,8 @@
 
 import android.content.Context;
 import android.media.AudioManager;
-import com.android.settings.notification.VolumeSeekBarPreference.Callback;
+
 import com.android.settings.R;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 
 public class MediaVolumePreferenceController extends
     VolumeSeekBarPreferenceController {
@@ -35,7 +34,7 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_media_volume)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
index 8e7171d..c3c8793 100644
--- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java
+++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.media.AudioManager;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.Utils;
 
@@ -36,7 +35,7 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_notification_volume)
                 && !Utils.isVoiceCapable(mContext) && !mHelper.isSingleVolume()
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/RingVolumePreferenceController.java b/src/com/android/settings/notification/RingVolumePreferenceController.java
index e74b110..e328cd2 100644
--- a/src/com/android/settings/notification/RingVolumePreferenceController.java
+++ b/src/com/android/settings/notification/RingVolumePreferenceController.java
@@ -30,7 +30,6 @@
 import android.os.Message;
 import android.os.Vibrator;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -85,7 +84,7 @@
     @Override
     public int getAvailabilityStatus() {
         return Utils.isVoiceCapable(mContext) && !mHelper.isSingleVolume()
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/ZenModeBackend.java b/src/com/android/settings/notification/ZenModeBackend.java
index 3c6a026..d579256 100644
--- a/src/com/android/settings/notification/ZenModeBackend.java
+++ b/src/com/android/settings/notification/ZenModeBackend.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.notification;
 
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
+
 import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
@@ -146,19 +149,24 @@
     protected void savePolicy(int priorityCategories, int priorityCallSenders,
             int priorityMessageSenders, int suppressedVisualEffects) {
         mPolicy = new NotificationManager.Policy(priorityCategories, priorityCallSenders,
-                priorityMessageSenders,
-                suppressedVisualEffects);
+                priorityMessageSenders, suppressedVisualEffects);
         mNotificationManager.setNotificationPolicy(mPolicy);
     }
 
-    protected int getNewSuppressedEffects(boolean suppress, int effectType) {
+    private int getNewSuppressedEffects(boolean suppress, int effectType) {
         int effects = mPolicy.suppressedVisualEffects;
+
         if (suppress) {
             effects |= effectType;
         } else {
             effects &= ~effectType;
         }
-        return effects;
+
+        return clearDeprecatedEffects(effects);
+    }
+
+    private int clearDeprecatedEffects(int effects) {
+        return effects & ~(SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF);
     }
 
     protected boolean isEffectAllowed(int effect) {
diff --git a/src/com/android/settings/notification/ZenModeCallsSettings.java b/src/com/android/settings/notification/ZenModeCallsSettings.java
index 20396b5..cd56d7b 100644
--- a/src/com/android/settings/notification/ZenModeCallsSettings.java
+++ b/src/com/android/settings/notification/ZenModeCallsSettings.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification;
 
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+
 import android.content.Context;
 import android.provider.SearchIndexableResource;
 
@@ -40,8 +42,8 @@
             Lifecycle lifecycle) {
         List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new ZenModeCallsPreferenceController(context, lifecycle));
-        // TODO: is a controller needed for a pref that just launches an external activity?
-        // or can the contacts app insert this setting themselves?
+        controllers.add(new ZenModeStarredContactsPreferenceController(context, lifecycle,
+                PRIORITY_CATEGORY_CALLS));
         controllers.add(new ZenModeRepeatCallersPreferenceController(context, lifecycle,
                 context.getResources().getInteger(com.android.internal.R.integer
                 .config_zen_repeat_callers_threshold)));
diff --git a/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java b/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java
index c2508e3..95b9f7e 100644
--- a/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java
+++ b/src/com/android/settings/notification/ZenModeMsgEventReminderSettings.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification;
 
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+
 import android.content.Context;
 import android.provider.SearchIndexableResource;
 
@@ -42,6 +44,8 @@
         controllers.add(new ZenModeEventsPreferenceController(context, lifecycle));
         controllers.add(new ZenModeRemindersPreferenceController(context, lifecycle));
         controllers.add(new ZenModeMessagesPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeStarredContactsPreferenceController(context, lifecycle,
+                PRIORITY_CATEGORY_MESSAGES));
         controllers.add(new ZenModeBehaviorFooterPreferenceController(context, lifecycle,
                 R.string.zen_msg_event_reminder_footer));
         return controllers;
diff --git a/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java b/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java
index 307dfcf..14b82f5 100644
--- a/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java
+++ b/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java
@@ -68,7 +68,6 @@
             custom.displayPreference(getPreferenceScreen());
 
             if (mShowMenuSelected) {
-                custom.select();
                 metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, true);
             } else {
                 metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, false);
diff --git a/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java b/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java
new file mode 100644
index 0000000..1b8a3d5
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java
@@ -0,0 +1,151 @@
+/*
+ * 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.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.icu.text.ListFormatter;
+import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ZenModeStarredContactsPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceClickListener {
+
+    protected static final String KEY = "zen_mode_starred_contacts";
+    private Preference mPreference;
+    private final int mPriorityCategory;
+    private final PackageManager mPackageManager;
+
+    @VisibleForTesting
+    Intent mStarredContactsIntent;
+    @VisibleForTesting
+    Intent mFallbackIntent;
+
+    public ZenModeStarredContactsPreferenceController(Context context, Lifecycle lifecycle, int
+            priorityCategory) {
+        super(context, KEY, lifecycle);
+        mPriorityCategory = priorityCategory;
+        mPackageManager = mContext.getPackageManager();
+
+        mStarredContactsIntent = new Intent(Contacts.Intents.UI.LIST_STARRED_ACTION);
+
+        mFallbackIntent =  new Intent(Intent.ACTION_MAIN);
+        mFallbackIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(KEY);
+        mPreference.setOnPreferenceClickListener(this);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (mPriorityCategory == PRIORITY_CATEGORY_CALLS) {
+            return mBackend.isPriorityCategoryEnabled(PRIORITY_CATEGORY_CALLS)
+                    && mBackend.getPriorityCallSenders() == PRIORITY_SENDERS_STARRED
+                    && isIntentValid();
+        } else if (mPriorityCategory == PRIORITY_CATEGORY_MESSAGES) {
+            return mBackend.isPriorityCategoryEnabled(PRIORITY_CATEGORY_MESSAGES)
+                    && mBackend.getPriorityMessageSenders() == PRIORITY_SENDERS_STARRED
+                    && isIntentValid();
+        } else {
+            // invalid category
+            return false;
+        }
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        List<String> starredContacts = getStarredContacts();
+        int numStarredContacts = starredContacts.size();
+
+        List<String> displayContacts = new ArrayList<>();
+
+        if (numStarredContacts == 0) {
+            displayContacts.add(mContext.getString(R.string.zen_mode_from_none));
+        } else {
+            for (int i = 0; i < 2 && i < numStarredContacts; i++) {
+                displayContacts.add(starredContacts.get(i));
+            }
+
+            if (numStarredContacts == 3) {
+                displayContacts.add(starredContacts.get(2));
+            } else if (numStarredContacts > 2) {
+                displayContacts.add(mContext.getResources().getQuantityString(
+                        R.plurals.zen_mode_starred_contacts_summary_additional_contacts,
+                        numStarredContacts - 2, numStarredContacts - 2));
+            }
+        }
+
+        mPreference.setSummary(ListFormatter.getInstance().format(displayContacts));
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        if (mStarredContactsIntent.resolveActivity(mPackageManager) != null) {
+            mContext.startActivity(mStarredContactsIntent);
+        } else {
+            mContext.startActivity(mFallbackIntent);
+        }
+        return true;
+    }
+
+    private List<String> getStarredContacts() {
+        List<String> starredContacts = new ArrayList<>();
+
+        Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+                new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY},
+                ContactsContract.Data.STARRED + "=1", null,
+                ContactsContract.Data.TIMES_CONTACTED);
+
+        if (cursor.moveToFirst()) {
+            do {
+                starredContacts.add(cursor.getString(0));
+            } while (cursor.moveToNext());
+        }
+        return starredContacts;
+    }
+
+    private boolean isIntentValid() {
+        return mStarredContactsIntent.resolveActivity(mPackageManager) != null
+                || mFallbackIntent.resolveActivity(mPackageManager) != null;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java b/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java
index fd235e6..83ab037 100644
--- a/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java
+++ b/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java
@@ -19,7 +19,6 @@
 import android.app.NotificationManager.Policy;
 import android.content.Context;
 import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
@@ -57,15 +56,11 @@
         pref.setChecked(areCustomOptionsSelected());
 
         pref.setOnGearClickListener(p -> {
-            new SubSettingLauncher(mContext)
-                    .setDestination(ZenModeBlockedEffectsSettings.class.getName())
-                    .setTitle(R.string.zen_mode_what_to_block_title)
-                    .setSourceMetricsCategory(MetricsProto.MetricsEvent.SETTINGS_ZEN_NOTIFICATIONS)
-                    .launch();
+            launchCustomSettings();
         });
 
         pref.setOnRadioButtonClickListener(p -> {
-            select();
+            launchCustomSettings();
         });
     }
 
@@ -84,9 +79,14 @@
     protected void select() {
         mMetricsFeatureProvider.action(mContext,
                 MetricsProto.MetricsEvent.ACTION_ZEN_CUSTOM, true);
-        mBackend.savePolicy(mBackend.mPolicy.priorityCategories,
-                mBackend.mPolicy.priorityCallSenders,
-                mBackend.mPolicy.priorityMessageSenders,
-                INTERRUPTIVE_EFFECTS);
+    }
+
+    private void launchCustomSettings() {
+        select();
+        new SubSettingLauncher(mContext)
+                .setDestination(ZenModeBlockedEffectsSettings.class.getName())
+                .setTitle(R.string.zen_mode_what_to_block_title)
+                .setSourceMetricsCategory(MetricsProto.MetricsEvent.SETTINGS_ZEN_NOTIFICATIONS)
+                .launch();
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/print/PrintSettingPreferenceController.java b/src/com/android/settings/print/PrintSettingPreferenceController.java
index 14eb907..2307b2c 100644
--- a/src/com/android/settings/print/PrintSettingPreferenceController.java
+++ b/src/com/android/settings/print/PrintSettingPreferenceController.java
@@ -59,7 +59,7 @@
     @Override
     public int getAvailabilityStatus() {
         return mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/security/EncryptionStatusPreferenceController.java b/src/com/android/settings/security/EncryptionStatusPreferenceController.java
index 8125599..0ecab36 100644
--- a/src/com/android/settings/security/EncryptionStatusPreferenceController.java
+++ b/src/com/android/settings/security/EncryptionStatusPreferenceController.java
@@ -44,7 +44,7 @@
         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 UNSUPPORTED_ON_DEVICE;
         }
 
         return mUserManager.isAdminUser() ? AVAILABLE : DISABLED_FOR_USER;
diff --git a/src/com/android/settings/security/ScreenPinningPreferenceController.java b/src/com/android/settings/security/ScreenPinningPreferenceController.java
index a90746e..37a3f9c 100644
--- a/src/com/android/settings/security/ScreenPinningPreferenceController.java
+++ b/src/com/android/settings/security/ScreenPinningPreferenceController.java
@@ -33,7 +33,7 @@
     @Override
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_screen_pinning_settings)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/security/ShowPasswordPreferenceController.java b/src/com/android/settings/security/ShowPasswordPreferenceController.java
index 82f1935..8672974 100644
--- a/src/com/android/settings/security/ShowPasswordPreferenceController.java
+++ b/src/com/android/settings/security/ShowPasswordPreferenceController.java
@@ -55,7 +55,7 @@
     @Override
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_show_password)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
 }
diff --git a/src/com/android/settings/security/trustagent/ManageTrustAgentsPreferenceController.java b/src/com/android/settings/security/trustagent/ManageTrustAgentsPreferenceController.java
index 3e39860..322682e 100644
--- a/src/com/android/settings/security/trustagent/ManageTrustAgentsPreferenceController.java
+++ b/src/com/android/settings/security/trustagent/ManageTrustAgentsPreferenceController.java
@@ -47,7 +47,7 @@
     @Override
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_manage_trust_agents)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 55ad6b0..6d6c0ec 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -32,7 +32,6 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.graphics.drawable.IconCompat;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 
@@ -42,10 +41,10 @@
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 import androidx.slice.Slice;
 import androidx.slice.SliceProvider;
@@ -117,6 +116,7 @@
 
     @VisibleForTesting
     Map<Uri, SliceData> mSliceWeakDataCache;
+    @VisibleForTesting
     Map<Uri, SliceData> mSliceDataCache;
 
     public SettingsSliceProvider() {
@@ -126,7 +126,7 @@
     @Override
     public boolean onCreateSliceProvider() {
         mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
-        mSliceDataCache = new ArrayMap<>();
+        mSliceDataCache = new ConcurrentHashMap<>();
         mSliceWeakDataCache = new WeakHashMap<>();
         return true;
     }
diff --git a/src/com/android/settings/slices/SliceBuilderUtils.java b/src/com/android/settings/slices/SliceBuilderUtils.java
index 0a4d51b..be2e8df 100644
--- a/src/com/android/settings/slices/SliceBuilderUtils.java
+++ b/src/com/android/settings/slices/SliceBuilderUtils.java
@@ -17,10 +17,10 @@
 package com.android.settings.slices;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
 import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
-import static com.android.settings.core.BasePreferenceController.UNAVAILABLE_UNKNOWN;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY;
 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED;
 
@@ -238,7 +238,7 @@
                 (TogglePreferenceController) controller;
         final SliceAction sliceAction = getToggleAction(context, sliceData,
                 toggleController.isChecked());
-        final List<String> keywords = buildSliceKeywords(sliceData.getKeywords());
+        final List<String> keywords = buildSliceKeywords(sliceData);
 
         return new ListBuilder(context, sliceData.getUri(), SLICE_TTL_MILLIS)
                 .addRow(rowBuilder -> rowBuilder
@@ -256,7 +256,7 @@
         final PendingIntent contentIntent = getContentPendingIntent(context, sliceData);
         final IconCompat icon = IconCompat.createWithResource(context, sliceData.getIconResource());
         final CharSequence subtitleText = getSubtitleText(context, controller, sliceData);
-        final List<String> keywords = buildSliceKeywords(sliceData.getKeywords());
+        final List<String> keywords = buildSliceKeywords(sliceData);
 
         return new ListBuilder(context, sliceData.getUri(), SLICE_TTL_MILLIS)
                 .addRow(rowBuilder -> rowBuilder
@@ -276,7 +276,7 @@
         final IconCompat icon = IconCompat.createWithResource(context, sliceData.getIconResource());
         final SliceAction primaryAction = new SliceAction(contentIntent, icon,
                 sliceData.getTitle());
-        final List<String> keywords = buildSliceKeywords(sliceData.getKeywords());
+        final List<String> keywords = buildSliceKeywords(sliceData);
 
         return new ListBuilder(context, sliceData.getUri(), SLICE_TTL_MILLIS)
                 .addInputRange(builder -> builder
@@ -324,25 +324,34 @@
                 || TextUtils.equals(summary, doublePlaceHolder));
     }
 
-    private static List<String> buildSliceKeywords(String keywordString) {
-        if (keywordString == null) {
-            return new ArrayList<>();
+    private static List<String> buildSliceKeywords(SliceData data) {
+        final List<String> keywords = new ArrayList<>();
+
+        keywords.add(data.getTitle());
+
+        if (!TextUtils.equals(data.getTitle(), data.getScreenTitle())) {
+            keywords.add(data.getScreenTitle().toString());
         }
 
-        final String[] keywords = keywordString.split(",");
-        return Arrays.asList(keywords);
+        final String keywordString = data.getKeywords();
+        if (keywordString != null) {
+            final String[] keywordArray = keywordString.split(",");
+            keywords.addAll(Arrays.asList(keywordArray));
+        }
+
+        return keywords;
     }
 
     private static Slice buildUnavailableSlice(Context context, SliceData data,
             BasePreferenceController controller) {
         final String title = data.getTitle();
-        final List<String> keywords = buildSliceKeywords(data.getKeywords());
+        final List<String> keywords = buildSliceKeywords(data);
         final String summary;
         final SliceAction primaryAction;
         final IconCompat icon = IconCompat.createWithResource(context, data.getIconResource());
 
         switch (controller.getAvailabilityStatus()) {
-            case DISABLED_UNSUPPORTED:
+            case UNSUPPORTED_ON_DEVICE:
                 summary = context.getString(R.string.unsupported_setting_summary);
                 primaryAction = new SliceAction(getSettingsIntent(context), icon, title);
                 break;
@@ -356,7 +365,7 @@
                 primaryAction = new SliceAction(getContentPendingIntent(context, data), icon,
                         title);
                 break;
-            case UNAVAILABLE_UNKNOWN:
+            case CONDITIONALLY_UNAVAILABLE:
             default:
                 summary = context.getString(R.string.unknown_unavailability_setting_summary);
                 primaryAction = new SliceAction(getSettingsIntent(context), icon, title);
diff --git a/src/com/android/settings/slices/SliceDataConverter.java b/src/com/android/settings/slices/SliceDataConverter.java
index eb6a44f..7bd66ba 100644
--- a/src/com/android/settings/slices/SliceDataConverter.java
+++ b/src/com/android/settings/slices/SliceDataConverter.java
@@ -185,7 +185,6 @@
                             | MetadataFlag.FLAG_NEED_PREF_TITLE
                             | MetadataFlag.FLAG_NEED_PREF_ICON
                             | MetadataFlag.FLAG_NEED_PREF_SUMMARY
-                            | MetadataFlag.FLAG_NEED_KEYWORDS
                             | MetadataFlag.FLAG_NEED_PLATFORM_SLICE_FLAG);
 
             for (Bundle bundle : metadata) {
@@ -198,7 +197,6 @@
                 final String key = bundle.getString(METADATA_KEY);
                 final String title = bundle.getString(METADATA_TITLE);
                 final String summary = bundle.getString(METADATA_SUMMARY);
-                final String keywords = bundle.getString(METADATA_KEYWORDS);
                 final int iconResId = bundle.getInt(METADATA_ICON);
                 final int sliceType = SliceBuilderUtils.getSliceType(mContext, controllerClassName,
                         key);
@@ -210,7 +208,6 @@
                         .setSummary(summary)
                         .setIcon(iconResId)
                         .setScreenTitle(screenTitle)
-                        .setKeywords(keywords)
                         .setPreferenceControllerClassName(controllerClassName)
                         .setFragmentName(fragmentName)
                         .setSliceType(sliceType)
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index 957cebf..81479c9 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -16,8 +16,12 @@
 
 package com.android.settings.sound;
 
-
 import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
 import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
 
 import android.bluetooth.BluetoothDevice;
@@ -38,19 +42,22 @@
 import android.text.TextUtils;
 import android.util.FeatureFlagUtils;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.settings.R;
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.FeatureFlags;
+import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -65,12 +72,12 @@
 
     private static final int INVALID_INDEX = -1;
 
+    protected final List<BluetoothDevice> mConnectedDevices;
     protected final AudioManager mAudioManager;
     protected final MediaRouter mMediaRouter;
     protected final LocalBluetoothProfileManager mProfileManager;
     protected int mSelectedIndex;
     protected Preference mPreference;
-    protected List<BluetoothDevice> mConnectedDevices;
 
     private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
     private final LocalBluetoothManager mLocalBluetoothManager;
@@ -89,6 +96,7 @@
         mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback();
         mReceiver = new WiredHeadsetBroadcastReceiver();
         mMediaRouterCallback = new MediaRouterCallback();
+        mConnectedDevices = new ArrayList<>();
     }
 
     /**
@@ -98,7 +106,7 @@
     @Override
     public final int getAvailabilityStatus() {
         return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.AUDIO_SWITCHER_SETTINGS)
-                ? AVAILABLE : DISABLED_UNSUPPORTED;
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
@@ -195,12 +203,105 @@
     }
 
     protected boolean isStreamFromOutputDevice(int streamType, int device) {
-        return mAudioManager.getDevicesForStream(streamType) == device;
+        return (device & mAudioManager.getDevicesForStream(streamType)) != 0;
+    }
+
+    /**
+     * get hands free profile(HFP) connected device
+     */
+    protected List<BluetoothDevice> getConnectedHfpDevices() {
+        final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+        final HeadsetProfile hfpProfile = mProfileManager.getHeadsetProfile();
+        if (hfpProfile == null) {
+            return connectedDevices;
+        }
+        final List<BluetoothDevice> devices = hfpProfile.getConnectedDevices();
+        for (BluetoothDevice device : devices) {
+            if (device.isConnected()) {
+                connectedDevices.add(device);
+            }
+        }
+        return connectedDevices;
+    }
+
+    /**
+     * get A2dp connected device
+     */
+    protected List<BluetoothDevice> getConnectedA2dpDevices() {
+        final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+        final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (a2dpProfile == null) {
+            return connectedDevices;
+        }
+        final List<BluetoothDevice> devices = a2dpProfile.getConnectedDevices();
+        for (BluetoothDevice device : devices) {
+            if (device.isConnected()) {
+                connectedDevices.add(device);
+            }
+        }
+        return connectedDevices;
+    }
+
+    /**
+     * get hearing aid profile connected device, exclude other devices with same hiSyncId.
+     */
+    protected List<BluetoothDevice> getConnectedHearingAidDevices() {
+        final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+        final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+        if (hapProfile == null) {
+            return connectedDevices;
+        }
+        final List<Long> devicesHiSyncIds = new ArrayList<>();
+        final List<BluetoothDevice> devices = hapProfile.getConnectedDevices();
+        for (BluetoothDevice device : devices) {
+            final long hiSyncId = hapProfile.getHiSyncId(device);
+            // device with same hiSyncId should not be shown in the UI.
+            // So do not add it into connectedDevices.
+            if (!devicesHiSyncIds.contains(hiSyncId) && device.isConnected()) {
+                devicesHiSyncIds.add(hiSyncId);
+                connectedDevices.add(device);
+            }
+        }
+        return connectedDevices;
+    }
+
+    /**
+     * According to different stream and output device, find the active device from
+     * the corresponding profile. Hearing aid device could stream both STREAM_MUSIC
+     * and STREAM_VOICE_CALL.
+     *
+     * @param streamType the type of audio streams.
+     * @return the active device. Return null if the active device is current device
+     * or streamType is not STREAM_MUSIC or STREAM_VOICE_CALL.
+     */
+    protected BluetoothDevice findActiveDevice(int streamType) {
+        if (streamType != STREAM_MUSIC && streamType != STREAM_VOICE_CALL) {
+            return null;
+        }
+        if (isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_ALL_A2DP)) {
+            return mProfileManager.getA2dpProfile().getActiveDevice();
+        } else if (isStreamFromOutputDevice(STREAM_VOICE_CALL, DEVICE_OUT_ALL_SCO)) {
+            return mProfileManager.getHeadsetProfile().getActiveDevice();
+        } else if (isStreamFromOutputDevice(streamType, DEVICE_OUT_HEARING_AID)) {
+            // The first element is the left active device; the second element is
+            // the right active device. And they will have same hiSyncId. If either
+            // or both side is not active, it will be null on that position.
+            List<BluetoothDevice> activeDevices =
+                    mProfileManager.getHearingAidProfile().getActiveDevices();
+            for (BluetoothDevice btDevice : activeDevices) {
+                if (btDevice != null && mConnectedDevices.contains(btDevice)) {
+                    // also need to check mConnectedDevices, because one of
+                    // the device(same hiSyncId) might not be shown in the UI.
+                    return btDevice;
+                }
+            }
+        }
+        return null;
     }
 
     int getDefaultDeviceIndex() {
         // Default device is after all connected devices.
-        return ArrayUtils.size(mConnectedDevices);
+        return mConnectedDevices.size();
     }
 
     void setupPreferenceEntries(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
@@ -261,7 +362,7 @@
         mContext.unregisterReceiver(mReceiver);
     }
 
-    /** Callback for headset plugged and unplugged events. */
+    /** Notifications of audio device connection and disconnection events. */
     private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback {
         @Override
         public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
diff --git a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
index 6907a4a..2039913 100644
--- a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
+++ b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.sound;
 
+import static android.bluetooth.IBluetoothHearingAid.HI_SYNC_ID_INVALID;
 import static android.media.AudioManager.STREAM_VOICE_CALL;
 import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
 
@@ -25,12 +26,12 @@
 import android.content.Context;
 import android.support.v7.preference.Preference;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.settings.R;
 import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
 
 /**
- * This class allows switching between HFP-connected BT devices
+ * This class allows switching between HFP-connected & HAP-connected BT devices
  * while in on-call state.
  */
 public class HandsFreeProfileOutputPreferenceController extends
@@ -57,16 +58,11 @@
         // Ongoing call status, list all the connected devices support hands free profile.
         // Select current active device.
         // Disable switch entry if there is no connected device.
-        mConnectedDevices = null;
-        BluetoothDevice activeDevice = null;
+        mConnectedDevices.clear();
+        mConnectedDevices.addAll(getConnectedHfpDevices());
+        mConnectedDevices.addAll(getConnectedHearingAidDevices());
 
-        final HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
-        if (headsetProfile != null) {
-            mConnectedDevices = headsetProfile.getConnectedDevices();
-            activeDevice = headsetProfile.getActiveDevice();
-        }
-
-        final int numDevices = ArrayUtils.size(mConnectedDevices);
+        final int numDevices = mConnectedDevices.size();
         if (numDevices == 0) {
             // No connected devices, disable switch entry.
             mPreference.setVisible(false);
@@ -79,7 +75,7 @@
         CharSequence[] mediaValues = new CharSequence[numDevices + 1];
 
         // Setup devices entries, select active connected device
-        setupPreferenceEntries(mediaOutputs, mediaValues, activeDevice);
+        setupPreferenceEntries(mediaOutputs, mediaValues, findActiveDevice(STREAM_VOICE_CALL));
 
         if (isStreamFromOutputDevice(STREAM_VOICE_CALL, DEVICE_OUT_USB_HEADSET)) {
             // If wired headset is plugged in and active, select to default device.
@@ -92,8 +88,21 @@
 
     @Override
     public void setActiveBluetoothDevice(BluetoothDevice device) {
-        if (Utils.isAudioModeOngoingCall(mContext)) {
-            mProfileManager.getHeadsetProfile().setActiveDevice(device);
+        if (!Utils.isAudioModeOngoingCall(mContext)) {
+            return;
+        }
+        final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+        final HeadsetProfile hfpProfile = mProfileManager.getHeadsetProfile();
+        if (hapProfile != null && hfpProfile != null && device == null) {
+            hfpProfile.setActiveDevice(null);
+            hapProfile.setActiveDevice(null);
+            return;
+        }
+        if (hapProfile != null && hapProfile.getHiSyncId(device) != HI_SYNC_ID_INVALID) {
+            hapProfile.setActiveDevice(device);
+        }
+        if (hfpProfile != null) {
+            hfpProfile.setActiveDevice(device);
         }
     }
 }
diff --git a/src/com/android/settings/sound/MediaOutputPreferenceController.java b/src/com/android/settings/sound/MediaOutputPreferenceController.java
index d0677a7..79f3c9d 100644
--- a/src/com/android/settings/sound/MediaOutputPreferenceController.java
+++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.sound;
 
+import static android.bluetooth.IBluetoothHearingAid.HI_SYNC_ID_INVALID;
 import static android.media.AudioManager.STREAM_MUSIC;
 import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
 import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
@@ -27,13 +28,12 @@
 import android.media.AudioManager;
 import android.support.v7.preference.Preference;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.settings.R;
 import com.android.settingslib.bluetooth.A2dpProfile;
-
+import com.android.settingslib.bluetooth.HearingAidProfile;
 
 /**
- * This class which allows switching between a2dp-connected BT devices.
+ * This class which allows switching between A2dp-connected & HAP-connected BT devices.
  * A few conditions will disable this switcher:
  * - No available BT device(s)
  * - Media stream captured by cast device
@@ -67,18 +67,14 @@
             return;
         }
 
+        mConnectedDevices.clear();
         // Otherwise, list all of the A2DP connected device and display the active device.
-        mConnectedDevices = null;
-        BluetoothDevice activeDevice = null;
         if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
-            final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
-            if (a2dpProfile != null) {
-                mConnectedDevices = a2dpProfile.getConnectedDevices();
-                activeDevice = a2dpProfile.getActiveDevice();
-            }
+            mConnectedDevices.addAll(getConnectedA2dpDevices());
+            mConnectedDevices.addAll(getConnectedHearingAidDevices());
         }
 
-        final int numDevices = ArrayUtils.size(mConnectedDevices);
+        final int numDevices = mConnectedDevices.size();
         if (numDevices == 0) {
             // Disable switch entry if there is no connected devices.
             mPreference.setVisible(false);
@@ -91,7 +87,7 @@
         CharSequence[] mediaValues = new CharSequence[numDevices + 1];
 
         // Setup devices entries, select active connected device
-        setupPreferenceEntries(mediaOutputs, mediaValues, activeDevice);
+        setupPreferenceEntries(mediaOutputs, mediaValues, findActiveDevice(STREAM_MUSIC));
 
         if (isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_USB_HEADSET)) {
             // If wired headset is plugged in and active, select to default device.
@@ -104,8 +100,21 @@
 
     @Override
     public void setActiveBluetoothDevice(BluetoothDevice device) {
-        if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
-            mProfileManager.getA2dpProfile().setActiveDevice(device);
+        if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
+            return;
+        }
+        final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+        final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (hapProfile != null && a2dpProfile != null && device == null) {
+            hapProfile.setActiveDevice(null);
+            a2dpProfile.setActiveDevice(null);
+            return;
+        }
+        if (hapProfile != null && hapProfile.getHiSyncId(device) != HI_SYNC_ID_INVALID) {
+            hapProfile.setActiveDevice(device);
+        }
+        if (a2dpProfile != null) {
+            a2dpProfile.setActiveDevice(device);
         }
     }
 }
diff --git a/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java b/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java
index 1fbf835..868f10f 100644
--- a/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java
+++ b/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java
@@ -32,6 +32,6 @@
         return mContext.getResources().getBoolean(
                 com.android.settings.R.bool.config_additional_system_update_setting_enable)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/system/ResetPreferenceController.java b/src/com/android/settings/system/ResetPreferenceController.java
index 16f7ce3..ec0c27b 100644
--- a/src/com/android/settings/system/ResetPreferenceController.java
+++ b/src/com/android/settings/system/ResetPreferenceController.java
@@ -30,6 +30,6 @@
     public int getAvailabilityStatus() {
         return mContext.getResources().getBoolean(R.bool.config_show_reset_dashboard)
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 }
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.java b/src/com/android/settings/system/SystemUpdatePreferenceController.java
index 80d4ecb..7bc76a2 100644
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.java
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.java
@@ -55,7 +55,7 @@
         return mContext.getResources().getBoolean(R.bool.config_show_system_update_settings)
                 && mUm.isAdminUser()
                 ? AVAILABLE
-                : DISABLED_UNSUPPORTED;
+                : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/widget/PreferenceCategoryController.java b/src/com/android/settings/widget/PreferenceCategoryController.java
index c6477f3..7b52be0 100644
--- a/src/com/android/settings/widget/PreferenceCategoryController.java
+++ b/src/com/android/settings/widget/PreferenceCategoryController.java
@@ -43,7 +43,7 @@
     @Override
     public int getAvailabilityStatus() {
         if (mChildren == null || mChildren.isEmpty()) {
-            return DISABLED_UNSUPPORTED;
+            return UNSUPPORTED_ON_DEVICE;
         }
         // Category is available if any child is available
         for (AbstractPreferenceController controller : mChildren) {
@@ -51,7 +51,7 @@
                 return AVAILABLE;
             }
         }
-        return DISABLED_UNSUPPORTED;
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index e995c31..79196f1 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -220,6 +220,7 @@
         mMeteredSettingsSpinner = mView.findViewById(R.id.metered_settings);
         mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
         mHiddenSettingsSpinner.setOnItemSelectedListener(this);
+        mHiddenSettingsSpinner.setVisibility(View.GONE);
         mHiddenSettingsSpinner.setEnabled(false);
         mHiddenWarningView = mView.findViewById(R.id.hidden_settings_warning);
         mHiddenWarningView.setVisibility(
@@ -240,6 +241,7 @@
             showProxyFields();
             mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
             // Hidden option can be changed only when the user adds a network manually.
+            mHiddenSettingsSpinner.setVisibility(View.VISIBLE);
             mHiddenSettingsSpinner.setEnabled(true);
             ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox))
                     .setOnCheckedChangeListener(this);
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySlicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySlicePreferenceControllerTest.java
index fe6d1a3..8392412 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySlicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySlicePreferenceControllerTest.java
@@ -17,7 +17,7 @@
 package com.android.settings.accessibility;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -30,7 +30,6 @@
 import android.provider.Settings;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.settings.accessibility.AccessibilitySlicePreferenceController;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settingslib.accessibility.AccessibilityUtils;
 
@@ -85,7 +84,7 @@
         AccessibilitySlicePreferenceController controller =
                 new AccessibilitySlicePreferenceController(mContext, "fake_service/name");
 
-        assertThat(controller.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java
index f87be57..72893b7 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java
@@ -80,7 +80,7 @@
         doReturn(false).when(mController).isBandwidthControlEnabled();
 
         assertThat(mController.getAvailabilityStatus())
-            .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+            .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java
index 64ad32b..5298fcd 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java
@@ -91,7 +91,7 @@
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
 
         assertThat(mController.getAvailabilityStatus())
-            .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+            .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
@@ -101,7 +101,7 @@
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
 
         assertThat(mController.getAvailabilityStatus())
-            .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+            .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
@@ -110,7 +110,7 @@
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
 
         assertThat(mController.getAvailabilityStatus())
-                .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+                .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java
index e8e4032..97007f8 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications.appinfo;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -84,7 +85,8 @@
         mController.capable = false;
         when(mUserManager.isManagedProfile()).thenReturn(false);
 
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                mController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
@@ -110,9 +112,9 @@
     public void handlePreferenceTreeClick_shouldStartDefaultAppSettings() {
         mController.handlePreferenceTreeClick(mPreference);
 
-        verify(mContext).startActivity(argThat(intent-> intent != null
+        verify(mContext).startActivity(argThat(intent -> intent != null
                 && intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT).equals(
-                        DefaultAppSettings.class.getName())
+                DefaultAppSettings.class.getName())
                 && intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
                 .getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY).equals("TestKey")));
     }
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java
index 0a54b82..e6cc831 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java
@@ -71,7 +71,7 @@
         mController.setPackageName(null);
 
         assertThat(mController.getAvailabilityStatus())
-                .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+                .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
@@ -79,7 +79,7 @@
         mController.setPackageName(TEST_INTENT.getStringExtra(EXTRA_PACKAGE_NAME));
 
         assertThat(mController.getAvailabilityStatus())
-                .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+                .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
@@ -88,7 +88,7 @@
         mController.setPackageName(TEST_INTENT.getStringExtra(EXTRA_PACKAGE_NAME));
 
         assertThat(mController.getAvailabilityStatus())
-                .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+                .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
 
     }
 
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java
index 15872d1..ecf024b 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java
@@ -16,7 +16,7 @@
 package com.android.settings.connecteddevice;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -121,7 +121,7 @@
     public void addDevice_Availability_UnSupported() {
         mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
         assertThat(mAddDevicePreferenceController.getAvailabilityStatus())
-                .isEqualTo(DISABLED_UNSUPPORTED);
+                .isEqualTo(UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java
index 93a0411..aeab451 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupControllerTest.java
@@ -16,7 +16,7 @@
 package com.android.settings.connecteddevice;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.any;
@@ -169,7 +169,7 @@
         doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
 
         assertThat(mAvailableMediaDeviceGroupController.getAvailabilityStatus()).isEqualTo(
-                DISABLED_UNSUPPORTED);
+                UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceControllerTest.java
index 12fca1e..83763bb 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothOnWhileDrivingPreferenceControllerTest.java
@@ -54,7 +54,7 @@
     @Test
     public void getAvailabilityStatus_offWhenDisabled() {
         assertThat(mController.getAvailabilityStatus())
-            .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED);
+            .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
index 3039895..718acb9 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
@@ -16,7 +16,7 @@
 package com.android.settings.connecteddevice;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -39,7 +39,6 @@
 import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
 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;
@@ -157,7 +156,7 @@
         mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
 
         assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
-                DISABLED_UNSUPPORTED);
+                UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
index 71c1010..2e40bf5 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/SavedDeviceGroupControllerTest.java
@@ -16,7 +16,7 @@
 package com.android.settings.connecteddevice;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
@@ -91,7 +91,7 @@
     public void testGetAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
         doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
         assertThat(mSavedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
-                DISABLED_UNSUPPORTED);
+                UNSUPPORTED_ON_DEVICE);
     }
     @Test
     public void testGetAvailabilityStatus_BluetoothFeature_returnSupported() {
diff --git a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java
index 3074d9e..eec4e37 100644
--- a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java
@@ -16,10 +16,10 @@
 package com.android.settings.core;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
 import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
-import static com.android.settings.core.BasePreferenceController.UNAVAILABLE_UNKNOWN;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyString;
@@ -73,8 +73,16 @@
     }
 
     @Test
-    public void isAvailable_availableStatusUnsupported_returnsFalse() {
-        mPreferenceController.setAvailability(DISABLED_UNSUPPORTED);
+    public void isAvailable_availableStatusUnsupportedOnDevice_returnsFalse() {
+        mPreferenceController.setAvailability(UNSUPPORTED_ON_DEVICE);
+
+        assertThat(mPreferenceController.isAvailable()).isFalse();
+    }
+
+
+    @Test
+    public void isAvailable_availableStatusConditionallyUnavailable_returnsFalse() {
+        mPreferenceController.setAvailability(CONDITIONALLY_UNAVAILABLE);
 
         assertThat(mPreferenceController.isAvailable()).isFalse();
     }
@@ -94,13 +102,6 @@
     }
 
     @Test
-    public void isAvailable_availableStatusUnavailable_returnsFalse() {
-        mPreferenceController.setAvailability(UNAVAILABLE_UNKNOWN);
-
-        assertThat(mPreferenceController.isAvailable()).isFalse();
-    }
-
-    @Test
     public void isSupported_availableStatusAvailable_returnsTrue() {
         mPreferenceController.setAvailability(AVAILABLE);
 
@@ -109,7 +110,7 @@
 
     @Test
     public void isSupported_availableStatusUnsupported_returnsFalse() {
-        mPreferenceController.setAvailability(DISABLED_UNSUPPORTED);
+        mPreferenceController.setAvailability(UNSUPPORTED_ON_DEVICE);
 
         assertThat(mPreferenceController.isSupported()).isFalse();
     }
@@ -129,13 +130,6 @@
     }
 
     @Test
-    public void isSupported_availableStatusUnavailable_returnsTrue() {
-        mPreferenceController.setAvailability(UNAVAILABLE_UNKNOWN);
-
-        assertThat(mPreferenceController.isSupported()).isTrue();
-    }
-
-    @Test
     public void getSliceType_shouldReturnIntent() {
         assertThat(mPreferenceController.getSliceType()).isEqualTo(SliceData.SliceType.INTENT);
     }
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
index 8682ac1..9e5a2b7 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -18,7 +18,8 @@
 
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
@@ -342,7 +343,7 @@
 
         when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_ABSENT);
         when(mConnectivityManager.isNetworkSupported(TYPE_WIFI)).thenReturn(false);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
index e1c4b70..624b07f 100644
--- a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
@@ -77,7 +77,7 @@
         when(mConfig.alwaysOnAvailableForUser(anyInt())).thenReturn(false);
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(
-                AmbientDisplayAlwaysOnPreferenceController.DISABLED_UNSUPPORTED);
+                AmbientDisplayAlwaysOnPreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
index 5815f42..f0ce95f 100644
--- a/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
@@ -104,7 +104,7 @@
     @Test
     public void testGetAvailabilityStatus() {
         assertThat(mController.getAvailabilityStatus()).isEqualTo(BasePreferenceController
-                .DISABLED_UNSUPPORTED);
+                .CONDITIONALLY_UNAVAILABLE);
 
         enableAutoRotationPreference();
 
@@ -114,7 +114,7 @@
         disableAutoRotationPreference();
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(BasePreferenceController
-                .DISABLED_UNSUPPORTED);
+                .CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintStatusPreferenceControllerTest.java
index 441f5b6..730b833 100644
--- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintStatusPreferenceControllerTest.java
@@ -17,7 +17,7 @@
 package com.android.settings.fingerprint;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
@@ -84,7 +84,7 @@
     public void getAvailabilityStatus_noFingerprintManger_DISABLED() {
         when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
 
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceControllerTest.java
index 241f550..c66e6aa 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceControllerTest.java
@@ -91,7 +91,7 @@
         doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isSmartBatterySupported();
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(
-                BasePreferenceController.DISABLED_UNSUPPORTED);
+                BasePreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index dc34016..5c2a1c7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -581,18 +581,6 @@
     }
 
     @Test
-    public void testIsAppHeavilyUsed_usageMoreThanThreshold_returnTrue() {
-        assertThat(mBatteryUtils.isAppHeavilyUsed(mBatteryStatsHelper, mUserManager, UID,
-                10 /* threshold */ )).isTrue();
-    }
-
-    @Test
-    public void testIsAppHeavilyUsed_usageLessThanThreshold_returnFalse() {
-        assertThat(mBatteryUtils.isAppHeavilyUsed(mBatteryStatsHelper, mUserManager, UID,
-                DISCHARGE_AMOUNT /* threshold */ )).isFalse();
-    }
-
-    @Test
     public void testShouldHideAnomaly_systemAppWithLauncher_returnTrue() {
         final List<ResolveInfo> resolveInfos = new ArrayList<>();
         final ResolveInfo resolveInfo = new ResolveInfo();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
index b91c5e1..717ba83 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
@@ -103,7 +103,7 @@
         doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isSmartBatterySupported();
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(
-                BasePreferenceController.DISABLED_UNSUPPORTED);
+                BasePreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     private void putSmartBatteryValue(int value) {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index b50cc40..b0d6a7d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -48,9 +48,9 @@
             BatteryTip.TipType.APP_RESTRICTION,
             BatteryTip.TipType.BATTERY_SAVER,
             BatteryTip.TipType.HIGH_DEVICE_USAGE,
+            BatteryTip.TipType.LOW_BATTERY,
             BatteryTip.TipType.SUMMARY,
-            BatteryTip.TipType.SMART_BATTERY_MANAGER,
-            BatteryTip.TipType.LOW_BATTERY};
+            BatteryTip.TipType.SMART_BATTERY_MANAGER};
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private BatteryStatsHelper mBatteryStatsHelper;
     @Mock
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
index 654b247..350326a 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
@@ -48,7 +48,8 @@
             + ",excessive_bg_drain_percentage=25"
             + ",test_battery_saver_tip=true"
             + ",test_high_usage_tip=false"
-            + ",test_smart_battery_tip=true";
+            + ",test_smart_battery_tip=true"
+            + ",test_low_battery_tip=true";
     private Context mContext;
 
     @Before
@@ -80,6 +81,7 @@
         assertThat(batteryTipPolicy.testBatterySaverTip).isTrue();
         assertThat(batteryTipPolicy.testHighUsageTip).isFalse();
         assertThat(batteryTipPolicy.testSmartBatteryTip).isTrue();
+        assertThat(batteryTipPolicy.testLowBatteryTip).isTrue();
     }
 
     @Test
@@ -100,11 +102,12 @@
         assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse();
         assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);
         assertThat(batteryTipPolicy.lowBatteryEnabled).isFalse();
-        assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(16);
+        assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(3);
         assertThat(batteryTipPolicy.dataHistoryRetainDay).isEqualTo(30);
         assertThat(batteryTipPolicy.excessiveBgDrainPercentage).isEqualTo(10);
         assertThat(batteryTipPolicy.testBatterySaverTip).isFalse();
         assertThat(batteryTipPolicy.testHighUsageTip).isFalse();
         assertThat(batteryTipPolicy.testSmartBatteryTip).isFalse();
+        assertThat(batteryTipPolicy.testLowBatteryTip).isFalse();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
index 1f4affa..9764559 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
@@ -17,12 +17,15 @@
 package com.android.settings.fuelgauge.batterytip.detectors;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.spy;
 
-import android.text.format.DateUtils;
+import android.content.Context;
+import android.os.PowerManager;
 
 import com.android.settings.fuelgauge.BatteryInfo;
 import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
@@ -31,8 +34,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowPowerManager;
 import org.robolectric.util.ReflectionHelpers;
 
+import java.util.concurrent.TimeUnit;
+
 @RunWith(SettingsRobolectricTestRunner.class)
 public class LowBatteryDetectorTest {
 
@@ -40,36 +47,68 @@
     private BatteryInfo mBatteryInfo;
     private BatteryTipPolicy mPolicy;
     private LowBatteryDetector mLowBatteryDetector;
+    private ShadowPowerManager mShadowPowerManager;
+    private Context mContext;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
         mPolicy = spy(new BatteryTipPolicy(RuntimeEnvironment.application));
+        mContext = RuntimeEnvironment.application;
+        mShadowPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class));
         ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", true);
+        ReflectionHelpers.setField(mPolicy, "lowBatteryHour", 3);
+        mBatteryInfo.discharging = true;
 
-        mLowBatteryDetector = new LowBatteryDetector(mPolicy, mBatteryInfo);
+        mLowBatteryDetector = new LowBatteryDetector(mContext, mPolicy, mBatteryInfo);
     }
 
     @Test
     public void testDetect_disabledByPolicy_tipInvisible() {
         ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", false);
+        mShadowPowerManager.setIsPowerSaveMode(true);
 
         assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
     }
 
     @Test
-    public void testDetect_shortBatteryLife_tipVisible() {
-        mBatteryInfo.discharging = true;
-        mBatteryInfo.remainingTimeUs = DateUtils.MINUTE_IN_MILLIS;
+    public void testDetect_enabledByTest_tipNew() {
+        ReflectionHelpers.setField(mPolicy, "testLowBatteryTip", true);
 
-        assertThat(mLowBatteryDetector.detect().isVisible()).isTrue();
+        assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
     }
 
     @Test
-    public void testDetect_longBatteryLife_tipInvisible() {
-        mBatteryInfo.discharging = true;
-        mBatteryInfo.remainingTimeUs = DateUtils.DAY_IN_MILLIS;
+    public void testDetect_lowBattery_tipNew() {
+        mBatteryInfo.batteryLevel = 3;
+        mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMillis(1);
+        assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
+
+        mBatteryInfo.batteryLevel = 50;
+        mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1);
+        assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
+    }
+
+    @Test
+    public void testDetect_batterySaverOn_tipHandled() {
+        mShadowPowerManager.setIsPowerSaveMode(true);
+
+        assertThat(mLowBatteryDetector.detect().getState())
+                .isEqualTo(BatteryTip.StateType.HANDLED);
+    }
+
+    @Test
+    public void testDetect_charging_tipInvisible() {
+        mBatteryInfo.discharging = false;
+
+        assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDetect_noEarlyWarning_tipInvisible() {
+        mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMicros(1);
+        mBatteryInfo.batteryLevel = 100;
 
         assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java
new file mode 100644
index 0000000..359d260
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 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 LowBatteryTipTest {
+
+    private static final CharSequence SUMMARY = "Only 15 minutes left";
+
+    @Mock
+    private MetricsFeatureProvider mMetricsFeatureProvider;
+    private Context mContext;
+    private LowBatteryTip mLowBatteryTip;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mLowBatteryTip = new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */,
+                SUMMARY);
+    }
+
+    @Test
+    public void testParcelable() {
+        Parcel parcel = Parcel.obtain();
+        mLowBatteryTip.writeToParcel(parcel, mLowBatteryTip.describeContents());
+        parcel.setDataPosition(0);
+
+        final LowBatteryTip parcelTip = new LowBatteryTip(parcel);
+
+        assertThat(parcelTip.isPowerSaveModeOn()).isFalse();
+        assertThat(parcelTip.getSummary(mContext)).isEqualTo(SUMMARY);
+    }
+
+    @Test
+    public void getSummary_tipHandled_showSummary() {
+        mLowBatteryTip.mState = BatteryTip.StateType.HANDLED;
+
+        assertThat(mLowBatteryTip.getSummary(mContext)).isEqualTo("Some features may be limited");
+    }
+
+    @Test
+    public void getSummary_tipNew_showSummary() {
+        mLowBatteryTip.mState = BatteryTip.StateType.NEW;
+
+        assertThat(mLowBatteryTip.getSummary(mContext)).isEqualTo(SUMMARY);
+    }
+
+    @Test
+    public void log_lowBatteryActionWithCorrectState() {
+        mLowBatteryTip.log(mContext, mMetricsFeatureProvider);
+
+        verify(mMetricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_LOW_BATTERY_TIP, BatteryTip.StateType.NEW);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java
index d4b7706..df40ba5 100644
--- a/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/GesturePreferenceControllerTest.java
@@ -204,7 +204,7 @@
 
         @Override
         public int getAvailabilityStatus() {
-            return mIsPrefAvailable ? AVAILABLE : DISABLED_UNSUPPORTED;
+            return mIsPrefAvailable ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java
index 360609b..4107b73 100644
--- a/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java
@@ -18,35 +18,32 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
 import android.os.UserManager;
 import android.provider.Settings;
 
 import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowPackageManager;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = SettingsShadowResources.class)
 public class SwipeUpPreferenceControllerTest {
 
     private Context mContext;
@@ -58,11 +55,21 @@
 
     @Before
     public void setUp() {
+        SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available,
+                true);
+        SettingsShadowResources.overrideResource(
+                com.android.internal.R.bool.config_swipe_up_gesture_default, true);
+
         mContext = RuntimeEnvironment.application;
         mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
         mController = new SwipeUpPreferenceController(mContext, KEY_SWIPE_UP);
     }
 
+    @After
+    public void tearDown() {
+        SettingsShadowResources.reset();
+    }
+
     @Test
     public void testIsGestureAvailable_matchingServiceExists_shouldReturnTrue() {
         final ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -75,19 +82,45 @@
     }
 
     @Test
+    public void testIsGestureAvailable_overlayDisabled_matchingServiceExists_shouldReturnFalse() {
+        SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available,
+                false);
+
+        final ComponentName recentsComponentName = ComponentName.unflattenFromString(
+                mContext.getString(com.android.internal.R.string.config_recentsComponentName));
+        final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP)
+                .setPackage(recentsComponentName.getPackageName());
+        mPackageManager.addResolveInfoForIntent(quickStepIntent, new ResolveInfo());
+
+        assertThat(SwipeUpPreferenceController.isGestureAvailable(mContext)).isFalse();
+    }
+
+    @Test
     public void testIsGestureAvailable_noMatchingServiceExists_shouldReturnFalse() {
         assertThat(SwipeUpPreferenceController.isGestureAvailable(mContext)).isFalse();
     }
 
     @Test
-    public void testIsChecked_configIsSet_shouldReturnTrue() {
+    public void testIsChecked_defaultIsTrue_shouldReturnTrue() {
+        assertThat(mController.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testIsChecked_defaultIsFalse_shouldReturnFalse() {
+        SettingsShadowResources.overrideResource(
+                com.android.internal.R.bool.config_swipe_up_gesture_default, false);
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void testIsChecked_setCheckedTrue_shouldReturnTrue() {
         // Set the setting to be enabled.
         mController.setChecked(true);
         assertThat(mController.isChecked()).isTrue();
     }
 
     @Test
-    public void testIsChecked_configIsNotSet_shouldReturnFalse() {
+    public void testIsChecked_setCheckedFalse_shouldReturnFalse() {
         // Set the setting to be disabled.
         mController.setChecked(false);
         assertThat(mController.isChecked()).isFalse();
diff --git a/tests/robotests/src/com/android/settings/inputmethod/GameControllerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/GameControllerPreferenceControllerTest.java
index 61283fa..e5ecd6d 100644
--- a/tests/robotests/src/com/android/settings/inputmethod/GameControllerPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/inputmethod/GameControllerPreferenceControllerTest.java
@@ -17,7 +17,8 @@
 package com.android.settings.inputmethod;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -89,7 +90,7 @@
         when(mInputDevice.isVirtual()).thenReturn(false);
         when(mInputDevice.getVibrator().hasVibrator()).thenReturn(false);
 
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
@@ -98,14 +99,14 @@
         when(mInputManager.getInputDevice(1)).thenReturn(mInputDevice);
         when(mInputDevice.isVirtual()).thenReturn(true);
 
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
     public void getAvailabilityStatus_hasNoDevice_shouldReturnDisabled() {
         when(mInputManager.getInputDeviceIds()).thenReturn(new int[] {});
 
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
@@ -113,7 +114,7 @@
     public void getAvailabilityStatus_ifDisabled_shouldReturnDisabled() {
         mController = new GameControllerPreferenceController(mContext, "testkey");
 
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java
index f999194..12ecbfe 100644
--- a/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/BadgingNotificationPreferenceControllerTest.java
@@ -18,8 +18,6 @@
 
 import static android.provider.Settings.Secure.NOTIFICATION_BADGING;
 
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
 import static com.android.settings.notification.BadgingNotificationPreferenceController.OFF;
 import static com.android.settings.notification.BadgingNotificationPreferenceController.ON;
 
diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java
index 675ac57..8fef5fc 100644
--- a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java
@@ -169,7 +169,7 @@
 
         @Override
         public int getAvailabilityStatus() {
-            return mAvailable ? AVAILABLE : DISABLED_UNSUPPORTED;
+            return mAvailable ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java
new file mode 100644
index 0000000..7a45d7f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+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 ZenModeStarredContactsPreferenceControllerTest {
+
+    private ZenModeStarredContactsPreferenceController mCallsController;
+    private ZenModeStarredContactsPreferenceController mMessagesController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private ListPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private Intent testIntent;
+    @Mock
+    private ComponentName mComponentName;
+    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);
+        when(testIntent.resolveActivity(any())).thenReturn(mComponentName);
+
+        mCallsController = new ZenModeStarredContactsPreferenceController(
+                mContext, mock(Lifecycle.class), PRIORITY_CATEGORY_CALLS);
+        ReflectionHelpers.setField(mCallsController, "mBackend", mBackend);
+        ReflectionHelpers.setField(mCallsController, "mStarredContactsIntent", testIntent);
+        when(mPreferenceScreen.findPreference(mCallsController.getPreferenceKey()))
+                .thenReturn(mockPref);
+        mCallsController.displayPreference(mPreferenceScreen);
+
+        mMessagesController = new ZenModeStarredContactsPreferenceController(
+                mContext, mock(Lifecycle.class), PRIORITY_CATEGORY_MESSAGES);
+        ReflectionHelpers.setField(mMessagesController, "mBackend", mBackend);
+        ReflectionHelpers.setField(mMessagesController, "mStarredContactsIntent", testIntent);
+        when(mPreferenceScreen.findPreference(mMessagesController.getPreferenceKey()))
+                .thenReturn(mockPref);
+        mMessagesController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void isAvailable_noCallers() {
+        when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(false);
+        assertThat(mCallsController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_anyCallers() {
+        when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(true);
+        when(mBackend.getPriorityCallSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+
+
+        assertThat(mCallsController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_starredCallers() {
+        when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(true);
+        when(mBackend.getPriorityCallSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+
+        assertThat(mCallsController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noMessages() {
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)).thenReturn(false);
+        assertThat(mMessagesController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_anyMessages() {
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)).thenReturn(true);
+        when(mBackend.getPriorityMessageSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+
+        assertThat(mMessagesController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_starredMessageContacts() {
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)).thenReturn(true);
+        when(mBackend.getPriorityMessageSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+
+        assertThat(mMessagesController.isAvailable()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java
index 56fa1c2..14de98c 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java
@@ -150,18 +150,4 @@
         verify(mockPref).setOnGearClickListener(any());
         verify(mockPref).setOnRadioButtonClickListener(any());
     }
-
-    @Test
-    public void select() {
-        int interruptiveSuppressed = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
-                | SUPPRESSED_EFFECT_AMBIENT
-                | SUPPRESSED_EFFECT_LIGHTS
-                | SUPPRESSED_EFFECT_PEEK;
-        mBackend.mPolicy = new NotificationManager.Policy(0, 0, 0, 1);
-        mController.select();
-        verify(mBackend).savePolicy(anyInt(), anyInt(), anyInt(), eq(interruptiveSuppressed));
-        verify(mFeatureFactory.metricsFeatureProvider).action(eq(mContext),
-                eq(ACTION_ZEN_CUSTOM),
-                eq(true));
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
index c7bf043..f03f88e 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
@@ -211,7 +211,7 @@
         // Disable Setting
         Settings.Global.putInt(mContext.getContentResolver(),
                 FakeToggleController.AVAILABILITY_KEY,
-                BasePreferenceController.DISABLED_UNSUPPORTED);
+                BasePreferenceController.UNSUPPORTED_ON_DEVICE);
 
         // Insert Fake Toggle into Database
         final String key = "key";
@@ -245,7 +245,7 @@
         // Disable Setting
         Settings.Global.putInt(mContext.getContentResolver(),
                 FakeSliderController.AVAILABILITY_KEY,
-                BasePreferenceController.DISABLED_UNSUPPORTED);
+                BasePreferenceController.UNSUPPORTED_ON_DEVICE);
 
         // Insert Fake Slider into Database
         final String key = "key";
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
index a74e2ba..bd589bf 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
@@ -330,7 +330,7 @@
                 SliceData.SliceType.SWITCH);
         Settings.Global.putInt(mContext.getContentResolver(),
                 FakeUnavailablePreferenceController.AVAILABILITY_KEY,
-                BasePreferenceController.DISABLED_UNSUPPORTED);
+                BasePreferenceController.UNSUPPORTED_ON_DEVICE);
 
         final Slice slice = SliceBuilderUtils.buildSlice(mContext, data);
 
@@ -373,12 +373,12 @@
     }
 
     @Test
-    public void testUnavailableUnknownSlice_validTitleSummary() {
+    public void testConditionallyUnavailableSlice_validTitleSummary() {
         final SliceData data = getDummyData(FakeUnavailablePreferenceController.class,
                 SliceData.SliceType.SWITCH);
         Settings.Global.putInt(mContext.getContentResolver(),
                 FakeUnavailablePreferenceController.AVAILABILITY_KEY,
-                BasePreferenceController.UNAVAILABLE_UNKNOWN);
+                BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
 
         final Slice slice = SliceBuilderUtils.buildSlice(mContext, data);
 
diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
index 87ada36..674d629 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
@@ -56,7 +56,6 @@
     private static final String FAKE_TITLE = "title";
     private static final String FAKE_SUMMARY = "summary";
     private static final String FAKE_SCREEN_TITLE = "screen_title";
-    private static final String FAKE_KEYWORDS = "a, b, c";
     private static final String FAKE_FRAGMENT_CLASSNAME = FakeIndexProvider.class.getName();
     private static final String FAKE_CONTROLLER_NAME = FakePreferenceController.class.getName();
     private static final String ACCESSIBILITY_FRAGMENT = AccessibilitySettings.class.getName();
@@ -118,7 +117,7 @@
         assertThat(fakeSlice.getTitle()).isEqualTo(FAKE_TITLE);
         assertThat(fakeSlice.getSummary()).isEqualTo(FAKE_SUMMARY);
         assertThat(fakeSlice.getScreenTitle()).isEqualTo(FAKE_SCREEN_TITLE);
-        assertThat(fakeSlice.getKeywords()).isEqualTo(FAKE_KEYWORDS);
+        assertThat(fakeSlice.getKeywords()).isNull();
         assertThat(fakeSlice.getIconResource()).isNotNull();
         assertThat(fakeSlice.getUri()).isNull();
         assertThat(fakeSlice.getFragmentClassName()).isEqualTo(FAKE_FRAGMENT_CLASSNAME);
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index e0877e4..272fd20 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -17,8 +17,17 @@
 package com.android.settings.sound;
 
 
+import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_SCO;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO;
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
+import static android.media.AudioSystem.STREAM_MUSIC;
+
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -28,6 +37,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static org.robolectric.Shadows.shadowOf;
+
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothManager;
@@ -48,7 +59,8 @@
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.BluetoothEventManager;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
@@ -59,7 +71,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowBluetoothDevice;
 
@@ -77,19 +88,24 @@
     private static final String TEST_KEY = "Test_Key";
     private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1";
     private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2";
-    private static final String TEST_DEVICE_ADDRESS_1 = "00:07:80:78:A4:69";
-    private static final String TEST_DEVICE_ADDRESS_2 = "00:00:00:00:00:00";
+    private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
+    private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
+    private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
+    private final static long HISYNCID1 = 10;
+    private final static long HISYNCID2 = 11;
 
     @Mock
     private LocalBluetoothManager mLocalManager;
     @Mock
     private BluetoothEventManager mBluetoothEventManager;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
     @Mock
     private A2dpProfile mA2dpProfile;
+    @Mock
+    private HeadsetProfile mHeadsetProfile;
+    @Mock
+    private HearingAidProfile mHearingAidProfile;
 
     private Context mContext;
     private PreferenceScreen mScreen;
@@ -99,10 +115,13 @@
     private BluetoothManager mBluetoothManager;
     private BluetoothAdapter mBluetoothAdapter;
     private BluetoothDevice mBluetoothDevice;
-    private ShadowBluetoothDevice mShadowBluetoothDevice;
+    private BluetoothDevice mLeftBluetoothHapDevice;
+    private BluetoothDevice mRightBluetoothHapDevice;
     private LocalBluetoothManager mLocalBluetoothManager;
     private AudioSwitchPreferenceController mController;
-    private List<BluetoothDevice> mConnectedDevices;
+    private List<BluetoothDevice> mProfileConnectedDevices;
+    private List<BluetoothDevice> mHearingAidActiveDevices;
+    private List<BluetoothDevice> mEmptyDevices;
 
     @Before
     public void setUp() {
@@ -118,20 +137,27 @@
         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
         when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+        when(mLocalBluetoothProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile);
 
         mBluetoothManager = new BluetoothManager(mContext);
         mBluetoothAdapter = mBluetoothManager.getAdapter();
 
-        mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1);
-        mShadowBluetoothDevice = Shadows.shadowOf(mBluetoothDevice);
-        mShadowBluetoothDevice.setName(TEST_DEVICE_NAME_1);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
+        when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
+        when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+        mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
+        when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
+        mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
+        when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
 
         mController = new AudioSwitchPreferenceControllerTestable(mContext, TEST_KEY);
         mScreen = spy(new PreferenceScreen(mContext, null));
         mPreference = new ListPreference(mContext);
-        mConnectedDevices = new ArrayList<>(1);
-        mConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices = new ArrayList<>();
+        mHearingAidActiveDevices = new ArrayList<>(2);
+        mEmptyDevices = new ArrayList<>(2);
 
         when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
         when(mScreen.getContext()).thenReturn(mContext);
@@ -155,7 +181,7 @@
     @Test
     public void getAvailabilityStatus_whenNotVisible_isDisable() {
         FeatureFlagUtils.setEnabled(mContext, FeatureFlags.AUDIO_SWITCHER_SETTINGS, false);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_UNSUPPORTED);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
@@ -179,7 +205,8 @@
 
     @Test
     public void onPreferenceChange_toThisDevice_shouldSetDefaultSummary() {
-        mController.mConnectedDevices = mConnectedDevices;
+        mController.mConnectedDevices.clear();
+        mController.mConnectedDevices.add(mBluetoothDevice);
 
         mController.onPreferenceChange(mPreference,
                 mContext.getText(R.string.media_output_default_summary));
@@ -194,7 +221,8 @@
      */
     @Test
     public void onPreferenceChange_toBtDevice_shouldSetBtDeviceName() {
-        mController.mConnectedDevices = mConnectedDevices;
+        mController.mConnectedDevices.clear();
+        mController.mConnectedDevices.add(mBluetoothDevice);
 
         mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1);
 
@@ -210,12 +238,11 @@
         ShadowBluetoothDevice shadowBluetoothDevice;
         BluetoothDevice secondBluetoothDevice;
         secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
-        shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
+        shadowBluetoothDevice = shadowOf(secondBluetoothDevice);
         shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
-        List<BluetoothDevice> connectedDevices = new ArrayList<>(2);
-        connectedDevices.add(mBluetoothDevice);
-        connectedDevices.add(secondBluetoothDevice);
-        mController.mConnectedDevices = connectedDevices;
+        mController.mConnectedDevices.clear();
+        mController.mConnectedDevices.add(mBluetoothDevice);
+        mController.mConnectedDevices.add(secondBluetoothDevice);
 
         mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_2);
 
@@ -223,16 +250,263 @@
     }
 
     /**
-     * mConnectedDevices is Null.
+     * mConnectedDevices is empty.
      * onPreferenceChange should return false.
      */
     @Test
     public void onPreferenceChange_connectedDeviceIsNull_shouldReturnFalse() {
-        mController.mConnectedDevices = null;
+        mController.mConnectedDevices.clear();
 
         assertThat(mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1)).isFalse();
     }
 
+    /**
+     * Audio stream output to bluetooth sco headset which is the subset of all sco device.
+     * isStreamFromOutputDevice should return true.
+     */
+    @Test
+    public void isStreamFromOutputDevice_outputDeviceIsBtScoHeadset_shouldReturnTrue() {
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
+
+        assertThat(mController.isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_ALL_SCO)).isTrue();
+    }
+
+    /**
+     * Audio stream is not STREAM_MUSIC or STREAM_VOICE_CALL.
+     * findActiveDevice should return null.
+     */
+    @Test
+    public void findActiveDevice_streamIsRing_shouldReturnNull() {
+        assertThat(mController.findActiveDevice(STREAM_RING)).isNull();
+    }
+
+    /**
+     * Audio stream is STREAM_MUSIC and output device is A2dp bluetooth device.
+     * findActiveDevice should return A2dp active device.
+     */
+    @Test
+    public void findActiveDevice_streamMusicToA2dpDevice_shouldReturnActiveA2dpDevice() {
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        when(mHeadsetProfile.getActiveDevice()).thenReturn(mLeftBluetoothHapDevice);
+        when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+        assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mBluetoothDevice);
+    }
+
+    /**
+     * Audio stream is STREAM_VOICE_CALL and output device is Hands free profile bluetooth device.
+     * findActiveDevice should return Hands free profile active device.
+     */
+    @Test
+    public void findActiveDevice_streamVoiceCallToHfpDevice_shouldReturnActiveHfpDevice() {
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mA2dpProfile.getActiveDevice()).thenReturn(mLeftBluetoothHapDevice);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+        assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(mBluetoothDevice);
+    }
+
+    /**
+     * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid profile
+     * bluetooth device. And left side of HAP device is active.
+     * findActiveDevice should return hearing aid device active device.
+     */
+    @Test
+    public void findActiveDevice_streamToHapDeviceLeftActiveDevice_shouldReturnActiveHapDevice() {
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mController.mConnectedDevices.clear();
+        mController.mConnectedDevices.add(mBluetoothDevice);
+        mController.mConnectedDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.add(null);
+        when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+        assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mLeftBluetoothHapDevice);
+        assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(
+                mLeftBluetoothHapDevice);
+    }
+
+    /**
+     * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid profile
+     * bluetooth device. And right side of HAP device is active.
+     * findActiveDevice should return hearing aid device active device.
+     */
+    @Test
+    public void findActiveDevice_streamToHapDeviceRightActiveDevice_shouldReturnActiveHapDevice() {
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mController.mConnectedDevices.clear();
+        mController.mConnectedDevices.add(mBluetoothDevice);
+        mController.mConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(null);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+        assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mRightBluetoothHapDevice);
+        assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(
+                mRightBluetoothHapDevice);
+    }
+
+    /**
+     * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid
+     * profile bluetooth device. And both are active device.
+     * findActiveDevice should return only return the active device in mConnectedDevices.
+     */
+    @Test
+    public void findActiveDevice_streamToHapDeviceTwoActiveDevice_shouldReturnActiveHapDevice() {
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mController.mConnectedDevices.clear();
+        mController.mConnectedDevices.add(mBluetoothDevice);
+        mController.mConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+        assertThat(mController.findActiveDevice(STREAM_MUSIC)).isEqualTo(mRightBluetoothHapDevice);
+        assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isEqualTo(
+                mRightBluetoothHapDevice);
+    }
+
+    /**
+     * Audio stream is STREAM_MUSIC or STREAM_VOICE_CALL and output device is hearing aid
+     * profile bluetooth device. And none of them are active.
+     * findActiveDevice should return null.
+     */
+    @Test
+    public void findActiveDevice_streamToOtherDevice_shouldReturnActiveHapDevice() {
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mController.mConnectedDevices.clear();
+        mController.mConnectedDevices.add(mBluetoothDevice);
+        mController.mConnectedDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+        assertThat(mController.findActiveDevice(STREAM_MUSIC)).isNull();
+        assertThat(mController.findActiveDevice(STREAM_VOICE_CALL)).isNull();
+    }
+
+    /**
+     * Two hearing aid devices with different HisyncId
+     * getConnectedHearingAidDevices should add both device to list.
+     */
+    @Test
+    public void getConnectedHearingAidDevices_deviceHisyncIdIsDifferent_shouldAddBothToList() {
+        mEmptyDevices.clear();
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+        mEmptyDevices.addAll(mController.getConnectedHearingAidDevices());
+
+        assertThat(mEmptyDevices).containsExactly(mLeftBluetoothHapDevice,
+                mRightBluetoothHapDevice);
+    }
+
+    /**
+     * Two hearing aid devices with same HisyncId
+     * getConnectedHearingAidDevices should only add first device to list.
+     */
+    @Test
+    public void getConnectedHearingAidDevices_deviceHisyncIdIsSame_shouldAddOneToList() {
+        mEmptyDevices.clear();
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mEmptyDevices.addAll(mController.getConnectedHearingAidDevices());
+
+        assertThat(mEmptyDevices).containsExactly(mLeftBluetoothHapDevice);
+    }
+
+    /**
+     * One A2dp device is connected.
+     * getConnectedA2dpDevices should add this device to list.
+     */
+    @Test
+    public void getConnectedA2dpDevices_oneConnectedA2dpDevice_shouldAddDeviceToList() {
+        mEmptyDevices.clear();
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+        mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
+
+        assertThat(mEmptyDevices).containsExactly(mBluetoothDevice);
+    }
+
+    /**
+     * More than one A2dp devices are connected.
+     * getConnectedA2dpDevices should add all devices to list.
+     */
+    @Test
+    public void getConnectedA2dpDevices_moreThanOneConnectedA2dpDevice_shouldAddDeviceToList() {
+        mEmptyDevices.clear();
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+        mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
+
+        assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mLeftBluetoothHapDevice);
+    }
+
+    /**
+     * One hands free profile device is connected.
+     * getConnectedA2dpDevices should add this device to list.
+     */
+    @Test
+    public void getConnectedHfpDevices_oneConnectedHfpDevice_shouldAddDeviceToList() {
+        mEmptyDevices.clear();
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+        mEmptyDevices.addAll(mController.getConnectedHfpDevices());
+
+        assertThat(mEmptyDevices).containsExactly(mBluetoothDevice);
+    }
+
+    /**
+     * More than one hands free profile devices are connected.
+     * getConnectedA2dpDevices should add all devices to list.
+     */
+    @Test
+    public void getConnectedHfpDevices_moreThanOneConnectedHfpDevice_shouldAddDeviceToList() {
+        mEmptyDevices.clear();
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+
+        mEmptyDevices.addAll(mController.getConnectedHfpDevices());
+
+        assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mLeftBluetoothHapDevice);
+    }
+
     private class AudioSwitchPreferenceControllerTestable extends
             AudioSwitchPreferenceController {
         AudioSwitchPreferenceControllerTestable(Context context, String key) {
diff --git a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
index 0692c9c..db09eab 100644
--- a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
@@ -17,12 +17,16 @@
 package com.android.settings.sound;
 
 
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
 import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -41,8 +45,8 @@
 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
 import com.android.settings.testutils.shadow.ShadowMediaRouter;
 import com.android.settingslib.bluetooth.BluetoothEventManager;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
@@ -53,7 +57,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowBluetoothDevice;
 
@@ -69,21 +72,27 @@
 )
 public class HandsFreeProfileOutputPreferenceControllerTest {
     private static final String TEST_KEY = "Test_Key";
-    private static final String TEST_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
-    private static final String TEST_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
-    private static final String TEST_DEVICE_ADDRESS_1 = "00:07:80:78:A4:69";
-    private static final String TEST_DEVICE_ADDRESS_2 = "00:00:00:00:00:00";
+    private static final String TEST_DEVICE_NAME_1 = "Test_HFP_BT_Device_NAME_1";
+    private static final String TEST_DEVICE_NAME_2 = "Test_HFP_BT_Device_NAME_2";
+    private static final String TEST_HAP_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
+    private static final String TEST_HAP_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
+    private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
+    private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
+    private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
+    private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
+    private final static long HISYNCID1 = 10;
+    private final static long HISYNCID2 = 11;
 
     @Mock
     private LocalBluetoothManager mLocalManager;
     @Mock
     private BluetoothEventManager mBluetoothEventManager;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
     @Mock
     private HeadsetProfile mHeadsetProfile;
+    @Mock
+    private HearingAidProfile mHearingAidProfile;
 
     private Context mContext;
     private PreferenceScreen mScreen;
@@ -93,10 +102,13 @@
     private BluetoothManager mBluetoothManager;
     private BluetoothAdapter mBluetoothAdapter;
     private BluetoothDevice mBluetoothDevice;
-    private ShadowBluetoothDevice mShadowBluetoothDevice;
+    private BluetoothDevice mSecondBluetoothDevice;
+    private BluetoothDevice mLeftBluetoothHapDevice;
+    private BluetoothDevice mRightBluetoothHapDevice;
     private LocalBluetoothManager mLocalBluetoothManager;
     private AudioSwitchPreferenceController mController;
-    private List<BluetoothDevice> mConnectedDevices;
+    private List<BluetoothDevice> mProfileConnectedDevices;
+    private List<BluetoothDevice> mHearingAidActiveDevices;
 
     @Before
     public void setUp() {
@@ -112,19 +124,32 @@
         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
         when(mLocalBluetoothProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile);
+        when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
 
         mBluetoothManager = new BluetoothManager(mContext);
         mBluetoothAdapter = mBluetoothManager.getAdapter();
-        mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1);
-        mShadowBluetoothDevice = Shadows.shadowOf(mBluetoothDevice);
-        mShadowBluetoothDevice.setName(TEST_DEVICE_NAME_1);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+        mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
+        when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
+        when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+        mSecondBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
+        when(mSecondBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_2);
+        when(mSecondBluetoothDevice.isConnected()).thenReturn(true);
+
+        mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
+        when(mLeftBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_1);
+        when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
+
+        mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_4));
+        when(mRightBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_2);
+        when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
 
         mController = new HandsFreeProfileOutputPreferenceController(mContext, TEST_KEY);
         mScreen = spy(new PreferenceScreen(mContext, null));
         mPreference = new ListPreference(mContext);
-        mConnectedDevices = new ArrayList<>(1);
-        mConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices = new ArrayList<>();
+        mHearingAidActiveDevices = new ArrayList<>(2);
 
         when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
         when(mScreen.getContext()).thenReturn(mContext);
@@ -140,8 +165,26 @@
         ShadowBluetoothUtils.reset();
     }
 
+    /**
+     * During a call, bluetooth device with HisyncId.
+     * HearingAidProfile should set active device to this device.
+     */
     @Test
-    public void setActiveBluetoothDevice_duringACalling_shouldSetBtDeviceActive() {
+    public void setActiveBluetoothDevice_btDeviceWithHisyncId_shouldSetBtDeviceActive() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.setActiveBluetoothDevice(mLeftBluetoothHapDevice);
+
+        verify(mHearingAidProfile).setActiveDevice(mLeftBluetoothHapDevice);
+    }
+
+    /**
+     * During a call, Bluetooth device without HisyncId.
+     * HeadsetProfile should set active device to this device.
+     */
+    @Test
+    public void setActiveBluetoothDevice_btDeviceWithoutHisyncId_shouldSetBtDeviceActive() {
         mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
 
         mController.setActiveBluetoothDevice(mBluetoothDevice);
@@ -149,23 +192,60 @@
         verify(mHeadsetProfile).setActiveDevice(mBluetoothDevice);
     }
 
+    /**
+     * During a call, set active device to "this device".
+     * HeadsetProfile should set to null.
+     * HearingAidProfile should set to null.
+     */
+    @Test
+    public void setActiveBluetoothDevice_setNull_shouldSetNullToBothProfiles() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+
+        mController.setActiveBluetoothDevice(null);
+
+        verify(mHeadsetProfile).setActiveDevice(null);
+        verify(mHearingAidProfile).setActiveDevice(null);
+    }
+
+    /**
+     * In normal mode
+     * HeadsetProfile should not set active device.
+     */
+    @Test
+    public void setActiveBluetoothDevice_inNormalMode_shouldNotSetActiveDeviceToHeadsetProfile() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+
+        mController.setActiveBluetoothDevice(mBluetoothDevice);
+
+        verify(mHeadsetProfile, times(0)).setActiveDevice(any(BluetoothDevice.class));
+    }
+
+    /**
+     * Default status
+     * Preference should be invisible
+     * Summary should be default summary
+     */
     @Test
     public void updateState_shouldSetSummary() {
         mController.updateState(mPreference);
 
+        assertThat(mPreference.isVisible()).isFalse();
         assertThat(mPreference.getSummary()).isEqualTo(
                 mContext.getText(R.string.media_output_default_summary));
     }
 
     /**
-     * One Headset Bluetooth device is available and activated
+     * One Hands Free Profile Bluetooth device is available and activated
      * Preference should be visible
-     * Preference summary should be activate device name
+     * Preference summary should be the activated device name
      */
     @Test
     public void updateState_oneHeadsetsAvailableAndActivated_shouldSetDeviceName() {
         mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
-        when(mHeadsetProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
         when(mHeadsetProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
 
         mController.updateState(mPreference);
@@ -175,29 +255,25 @@
     }
 
     /**
-     * More than one Headset Bluetooth devices are available, and second device is active.
+     * More than one Hands Free Profile Bluetooth devices are available, and second
+     * device is active.
      * Preference should be visible
-     * Preference summary should be activate device name
+     * Preference summary should be the activated device name
      */
     @Test
-    public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
-        ShadowBluetoothDevice shadowBluetoothDevice;
+    public void updateState_moreThanOneHfpBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
         mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
-        BluetoothDevice secondBluetoothDevice;
-        secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
-        shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
-        shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO);
         List<BluetoothDevice> connectedDevices = new ArrayList<>(2);
         connectedDevices.add(mBluetoothDevice);
-        connectedDevices.add(secondBluetoothDevice);
-
+        connectedDevices.add(mSecondBluetoothDevice);
         when(mHeadsetProfile.getConnectedDevices()).thenReturn(connectedDevices);
-        when(mHeadsetProfile.getActiveDevice()).thenReturn(secondBluetoothDevice);
+        when(mHeadsetProfile.getActiveDevice()).thenReturn(mSecondBluetoothDevice);
 
         mController.updateState(mPreference);
 
         assertThat(mPreference.isVisible()).isTrue();
-        assertThat(mPreference.getSummary()).isEqualTo(secondBluetoothDevice.getName());
+        assertThat(mPreference.getSummary()).isEqualTo(mSecondBluetoothDevice.getName());
     }
 
     /**
@@ -209,8 +285,10 @@
     @Test
     public void updateState_withAvailableDevicesWiredHeadsetActivated_shouldSetDefaultSummary() {
         mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
-        mShadowAudioManager.setStream(DEVICE_OUT_USB_HEADSET);
-        when(mHeadsetProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_USB_HEADSET);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        when(mHeadsetProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
         when(mHeadsetProfile.getActiveDevice()).thenReturn(
                 mBluetoothDevice); // BT device is still activated in this case
 
@@ -238,4 +316,150 @@
         assertThat(mPreference.getSummary()).isEqualTo(
                 mContext.getText(R.string.media_output_default_summary));
     }
+
+    /**
+     * One hearing aid profile Bluetooth device is available and active.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     */
+    @Test
+    public void updateState_oneHapBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+    }
+
+    /**
+     * More than one hearing aid profile Bluetooth devices are available, and second
+     * device is active.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     */
+    @Test
+    public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+    }
+
+    /**
+     * Both hearing aid profile and hands free profile Bluetooth devices are available, and
+     * two hearing aid profile devices with same HisyncId. Both of HAP device are active,
+     * "left" side HAP device is added first.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     * ConnectedDevice should not contain second HAP device with same HisyncId
+     */
+    @Test
+    public void updateState_hapBtDeviceWithSameId_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        //with same HisyncId, only the first one will remain in UI.
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+        assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isTrue();
+        assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isFalse();
+    }
+
+    /**
+     * Both hearing aid profile and hands free profile Bluetooth devices are available, and
+     * two hearing aid profile devices with same HisyncId. Both of HAP device are active,
+     * "right" side HAP device is added first.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     * ConnectedDevice should not contain second HAP device with same HisyncId
+     */
+    @Test
+    public void updateState_hapBtDeviceWithSameIdButDifferentOrder_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        //with same HisyncId, only the first one will remain in UI.
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isTrue();
+        assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isFalse();
+        assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+    }
+
+    /**
+     * Both hearing aid profile and hands free profile  Bluetooth devices are available, and
+     * two hearing aid profile devices with different HisyncId. One of HAP device is active.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     * ConnectedDevice should contain both HAP device with different HisyncId
+     */
+    @Test
+    public void updateState_hapBtDeviceWithDifferentId_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(null);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+        assertThat(mController.mConnectedDevices).containsExactly(mBluetoothDevice,
+                mLeftBluetoothHapDevice, mRightBluetoothHapDevice);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index b62e6b3..b777239 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -17,13 +17,17 @@
 package com.android.settings.sound;
 
 
+import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
 import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
 import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -43,7 +47,7 @@
 import com.android.settings.testutils.shadow.ShadowMediaRouter;
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.BluetoothEventManager;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
@@ -54,7 +58,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowBluetoothDevice;
 
@@ -72,19 +75,25 @@
     private static final String TEST_KEY = "Test_Key";
     private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1";
     private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2";
-    private static final String TEST_DEVICE_ADDRESS_1 = "00:07:80:78:A4:69";
-    private static final String TEST_DEVICE_ADDRESS_2 = "00:00:00:00:00:00";
+    private static final String TEST_HAP_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
+    private static final String TEST_HAP_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
+    private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
+    private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
+    private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
+    private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
+    private final static long HISYNCID1 = 10;
+    private final static long HISYNCID2 = 11;
 
     @Mock
     private LocalBluetoothManager mLocalManager;
     @Mock
     private BluetoothEventManager mBluetoothEventManager;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
     @Mock
     private A2dpProfile mA2dpProfile;
+    @Mock
+    private HearingAidProfile mHearingAidProfile;
 
     private Context mContext;
     private PreferenceScreen mScreen;
@@ -94,10 +103,13 @@
     private BluetoothManager mBluetoothManager;
     private BluetoothAdapter mBluetoothAdapter;
     private BluetoothDevice mBluetoothDevice;
-    private ShadowBluetoothDevice mShadowBluetoothDevice;
+    private BluetoothDevice mSecondBluetoothDevice;
+    private BluetoothDevice mLeftBluetoothHapDevice;
+    private BluetoothDevice mRightBluetoothHapDevice;
     private LocalBluetoothManager mLocalBluetoothManager;
     private AudioSwitchPreferenceController mController;
-    private List<BluetoothDevice> mConnectedDevices;
+    private List<BluetoothDevice> mProfileConnectedDevices;
+    private List<BluetoothDevice> mHearingAidActiveDevices;
 
     @Before
     public void setUp() {
@@ -113,19 +125,32 @@
         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
         when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
 
         mBluetoothManager = new BluetoothManager(mContext);
         mBluetoothAdapter = mBluetoothManager.getAdapter();
-        mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1);
-        mShadowBluetoothDevice = Shadows.shadowOf(mBluetoothDevice);
-        mShadowBluetoothDevice.setName(TEST_DEVICE_NAME_1);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+        mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
+        when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
+        when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+        mSecondBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
+        when(mSecondBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_2);
+        when(mSecondBluetoothDevice.isConnected()).thenReturn(true);
+
+        mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
+        when(mLeftBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_1);
+        when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
+
+        mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_4));
+        when(mRightBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_2);
+        when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
 
         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
         mScreen = spy(new PreferenceScreen(mContext, null));
         mPreference = new ListPreference(mContext);
-        mConnectedDevices = new ArrayList<>(1);
-        mConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices = new ArrayList<>();
+        mHearingAidActiveDevices = new ArrayList<>(2);
 
         when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
         when(mScreen.getContext()).thenReturn(mContext);
@@ -141,8 +166,26 @@
         ShadowBluetoothUtils.reset();
     }
 
+    /**
+     * In normal mode, bluetooth device with HisyncId.
+     * HearingAidProfile should set active device to this device.
+     */
     @Test
-    public void setActiveBluetoothDevice_withoutRingAndCall_shouldSetBtDeviceActive() {
+    public void setActiveBluetoothDevice_btDeviceWithHisyncId_shouldSetBtDeviceActive() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.setActiveBluetoothDevice(mLeftBluetoothHapDevice);
+
+        verify(mHearingAidProfile).setActiveDevice(mLeftBluetoothHapDevice);
+    }
+
+    /**
+     * In normal mode, bluetooth device without HisyncId.
+     * A2dpProfile should set active device to this device.
+     */
+    @Test
+    public void setActiveBluetoothDevice_btDeviceWithoutHisyncId_shouldSetBtDeviceActive() {
         mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
 
         mController.setActiveBluetoothDevice(mBluetoothDevice);
@@ -150,16 +193,50 @@
         verify(mA2dpProfile).setActiveDevice(mBluetoothDevice);
     }
 
+    /**
+     * In normal mode, set active device to "this device".
+     * A2dpProfile should set to null.
+     * HearingAidProfile should set to null.
+     */
+    @Test
+    public void setActiveBluetoothDevice_setNull_shouldSetNullToBothProfiles() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+
+        mController.setActiveBluetoothDevice(null);
+
+        verify(mA2dpProfile).setActiveDevice(null);
+        verify(mHearingAidProfile).setActiveDevice(null);
+    }
+
+    /**
+     * During a call
+     * A2dpProfile should not set active device.
+     */
+    @Test
+    public void setActiveBluetoothDevice_duringACall_shouldNotSetActiveDeviceToA2dpProfile() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+
+        mController.setActiveBluetoothDevice(mBluetoothDevice);
+
+        verify(mA2dpProfile, times(0)).setActiveDevice(any(BluetoothDevice.class));
+    }
+
+    /**
+     * Default status
+     * Preference should be invisible
+     * Summary should be default summary
+     */
     @Test
     public void updateState_shouldSetSummary() {
         mController.updateState(mPreference);
 
+        assertThat(mPreference.isVisible()).isFalse();
         assertThat(mPreference.getSummary()).isEqualTo(
                 mContext.getText(R.string.media_output_default_summary));
     }
 
     /**
-     * On going call state:
+     * During a call
      * Preference should be invisible
      * Default string should be "Unavailable during calls"
      */
@@ -199,7 +276,7 @@
      */
     @Test
     public void updateState_mediaStreamIsCapturedByCast_shouldDisableAndSetDefaultSummary() {
-        mShadowAudioManager.setStream(DEVICE_OUT_REMOTE_SUBMIX);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_REMOTE_SUBMIX);
 
         mController.updateState(mPreference);
 
@@ -211,12 +288,15 @@
     /**
      * One A2DP Bluetooth device is available and active.
      * Preference should be visible
-     * Preference summary should be activate device name
+     * Preference summary should be the activated device name
      */
     @Test
     public void updateState_oneA2dpBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
         mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
-        when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
 
         mController.updateState(mPreference);
@@ -228,27 +308,22 @@
     /**
      * More than one A2DP Bluetooth devices are available, and second device is active.
      * Preference should be visible
-     * Preference summary should be activate device name
+     * Preference summary should be the activated device name
      */
     @Test
     public void updateState_moreThanOneA2DpBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
-        ShadowBluetoothDevice shadowBluetoothDevice;
         mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
-        BluetoothDevice secondBluetoothDevice;
-        secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
-        shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
-        shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
-        List<BluetoothDevice> connectedDevices = new ArrayList<>(2);
-        connectedDevices.add(mBluetoothDevice);
-        connectedDevices.add(secondBluetoothDevice);
-
-        when(mA2dpProfile.getConnectedDevices()).thenReturn(connectedDevices);
-        when(mA2dpProfile.getActiveDevice()).thenReturn(secondBluetoothDevice);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices.add(mSecondBluetoothDevice);
+        when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mA2dpProfile.getActiveDevice()).thenReturn(mSecondBluetoothDevice);
 
         mController.updateState(mPreference);
 
         assertThat(mPreference.isVisible()).isTrue();
-        assertThat(mPreference.getSummary()).isEqualTo(secondBluetoothDevice.getName());
+        assertThat(mPreference.getSummary()).isEqualTo(mSecondBluetoothDevice.getName());
     }
 
     /**
@@ -259,16 +334,18 @@
     @Test
     public void updateState_a2dpDevicesAvailableWiredHeadsetIsActivated_shouldSetDefaultSummary() {
         mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
-        mShadowAudioManager.setStream(DEVICE_OUT_USB_HEADSET);
-        when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_USB_HEADSET);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
         when(mA2dpProfile.getActiveDevice()).thenReturn(
                 mBluetoothDevice); // BT device is still activated in this case
 
         mController.updateState(mPreference);
 
         assertThat(mPreference.isVisible()).isTrue();
-        String defaultString = mContext.getString(R.string.media_output_default_summary);
-        assertThat(mPreference.getSummary()).isEqualTo(defaultString);
+        assertThat(mPreference.getSummary()).isEqualTo(
+                mContext.getString(R.string.media_output_default_summary));
     }
 
 
@@ -280,13 +357,161 @@
     @Test
     public void updateState_a2dpDevicesAvailableCurrentDeviceActivated_shouldSetDefaultSummary() {
         mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
-        when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
 
         mController.updateState(mPreference);
 
         assertThat(mPreference.isVisible()).isTrue();
-        String defaultString = mContext.getString(R.string.media_output_default_summary);
-        assertThat(mPreference.getSummary()).isEqualTo(defaultString);
+        assertThat(mPreference.getSummary()).isEqualTo(
+                mContext.getString(R.string.media_output_default_summary));
+    }
+
+    /**
+     * One hearing aid profile Bluetooth device is available and active.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     */
+    @Test
+    public void updateState_oneHapBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+    }
+
+    /**
+     * More than one hearing aid profile Bluetooth devices are available, and second
+     * device is active.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     */
+    @Test
+    public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+    }
+
+    /**
+     * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
+     * profile devices with same HisyncId are active. Both of HAP device are active,
+     * "left" side HAP device is added first.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     * ConnectedDevice should not contain second HAP device with same HisyncId
+     */
+    @Test
+    public void updateState_hapBtDeviceWithSameId_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        //with same HisyncId, first one will remain in UI.
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
+        assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isTrue();
+        assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isFalse();
+    }
+
+    /**
+     * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
+     * profile devices with same HisyncId. Both of HAP device are active,
+     * "right" side HAP device is added first.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     * ConnectedDevice should not contain second HAP device with same HisyncId
+     */
+    @Test
+    public void updateState_hapBtDeviceWithSameIdButDifferentOrder_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        //with same HisyncId, first one will remain in UI.
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+        assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isTrue();
+        assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isFalse();
+    }
+
+    /**
+     * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
+     * profile devices with different HisyncId. One of HAP device is active.
+     * Preference should be visible
+     * Preference summary should be the activated device name
+     * ConnectedDevice should contain both HAP device with different HisyncId
+     */
+    @Test
+    public void updateState_hapBtDeviceWithDifferentId_shouldSetActivatedDeviceName() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+        mProfileConnectedDevices.clear();
+        mProfileConnectedDevices.add(mBluetoothDevice);
+        mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
+        mProfileConnectedDevices.add(mRightBluetoothHapDevice);
+        mHearingAidActiveDevices.clear();
+        mHearingAidActiveDevices.add(null);
+        mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
+        when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
+        when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+        when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
+        assertThat(mController.mConnectedDevices).containsExactly(mBluetoothDevice,
+                mLeftBluetoothHapDevice, mRightBluetoothHapDevice);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/SliceTester.java b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
index a7840e8..529e32d 100644
--- a/tests/robotests/src/com/android/settings/testutils/SliceTester.java
+++ b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
@@ -19,10 +19,10 @@
 import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
 import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
-import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED;
-import static com.android.settings.core.BasePreferenceController.UNAVAILABLE_UNKNOWN;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -30,7 +30,9 @@
 import android.content.Context;
 
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import androidx.slice.Slice;
 import androidx.slice.SliceItem;
@@ -151,8 +153,8 @@
         final int availabilityStatus = SliceBuilderUtils.getPreferenceController(context,
                 sliceData).getAvailabilityStatus();
         switch (availabilityStatus) {
-            case DISABLED_UNSUPPORTED:
-            case UNAVAILABLE_UNKNOWN:
+            case UNSUPPORTED_ON_DEVICE:
+            case CONDITIONALLY_UNAVAILABLE:
                 assertThat(primaryPendingIntent).isEqualTo(
                         SliceBuilderUtils.getSettingsIntent(context));
                 break;
@@ -188,7 +190,10 @@
 
     private static void assertKeywords(SliceMetadata metadata, SliceData data) {
         final List<String> keywords = metadata.getSliceKeywords();
-        final List<String> expectedKeywords = Arrays.asList(data.getKeywords().split(","));
+        final Set<String> expectedKeywords = new HashSet<>(
+                Arrays.asList(data.getKeywords().split(",")));
+        expectedKeywords.add(data.getTitle());
+        expectedKeywords.add(data.getScreenTitle().toString());
         assertThat(keywords).containsExactlyElementsIn(expectedKeywords);
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java
index 0de2156..88a0fb6 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAudioManager.java
@@ -41,7 +41,7 @@
 @Implements(value = AudioManager.class, inheritImplementationMethods = true)
 public class ShadowAudioManager extends org.robolectric.shadows.ShadowAudioManager {
     private int mRingerMode;
-    private int mStream;
+    private int mDeviceCodes;
     private boolean mMusicActiveRemotely = false;
     private ArrayList<AudioDeviceCallback> mDeviceCallbacks = new ArrayList();
 
@@ -79,8 +79,8 @@
         return mMusicActiveRemotely;
     }
 
-    public void setStream(int stream) {
-        mStream = stream;
+    public void setOutputDevice(int deviceCodes) {
+        mDeviceCodes = deviceCodes;
     }
 
     @Implementation
@@ -94,7 +94,7 @@
             case STREAM_NOTIFICATION:
             case STREAM_DTMF:
             case STREAM_ACCESSIBILITY:
-                return mStream;
+                return mDeviceCodes;
             default:
                 return 0;
         }
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
index d445c0d..ddbc851 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
@@ -268,6 +268,19 @@
         assertThat(hiddenSpinner.isEnabled()).isTrue();
     }
 
+
+    @Test
+    public void hiddenSpinner_visibilityUpdatesCorrectly() {
+        View hiddenSpinner = mView.findViewById(R.id.hidden_settings);
+        assertThat(hiddenSpinner.isEnabled()).isFalse();
+        assertThat(hiddenSpinner.getVisibility()).isEqualTo(View.GONE);
+
+        mController = new TestWifiConfigController(mConfigUiBase, mView, null /* accessPoint */,
+                WifiConfigUiBase.MODE_CONNECT);
+        assertThat(hiddenSpinner.isEnabled()).isTrue();
+        assertThat(hiddenSpinner.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
     public class TestWifiConfigController extends WifiConfigController {
 
         private TestWifiConfigController(