Merge changes I13495cad,I3a44c4c4,I15bff230,I8a492866,Ia7ffe34a

* changes:
  [Pair hearing devices] Add pair hearing device functionality
  [Pair hearing devices] Extract common behavior in BluetoothPairingDetail into Base class
  [Pair hearing devices] Add "Hearing devices" to show connected hearing devices
  [Pair hearing devices] Add "Saved devices" to show bonded but not connected hearing devices
  [Audio routing] Setup basic structure for audio routing page
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index e4bd550..f0de6d0 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -265,6 +265,23 @@
         <item>5</item>
     </string-array>
 
+    <!-- Bluetooth audio routing settings: Titles for audio routing options -->
+    <string-array name="bluetooth_audio_routing_titles">
+        <item>Decide automatically</item>
+        <item>Play on hearing device</item>
+        <item>Play on phone speaker</item>
+    </string-array>
+
+    <!-- Bluetooth audio routing settings: Values for audio routing options -->
+    <string-array name="bluetooth_audio_routing_values" translatable="false">
+        <!-- Decide automatically -->
+        <item>0</item>
+        <!-- Play on hearing device -->
+        <item>1</item>
+        <!-- Play on phone speaker -->
+        <item>2</item>
+    </string-array>
+
     <!-- Match this with drawable.wifi_signal. --> <skip />
     <!-- Wi-Fi settings. The signal strength a Wi-Fi network has. -->
     <string-array name="wifi_signal">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 83698eb..31dd0b8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4545,6 +4545,8 @@
     <!-- Title for the hearing device pairing preference. [CHAR LIMIT=20] -->
     <string name="accessibility_hearing_device_pairing_title">Pair new device</string>
     <!-- Title for the preference category containing the connected hearing device group. [CHAR LIMIT=20]-->
+    <string name="accessibility_hearing_device_connected_title">Hearing devices</string>
+    <!-- Title for the preference category containing the previously connected hearing device group. [CHAR LIMIT=20]-->
     <string name="accessibility_hearing_device_saved_title">Saved devices</string>
     <!-- Title for the preference category containing the controls of the hearing device. [CHAR LIMIT=35] -->
     <string name="accessibility_hearing_device_control">Hearing device controls</string>
@@ -4559,7 +4561,7 @@
     <!-- Title for the pair hearing device page. [CHAR LIMIT=25] -->
     <string name="accessibility_hearing_device_pairing_page_title">Pair hearing device</string>
     <!-- Title for the preference category containing the list of the available hearing during and after bluetooth scanning devices. [CHAR LIMIT=30] -->
-    <string name="accessibility_found_hearing_devices">Available devices</string>
+    <string name="accessibility_found_hearing_devices">Available hearing devices</string>
     <!-- Title for the preference category containing the all bluetooth devices during and after bluetooth scanning devices. Used when people can not find their hearing device in hearing device pairing list. [CHAR LIMIT=45] -->
     <string name="accessibility_found_all_devices">Don\u2019t see your hearing device?</string>
     <!-- Title for listing all bluetooth devices preference in the accessibility page. [CHAR LIMIT=40] -->
diff --git a/res/xml/accessibility_hearing_aids.xml b/res/xml/accessibility_hearing_aids.xml
index f5e65ae..76910a0 100644
--- a/res/xml/accessibility_hearing_aids.xml
+++ b/res/xml/accessibility_hearing_aids.xml
@@ -19,13 +19,25 @@
     android:key="accessibility_hearing_devices_screen"
     android:title="@string/accessibility_hearingaid_title">
 
+    <PreferenceCategory
+        android:key="available_hearing_devices"
+        android:title="@string/accessibility_hearing_device_connected_title"
+        settings:controller="com.android.settings.accessibility.AvailableHearingDevicePreferenceController"/>
+
     <com.android.settingslib.RestrictedPreference
         android:key="add_bt_devices"
         android:title="@string/bluetooth_pairing_pref_title"
         android:icon="@drawable/ic_add_24dp"
         android:summary="@string/connected_device_add_device_summary"
+        android:fragment="com.android.settings.accessibility.HearingDevicePairingDetail"
         settings:userRestriction="no_config_bluetooth"
-        settings:useAdminDisabledSummary="true" />
+        settings:useAdminDisabledSummary="true"
+        settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
+
+    <PreferenceCategory
+        android:key="previously_connected_hearing_devices"
+        android:title="@string/accessibility_hearing_device_saved_title"
+        settings:controller="com.android.settings.accessibility.SavedHearingDevicePreferenceController"/>
 
     <PreferenceCategory
         android:key="device_control_category"
diff --git a/res/xml/bluetooth_audio_routing_fragment.xml b/res/xml/bluetooth_audio_routing_fragment.xml
new file mode 100644
index 0000000..18f18f2
--- /dev/null
+++ b/res/xml/bluetooth_audio_routing_fragment.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/bluetooth_audio_routing_title">
+
+    <com.android.settingslib.widget.TopIntroPreference
+        android:key="bluetooth_audio_routing_top_intro"
+        android:title="@string/bluetooth_audio_routing_summary"
+        settings:searchable="false"/>
+
+    <ListPreference
+        android:entries="@array/bluetooth_audio_routing_titles"
+        android:entryValues="@array/bluetooth_audio_routing_values"
+        android:summary="%s"
+        android:key="audio_routing_ringtone"
+        android:persistent="false"
+        android:title="@string/bluetooth_ringtone_title" />
+
+    <ListPreference
+        android:entries="@array/bluetooth_audio_routing_titles"
+        android:entryValues="@array/bluetooth_audio_routing_values"
+        android:summary="%s"
+        android:key="audio_routing_call"
+        android:persistent="false"
+        android:title="@string/bluetooth_call_title" />
+
+    <ListPreference
+        android:entries="@array/bluetooth_audio_routing_titles"
+        android:entryValues="@array/bluetooth_audio_routing_values"
+        android:summary="%s"
+        android:key="audio_routing_media"
+        android:persistent="false"
+        android:title="@string/bluetooth_media_title" />
+
+    <ListPreference
+        android:entries="@array/bluetooth_audio_routing_titles"
+        android:entryValues="@array/bluetooth_audio_routing_values"
+        android:summary="%s"
+        android:key="audio_routing_system_sounds"
+        android:persistent="false"
+        android:title="@string/bluetooth_system_sounds_title" />
+
+</PreferenceScreen>
diff --git a/res/xml/bluetooth_pairing_detail.xml b/res/xml/bluetooth_pairing_detail.xml
index 86fb9e4..460a708 100644
--- a/res/xml/bluetooth_pairing_detail.xml
+++ b/res/xml/bluetooth_pairing_detail.xml
@@ -27,7 +27,7 @@
 
     <com.android.settings.bluetooth.BluetoothProgressCategory
         android:key="available_devices"
-        android:title="@string/bluetooth_paired_device_title"/>
+        android:title="@string/bluetooth_preference_found_media_devices"/>
 
     <com.android.settingslib.widget.FooterPreference/>
 
diff --git a/res/xml/hearing_device_pairing_detail.xml b/res/xml/hearing_device_pairing_detail.xml
new file mode 100644
index 0000000..65cc6c72
--- /dev/null
+++ b/res/xml/hearing_device_pairing_detail.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/bluetooth_pairing_pref_title">
+
+    <com.android.settings.bluetooth.BluetoothProgressCategory
+        android:key="available_hearing_devices"
+        android:title="@string/accessibility_found_hearing_devices" />
+
+    <PreferenceCategory
+        android:key="device_control_category"
+        android:title="@string/accessibility_found_all_devices">
+        <com.android.settingslib.RestrictedPreference
+            android:key="add_bt_devices"
+            android:title="@string/accessibility_list_all_devices_title"
+            android:fragment="com.android.settings.bluetooth.BluetoothPairingDetail"
+            settings:userRestriction="no_config_bluetooth"
+            settings:useAdminDisabledSummary="true" />
+    </PreferenceCategory>
+
+    <com.android.settings.accessibility.AccessibilityFooterPreference
+        android:key="hearing_device_footer"
+        android:title="@string/accessibility_hearing_device_footer_summary"
+        android:selectable="false"
+        settings:searchable="false" />
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index cec48bb..cd76b47 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -29,7 +29,6 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.FeatureFlagUtils;
-import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.FragmentManager;
@@ -55,8 +54,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
 import java.util.stream.Collectors;
 
 /**
@@ -84,7 +81,8 @@
 
     public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
-        mLocalBluetoothManager = getLocalBluetoothManager();
+        mLocalBluetoothManager = com.android.settings.bluetooth.Utils.getLocalBluetoothManager(
+                context);
         mProfileManager = mLocalBluetoothManager.getProfileManager();
         mCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager();
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -269,19 +267,6 @@
         return false;
     }
 
-    private LocalBluetoothManager getLocalBluetoothManager() {
-        final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>(
-                // Avoid StrictMode ThreadPolicy violation
-                () -> com.android.settings.bluetooth.Utils.getLocalBtManager(mContext));
-        try {
-            localBtManagerFutureTask.run();
-            return localBtManagerFutureTask.get();
-        } catch (InterruptedException | ExecutionException e) {
-            Log.w(TAG, "Error getting LocalBluetoothManager.", e);
-            return null;
-        }
-    }
-
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     void setPreference(Preference preference) {
         mHearingAidPreference = preference;
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
index 5df8afb..85783b73 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
@@ -19,6 +19,7 @@
 import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -45,6 +46,13 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        use(AvailableHearingDevicePreferenceController.class).init(this);
+        use(SavedHearingDevicePreferenceController.class).init(this);
+    }
+
+    @Override
     public void onCreate(Bundle savedInstanceState) {
         mFeatureName = getContext().getString(R.string.accessibility_hearingaid_title);
         super.onCreate(savedInstanceState);
diff --git a/src/com/android/settings/accessibility/AvailableHearingDevicePreferenceController.java b/src/com/android/settings/accessibility/AvailableHearingDevicePreferenceController.java
new file mode 100644
index 0000000..076432c
--- /dev/null
+++ b/src/com/android/settings/accessibility/AvailableHearingDevicePreferenceController.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import androidx.fragment.app.FragmentManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.BluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Controller to update the {@link androidx.preference.PreferenceCategory} for all
+ * connected hearing devices, including ASHA and HAP profile.
+ * Parent class {@link BaseBluetoothDevicePreferenceController} will use
+ * {@link DevicePreferenceCallback} to add/remove {@link Preference}.
+ */
+public class AvailableHearingDevicePreferenceController extends
+        BaseBluetoothDevicePreferenceController implements LifecycleObserver, OnStart, OnStop,
+        BluetoothCallback {
+
+    private static final String TAG = "AvailableHearingDevicePreferenceController";
+
+    private BluetoothDeviceUpdater mAvailableHearingDeviceUpdater;
+    private final LocalBluetoothManager mLocalBluetoothManager;
+    private FragmentManager mFragmentManager;
+
+    public AvailableHearingDevicePreferenceController(Context context,
+            String preferenceKey) {
+        super(context, preferenceKey);
+        mLocalBluetoothManager = com.android.settings.bluetooth.Utils.getLocalBluetoothManager(
+                context);
+    }
+
+    /**
+     * Initializes objects in this controller. Need to call this before onStart().
+     *
+     * <p>Should not call this more than 1 time.
+     *
+     * @param fragment The {@link DashboardFragment} uses the controller.
+     */
+    public void init(DashboardFragment fragment) {
+        if (mAvailableHearingDeviceUpdater != null) {
+            throw new IllegalStateException("Should not call init() more than 1 time.");
+        }
+        mAvailableHearingDeviceUpdater = new AvailableHearingDeviceUpdater(fragment.getContext(),
+                this, fragment.getMetricsCategory());
+        mFragmentManager = fragment.getParentFragmentManager();
+    }
+
+    @Override
+    public void onStart() {
+        mAvailableHearingDeviceUpdater.registerCallback();
+        mAvailableHearingDeviceUpdater.refreshPreference();
+        mLocalBluetoothManager.getEventManager().registerCallback(this);
+    }
+
+    @Override
+    public void onStop() {
+        mAvailableHearingDeviceUpdater.unregisterCallback();
+        mLocalBluetoothManager.getEventManager().unregisterCallback(this);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        if (isAvailable()) {
+            final Context context = screen.getContext();
+            mAvailableHearingDeviceUpdater.setPrefContext(context);
+            mAvailableHearingDeviceUpdater.forceUpdate();
+        }
+    }
+
+    @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+        if (activeDevice == null) {
+            return;
+        }
+
+        if (bluetoothProfile == BluetoothProfile.HEARING_AID) {
+            HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, activeDevice);
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/AvailableHearingDeviceUpdater.java b/src/com/android/settings/accessibility/AvailableHearingDeviceUpdater.java
new file mode 100644
index 0000000..b3d3715
--- /dev/null
+++ b/src/com/android/settings/accessibility/AvailableHearingDeviceUpdater.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * Maintains and updates connected hearing devices, including ASHA and HAP profile.
+ */
+public class AvailableHearingDeviceUpdater extends AvailableMediaBluetoothDeviceUpdater {
+
+    private static final String PREF_KEY = "connected_hearing_device";
+
+    public AvailableHearingDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+        super(context, devicePreferenceCallback, metricsCategory);
+    }
+
+    @Override
+    public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
+        final BluetoothDevice device = cachedDevice.getDevice();
+        final boolean isConnectedHearingAidDevice = (cachedDevice.isConnectedHearingAidDevice()
+                && (device.getBondState() == BluetoothDevice.BOND_BONDED));
+
+        return isConnectedHearingAidDevice && isDeviceInCachedDevicesList(cachedDevice);
+    }
+
+    @Override
+    protected String getPreferenceKey() {
+        return PREF_KEY;
+    }
+}
diff --git a/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceController.java b/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceController.java
new file mode 100644
index 0000000..c5c6297
--- /dev/null
+++ b/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceController.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Abstract base class for bluetooth preference controller to handle UI logic, e.g. availability
+ * status, preference added, and preference removed.
+ */
+public abstract class BaseBluetoothDevicePreferenceController extends BasePreferenceController
+        implements DevicePreferenceCallback {
+
+    private PreferenceCategory mPreferenceCategory;
+
+    public BaseBluetoothDevicePreferenceController(Context context,
+            String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH))
+                ? AVAILABLE
+                : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreferenceCategory = screen.findPreference(getPreferenceKey());
+        mPreferenceCategory.setVisible(false);
+    }
+
+    @Override
+    public void onDeviceAdded(Preference preference) {
+        if (mPreferenceCategory.getPreferenceCount() == 0) {
+            mPreferenceCategory.setVisible(true);
+        }
+        mPreferenceCategory.addPreference(preference);
+    }
+
+    @Override
+    public void onDeviceRemoved(Preference preference) {
+        mPreferenceCategory.removePreference(preference);
+        if (mPreferenceCategory.getPreferenceCount() == 0) {
+            mPreferenceCategory.setVisible(false);
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/HearingDevicePairingDetail.java b/src/com/android/settings/accessibility/HearingDevicePairingDetail.java
new file mode 100644
index 0000000..aa9b587
--- /dev/null
+++ b/src/com/android/settings/accessibility/HearingDevicePairingDetail.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothDevicePairingDetailBase;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+import java.util.Collections;
+
+/**
+ * HearingDevicePairingDetail is a page to scan hearing devices. This page shows scanning icons and
+ * pairing them.
+ */
+public class HearingDevicePairingDetail extends BluetoothDevicePairingDetailBase {
+
+    private static final String TAG = "HearingDevicePairingDetail";
+    @VisibleForTesting
+    static final String KEY_AVAILABLE_HEARING_DEVICES = "available_hearing_devices";
+
+    public HearingDevicePairingDetail() {
+        super();
+        final ScanFilter filter = new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}, new byte[]{0})
+                .build();
+        setFilter(Collections.singletonList(filter));
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isEnabled());
+    }
+
+    @Override
+    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+        super.onDeviceBondStateChanged(cachedDevice, bondState);
+
+        mAvailableDevicesCategory.setProgress(bondState == BluetoothDevice.BOND_NONE);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        // TODO(b/262839191): To be updated settings_enums.proto
+        return 0;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.hearing_device_pairing_detail;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    public String getDeviceListKey() {
+        return KEY_AVAILABLE_HEARING_DEVICES;
+    }
+}
diff --git a/src/com/android/settings/accessibility/SavedHearingDevicePreferenceController.java b/src/com/android/settings/accessibility/SavedHearingDevicePreferenceController.java
new file mode 100644
index 0000000..20e227c
--- /dev/null
+++ b/src/com/android/settings/accessibility/SavedHearingDevicePreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.BluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Controller to update the {@link androidx.preference.PreferenceCategory} for all
+ * saved ((bonded but not connected)) hearing devices, including ASHA and HAP profile.
+ * Parent class {@link BaseBluetoothDevicePreferenceController} will use
+ * {@link DevicePreferenceCallback} to add/remove {@link Preference}.
+ */
+public class SavedHearingDevicePreferenceController extends
+        BaseBluetoothDevicePreferenceController implements LifecycleObserver, OnStart, OnResume,
+        OnStop {
+
+    private BluetoothDeviceUpdater mSavedHearingDeviceUpdater;
+
+    public SavedHearingDevicePreferenceController(Context context,
+            String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    /**
+     * Initializes objects in this controller. Need to call this before onStart().
+     *
+     * <p>Should not call this more than 1 time.
+     *
+     * @param fragment The {@link DashboardFragment} uses the controller.
+     */
+    public void init(DashboardFragment fragment) {
+        if (mSavedHearingDeviceUpdater != null) {
+            throw new IllegalStateException("Should not call init() more than 1 time.");
+        }
+        mSavedHearingDeviceUpdater = new SavedHearingDeviceUpdater(fragment.getContext(), this,
+                fragment.getMetricsCategory());
+    }
+
+    @Override
+    public void onStart() {
+        mSavedHearingDeviceUpdater.registerCallback();
+    }
+
+    @Override
+    public void onResume() {
+        mSavedHearingDeviceUpdater.refreshPreference();
+    }
+
+    @Override
+    public void onStop() {
+        mSavedHearingDeviceUpdater.unregisterCallback();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        if (isAvailable()) {
+            final Context context = screen.getContext();
+            mSavedHearingDeviceUpdater.setPrefContext(context);
+            mSavedHearingDeviceUpdater.forceUpdate();
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/SavedHearingDeviceUpdater.java b/src/com/android/settings/accessibility/SavedHearingDeviceUpdater.java
new file mode 100644
index 0000000..645d091
--- /dev/null
+++ b/src/com/android/settings/accessibility/SavedHearingDeviceUpdater.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * Maintains and updates saved (bonded but not connected) hearing devices, including ASHA and HAP
+ * profile.
+ */
+public class SavedHearingDeviceUpdater extends SavedBluetoothDeviceUpdater {
+
+    private static final String PREF_KEY = "saved_hearing_device";
+
+    public SavedHearingDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+        super(context, devicePreferenceCallback, /* showConnectedDevice= */ false, metricsCategory);
+    }
+
+    @Override
+    public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
+        final BluetoothDevice device = cachedDevice.getDevice();
+        final boolean isSavedHearingAidDevice = cachedDevice.isHearingAidDevice()
+                && device.getBondState() == BluetoothDevice.BOND_BONDED
+                && !device.isConnected();
+
+        return isSavedHearingAidDevice && isDeviceInCachedDevicesList(cachedDevice);
+    }
+
+    @Override
+    protected String getPreferenceKey() {
+        return PREF_KEY;
+    }
+}
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index c3d3b82..ec131d4 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -23,7 +23,6 @@
 import androidx.preference.Preference;
 
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
 /**
@@ -39,9 +38,9 @@
 
     private final AudioManager mAudioManager;
 
-    public AvailableMediaBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
-            DevicePreferenceCallback devicePreferenceCallback) {
-        super(context, fragment, devicePreferenceCallback);
+    public AvailableMediaBluetoothDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+        super(context, devicePreferenceCallback, metricsCategory);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
     }
 
@@ -102,7 +101,7 @@
 
     @Override
     public boolean onPreferenceClick(Preference preference) {
-        mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
+        mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
         final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
                 .getBluetoothDevice();
         return device.setActive();
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
index 509a9b0..4e40323 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingController.java
@@ -17,10 +17,13 @@
 package com.android.settings.bluetooth;
 
 import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.FEATURE_AUDIO_ROUTING_ORDER;
+import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.util.FeatureFlagUtils;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceFragmentCompat;
@@ -36,7 +39,8 @@
 public class BluetoothDetailsAudioRoutingController extends BluetoothDetailsController  {
 
     private static final String KEY_FEATURE_CONTROLS_GROUP = "feature_controls_group";
-    private static final String KEY_AUDIO_ROUTING = "audio_routing";
+    @VisibleForTesting
+    static final String KEY_AUDIO_ROUTING = "audio_routing";
 
     public BluetoothDetailsAudioRoutingController(Context context,
             PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) {
@@ -71,9 +75,13 @@
 
     private Preference createAudioRoutingPreference(Context context) {
         final Preference preference = new Preference(context);
+
         preference.setKey(KEY_AUDIO_ROUTING);
         preference.setTitle(context.getString(R.string.bluetooth_audio_routing_title));
         preference.setSummary(context.getString(R.string.bluetooth_audio_routing_summary));
+        final Bundle extras = preference.getExtras();
+        extras.putString(KEY_DEVICE_ADDRESS, mCachedDevice.getAddress());
+        preference.setFragment(BluetoothDetailsAudioRoutingFragment.class.getName());
 
         return preference;
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java
new file mode 100644
index 0000000..691acee
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragment.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.search.SearchIndexable;
+
+/** Settings fragment containing bluetooth audio routing. */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class BluetoothDetailsAudioRoutingFragment extends RestrictedDashboardFragment {
+
+    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider(R.xml.bluetooth_audio_routing_fragment);
+    private static final String TAG = "BluetoothDetailsAudioRoutingFragment";
+    @VisibleForTesting
+    CachedBluetoothDevice mCachedDevice;
+
+    public BluetoothDetailsAudioRoutingFragment() {
+        super(DISALLOW_CONFIG_BLUETOOTH);
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        final LocalBluetoothManager localBtMgr = Utils.getLocalBtManager(context);
+        final CachedBluetoothDeviceManager cachedDeviceMgr = localBtMgr.getCachedDeviceManager();
+        final BluetoothDevice bluetoothDevice = localBtMgr.getBluetoothAdapter().getRemoteDevice(
+                getArguments().getString(KEY_DEVICE_ADDRESS));
+
+        mCachedDevice = cachedDeviceMgr.findDevice(bluetoothDevice);
+        if (mCachedDevice == null) {
+            // Close this page if device is null with invalid device mac address
+            Log.w(TAG, "onAttach() CachedDevice is null! Can not find address: "
+                    + bluetoothDevice.getAnonymizedAddress());
+            finish();
+            return;
+        }
+
+        // TODO: mCachedDevice will pass to control in next CLs.
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        // TODO(b/262839191): To be updated settings_enums.proto
+        return 0;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.bluetooth_audio_routing_fragment;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
new file mode 100644
index 0000000..c71decb
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2023 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.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilityStatsLogUtils;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
+
+/**
+ * Abstract class for providing basic interaction for a list of Bluetooth devices in bluetooth
+ * device pairing detail page.
+ */
+public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment {
+
+    protected boolean mInitialScanStarted;
+    @VisibleForTesting
+    protected BluetoothProgressCategory mAvailableDevicesCategory;
+
+    public BluetoothDevicePairingDetailBase() {
+        super(DISALLOW_CONFIG_BLUETOOTH);
+    }
+
+    @Override
+    public void initPreferencesFromPreferenceScreen() {
+        mAvailableDevicesCategory = findPreference(getDeviceListKey());
+    }
+
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        mInitialScanStarted = false;
+        super.onViewCreated(view, savedInstanceState);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mLocalManager == null) {
+            Log.e(getLogTag(), "Bluetooth is not supported on this device");
+            return;
+        }
+        updateBluetooth();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mLocalManager == null) {
+            Log.e(getLogTag(), "Bluetooth is not supported on this device");
+            return;
+        }
+        disableScanning();
+    }
+
+    @Override
+    public void onBluetoothStateChanged(int bluetoothState) {
+        super.onBluetoothStateChanged(bluetoothState);
+        updateContent(bluetoothState);
+        if (bluetoothState == BluetoothAdapter.STATE_ON) {
+            showBluetoothTurnedOnToast();
+        }
+    }
+
+    @Override
+    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+        if (bondState == BluetoothDevice.BOND_BONDED) {
+            // If one device is connected(bonded), then close this fragment.
+            finish();
+            return;
+        } else if (bondState == BluetoothDevice.BOND_BONDING) {
+            // Set the bond entry where binding process starts for logging hearing aid device info
+            final int pageId = FeatureFactory.getFactory(
+                    getContext()).getMetricsFeatureProvider().getAttribution(getActivity());
+            final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry(
+                    pageId);
+            HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
+        }
+        if (mSelectedDevice != null && cachedDevice != null) {
+            BluetoothDevice device = cachedDevice.getDevice();
+            if (device != null && mSelectedDevice.equals(device)
+                    && bondState == BluetoothDevice.BOND_NONE) {
+                // If currently selected device failed to bond, restart scanning
+                enableScanning();
+            }
+        }
+    }
+
+    @Override
+    public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
+            int bluetoothProfile) {
+        // This callback is used to handle the case that bonded device is connected in pairing list.
+        // 1. If user selected multiple bonded devices in pairing list, after connected
+        // finish this page.
+        // 2. If the bonded devices auto connected in paring list, after connected it will be
+        // removed from paring list.
+        if (cachedDevice != null && cachedDevice.isConnected()) {
+            final BluetoothDevice device = cachedDevice.getDevice();
+            if (device != null && mSelectedList.contains(device)) {
+                finish();
+            } else if (mDevicePreferenceMap.containsKey(cachedDevice)) {
+                onDeviceDeleted(cachedDevice);
+            }
+        }
+    }
+
+    @Override
+    public void enableScanning() {
+        // Clear all device states before first scan
+        if (!mInitialScanStarted) {
+            if (mAvailableDevicesCategory != null) {
+                removeAllDevices();
+            }
+            mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
+            mInitialScanStarted = true;
+        }
+        super.enableScanning();
+    }
+
+    @Override
+    public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+        disableScanning();
+        super.onDevicePreferenceClick(btPreference);
+    }
+
+    @VisibleForTesting
+    void updateBluetooth() {
+        if (mBluetoothAdapter.isEnabled()) {
+            updateContent(mBluetoothAdapter.getState());
+        } else {
+            // Turn on bluetooth if it is disabled
+            mBluetoothAdapter.enable();
+        }
+    }
+
+    /**
+     * Enables the scanning when {@code bluetoothState} is on, or finish the page when
+     * {@code bluetoothState} is off.
+     *
+     * @param bluetoothState the current Bluetooth state, the possible values that will handle here:
+     * {@link android.bluetooth.BluetoothAdapter#STATE_OFF},
+     * {@link android.bluetooth.BluetoothAdapter#STATE_ON},
+     */
+    @VisibleForTesting
+    public void updateContent(int bluetoothState) {
+        switch (bluetoothState) {
+            case BluetoothAdapter.STATE_ON:
+                mDevicePreferenceMap.clear();
+                clearPreferenceGroupCache();
+                mBluetoothAdapter.enable();
+                enableScanning();
+                break;
+
+            case BluetoothAdapter.STATE_OFF:
+                finish();
+                break;
+        }
+    }
+
+    /**
+     * Clears all cached preferences in {@code preferenceGroup}.
+     */
+    private void clearPreferenceGroupCache() {
+        cacheRemoveAllPrefs(mAvailableDevicesCategory);
+        removeCachedPrefs(mAvailableDevicesCategory);
+    }
+
+    @VisibleForTesting
+    void showBluetoothTurnedOnToast() {
+        Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
+                Toast.LENGTH_SHORT).show();
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 8934676..25e5fbb 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -27,11 +27,9 @@
 import com.android.settings.R;
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.GearPreference;
 import com.android.settingslib.bluetooth.BluetoothCallback;
-import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -46,39 +44,42 @@
  * {@link BluetoothCallback}. It notifies the upper level whether to add/remove the preference
  * through {@link DevicePreferenceCallback}
  *
- * In {@link BluetoothDeviceUpdater}, it uses {@link BluetoothDeviceFilter.Filter} to detect
- * whether the {@link CachedBluetoothDevice} is relevant.
+ * In {@link BluetoothDeviceUpdater}, it uses {@link #isFilterMatched(CachedBluetoothDevice)} to
+ * detect whether the {@link CachedBluetoothDevice} is relevant.
  */
 public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
         LocalBluetoothProfileManager.ServiceListener {
-    private static final String TAG = "BluetoothDeviceUpdater";
-    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-
     protected final MetricsFeatureProvider mMetricsFeatureProvider;
     protected final DevicePreferenceCallback mDevicePreferenceCallback;
     protected final Map<BluetoothDevice, Preference> mPreferenceMap;
+    protected Context mContext;
     protected Context mPrefContext;
-    protected DashboardFragment mFragment;
     @VisibleForTesting
     protected LocalBluetoothManager mLocalManager;
+    protected int mMetricsCategory;
+
+    private static final String TAG = "BluetoothDeviceUpdater";
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     @VisibleForTesting
     final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> {
         launchDeviceDetails(pref);
     };
 
-    public BluetoothDeviceUpdater(Context context, DashboardFragment fragment,
-            DevicePreferenceCallback devicePreferenceCallback) {
-        this(context, fragment, devicePreferenceCallback, Utils.getLocalBtManager(context));
+    public BluetoothDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+        this(context, devicePreferenceCallback, Utils.getLocalBtManager(context), metricsCategory);
     }
 
     @VisibleForTesting
-    BluetoothDeviceUpdater(Context context, DashboardFragment fragment,
-            DevicePreferenceCallback devicePreferenceCallback, LocalBluetoothManager localManager) {
-        mFragment = fragment;
+    BluetoothDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback, LocalBluetoothManager localManager,
+            int metricsCategory) {
+        mContext = context;
         mDevicePreferenceCallback = devicePreferenceCallback;
         mPreferenceMap = new HashMap<>();
         mLocalManager = localManager;
+        mMetricsCategory = metricsCategory;
         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 
@@ -90,7 +91,7 @@
             Log.e(TAG, "registerCallback() Bluetooth is not supported on this device");
             return;
         }
-        mLocalManager.setForegroundActivity(mFragment.getContext());
+        mLocalManager.setForegroundActivity(mContext);
         mLocalManager.getEventManager().registerCallback(this);
         mLocalManager.getProfileManager().addServiceListener(this);
         forceUpdate();
@@ -283,7 +284,7 @@
      * {@link SubSettingLauncher} to launch {@link BluetoothDeviceDetailsFragment}
      */
     protected void launchDeviceDetails(Preference preference) {
-        mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
+        mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
         final CachedBluetoothDevice device =
                 ((BluetoothDevicePreference) preference).getBluetoothDevice();
         if (device == null) {
@@ -293,11 +294,11 @@
         args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
                 device.getDevice().getAddress());
 
-        new SubSettingLauncher(mFragment.getContext())
+        new SubSettingLauncher(mContext)
                 .setDestination(BluetoothDeviceDetailsFragment.class.getName())
                 .setArguments(args)
                 .setTitleRes(R.string.device_details_title)
-                .setSourceMetricsCategory(mFragment.getMetricsCategory())
+                .setSourceMetricsCategory(mMetricsCategory)
                 .launch();
     }
 
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
index 9a92783..a78bf27 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
@@ -16,31 +16,25 @@
 
 package com.android.settings.bluetooth;
 
-import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
-
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.os.Bundle;
-import android.util.Log;
-import android.widget.Toast;
+import android.view.View;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
-import com.android.settings.accessibility.AccessibilityStatsLogUtils;
-import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
 import com.android.settingslib.search.Indexable;
 import com.android.settingslib.widget.FooterPreference;
 
 /**
  * BluetoothPairingDetail is a page to scan bluetooth devices and pair them.
  */
-public class BluetoothPairingDetail extends DeviceListPreferenceFragment implements
+public class BluetoothPairingDetail extends BluetoothDevicePairingDetailBase implements
         Indexable {
     private static final String TAG = "BluetoothPairingDetail";
 
@@ -50,34 +44,12 @@
     static final String KEY_FOOTER_PREF = "footer_preference";
 
     @VisibleForTesting
-    BluetoothProgressCategory mAvailableDevicesCategory;
-    @VisibleForTesting
     FooterPreference mFooterPreference;
     @VisibleForTesting
     AlwaysDiscoverable mAlwaysDiscoverable;
 
-    private boolean mInitialScanStarted;
-
     public BluetoothPairingDetail() {
-        super(DISALLOW_CONFIG_BLUETOOTH);
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        mInitialScanStarted = false;
-        mAlwaysDiscoverable = new AlwaysDiscoverable(getContext());
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mLocalManager == null){
-            Log.e(TAG, "Bluetooth is not supported on this device");
-            return;
-        }
-        updateBluetooth();
-        mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
+        super();
     }
 
     @Override
@@ -86,32 +58,29 @@
         use(BluetoothDeviceRenamePreferenceController.class).setFragment(this);
     }
 
-    @VisibleForTesting
-    void updateBluetooth() {
-        if (mBluetoothAdapter.isEnabled()) {
-            updateContent(mBluetoothAdapter.getState());
-        } else {
-            // Turn on bluetooth if it is disabled
-            mBluetoothAdapter.enable();
-        }
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        mAlwaysDiscoverable = new AlwaysDiscoverable(getContext());
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
     }
 
     @Override
     public void onStop() {
         super.onStop();
-        if (mLocalManager == null){
-            Log.e(TAG, "Bluetooth is not supported on this device");
-            return;
-        }
         // Make the device only visible to connected devices.
         mAlwaysDiscoverable.stop();
-        disableScanning();
     }
 
     @Override
-    void initPreferencesFromPreferenceScreen() {
-        mAvailableDevicesCategory = (BluetoothProgressCategory) findPreference(KEY_AVAIL_DEVICES);
-        mFooterPreference = (FooterPreference) findPreference(KEY_FOOTER_PREF);
+    public void initPreferencesFromPreferenceScreen() {
+        super.initPreferencesFromPreferenceScreen();
+        mFooterPreference = findPreference(KEY_FOOTER_PREF);
         mFooterPreference.setSelectable(false);
     }
 
@@ -120,23 +89,25 @@
         return SettingsEnums.BLUETOOTH_PAIRING;
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * Will update footer and keep the device discoverable as long as the page is visible.
+     */
+    @VisibleForTesting
     @Override
-    void enableScanning() {
-        // Clear all device states before first scan
-        if (!mInitialScanStarted) {
-            if (mAvailableDevicesCategory != null) {
-                removeAllDevices();
+    public void updateContent(int bluetoothState) {
+        super.updateContent(bluetoothState);
+        if (bluetoothState == BluetoothAdapter.STATE_ON) {
+            if (mInitialScanStarted) {
+                // Don't show bonded devices when screen turned back on
+                setFilter(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
+                addCachedDevices();
             }
-            mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
-            mInitialScanStarted = true;
+            setFilter(BluetoothDeviceFilter.ALL_FILTER);
+            updateFooterPreference(mFooterPreference);
+            mAlwaysDiscoverable.start();
         }
-        super.enableScanning();
-    }
-
-    @Override
-    void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
-        disableScanning();
-        super.onDevicePreferenceClick(btPreference);
     }
 
     @Override
@@ -146,78 +117,6 @@
         mAvailableDevicesCategory.setProgress(started);
     }
 
-    @VisibleForTesting
-    void updateContent(int bluetoothState) {
-        switch (bluetoothState) {
-            case BluetoothAdapter.STATE_ON:
-                mDevicePreferenceMap.clear();
-                mBluetoothAdapter.enable();
-
-                addDeviceCategory(mAvailableDevicesCategory,
-                        R.string.bluetooth_preference_found_media_devices,
-                        BluetoothDeviceFilter.ALL_FILTER, mInitialScanStarted);
-                updateFooterPreference(mFooterPreference);
-                mAlwaysDiscoverable.start();
-                enableScanning();
-                break;
-
-            case BluetoothAdapter.STATE_OFF:
-                finish();
-                break;
-        }
-    }
-
-    @Override
-    public void onBluetoothStateChanged(int bluetoothState) {
-        super.onBluetoothStateChanged(bluetoothState);
-        updateContent(bluetoothState);
-        if (bluetoothState == BluetoothAdapter.STATE_ON) {
-            showBluetoothTurnedOnToast();
-        }
-    }
-
-    @Override
-    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
-        if (bondState == BluetoothDevice.BOND_BONDED) {
-            // If one device is connected(bonded), then close this fragment.
-            finish();
-            return;
-        } else if (bondState == BluetoothDevice.BOND_BONDING) {
-            // Set the bond entry where binding process starts for logging hearing aid device info
-            final int pageId = FeatureFactory.getFactory(
-                    getContext()).getMetricsFeatureProvider().getAttribution(getActivity());
-            final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry(
-                    pageId);
-            HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
-        }
-        if (mSelectedDevice != null && cachedDevice != null) {
-            BluetoothDevice device = cachedDevice.getDevice();
-            if (device != null && mSelectedDevice.equals(device)
-                    && bondState == BluetoothDevice.BOND_NONE) {
-                // If currently selected device failed to bond, restart scanning
-                enableScanning();
-            }
-        }
-    }
-
-    @Override
-    public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
-            int bluetoothProfile) {
-        // This callback is used to handle the case that bonded device is connected in pairing list.
-        // 1. If user selected multiple bonded devices in pairing list, after connected
-        // finish this page.
-        // 2. If the bonded devices auto connected in paring list, after connected it will be
-        // removed from paring list.
-        if (cachedDevice != null && cachedDevice.isConnected()) {
-            final BluetoothDevice device = cachedDevice.getDevice();
-            if (device != null && mSelectedList.contains(device)) {
-                finish();
-            } else if (mDevicePreferenceMap.containsKey(cachedDevice)) {
-                onDeviceDeleted(cachedDevice);
-            }
-        }
-    }
-
     @Override
     public int getHelpResource() {
         return R.string.help_url_bluetooth;
@@ -237,10 +136,4 @@
     public String getDeviceListKey() {
         return KEY_AVAIL_DEVICES;
     }
-
-    @VisibleForTesting
-    void showBluetoothTurnedOnToast() {
-        Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
-                Toast.LENGTH_SHORT).show();
-    }
 }
diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
index 46c4fc3..7bb2696 100644
--- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
@@ -24,7 +24,6 @@
 import androidx.preference.Preference;
 
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
 /**
@@ -39,9 +38,9 @@
 
     private final AudioManager mAudioManager;
 
-    public ConnectedBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
-            DevicePreferenceCallback devicePreferenceCallback) {
-        super(context, fragment, devicePreferenceCallback);
+    public ConnectedBluetoothDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
+        super(context, devicePreferenceCallback, metricsCategory);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
     }
 
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
index 2181309..522b5cb 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
@@ -18,6 +18,11 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
 import android.os.Bundle;
 import android.os.SystemProperties;
 import android.text.BidiFormatter;
@@ -33,6 +38,7 @@
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 import java.util.ArrayList;
@@ -59,37 +65,51 @@
             "persist.bluetooth.showdeviceswithoutnames";
 
     private BluetoothDeviceFilter.Filter mFilter;
+    private List<ScanFilter> mLeScanFilters;
+    private ScanCallback mScanCallback;
 
     @VisibleForTesting
-    boolean mScanEnabled;
+    protected boolean mScanEnabled;
 
-    BluetoothDevice mSelectedDevice;
+    protected BluetoothDevice mSelectedDevice;
 
-    BluetoothAdapter mBluetoothAdapter;
-    LocalBluetoothManager mLocalManager;
+    protected BluetoothAdapter mBluetoothAdapter;
+    protected LocalBluetoothManager mLocalManager;
+    protected CachedBluetoothDeviceManager mCachedDeviceManager;
 
     @VisibleForTesting
-    PreferenceGroup mDeviceListGroup;
+    protected PreferenceGroup mDeviceListGroup;
 
-    final HashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
+    protected final HashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
             new HashMap<>();
-    final List<BluetoothDevice> mSelectedList = new ArrayList<>();
+    protected final List<BluetoothDevice> mSelectedList = new ArrayList<>();
 
-    boolean mShowDevicesWithoutNames;
+    protected boolean mShowDevicesWithoutNames;
 
-    DeviceListPreferenceFragment(String restrictedKey) {
+    public DeviceListPreferenceFragment(String restrictedKey) {
         super(restrictedKey);
         mFilter = BluetoothDeviceFilter.ALL_FILTER;
     }
 
-    final void setFilter(BluetoothDeviceFilter.Filter filter) {
+    protected final void setFilter(BluetoothDeviceFilter.Filter filter) {
         mFilter = filter;
     }
 
-    final void setFilter(int filterType) {
+    protected final void setFilter(int filterType) {
         mFilter = BluetoothDeviceFilter.getFilter(filterType);
     }
 
+    /**
+     * Sets the bluetooth device scanning filter with {@link ScanFilter}s. It will change to start
+     * {@link BluetoothLeScanner} which will scan BLE device only.
+     *
+     * @param leScanFilters list of settings to filter scan result
+     */
+    protected void setFilter(List<ScanFilter> leScanFilters) {
+        mFilter = null;
+        mLeScanFilters = leScanFilters;
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -100,6 +120,7 @@
             return;
         }
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mCachedDeviceManager = mLocalManager.getCachedDeviceManager();
         mShowDevicesWithoutNames = SystemProperties.getBoolean(
                 BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
 
@@ -109,7 +130,7 @@
     }
 
     /** find and update preference that already existed in preference screen */
-    abstract void initPreferencesFromPreferenceScreen();
+    protected abstract void initPreferencesFromPreferenceScreen();
 
     @Override
     public void onStart() {
@@ -139,7 +160,7 @@
 
     void addCachedDevices() {
         Collection<CachedBluetoothDevice> cachedDevices =
-                mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
+                mCachedDeviceManager.getCachedDevicesCopy();
         for (CachedBluetoothDevice cachedDevice : cachedDevices) {
             onDeviceAdded(cachedDevice);
         }
@@ -164,7 +185,7 @@
         return super.onPreferenceTreeClick(preference);
     }
 
-    void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+    protected void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
         btPreference.onClicked();
     }
 
@@ -177,7 +198,8 @@
         // Prevent updates while the list shows one of the state messages
         if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) return;
 
-        if (mFilter.matches(cachedDevice.getDevice())) {
+        if (mLeScanFilters != null
+                || (mFilter != null && mFilter.matches(cachedDevice.getDevice()))) {
             createDevicePreference(cachedDevice);
         }
     }
@@ -227,7 +249,7 @@
     }
 
     @VisibleForTesting
-    void enableScanning() {
+    protected void enableScanning() {
         // BluetoothAdapter already handles repeated scan requests
         if (!mScanEnabled) {
             startScanning();
@@ -236,7 +258,7 @@
     }
 
     @VisibleForTesting
-    void disableScanning() {
+    protected void disableScanning() {
         if (mScanEnabled) {
             stopScanning();
             mScanEnabled = false;
@@ -251,31 +273,6 @@
     }
 
     /**
-     * Add bluetooth device preferences to {@code preferenceGroup} which satisfy the {@code filter}
-     *
-     * This method will also (1) set the title for {@code preferenceGroup} and (2) change the
-     * default preferenceGroup and filter
-     * @param preferenceGroup
-     * @param titleId
-     * @param filter
-     * @param addCachedDevices
-     */
-    public void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
-            BluetoothDeviceFilter.Filter filter, boolean addCachedDevices) {
-        cacheRemoveAllPrefs(preferenceGroup);
-        preferenceGroup.setTitle(titleId);
-        mDeviceListGroup = preferenceGroup;
-        if (addCachedDevices) {
-            // Don't show bonded devices when screen turned back on
-            setFilter(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
-            addCachedDevices();
-        }
-        setFilter(filter);
-        preferenceGroup.setEnabled(true);
-        removeCachedPrefs(preferenceGroup);
-    }
-
-    /**
      * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
      */
     public abstract String getDeviceListKey();
@@ -284,15 +281,65 @@
         return mShowDevicesWithoutNames;
     }
 
+    @VisibleForTesting
     void startScanning() {
+        if (mFilter != null) {
+            startClassicScanning();
+        } else if (mLeScanFilters != null) {
+            startLeScanning();
+        }
+
+    }
+
+    @VisibleForTesting
+    void stopScanning() {
+        if (mFilter != null) {
+            stopClassicScanning();
+        } else if (mLeScanFilters != null) {
+            stopLeScanning();
+        }
+    }
+
+    private void startClassicScanning() {
         if (!mBluetoothAdapter.isDiscovering()) {
             mBluetoothAdapter.startDiscovery();
         }
     }
 
-    void stopScanning() {
+    private void stopClassicScanning() {
         if (mBluetoothAdapter.isDiscovering()) {
             mBluetoothAdapter.cancelDiscovery();
         }
     }
+
+    private void startLeScanning() {
+        final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+        final ScanSettings settings = new ScanSettings.Builder()
+                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+                .build();
+        mScanCallback = new ScanCallback() {
+            @Override
+            public void onScanResult(int callbackType, ScanResult result) {
+                final BluetoothDevice device = result.getDevice();
+                CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
+                if (cachedDevice == null) {
+                    cachedDevice = mCachedDeviceManager.addDevice(device);
+                }
+                onDeviceAdded(cachedDevice);
+            }
+
+            @Override
+            public void onScanFailed(int errorCode) {
+                Log.w(TAG, "BLE Scan failed with error code " + errorCode);
+            }
+        };
+        scanner.startScan(mLeScanFilters, settings, mScanCallback);
+    }
+
+    private void stopLeScanning() {
+        final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+        if (scanner != null) {
+            scanner.stopScan(mScanCallback);
+        }
+    }
 }
diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java
index e8adac0..2e81062 100644
--- a/src/com/android/settings/bluetooth/DevicePickerFragment.java
+++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java
@@ -27,8 +27,8 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserManager;
-import android.util.Log;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 
@@ -68,7 +68,7 @@
     }
 
     @Override
-    void initPreferencesFromPreferenceScreen() {
+    public void initPreferencesFromPreferenceScreen() {
         Intent intent = getActivity().getIntent();
         mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
         setFilter(intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
@@ -136,7 +136,7 @@
     }
 
     @Override
-    void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+    public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
         disableScanning();
         LocalBluetoothPreferences.persistSelectedDeviceInPicker(
                 getActivity(), mSelectedDevice.getAddress());
diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
index e7a8317..a4a9451 100644
--- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
@@ -25,8 +25,6 @@
 import androidx.preference.Preference;
 
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
-import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 
@@ -44,15 +42,16 @@
 
     private static final String PREF_KEY = "saved_bt";
 
-    private final boolean mDisplayConnected;
+    private final boolean mShowConnectedDevice;
 
     @VisibleForTesting
     BluetoothAdapter mBluetoothAdapter;
 
-    public SavedBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
-            DevicePreferenceCallback devicePreferenceCallback) {
-        super(context, fragment, devicePreferenceCallback);
-        mDisplayConnected = (fragment instanceof PreviouslyConnectedDeviceDashboardFragment);
+    public SavedBluetoothDeviceUpdater(Context context,
+            DevicePreferenceCallback devicePreferenceCallback, boolean showConnectedDevice,
+            int metricsCategory) {
+        super(context, devicePreferenceCallback, metricsCategory);
+        mShowConnectedDevice = showConnectedDevice;
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
@@ -106,13 +105,13 @@
                     + cachedDevice.isConnected());
         }
         return device.getBondState() == BluetoothDevice.BOND_BONDED
-                && (mDisplayConnected || (!device.isConnected() && isDeviceInCachedDevicesList(
+                && (mShowConnectedDevice || (!device.isConnected() && isDeviceInCachedDevicesList(
                 cachedDevice)));
     }
 
     @Override
     public boolean onPreferenceClick(Preference preference) {
-        mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
+        mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
         final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
                 .getBluetoothDevice();
         if (device.isConnected()) {
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index 37c826f..46d8e70 100644
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -42,6 +42,9 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
 /**
  * Utils is a helper class that contains constants for various
  * Android resource IDs, debug logging flags, and static methods
@@ -132,6 +135,24 @@
         return LocalBluetoothManager.getInstance(context, mOnInitCallback);
     }
 
+    /**
+     * Obtains a {@link LocalBluetoothManager}.
+     *
+     * To avoid StrictMode ThreadPolicy violation, will get it in another thread.
+     */
+    public static LocalBluetoothManager getLocalBluetoothManager(Context context) {
+        final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>(
+                // Avoid StrictMode ThreadPolicy violation
+                () -> getLocalBtManager(context));
+        try {
+            localBtManagerFutureTask.run();
+            return localBtManagerFutureTask.get();
+        } catch (InterruptedException | ExecutionException e) {
+            Log.w(TAG, "Error getting LocalBluetoothManager.", e);
+            return null;
+        }
+    }
+
     public static String createRemoteName(Context context, BluetoothDevice device) {
         String mRemoteName = device != null ? device.getAlias() : null;
 
diff --git a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
index 6623b97..a340015 100644
--- a/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/AvailableMediaDeviceGroupController.java
@@ -131,7 +131,7 @@
     public void init(DashboardFragment fragment) {
         mFragmentManager = fragment.getParentFragmentManager();
         mBluetoothDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(fragment.getContext(),
-                fragment, AvailableMediaDeviceGroupController.this);
+                AvailableMediaDeviceGroupController.this, fragment.getMetricsCategory());
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
index 0d51ebe..f7517b4 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
@@ -184,7 +184,8 @@
         final DockUpdater connectedDockUpdater =
                 dockUpdaterFeatureProvider.getConnectedDockUpdater(context, this);
         init(hasBluetoothFeature()
-                        ? new ConnectedBluetoothDeviceUpdater(context, fragment, this)
+                        ? new ConnectedBluetoothDeviceUpdater(context, this,
+                        fragment.getMetricsCategory())
                         : null,
                 hasUsbFeature()
                         ? new ConnectedUsbDeviceUpdater(context, fragment, this)
diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
index afeca51..5c906fd 100644
--- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
@@ -125,7 +125,8 @@
 
     public void init(DashboardFragment fragment) {
         mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(),
-                fragment, PreviouslyConnectedDevicePreferenceController.this);
+                PreviouslyConnectedDevicePreferenceController.this, /* showConnectedDevice= */
+                false, fragment.getMetricsCategory());
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
index df721f1..3034e2f 100644
--- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java
@@ -117,7 +117,8 @@
 
     public void init(DashboardFragment fragment) {
         mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(),
-                fragment, SavedDeviceGroupController.this);
+                SavedDeviceGroupController.this, /* showConnectedDevice= */true,
+                fragment.getMetricsCategory());
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
index 4e276c1..f1f9521 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
@@ -332,12 +332,13 @@
     private void lazyInitUpdaters() {
         if (mAvailableMediaBtDeviceUpdater == null) {
             mAvailableMediaBtDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(mContext,
-                    null /* fragment */, null /* devicePreferenceCallback */);
+                    /* devicePreferenceCallback= */ null, /* metricsCategory= */ 0);
         }
 
         if (mSavedBtDeviceUpdater == null) {
             mSavedBtDeviceUpdater = new SavedBluetoothDeviceUpdater(mContext,
-                    null /* fragment */, null /* devicePreferenceCallback */);
+                    /* devicePreferenceCallback= */ null, /* showConnectedDevice= */
+                    false, /* metricsCategory= */ 0);
         }
     }
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/AvailableHearingDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/accessibility/AvailableHearingDeviceUpdaterTest.java
new file mode 100644
index 0000000..6305014
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AvailableHearingDeviceUpdaterTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Tests for {@link AvailableHearingDeviceUpdater}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class AvailableHearingDeviceUpdaterTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Mock
+    private DevicePreferenceCallback mDevicePreferenceCallback;
+    @Mock
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private AvailableHearingDeviceUpdater mUpdater;
+
+    @Before
+    public void setUp() {
+        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+        mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        mUpdater = new AvailableHearingDeviceUpdater(mContext,
+                mDevicePreferenceCallback, /* metricsCategory= */ 0);
+    }
+
+    @Test
+    public void isFilterMatch_connectedHearingDevice_returnTrue() {
+        CachedBluetoothDevice connectedHearingDevice = mCachedBluetoothDevice;
+        when(connectedHearingDevice.isConnectedHearingAidDevice()).thenReturn(true);
+        doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                new ArrayList<>(List.of(connectedHearingDevice)));
+
+        assertThat(mUpdater.isFilterMatched(connectedHearingDevice)).isEqualTo(true);
+    }
+
+    @Test
+    public void isFilterMatch_nonConnectedHearingDevice_returnFalse() {
+        CachedBluetoothDevice nonConnectedHearingDevice = mCachedBluetoothDevice;
+        when(nonConnectedHearingDevice.isConnectedHearingAidDevice()).thenReturn(false);
+        doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                new ArrayList<>(List.of(nonConnectedHearingDevice)));
+
+        assertThat(mUpdater.isFilterMatched(nonConnectedHearingDevice)).isEqualTo(false);
+    }
+
+    @Test
+    public void isFilterMatch_connectedBondingHearingDevice_returnFalse() {
+        CachedBluetoothDevice connectedBondingHearingDevice = mCachedBluetoothDevice;
+        when(connectedBondingHearingDevice.isHearingAidDevice()).thenReturn(true);
+        doReturn(BluetoothDevice.BOND_BONDING).when(mBluetoothDevice).getBondState();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                new ArrayList<>(List.of(connectedBondingHearingDevice)));
+
+        assertThat(mUpdater.isFilterMatched(connectedBondingHearingDevice)).isEqualTo(false);
+    }
+
+    @Test
+    public void isFilterMatch_hearingDeviceNotInCachedDevicesList_returnFalse() {
+        CachedBluetoothDevice notInCachedDevicesListDevice = mCachedBluetoothDevice;
+        when(notInCachedDevicesListDevice.isHearingAidDevice()).thenReturn(true);
+        doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+        doReturn(false).when(mBluetoothDevice).isConnected();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(new ArrayList<>());
+
+        assertThat(mUpdater.isFilterMatched(notInCachedDevicesListDevice)).isEqualTo(false);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceControllerTest.java
new file mode 100644
index 0000000..7e064c4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/BaseBluetoothDevicePreferenceControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+/** Tests for {@link BaseBluetoothDevicePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class BaseBluetoothDevicePreferenceControllerTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private static final String FAKE_KEY = "fake_key";
+    @Spy
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private PackageManager mPackageManager;
+    private PreferenceCategory mPreferenceCategory;
+    private PreferenceManager mPreferenceManager;
+    private PreferenceScreen mScreen;
+    private TestBaseBluetoothDevicePreferenceController mController;
+
+    @Before
+    public void setUp() {
+        FakeFeatureFactory.setupForTest();
+        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+        mPreferenceCategory = new PreferenceCategory(mContext);
+        mPreferenceCategory.setKey(FAKE_KEY);
+        mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+        mPreferenceManager = new PreferenceManager(mContext);
+        mScreen = mPreferenceManager.createPreferenceScreen(mContext);
+        mScreen.addPreference(mPreferenceCategory);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        mController = new TestBaseBluetoothDevicePreferenceController(mContext, FAKE_KEY);
+    }
+
+    @Test
+    public void getAvailabilityStatus_hasBluetoothFeature_available() {
+        doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_noBluetoothFeature_conditionallyUnavailalbe() {
+        doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    public void displayPreference_preferenceCategoryInVisible() {
+        mController.displayPreference(mScreen);
+
+        assertThat(mPreferenceCategory.isVisible()).isFalse();
+    }
+
+    @Test
+    public void onDeviceAdded_preferenceCategoryVisible() {
+        Preference preference = new Preference(mContext);
+        mController.displayPreference(mScreen);
+
+        mController.onDeviceAdded(preference);
+
+        assertThat(mPreferenceCategory.isVisible()).isTrue();
+    }
+
+    @Test
+    public void onDeviceRemoved_addedPreferenceFirst_preferenceCategoryInVisible() {
+        Preference preference = new Preference(mContext);
+        mController.displayPreference(mScreen);
+
+        mController.onDeviceAdded(preference);
+        mController.onDeviceRemoved(preference);
+
+        assertThat(mPreferenceCategory.isVisible()).isFalse();
+    }
+
+    public static class TestBaseBluetoothDevicePreferenceController extends
+            BaseBluetoothDevicePreferenceController {
+
+        public TestBaseBluetoothDevicePreferenceController(Context context,
+                String preferenceKey) {
+            super(context, preferenceKey);
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingDetailTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingDetailTest.java
new file mode 100644
index 0000000..e1651d9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingDetailTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.BluetoothProgressCategory;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+/** Tests for {@link HearingDevicePairingDetail}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class HearingDevicePairingDetailTest {
+
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    private BluetoothProgressCategory mProgressCategory;
+    private TestHearingDevicePairingDetail mFragment;
+
+    @Before
+    public void setUp() {
+        final BluetoothAdapter bluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter());
+        final ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(
+                BluetoothAdapter.getDefaultAdapter());
+        shadowBluetoothAdapter.setEnabled(true);
+
+        mProgressCategory = spy(new BluetoothProgressCategory(mContext));
+        mFragment = spy(new TestHearingDevicePairingDetail());
+        when(mFragment.getContext()).thenReturn(mContext);
+        when(mFragment.findPreference(
+                HearingDevicePairingDetail.KEY_AVAILABLE_HEARING_DEVICES)).thenReturn(
+                mProgressCategory);
+        mFragment.setBluetoothAdapter(bluetoothAdapter);
+
+    }
+
+    @Test
+    public void getDeviceListKey_expectedKey() {
+        assertThat(mFragment.getDeviceListKey()).isEqualTo(
+                HearingDevicePairingDetail.KEY_AVAILABLE_HEARING_DEVICES);
+    }
+
+    @Test
+    public void onDeviceBondStateChanged_bondNone_setProgressFalse() {
+        mFragment.initPreferencesFromPreferenceScreen();
+
+        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
+
+        verify(mProgressCategory).setProgress(true);
+    }
+
+    @Test
+    public void onDeviceBondStateChanged_bonding_setProgressTrue() {
+        mFragment.initPreferencesFromPreferenceScreen();
+
+        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
+
+        verify(mProgressCategory).setProgress(false);
+    }
+
+    private static class TestHearingDevicePairingDetail extends HearingDevicePairingDetail {
+        TestHearingDevicePairingDetail() {
+            super();
+        }
+
+        public void setBluetoothAdapter(BluetoothAdapter bluetoothAdapter) {
+            this.mBluetoothAdapter = bluetoothAdapter;
+        }
+
+        public void enableScanning() {
+            super.enableScanning();
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/SavedHearingDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/accessibility/SavedHearingDeviceUpdaterTest.java
new file mode 100644
index 0000000..9946b9e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/SavedHearingDeviceUpdaterTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Tests for {@link SavedHearingDeviceUpdater}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class SavedHearingDeviceUpdaterTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Mock
+    private DevicePreferenceCallback mDevicePreferenceCallback;
+    @Mock
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private SavedHearingDeviceUpdater mUpdater;
+
+    @Before
+    public void setUp() {
+        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+        mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        mUpdater = new SavedHearingDeviceUpdater(mContext,
+                mDevicePreferenceCallback, /* metricsCategory= */ 0);
+    }
+
+    @Test
+    public void isFilterMatch_savedHearingDevice_returnTrue() {
+        CachedBluetoothDevice savedHearingDevice = mCachedBluetoothDevice;
+        when(savedHearingDevice.isHearingAidDevice()).thenReturn(true);
+        doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+        doReturn(false).when(mBluetoothDevice).isConnected();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                new ArrayList<>(List.of(savedHearingDevice)));
+
+        assertThat(mUpdater.isFilterMatched(savedHearingDevice)).isEqualTo(true);
+    }
+
+    @Test
+    public void isFilterMatch_savedNonHearingDevice_returnFalse() {
+        CachedBluetoothDevice savedNonHearingDevice = mCachedBluetoothDevice;
+        when(savedNonHearingDevice.isHearingAidDevice()).thenReturn(false);
+        doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+        doReturn(false).when(mBluetoothDevice).isConnected();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                new ArrayList<>(List.of(savedNonHearingDevice)));
+
+        assertThat(mUpdater.isFilterMatched(savedNonHearingDevice)).isEqualTo(false);
+    }
+
+    @Test
+    public void isFilterMatch_savedBondingHearingDevice_returnFalse() {
+        CachedBluetoothDevice savedBondingHearingDevice = mCachedBluetoothDevice;
+        when(savedBondingHearingDevice.isHearingAidDevice()).thenReturn(true);
+        doReturn(BluetoothDevice.BOND_BONDING).when(mBluetoothDevice).getBondState();
+        doReturn(false).when(mBluetoothDevice).isConnected();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                new ArrayList<>(List.of(savedBondingHearingDevice)));
+
+        assertThat(mUpdater.isFilterMatched(savedBondingHearingDevice)).isEqualTo(false);
+    }
+
+    @Test
+    public void isFilterMatch_connectedHearingDevice_returnFalse() {
+        CachedBluetoothDevice connectdHearingDevice = mCachedBluetoothDevice;
+        when(connectdHearingDevice.isHearingAidDevice()).thenReturn(true);
+        doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+        doReturn(true).when(mBluetoothDevice).isConnected();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                new ArrayList<>(List.of(connectdHearingDevice)));
+
+        assertThat(mUpdater.isFilterMatched(connectdHearingDevice)).isEqualTo(false);
+    }
+
+    @Test
+    public void isFilterMatch_hearingDeviceNotInCachedDevicesList_returnFalse() {
+        CachedBluetoothDevice notInCachedDevicesListDevice = mCachedBluetoothDevice;
+        when(notInCachedDevicesListDevice.isHearingAidDevice()).thenReturn(true);
+        doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
+        doReturn(false).when(mBluetoothDevice).isConnected();
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(new ArrayList<>());
+
+        assertThat(mUpdater.isFilterMatched(notInCachedDevicesListDevice)).isEqualTo(false);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index 0b06f3e..d69b5d4 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -96,7 +96,7 @@
         when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
 
         mBluetoothDeviceUpdater = spy(new AvailableMediaBluetoothDeviceUpdater(mContext,
-                mDashboardFragment, mDevicePreferenceCallback));
+                mDevicePreferenceCallback, /* metricsCategory= */ 0));
         mBluetoothDeviceUpdater.setPrefContext(mContext);
         mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false,
                 BluetoothDevicePreference.SortType.TYPE_DEFAULT);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
index 92174f3..ea65856 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingControllerTest.java
@@ -16,12 +16,17 @@
 
 package com.android.settings.bluetooth;
 
+import static com.android.settings.bluetooth.BluetoothDetailsAudioRoutingController.KEY_AUDIO_ROUTING;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.when;
 
 import android.util.FeatureFlagUtils;
 
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,6 +41,8 @@
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
 
+    private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
+
     private BluetoothDetailsAudioRoutingController mController;
 
     @Override
@@ -44,7 +51,9 @@
 
         mController = new BluetoothDetailsAudioRoutingController(mContext, mFragment, mCachedDevice,
                 mLifecycle);
-        mController.init(mScreen);
+        final PreferenceCategory preferenceCategory = new PreferenceCategory(mContext);
+        preferenceCategory.setKey(mController.getPreferenceKey());
+        mScreen.addPreference(preferenceCategory);
     }
 
     @Test
@@ -64,4 +73,20 @@
 
         assertThat(mController.isAvailable()).isFalse();
     }
+
+    @Test
+    public void init_isHearingAidDevice_expectedAudioRoutingPreference() {
+        when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
+
+        mController.init(mScreen);
+        final Preference preference = mScreen.findPreference(KEY_AUDIO_ROUTING);
+        final String address = preference.getExtras().getString(
+                BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS);
+        final String fragment = preference.getFragment();
+
+        assertThat(address).isEqualTo(TEST_ADDRESS);
+        assertThat(fragment).isEqualTo(BluetoothDetailsAudioRoutingFragment.class.getName());
+
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java
new file mode 100644
index 0000000..b2da579
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsAudioRoutingFragmentTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+
+/** Tests for {@link BluetoothDetailsAudioRoutingFragment}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class BluetoothDetailsAudioRoutingFragmentTest {
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    private BluetoothDetailsAudioRoutingFragment mFragment;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    @Mock
+    private LocalBluetoothAdapter mLocalBluetoothAdapter;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice;
+
+    @Before
+    public void setUp() {
+        setupEnvironment();
+
+        when(mLocalBluetoothAdapter.getRemoteDevice(TEST_ADDRESS)).thenReturn(mBluetoothDevice);
+        when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice);
+
+        mFragment = new BluetoothDetailsAudioRoutingFragment();
+    }
+
+    @Test
+    public void onAttach_setArgumentsWithAddress_expectedCachedDeviceWithAddress() {
+        final Bundle args = new Bundle();
+        args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS);
+        mFragment.setArguments(args);
+
+        mFragment.onAttach(mContext);
+
+        assertThat(mFragment.mCachedDevice.getAddress()).isEqualTo(TEST_ADDRESS);
+    }
+
+    @Test
+    public void getNonIndexableKeys_existInXmlLayout() {
+        final List<String> niks = BluetoothDetailsAudioRoutingFragment.SEARCH_INDEX_DATA_PROVIDER
+                .getNonIndexableKeys(mContext);
+        final List<String> keys =
+                XmlTestUtils.getKeysFromPreferenceXml(mContext,
+                        R.xml.bluetooth_audio_routing_fragment);
+
+        assertThat(keys).containsAtLeastElementsIn(niks);
+    }
+
+    private void setupEnvironment() {
+        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+        when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
new file mode 100644
index 0000000..184f521
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2023 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Pair;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+/** Tests for {@link BluetoothDevicePairingDetailBase}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class BluetoothDevicePairingDetailBaseTest {
+
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    public static final String KEY_DEVICE_LIST_GROUP = "test_key";
+
+    private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
+    private static final String TEST_DEVICE_ADDRESS_B = "00:B1:B1:B1:B1:B1";
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Mock
+    private Resources mResource;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private LocalBluetoothManager mLocalManager;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private Drawable mDrawable;
+    private BluetoothAdapter mBluetoothAdapter;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+    private BluetoothProgressCategory mAvailableDevicesCategory;
+    private BluetoothDevice mBluetoothDevice;
+    private TestBluetoothDevicePairingDetailBase mFragment;
+
+    @Before
+    public void setUp() {
+        mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
+        final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
+        when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
+        mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+
+        mFragment = spy(new TestBluetoothDevicePairingDetailBase());
+        when(mFragment.findPreference(KEY_DEVICE_LIST_GROUP)).thenReturn(mAvailableDevicesCategory);
+        doReturn(mContext).when(mFragment).getContext();
+        doReturn(mResource).when(mFragment).getResources();
+        mFragment.mDeviceListGroup = mAvailableDevicesCategory;
+        mFragment.mLocalManager = mLocalManager;
+        mFragment.mBluetoothAdapter = mBluetoothAdapter;
+        mFragment.initPreferencesFromPreferenceScreen();
+
+    }
+
+    @Test
+    public void startScanning_startScanAndRemoveDevices() {
+        mFragment.enableScanning();
+
+        verify(mFragment).startScanning();
+        verify(mAvailableDevicesCategory).removeAll();
+    }
+
+    @Test
+    public void updateContent_stateOn() {
+        mFragment.updateContent(BluetoothAdapter.STATE_ON);
+
+        assertThat(mBluetoothAdapter.isEnabled()).isTrue();
+        verify(mFragment).enableScanning();
+    }
+
+    @Test
+    public void updateContent_stateOff_finish() {
+        mFragment.updateContent(BluetoothAdapter.STATE_OFF);
+
+        verify(mFragment).finish();
+    }
+
+    @Test
+    public void updateBluetooth_bluetoothOff_turnOnBluetooth() {
+        mShadowBluetoothAdapter.setEnabled(false);
+
+        mFragment.updateBluetooth();
+
+        assertThat(mBluetoothAdapter.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void updateBluetooth_bluetoothOn_updateState() {
+        mShadowBluetoothAdapter.setEnabled(true);
+        doNothing().when(mFragment).updateContent(anyInt());
+
+        mFragment.updateBluetooth();
+
+        verify(mFragment).updateContent(anyInt());
+    }
+
+    @Test
+    public void onBluetoothStateChanged_whenTurnedOnBTShowToast() {
+        doNothing().when(mFragment).updateContent(anyInt());
+
+        mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
+
+        verify(mFragment).showBluetoothTurnedOnToast();
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
+        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+        mFragment.mSelectedList.add(mBluetoothDevice);
+        mFragment.mSelectedList.add(device);
+
+        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+
+        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
+
+        verify(mFragment).finish();
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
+        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+        mFragment.mSelectedList.add(device);
+
+        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
+
+        // not crash
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
+        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+        mFragment.mSelectedList.add(mBluetoothDevice);
+        mFragment.mSelectedList.add(device);
+
+        when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+
+        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED);
+
+        // not crash
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
+        final BluetoothDevicePreference preference =
+                new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
+                        true, BluetoothDevicePreference.SortType.TYPE_FIFO);
+        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+
+        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+
+        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
+
+        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
+        final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+        final BluetoothDevicePreference preference =
+                new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
+                        true, BluetoothDevicePreference.SortType.TYPE_FIFO);
+        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+        final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
+        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+
+        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
+        when(cachedDevice.isConnected()).thenReturn(true);
+        when(cachedDevice.getDevice()).thenReturn(device2);
+        when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
+        when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
+
+        mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
+                BluetoothAdapter.STATE_CONNECTED);
+
+        // not crash
+    }
+
+    private static class TestBluetoothDevicePairingDetailBase extends
+            BluetoothDevicePairingDetailBase {
+
+        TestBluetoothDevicePairingDetailBase() {
+            super();
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return 0;
+        }
+
+        @Override
+        public String getDeviceListKey() {
+            return KEY_DEVICE_LIST_GROUP;
+        }
+
+        @Override
+        protected int getPreferenceScreenResId() {
+            return 0;
+        }
+
+        @Override
+        protected String getLogTag() {
+            return "test_tag";
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
index 6afa56c..d165aa5 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
@@ -34,7 +34,6 @@
 
 import com.android.settings.SettingsActivity;
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -63,8 +62,6 @@
     private static final String TEST_NAME = "test_name";
 
     @Mock
-    private DashboardFragment mDashboardFragment;
-    @Mock
     private DevicePreferenceCallback mDevicePreferenceCallback;
     @Mock
     private CachedBluetoothDevice mCachedBluetoothDevice;
@@ -84,7 +81,7 @@
     private Drawable mDrawable;
 
     private Context mContext;
-    private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
+    private TestBluetoothDeviceUpdater mBluetoothDeviceUpdater;
     private BluetoothDevicePreference mPreference;
     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
     private List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
@@ -97,7 +94,6 @@
         mContext = RuntimeEnvironment.application;
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mCachedDevices.add(mCachedBluetoothDevice);
-        doReturn(mContext).when(mDashboardFragment).getContext();
         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
         when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
         when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
@@ -107,20 +103,10 @@
         when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
 
         mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
-                false, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
-        mBluetoothDeviceUpdater =
-            new BluetoothDeviceUpdater(mContext, mDashboardFragment, mDevicePreferenceCallback,
-                    mLocalManager) {
-                @Override
-                public boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice) {
-                    return true;
-                }
-
-                @Override
-                protected String getPreferenceKey() {
-                    return "test_bt";
-                }
-            };
+                /* showDeviceWithoutNames= */ false,
+                BluetoothDevicePreference.SortType.TYPE_DEFAULT);
+        mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mContext,
+                mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */  0);
         mBluetoothDeviceUpdater.setPrefContext(mContext);
     }
 
@@ -185,7 +171,8 @@
 
     @Test
     public void testDeviceProfilesListener_click_startBluetoothDeviceDetailPage() {
-        doReturn(mSettingsActivity).when(mDashboardFragment).getContext();
+        mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mSettingsActivity,
+                mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */  0);
 
         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         mBluetoothDeviceUpdater.mDeviceProfilesListener.onGearClick(mPreference);
@@ -274,4 +261,22 @@
 
         assertThat(mPreference.getTitle()).isEqualTo(TEST_NAME);
     }
+
+    public static class TestBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
+        public TestBluetoothDeviceUpdater(Context context,
+                DevicePreferenceCallback devicePreferenceCallback,
+                LocalBluetoothManager localManager, int metricsCategory) {
+            super(context, devicePreferenceCallback, localManager, metricsCategory);
+        }
+
+        @Override
+        public boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice) {
+            return true;
+        }
+
+        @Override
+        protected String getPreferenceKey() {
+            return "test_bt";
+        }
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
index bac868f..865ed81 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
@@ -18,102 +18,66 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.Pair;
+import android.os.Bundle;
 
-import androidx.preference.PreferenceGroup;
+import androidx.test.core.app.ApplicationProvider;
 
-import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
-import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.widget.FooterPreference;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class})
 public class BluetoothPairingDetailTest {
-    private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
-    private static final String TEST_DEVICE_ADDRESS_B = "00:B1:B1:B1:B1:B1";
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
 
-    @Mock
-    private Resources mResource;
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private LocalBluetoothManager mLocalManager;
-    @Mock
-    private PreferenceGroup mPreferenceGroup;
-    @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
-    private Drawable mDrawable;
-
     private BluetoothPairingDetail mFragment;
-    private Context mContext;
     private BluetoothProgressCategory mAvailableDevicesCategory;
     private FooterPreference mFooterPreference;
     private BluetoothAdapter mBluetoothAdapter;
-    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
-    private BluetoothDevice mBluetoothDevice;
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
-        mContext = RuntimeEnvironment.application;
         mFragment = spy(new BluetoothPairingDetail());
         doReturn(mContext).when(mFragment).getContext();
-        doReturn(mResource).when(mFragment).getResources();
-        when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
-        when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
-
         mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
         mFooterPreference = new FooterPreference(mContext);
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
-        mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+
+        doReturn(mAvailableDevicesCategory).when(mFragment)
+                .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
+        doReturn(mFooterPreference).when(mFragment)
+                .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
 
         mFragment.mBluetoothAdapter = mBluetoothAdapter;
         mFragment.mLocalManager = mLocalManager;
-        mFragment.mDeviceListGroup = mPreferenceGroup;
-        mFragment.mAlwaysDiscoverable = new AlwaysDiscoverable(mContext);
+        mFragment.mDeviceListGroup = mAvailableDevicesCategory;
+        mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
     }
-
+//
     @Test
     public void initPreferencesFromPreferenceScreen_findPreferences() {
-        doReturn(mAvailableDevicesCategory).when(mFragment)
-            .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
-        doReturn(mFooterPreference).when(mFragment)
-            .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
-
         mFragment.initPreferencesFromPreferenceScreen();
 
         assertThat(mFragment.mAvailableDevicesCategory).isEqualTo(mAvailableDevicesCategory);
@@ -121,63 +85,19 @@
     }
 
     @Test
-    public void startScanning_startScanAndRemoveDevices() {
-        mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
-        mFragment.mDeviceListGroup = mAvailableDevicesCategory;
-
-        mFragment.enableScanning();
-
-        verify(mFragment).startScanning();
-        verify(mAvailableDevicesCategory).removeAll();
-    }
-
-    @Test
     public void updateContent_stateOn_addDevices() {
-        mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
-        mFragment.mFooterPreference = mFooterPreference;
-        doNothing().when(mFragment).addDeviceCategory(any(), anyInt(), any(), anyBoolean());
+        mFragment.initPreferencesFromPreferenceScreen();
 
         mFragment.updateContent(BluetoothAdapter.STATE_ON);
 
-        verify(mFragment).addDeviceCategory(mAvailableDevicesCategory,
-                R.string.bluetooth_preference_found_media_devices,
-                BluetoothDeviceFilter.ALL_FILTER, false);
+        assertThat(mFragment.mAlwaysDiscoverable.mStarted).isEqualTo(true);
         assertThat(mBluetoothAdapter.getScanMode())
                 .isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
     }
 
     @Test
-    public void updateContent_stateOff_finish() {
-        mFragment.updateContent(BluetoothAdapter.STATE_OFF);
-
-        verify(mFragment).finish();
-    }
-
-    @Test
-    public void updateBluetooth_bluetoothOff_turnOnBluetooth() {
-        mShadowBluetoothAdapter.setEnabled(false);
-
-        mFragment.updateBluetooth();
-
-        assertThat(mBluetoothAdapter.isEnabled()).isTrue();
-    }
-
-    @Test
-    public void updateBluetooth_bluetoothOn_updateState() {
-        mShadowBluetoothAdapter.setEnabled(true);
-        doNothing().when(mFragment).updateContent(anyInt());
-
-        mFragment.updateBluetooth();
-
-        verify(mFragment).updateContent(anyInt());
-    }
-
-    @Test
     public void onScanningStateChanged_restartScanAfterInitialScanning() {
-        mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
-        mFragment.mFooterPreference = mFooterPreference;
-        mFragment.mDeviceListGroup = mAvailableDevicesCategory;
-        doNothing().when(mFragment).addDeviceCategory(any(), anyInt(), any(), anyBoolean());
+        mFragment.initPreferencesFromPreferenceScreen();
 
         // Initial Bluetooth ON will trigger scan enable, list clear and scan start
         mFragment.updateContent(BluetoothAdapter.STATE_ON);
@@ -219,97 +139,4 @@
         // Verify that clean up only happen once at initialization
         verify(mAvailableDevicesCategory, times(1)).removeAll();
     }
-
-    @Test
-    public void onBluetoothStateChanged_whenTurnedOnBTShowToast() {
-        doNothing().when(mFragment).updateContent(anyInt());
-
-        mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
-
-        verify(mFragment).showBluetoothTurnedOnToast();
-    }
-
-    @Test
-    public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
-        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
-        mFragment.mSelectedList.add(mBluetoothDevice);
-        mFragment.mSelectedList.add(device);
-
-        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
-
-        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
-                BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
-
-        verify(mFragment).finish();
-    }
-
-    @Test
-    public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
-        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
-        mFragment.mSelectedList.add(device);
-
-        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
-
-        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
-                BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
-
-        // not crash
-    }
-
-    @Test
-    public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
-        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
-        mFragment.mSelectedList.add(mBluetoothDevice);
-        mFragment.mSelectedList.add(device);
-
-        when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
-
-        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
-                BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED);
-
-        // not crash
-    }
-
-    @Test
-    public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
-        final BluetoothDevicePreference preference =
-                new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
-                true, BluetoothDevicePreference.SortType.TYPE_FIFO);
-        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
-        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
-
-        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
-
-        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
-                BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
-
-        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
-    }
-
-    @Test
-    public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
-        final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
-        final BluetoothDevicePreference preference =
-                new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
-                        true, BluetoothDevicePreference.SortType.TYPE_FIFO);
-        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
-        final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
-        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
-
-        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
-        when(cachedDevice.isConnected()).thenReturn(true);
-        when(cachedDevice.getDevice()).thenReturn(device2);
-        when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
-        when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
-
-        mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
-                BluetoothAdapter.STATE_CONNECTED);
-
-        // not crash
-    }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index 1f90981..00115d7 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -97,7 +97,7 @@
         when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
         mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices);
         mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
-                mDashboardFragment, mDevicePreferenceCallback));
+                mDevicePreferenceCallback, /* metricsCategory= */ 0));
         mBluetoothDeviceUpdater.setPrefContext(mContext);
         doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
         doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
diff --git a/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java
index 19de2e4..4f46ce9 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/DeviceListPreferenceFragmentTest.java
@@ -26,6 +26,11 @@
 import static org.mockito.Mockito.verify;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
 import android.content.Context;
 import android.content.res.Resources;
 
@@ -45,6 +50,7 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import java.util.Collections;
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
@@ -57,10 +63,14 @@
     private Resources mResource;
     @Mock
     private Context mContext;
+    @Mock
+    private BluetoothLeScanner mBluetoothLeScanner;
 
     private TestFragment mFragment;
     private Preference mMyDevicePreference;
 
+
+    private BluetoothAdapter mBluetoothAdapter;
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -68,7 +78,8 @@
         mFragment = spy(new TestFragment());
         doReturn(mContext).when(mFragment).getContext();
         doReturn(mResource).when(mFragment).getResources();
-        mFragment.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mBluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter());
+        mFragment.mBluetoothAdapter = mBluetoothAdapter;
 
         mMyDevicePreference = new Preference(RuntimeEnvironment.application);
     }
@@ -169,6 +180,20 @@
         verify(mFragment, times(1)).startScanning();
     }
 
+    @Test
+    public void startScanning_setLeScanFilter_shouldStartLeScan() {
+        final ScanFilter leScanFilter = new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}, new byte[]{0})
+                .build();
+        doReturn(mBluetoothLeScanner).when(mBluetoothAdapter).getBluetoothLeScanner();
+
+        mFragment.setFilter(Collections.singletonList(leScanFilter));
+        mFragment.startScanning();
+
+        verify(mBluetoothLeScanner).startScan(eq(Collections.singletonList(leScanFilter)),
+                any(ScanSettings.class), any(ScanCallback.class));
+    }
+
     /**
      * Fragment to test since {@code DeviceListPreferenceFragment} is abstract
      */
@@ -187,7 +212,7 @@
         public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {}
 
         @Override
-        void initPreferencesFromPreferenceScreen() {}
+        protected void initPreferencesFromPreferenceScreen() {}
 
         @Override
         public String getDeviceListKey() {
diff --git a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
index 255a0be..c229449 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
@@ -91,8 +91,8 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
 
-        mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mContext, mDashboardFragment,
-                mDevicePreferenceCallback));
+        mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mContext,
+                mDevicePreferenceCallback, false, /* metricsCategory= */ 0));
         mBluetoothDeviceUpdater.setPrefContext(mContext);
         mBluetoothDeviceUpdater.mBluetoothAdapter = mBluetoothAdapter;
         mBluetoothDeviceUpdater.mLocalManager = mBluetoothManager;