Merge "Update Accessbility vibration settings." into pi-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e8e45e3..060429e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1114,7 +1114,9 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".slice.SliceDeepLinkSpringBoard"
+        <activity
+            android:name=".slices.SliceDeepLinkSpringBoard"
+            android:excludeFromRecents="true"
             android:theme="@android:style/Theme.NoDisplay">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
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 104b3c8..8bdfb5f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3386,6 +3386,8 @@
     <!-- Master Clear -->
     <!-- Button title to factory data reset the entire device -->
     <string name="master_clear_title">Erase all data (factory reset)</string>
+    <!-- Button title to factory data reset the entire device. The "(factory reset)" part is optional for translation. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=3531267871084279512]-->
+    <string name="master_clear_short_title">Erase all data (factory reset)</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
     <string name="master_clear_desc" product="tablet">"This will erase all data from your tablet\u2019s <b>internal storage</b>, including:\n\n<li>Your Google account</li>\n<li>System and app data and settings</li>\n<li>Downloaded apps</li>"</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
@@ -4927,9 +4929,7 @@
     <string name="device_cellular_network">Mobile network scanning</string>
 
     <!-- Label for list of apps using battery in power use UI. Note: ^1 should be used in all translations[CHAR_LIMIT=120] -->
-    <string name="power_usage_list_summary">App usage since full charge (<xliff:g id="relative_time">^1</xliff:g>)</string>
-    <!-- Label for device components using battery in power use UI. Note: ^1 should be used in all translations[CHAR_LIMIT=120] -->
-    <string name="power_usage_list_summary_device">Device usage since full charge (<xliff:g id="relative_time">^1</xliff:g>)</string>
+    <string name="power_usage_list_summary">Battery usage since full charge</string>
     <!-- Temp string used to debug new battery estimates [DO NOT TRANSLATE] -->
     <string name="power_usage_enhanced_debug" translatable="false"><xliff:g id="time">%1$s</xliff:g> left (New ML est)</string>
     <!-- Temp string used to debug old battery estimates [DO NOT TRANSLATE] -->
@@ -5034,11 +5034,11 @@
     <!-- Summary for the battery high usage tip, which presents battery may run out soon [CHAR LIMIT=NONE] -->
     <string name="battery_tip_high_usage_summary">Battery may run out soon</string>
     <!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
-    <string name="battery_tip_dialog_message" product="default">Your phone has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps since last full charge(<xliff:g id="time_period_ago" example="1 hr ago">%2$s</xliff:g>):</string>
+    <string name="battery_tip_dialog_message" product="default">Your phone has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
     <!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
-    <string name="battery_tip_dialog_message" product="tablet">Your tablet has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps since last full charge(<xliff:g id="time_period_ago" example="1 hr ago">%2$s</xliff:g>):</string>
+    <string name="battery_tip_dialog_message" product="tablet">Your tablet has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
     <!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
-    <string name="battery_tip_dialog_message" product="device">Your device has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps since last full charge(<xliff:g id="time_period_ago" example="1 hr ago">%2$s</xliff:g>):</string>
+    <string name="battery_tip_dialog_message" product="device">Your device has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
     <!-- Title for restricted app preference, showing how many app need to be restricted [CHAR LIMIT=NONE] -->
     <plurals name="battery_tip_restrict_title">
         <item quantity="one">Restrict %1$d app</item>
@@ -7931,6 +7931,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>
 
@@ -9862,6 +9871,9 @@
     <!-- Help URI, battery saver page [DO NOT TRANSLATE] -->
     <string name="help_url_battery_saver_settings" translatable="false"></string>
 
+    <!-- Help URI, smart battery page [DO NOT TRANSLATE] -->
+    <string name="help_uri_smart_battery_settings" translatable="false"></string>
+
     <!-- Title label for new device suggestion, which is displayed in Settings homepage [CHAR LIMIT=100] -->
     <string name="new_device_suggestion_title">What\'s new and exciting?</string>
 
diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml
index ec25520..407feb6 100644
--- a/res/xml/bluetooth_screen.xml
+++ b/res/xml/bluetooth_screen.xml
@@ -15,5 +15,19 @@
 -->
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:key="bluetooth_switchbar_screen"
-    android:title="@string/bluetooth_settings_title" />
\ No newline at end of file
+    android:title="@string/bluetooth_settings_title">
+
+    <com.android.settingslib.RestrictedPreference
+        android:key="bluetooth_screen_add_bt_devices"
+        android:title="@string/connected_device_add_device_title"
+        android:icon="@drawable/ic_menu_add"
+        android:summary="@string/connected_device_add_device_summary"
+        android:fragment="com.android.settings.bluetooth.BluetoothPairingDetail"
+        settings:allowDividerAbove="true"
+        settings:userRestriction="no_config_bluetooth"
+        settings:useAdminDisabledSummary="true"
+        settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
+
+</PreferenceScreen>
\ No newline at end of file
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/MasterClear.java b/src/com/android/settings/MasterClear.java
index 766c6ae..8f573c0 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -102,7 +102,7 @@
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        getActivity().setTitle(R.string.master_clear_title);
+        getActivity().setTitle(R.string.master_clear_short_title);
     }
 
     /**
@@ -114,7 +114,7 @@
     private boolean runKeyguardConfirmation(int request) {
         Resources res = getActivity().getResources();
         return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
-                request, res.getText(R.string.master_clear_title));
+                request, res.getText(R.string.master_clear_short_title));
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java
index 44d5b79..aa6d4f3 100644
--- a/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/MagnificationPreferenceFragment.java
@@ -32,6 +32,8 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
+import com.android.settings.search.actionbar.SearchMenuController;
+import com.android.settings.support.actionbar.HelpResourceProvider;
 
 import java.util.Arrays;
 import java.util.List;
@@ -88,6 +90,10 @@
             // If invoked from SUW, redirect to fragment instrumented for Vision Settings metrics
             preference.setFragment(
                     ToggleScreenMagnificationPreferenceFragmentForSetupWizard.class.getName());
+            Bundle args = preference.getExtras();
+            // Copy from AccessibilitySettingsForSetupWizardActivity, hide search and help menu
+            args.putInt(HelpResourceProvider.HELP_URI_RESOURCE_KEY, 0);
+            args.putBoolean(SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR, false);
         }
         return super.onPreferenceTreeClick(preference);
     }
diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
index 7b7fb5b..5e45c43 100644
--- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
+++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
@@ -27,7 +27,9 @@
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
 import com.android.settings.development.DevelopmentSettingsDashboardFragment;
 import com.android.settings.deviceinfo.StorageDashboardFragment;
+import com.android.settings.display.NightDisplaySettings;
 import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.gestures.GestureSettings;
 import com.android.settings.language.LanguageAndInputSettings;
 import com.android.settings.network.NetworkDashboardFragment;
 import com.android.settings.notification.ConfigureNotificationSettings;
@@ -95,6 +97,10 @@
                 CategoryKey.CATEGORY_SECURITY_LOCKSCREEN);
         PARENT_TO_CATEGORY_KEY_MAP.put(ZenModeSettings.class.getName(),
                 CategoryKey.CATEGORY_DO_NOT_DISTURB);
+        PARENT_TO_CATEGORY_KEY_MAP.put(GestureSettings.class.getName(),
+            CategoryKey.CATEGORY_GESTURES);
+        PARENT_TO_CATEGORY_KEY_MAP.put(NightDisplaySettings.class.getName(),
+            CategoryKey.CATEGORY_NIGHT_LIGHT);
 
         CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());
 
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index b5b98c35..01770a2 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -193,14 +193,7 @@
         }
 
         mBatteryStatsHelper = statsHelper;
-        final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(
-                mBatteryStatsHelper, System.currentTimeMillis());
-        final CharSequence timeSequence = StringUtil.formatRelativeTime(mContext,
-                lastFullChargeTime,
-                false);
-        final int resId = showAllApps ? R.string.power_usage_list_summary_device
-                : R.string.power_usage_list_summary;
-        mAppListGroup.setTitle(TextUtils.expandTemplate(mContext.getText(resId), timeSequence));
+        mAppListGroup.setTitle(R.string.power_usage_list_summary);
 
         final PowerProfile powerProfile = statsHelper.getPowerProfile();
         final BatteryStats stats = statsHelper.getStats();
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index f78d236..a1f5df2 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -521,7 +521,7 @@
             return true;
         }
 
-        return isSystemUid(uid) || powerWhitelistBackend.isSysWhitelistedExceptIdle(packageNames)
+        return isSystemUid(uid) || powerWhitelistBackend.isWhitelisted(packageNames)
                 || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames));
     }
 
diff --git a/src/com/android/settings/fuelgauge/SmartBatterySettings.java b/src/com/android/settings/fuelgauge/SmartBatterySettings.java
index 5e63976..d954e83 100644
--- a/src/com/android/settings/fuelgauge/SmartBatterySettings.java
+++ b/src/com/android/settings/fuelgauge/SmartBatterySettings.java
@@ -60,6 +60,11 @@
     }
 
     @Override
+    public int getHelpResource() {
+        return R.string.help_uri_smart_battery_settings;
+    }
+
+    @Override
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
         return buildPreferenceControllers(context, (SettingsActivity) getActivity(), this);
     }
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
index 2fac9b6..b6bcd54 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
@@ -71,7 +71,8 @@
 
     private final Object mLock = new Object();
     @GuardedBy("mLock")
-    private boolean mIsJobCanceled = false;
+    @VisibleForTesting
+    boolean mIsJobCanceled = false;
 
     public static void scheduleAnomalyDetection(Context context, Intent intent) {
         final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
@@ -89,6 +90,9 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        synchronized (mLock) {
+            mIsJobCanceled = false;
+        }
         ThreadUtils.postOnBackgroundThread(() -> {
             final Context context = AnomalyDetectionJobService.this;
             final BatteryDatabaseManager batteryDatabaseManager =
@@ -96,18 +100,15 @@
             final BatteryTipPolicy policy = new BatteryTipPolicy(this);
             final BatteryUtils batteryUtils = BatteryUtils.getInstance(this);
             final ContentResolver contentResolver = getContentResolver();
-            final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(this,
-                    true /* collectBatteryBroadcast */);
             final UserManager userManager = getSystemService(UserManager.class);
             final PowerWhitelistBackend powerWhitelistBackend = PowerWhitelistBackend.getInstance();
             final PowerUsageFeatureProvider powerUsageFeatureProvider = FeatureFactory
                     .getFactory(this).getPowerUsageFeatureProvider(this);
             final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory
                     .getFactory(this).getMetricsFeatureProvider();
-            batteryUtils.initBatteryStatsHelper(batteryStatsHelper, null /* bundle */, userManager);
 
             for (JobWorkItem item = dequeueWork(params); item != null; item = dequeueWork(params)) {
-                saveAnomalyToDatabase(context, batteryStatsHelper, userManager,
+                saveAnomalyToDatabase(context, userManager,
                         batteryDatabaseManager, batteryUtils, policy, powerWhitelistBackend,
                         contentResolver, powerUsageFeatureProvider, metricsFeatureProvider,
                         item.getIntent().getExtras());
@@ -128,8 +129,7 @@
     }
 
     @VisibleForTesting
-    void saveAnomalyToDatabase(Context context, BatteryStatsHelper batteryStatsHelper,
-            UserManager userManager,
+    void saveAnomalyToDatabase(Context context, UserManager userManager,
             BatteryDatabaseManager databaseManager, BatteryUtils batteryUtils,
             BatteryTipPolicy policy, PowerWhitelistBackend powerWhitelistBackend,
             ContentResolver contentResolver, PowerUsageFeatureProvider powerUsageFeatureProvider,
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
index ac6d622..3870e72 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -94,10 +94,7 @@
 
                 return new AlertDialog.Builder(context)
                         .setMessage(getString(R.string.battery_tip_dialog_message,
-                                highUsageTip.getHighUsageAppList().size(),
-                                StringUtil.formatRelativeTime(context,
-                                        highUsageTip.getLastFullChargeTimeMs(),
-                                        false /* withSeconds */)))
+                                highUsageTip.getHighUsageAppList().size()))
                         .setView(view)
                         .setPositiveButton(android.R.string.ok, null)
                         .create();
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..7743372 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) {
@@ -221,14 +230,15 @@
         appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
         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);
+        lowBatteryEnabled = mParser.getBoolean(KEY_LOW_BATTERY_ENABLED, true);
+        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/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index be25518..9e64c71 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -94,6 +94,7 @@
             case BatteryTip.TipType.SMART_BATTERY_MANAGER:
                 return new SmartBatteryAction(settingsActivity, fragment);
             case BatteryTip.TipType.BATTERY_SAVER:
+            case BatteryTip.TipType.LOW_BATTERY:
                 if (batteryTip.getState() == BatteryTip.StateType.HANDLED) {
                     return new OpenBatterySaverAction(settingsActivity);
                 } else {
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/SwipeUpPreferenceController.java b/src/com/android/settings/gestures/SwipeUpPreferenceController.java
index 8afd13b..27c7241 100644
--- a/src/com/android/settings/gestures/SwipeUpPreferenceController.java
+++ b/src/com/android/settings/gestures/SwipeUpPreferenceController.java
@@ -24,6 +24,8 @@
 import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
 
+import com.android.settings.R;
+
 public class SwipeUpPreferenceController extends GesturePreferenceController {
 
     private final int ON = 1;
@@ -39,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)
@@ -74,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/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/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/search/DeviceIndexFeatureProvider.java b/src/com/android/settings/search/DeviceIndexFeatureProvider.java
index 37978df..a171844 100644
--- a/src/com/android/settings/search/DeviceIndexFeatureProvider.java
+++ b/src/com/android/settings/search/DeviceIndexFeatureProvider.java
@@ -47,17 +47,19 @@
             List<String> keywords);
 
     default void updateIndex(Context context, boolean force) {
-        if (!isIndexingEnabled()) return;
+        if (!isIndexingEnabled()) {
+            return;
+        }
 
         if (!force && Objects.equals(
-                Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION),  VERSION)) {
+                Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION), VERSION)) {
             // No need to update.
             return;
         }
 
-        ComponentName jobComponent = new ComponentName(context.getPackageName(),
+        final ComponentName jobComponent = new ComponentName(context.getPackageName(),
                 DeviceIndexUpdateJobService.class.getName());
-        int jobId = context.getResources().getInteger(R.integer.device_index_update);
+        final int jobId = context.getResources().getInteger(R.integer.device_index_update);
         // Schedule a job so that we know it'll be able to complete, but try to run as
         // soon as possible.
         context.getSystemService(JobScheduler.class).schedule(
@@ -70,11 +72,10 @@
         Settings.Secure.putString(context.getContentResolver(), INDEX_VERSION, VERSION);
     }
 
-    static String createDeepLink(String s) {
+    static Uri createDeepLink(String s) {
         return new Uri.Builder().scheme(SETTINGS)
                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
                 .appendQueryParameter(INTENT, s)
-                .build()
-                .toString();
+                .build();
     }
 }
diff --git a/src/com/android/settings/search/DeviceIndexUpdateJobService.java b/src/com/android/settings/search/DeviceIndexUpdateJobService.java
index 573dcdf..12a9cf0 100644
--- a/src/com/android/settings/search/DeviceIndexUpdateJobService.java
+++ b/src/com/android/settings/search/DeviceIndexUpdateJobService.java
@@ -17,7 +17,6 @@
 import static android.app.slice.Slice.HINT_LARGE;
 import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
-
 import static com.android.settings.search.DeviceIndexFeatureProvider.createDeepLink;
 
 import android.app.job.JobParameters;
@@ -73,16 +72,20 @@
 
     @VisibleForTesting
     protected void updateIndex(JobParameters params) {
-        if (DEBUG) Log.d(TAG, "Starting index");
-        DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(
-                this).getDeviceIndexFeatureProvider();
-        SliceManager manager = getSliceManager();
-        Uri baseUri = new Builder()
+        if (DEBUG) {
+            Log.d(TAG, "Starting index");
+        }
+        final DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(this)
+                .getDeviceIndexFeatureProvider();
+        final SliceManager manager = getSliceManager();
+        final Uri baseUri = new Builder()
                 .scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(SettingsSliceProvider.SLICE_AUTHORITY)
                 .build();
-        Collection<Uri> slices = manager.getSliceDescendants(baseUri);
-        if (DEBUG) Log.d(TAG, "Indexing " + slices.size() + " slices");
+        final Collection<Uri> slices = manager.getSliceDescendants(baseUri);
+        if (DEBUG) {
+            Log.d(TAG, "Indexing " + slices.size() + " slices");
+        }
 
         for (Uri slice : slices) {
             if (!mRunningJob) {
@@ -93,16 +96,20 @@
             SliceMetadata metaData = getMetadata(loadedSlice);
             CharSequence title = findTitle(loadedSlice, metaData);
             if (title != null) {
-                if (DEBUG) Log.d(TAG, "Indexing: " + slice + " " + title + " " + loadedSlice);
-                indexProvider.index(this, title, slice, Uri.parse(createDeepLink(
+                if (DEBUG) {
+                    Log.d(TAG, "Indexing: " + slice + " " + title + " " + loadedSlice);
+                }
+                indexProvider.index(this, title, slice, createDeepLink(
                         new Intent(SliceDeepLinkSpringBoard.ACTION_VIEW_SLICE)
                                 .setPackage(getPackageName())
                                 .putExtra(SliceDeepLinkSpringBoard.EXTRA_SLICE, slice.toString())
-                                .toUri(Intent.URI_ANDROID_APP_SCHEME))),
+                                .toUri(Intent.URI_ANDROID_APP_SCHEME)),
                         metaData.getSliceKeywords());
             }
         }
-        if (DEBUG) Log.d(TAG, "Done indexing");
+        if (DEBUG) {
+            Log.d(TAG, "Done indexing");
+        }
         jobFinished(params, false);
     }
 
diff --git a/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java b/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
index 55448e2..22742e5 100644
--- a/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
+++ b/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
@@ -23,6 +23,7 @@
 import android.os.UserManager;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.Utils;
@@ -32,10 +33,14 @@
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
 public class VisiblePatternProfilePreferenceController extends TogglePreferenceController
         implements LifecycleObserver, OnResume {
 
     private static final String KEY_VISIBLE_PATTERN_PROFILE = "visiblepattern_profile";
+    private static final String TAG = "VisPtnProfPrefCtrl";
 
     private final LockPatternUtils mLockPatternUtils;
     private final UserManager mUm;
@@ -45,7 +50,7 @@
     private Preference mPreference;
 
     public VisiblePatternProfilePreferenceController(Context context) {
-         this(context, null /* lifecycle */);
+        this(context, null /* lifecycle */);
     }
 
     // TODO (b/73074893) Replace this constructor without Lifecycle using setter method instead.
@@ -63,12 +68,25 @@
 
     @Override
     public int getAvailabilityStatus() {
-        if (mLockPatternUtils.isSecure(mProfileChallengeUserId)
-                && mLockPatternUtils.getKeyguardStoredPasswordQuality(mProfileChallengeUserId)
-                == PASSWORD_QUALITY_SOMETHING) {
-            return AVAILABLE;
+        final FutureTask<Integer> futureTask = new FutureTask<>(
+                // Put the API call in a future to avoid StrictMode violation.
+                () -> {
+                    final boolean isSecure = mLockPatternUtils.isSecure(mProfileChallengeUserId);
+                    final boolean hasPassword = mLockPatternUtils
+                            .getKeyguardStoredPasswordQuality(mProfileChallengeUserId)
+                            == PASSWORD_QUALITY_SOMETHING;
+                    if (isSecure && hasPassword) {
+                        return AVAILABLE;
+                    }
+                    return DISABLED_FOR_USER;
+                });
+        try {
+            futureTask.run();
+            return futureTask.get();
+        } catch (InterruptedException | ExecutionException e) {
+            Log.w(TAG, "Error getting lock pattern state.");
+            return DISABLED_FOR_USER;
         }
-        return DISABLED_FOR_USER;
     }
 
     @Override
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 6d6c0ec..af165da 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -26,7 +26,6 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
-import android.os.StrictMode;
 import android.provider.Settings;
 import android.provider.SettingsSlicesContract;
 import android.support.annotation.VisibleForTesting;
@@ -155,11 +154,6 @@
 
     @Override
     public Slice onBindSlice(Uri sliceUri) {
-        // TODO: Remove this when all slices are not breaking strict mode
-        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
-                .permitAll()
-                .build());
-
         String path = sliceUri.getPath();
         // If adding a new Slice, do not directly match Slice URIs.
         // Use {@link SlicesDatabaseAccessor}.
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index 4e9ee4e..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<>();
     }
 
     /**
@@ -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/SystemUpdatePreferenceController.java b/src/com/android/settings/system/SystemUpdatePreferenceController.java
index 7bc76a2..88715bf 100644
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.java
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.java
@@ -35,6 +35,9 @@
 import com.android.settings.Utils;
 import com.android.settings.core.BasePreferenceController;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
 public class SystemUpdatePreferenceController extends BasePreferenceController {
 
     private static final String TAG = "SysUpdatePrefContr";
@@ -84,9 +87,19 @@
 
     @Override
     public CharSequence getSummary() {
-        final Bundle updateInfo = mUpdateManager.retrieveSystemUpdateInfo();
         CharSequence summary = mContext.getString(R.string.android_version_summary,
                 Build.VERSION.RELEASE);
+        final FutureTask<Bundle> bundleFutureTask = new FutureTask<>(
+                // Put the API call in a future to avoid StrictMode violation.
+                () -> mUpdateManager.retrieveSystemUpdateInfo());
+        final Bundle updateInfo;
+        try {
+            bundleFutureTask.run();
+            updateInfo = bundleFutureTask.get();
+        } catch (InterruptedException | ExecutionException e) {
+            Log.w(TAG, "Error getting system update info.");
+            return summary;
+        }
         switch (updateInfo.getInt(SystemUpdateManager.KEY_STATUS)) {
             case SystemUpdateManager.STATUS_WAITING_DOWNLOAD:
             case SystemUpdateManager.STATUS_IN_PROGRESS:
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
index f710613..4f7b3ba 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@@ -106,7 +106,7 @@
 
     void startTether() {
         mSwitchBar.setEnabled(false);
-        mConnectivityManager.startTethering(TETHERING_WIFI, false /* showProvisioningUi */,
+        mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */,
                 mOnStartTetheringCallback, new Handler(Looper.getMainLooper()));
     }
 
diff --git a/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java b/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java
index 8df4a31..e3f9092 100644
--- a/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/display/NightDisplaySettingsTest.java
@@ -22,6 +22,7 @@
 import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
+import com.android.settingslib.drawer.CategoryKey;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RuntimeEnvironment;
@@ -45,4 +46,10 @@
 
         assertThat(indexedXml).contains(R.xml.night_display_settings);
     }
+
+    @Test
+    public void getCategoryKey_isCategoryNightLight() {
+        NightDisplaySettings settings = new NightDisplaySettings();
+        assertThat(settings.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_NIGHT_LIGHT);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index 5c2a1c7..4fd3a82 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -612,6 +612,14 @@
     }
 
     @Test
+    public void testShouldHideAnomaly_AppInDozeList_returnTrue() {
+        doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
+        doReturn(true).when(mPowerWhitelistBackend).isWhitelisted(new String[]{HIGH_SDK_PACKAGE});
+
+        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerWhitelistBackend, UID)).isTrue();
+    }
+
+    @Test
     public void testShouldHideAnomaly_normalApp_returnFalse() {
         doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
index f159e79..8df1f21 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
@@ -46,8 +46,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.os.Process;
 import android.os.StatsDimensionsValue;
 import android.os.UserManager;
@@ -57,11 +55,11 @@
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.settings.R;
 import com.android.settings.fuelgauge.BatteryUtils;
-import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowConnectivityManager;
 import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
+import com.android.settings.testutils.shadow.ShadowPowerWhitelistBackend;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -80,7 +78,7 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(shadows = ShadowConnectivityManager.class)
+@Config(shadows = {ShadowConnectivityManager.class, ShadowPowerWhitelistBackend.class})
 public class AnomalyDetectionJobServiceTest {
     private static final int UID = 12345;
     private static final String SYSTEM_PACKAGE = "com.android.system";
@@ -91,8 +89,6 @@
     private static final int ANOMALY_TYPE = 6;
     private static final long VERSION_CODE = 15;
     @Mock
-    private BatteryStatsHelper mBatteryStatsHelper;
-    @Mock
     private UserManager mUserManager;
     @Mock
     private BatteryDatabaseManager mBatteryDatabaseManager;
@@ -148,9 +144,9 @@
     @Test
     public void saveAnomalyToDatabase_systemWhitelisted_doNotSave() {
         doReturn(UID).when(mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
-        doReturn(true).when(mPowerWhitelistBackend).isSysWhitelistedExceptIdle(any(String[].class));
+        doReturn(true).when(mPowerWhitelistBackend).isWhitelisted(any(String[].class));
 
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
                 mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
                 mPowerWhitelistBackend, mContext.getContentResolver(),
                 mFeatureFactory.powerUsageFeatureProvider,
@@ -171,7 +167,7 @@
                 mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
         doReturn(true).when(mBatteryUtils).shouldHideAnomaly(any(), anyInt());
 
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
                 mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
                 mPowerWhitelistBackend, mContext.getContentResolver(),
                 mFeatureFactory.powerUsageFeatureProvider,
@@ -191,7 +187,7 @@
         doReturn(Process.SYSTEM_UID).when(
                 mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
 
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
                 mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
                 mPowerWhitelistBackend, mContext.getContentResolver(),
                 mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -206,7 +202,7 @@
         doReturn(AnomalyDetectionJobService.UID_NULL).when(
                 mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
 
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
                 mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
                 mPowerWhitelistBackend, mContext.getContentResolver(),
                 mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -226,7 +222,7 @@
         doReturn(Process.FIRST_APPLICATION_UID).when(
                 mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
 
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
                 mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
                 mPowerWhitelistBackend, mContext.getContentResolver(),
                 mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -251,7 +247,7 @@
         doReturn(Process.FIRST_APPLICATION_UID).when(
                 mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
 
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext,
                 mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
                 mPowerWhitelistBackend, mContext.getContentResolver(),
                 mFeatureFactory.powerUsageFeatureProvider, mFeatureFactory.metricsFeatureProvider,
@@ -316,4 +312,12 @@
         // Should not crash even job is stopped
         mAnomalyDetectionJobService.completeWork(mJobParameters, mJobWorkItem);
     }
+
+    @Test
+    public void restartWorkAfterBeenStopped_jobStarted() {
+        mAnomalyDetectionJobService.onStopJob(mJobParameters);
+        mAnomalyDetectionJobService.onStartJob(mJobParameters);
+
+        assertThat(mAnomalyDetectionJobService.mIsJobCanceled).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
index 7a41921..a02bdc2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
@@ -113,7 +113,7 @@
         ShadowAlertDialog shadowDialog = shadowOf(dialog);
 
         assertThat(shadowDialog.getMessage()).isEqualTo(
-                mContext.getString(R.string.battery_tip_dialog_message, 1, "60 minutes ago"));
+                mContext.getString(R.string.battery_tip_dialog_message, 1));
     }
 
     @Test
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..547e0eb 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
@@ -99,12 +101,13 @@
         assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
         assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse();
         assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);
-        assertThat(batteryTipPolicy.lowBatteryEnabled).isFalse();
-        assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(16);
+        assertThat(batteryTipPolicy.lowBatteryEnabled).isTrue();
+        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/BatteryTipUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
index 7a25555..459c4e2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
@@ -29,6 +29,7 @@
 import com.android.settings.fuelgauge.batterytip.actions.RestrictAppAction;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.EarlyWarningTip;
+import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -51,6 +52,7 @@
     private InstrumentedPreferenceFragment mFragment;
     private RestrictAppTip mRestrictAppTip;
     private EarlyWarningTip mEarlyWarningTip;
+    private LowBatteryTip mLowBatteryTip;
 
     @Before
     public void setUp() {
@@ -59,7 +61,11 @@
         FakeFeatureFactory.setupForTest();
         doReturn(RuntimeEnvironment.application).when(mFragment).getContext();
         mRestrictAppTip = spy(new RestrictAppTip(BatteryTip.StateType.NEW, new ArrayList<>()));
-        mEarlyWarningTip = spy(new EarlyWarningTip(BatteryTip.StateType.NEW, true));
+        mEarlyWarningTip = spy(
+                new EarlyWarningTip(BatteryTip.StateType.NEW, true /* powerSaveModeOn */));
+        mLowBatteryTip = spy(
+                new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */,
+                        "" /* summary */));
     }
 
     @Test
@@ -93,4 +99,20 @@
         assertThat(BatteryTipUtils.getActionForBatteryTip(mEarlyWarningTip, mSettingsActivity,
                 mFragment)).isInstanceOf(OpenBatterySaverAction.class);
     }
+
+    @Test
+    public void testGetActionForBatteryTip_typeLowBatteryStateNew_returnActionBatterySaver() {
+        when(mLowBatteryTip.getState()).thenReturn(BatteryTip.StateType.NEW);
+
+        assertThat(BatteryTipUtils.getActionForBatteryTip(mLowBatteryTip, mSettingsActivity,
+                mFragment)).isInstanceOf(BatterySaverAction.class);
+    }
+
+    @Test
+    public void testGetActionForBatteryTip_typeLowBatteryStateHandled_returnActionOpen() {
+        when(mLowBatteryTip.getState()).thenReturn(BatteryTip.StateType.HANDLED);
+
+        assertThat(BatteryTipUtils.getActionForBatteryTip(mLowBatteryTip, mSettingsActivity,
+                mFragment)).isInstanceOf(OpenBatterySaverAction.class);
+    }
 }
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/GestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/GestureSettingsTest.java
new file mode 100644
index 0000000..29d2c19
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/gestures/GestureSettingsTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.gestures;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.drawer.CategoryKey;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class GestureSettingsTest {
+
+    @Test
+    public void getCategoryKey_isCategoryGestures() {
+        GestureSettings settings = new GestureSettings();
+        assertThat(settings.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_GESTURES);
+    }
+}
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/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/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 0fda33e..a0bd33a 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -18,20 +18,17 @@
 package com.android.settings.slices;
 
 import static android.content.ContentResolver.SCHEME_CONTENT;
-
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.app.slice.SliceManager;
-import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
+import android.os.StrictMode;
 import android.provider.SettingsSlicesContract;
 
 import com.android.settings.testutils.DatabaseTestUtils;
@@ -44,29 +41,30 @@
 import org.junit.runner.RunWith;
 import org.robolectric.RuntimeEnvironment;
 
-import androidx.slice.Slice;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 
+import androidx.slice.Slice;
+
 /**
  * TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
  */
 @RunWith(SettingsRobolectricTestRunner.class)
 public class SettingsSliceProviderTest {
 
-    private final String KEY = "KEY";
-    private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
-    private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY;
-    private final String TITLE = "title";
-    private final String SUMMARY = "summary";
-    private final String SCREEN_TITLE = "screen title";
-    private final String FRAGMENT_NAME = "fragment name";
-    private final int ICON = 1234; // I declare a thumb war
-    private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
-    private final String PREF_CONTROLLER = FakeToggleController.class.getName();
+    private static final String KEY = "KEY";
+    private static final String INTENT_PATH =
+            SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
+    private static final String TITLE = "title";
+    private static final String SUMMARY = "summary";
+    private static final String SCREEN_TITLE = "screen title";
+    private static final String FRAGMENT_NAME = "fragment name";
+    private static final int ICON = 1234; // I declare a thumb war
+    private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
+    private static final String PREF_CONTROLLER = FakeToggleController.class.getName();
+
     private Context mContext;
     private SettingsSliceProvider mProvider;
     private SQLiteDatabase mDb;
@@ -152,6 +150,18 @@
     }
 
     @Test
+    public void onBindSlice_shouldNotOverrideStrictMode() {
+        final StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
+        SliceData data = getDummyData();
+        mProvider.mSliceWeakDataCache.put(data.getUri(), data);
+        mProvider.onBindSlice(data.getUri());
+
+        final StrictMode.ThreadPolicy newThreadPolicy = StrictMode.getThreadPolicy();
+
+        assertThat(newThreadPolicy.toString()).isEqualTo(oldThreadPolicy.toString());
+    }
+
+    @Test
     public void testLoadSlice_cachedEntryRemovedOnUnpin() {
         SliceData data = getDummyData();
         mProvider.mSliceDataCache.put(data.getUri(), data);
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index cc1785b..272fd20 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -17,6 +17,15 @@
 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.CONDITIONALLY_UNAVAILABLE;
 
@@ -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);
@@ -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/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/testutils/shadow/ShadowPowerWhitelistBackend.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerWhitelistBackend.java
new file mode 100644
index 0000000..4500b23
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerWhitelistBackend.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.testutils.shadow;
+
+import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
+
+import org.robolectric.annotation.Implements;
+
+@Implements(PowerWhitelistBackend.class)
+public class ShadowPowerWhitelistBackend {
+    public void __constructor__() {
+        // Do nothing
+    }
+}
diff --git a/tests/unit/src/com/android/settings/slices/SliceDeepLinkSpringBoardTest.java b/tests/unit/src/com/android/settings/slices/SliceDeepLinkSpringBoardTest.java
new file mode 100644
index 0000000..e7c5d6e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/slices/SliceDeepLinkSpringBoardTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.slices;
+
+import static com.android.settings.search.DeviceIndexFeatureProvider.createDeepLink;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SliceDeepLinkSpringBoardTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void launcheDeepLinkIntent_shouldNotCrash() {
+        final Uri springBoardIntentUri = createDeepLink(
+                new Intent(SliceDeepLinkSpringBoard.ACTION_VIEW_SLICE)
+                        .setPackage(mContext.getPackageName())
+                        .putExtra(SliceDeepLinkSpringBoard.EXTRA_SLICE,
+                                "content://com.android.settings.slices/action/test_slice")
+                        .toUri(Intent.URI_ANDROID_APP_SCHEME));
+
+        final Intent deepLinkIntent = new Intent(Intent.ACTION_VIEW)
+                .setData(springBoardIntentUri)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        mContext.startActivity(deepLinkIntent);
+    }
+}