Merge "Add SfpsRestToUnlockFeature to FingerprintFeatureProvider" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4230b6a..2ba87d7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4692,20 +4692,6 @@
             android:permission="android.permission.MANAGE_SLICE_PERMISSIONS"
             android:exported="true" />
 
-        <!-- Couldn't be triggered from outside of settings. Statsd can trigger it because we send
-             PendingIntent to it-->
-        <receiver android:name=".fuelgauge.batterytip.AnomalyDetectionReceiver"
-                  android:exported="false" />
-
-        <service android:name=".fuelgauge.batterytip.AnomalyCleanupJobService"
-                 android:permission="android.permission.BIND_JOB_SERVICE" />
-
-        <service android:name=".fuelgauge.batterytip.AnomalyConfigJobService"
-                 android:permission="android.permission.BIND_JOB_SERVICE" />
-
-        <service android:name=".fuelgauge.batterytip.AnomalyDetectionJobService"
-                 android:permission="android.permission.BIND_JOB_SERVICE" />
-
         <provider
             android:name=".homepage.contextualcards.CardContentProvider"
             android:authorities="${applicationId}.homepage.CardContentProvider"
diff --git a/aconfig/settings_accessibility_flag_declarations_legacy.aconfig b/aconfig/settings_accessibility_flag_declarations_legacy.aconfig
index acdce96..5a464b5 100644
--- a/aconfig/settings_accessibility_flag_declarations_legacy.aconfig
+++ b/aconfig/settings_accessibility_flag_declarations_legacy.aconfig
@@ -32,7 +32,7 @@
 flag {
     name: "new_hearing_device_pairing_page"
     namespace: "accessibility"
-    description: "New hearing device pairing page with deny list method"
+    description: "New hearing device pairing page with extra MFi+ASHA filtering"
     bug: "307473972"
 }
 
diff --git a/res/layout/arrow_preference.xml b/res/layout/arrow_preference.xml
new file mode 100644
index 0000000..0924a44
--- /dev/null
+++ b/res/layout/arrow_preference.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingVertical="@dimen/settingslib_switchbar_margin"
+    android:background="@android:color/transparent">
+
+    <LinearLayout
+        android:id="@+id/background"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+        android:paddingEnd="@dimen/settingslib_switchbar_padding_right"
+        android:background="@drawable/settingslib_switch_bar_bg_on"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:layout_gravity="start|center_vertical"
+            android:layout_weight="1"
+            android:paddingVertical="@dimen/settingslib_switch_title_margin"
+            android:ellipsize="end"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            style="@style/MainSwitchText.Settingslib"/>
+
+        <ImageView
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_arrow_forward"/>
+
+    </LinearLayout>
+</FrameLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5246618..ff59f6a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10815,9 +10815,37 @@
         ]]>
     </string>
 
+    <!-- Message of the warning dialog for disabling the credential provider (new strings for 24Q3). [CHAR_LIMIT=NONE] -->
+    <string name="credman_confirmation_message_new_ui">
+        <![CDATA[
+        <b>Turn off all services?</b>
+        <br/>
+        <br/>
+        Passwords, passkeys, and other saved info won\'t be available for autofill when you sign in
+        ]]>
+    </string>
+
+    <!-- Message of the warning dialog for setting a new preferred credential provider (new strings for 24Q3). [CHAR_LIMIT=NONE] -->
+    <string name="credman_autofill_confirmation_message_new_ui">
+        <![CDATA[
+        <b>Change your preferred service to <xliff:g id="app_name" example="Provider">%1$s</xliff:g>\?</b>
+        <br/>
+        <br/>
+        New passwords, passkeys, and other info will be saved here from now on.
+        <xliff:g id="app_name" example="Provider">%1$s</xliff:g> may use what\'s on your
+        screen to determine what can be autofilled
+        ]]>
+    </string>
+
     <!-- Title of the warning dialog for enabling the credential provider. [CHAR_LIMIT=NONE] -->
     <string name="credman_enable_confirmation_message_title">Use %1$s\?</string>
 
+    <!-- Title of the error dialog when too many credential providers are selected (new strings for 24Q3). [CHAR_LIMIT=NONE] -->
+    <string name="credman_limit_error_msg_title">You can only have 5 services on</string>
+
+    <!-- Message of the error dialog when too many credential providers are selected (new strings for 24Q3). [CHAR_LIMIT=NONE] -->
+    <string name="credman_limit_error_msg">Turn off at least 1 service to add another</string>
+
     <!-- Message of the warning dialog for enabling the credential provider. [CHAR_LIMIT=NONE] -->
     <string name="credman_enable_confirmation_message">%1$s uses what\'s on your screen to determine what can be autofilled.</string>
 
diff --git a/res/xml/accessibility_hearing_aids.xml b/res/xml/accessibility_hearing_aids.xml
index 20c8e29..57a0fe2 100644
--- a/res/xml/accessibility_hearing_aids.xml
+++ b/res/xml/accessibility_hearing_aids.xml
@@ -28,11 +28,10 @@
         settings:controller="com.android.settings.accessibility.AvailableHearingDevicePreferenceController"/>
 
     <com.android.settingslib.RestrictedPreference
-        android:key="add_bt_devices"
+        android:key="hearing_device_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:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
diff --git a/res/xml/bluetooth_audio_sharing.xml b/res/xml/bluetooth_audio_sharing.xml
index 9ffa2b2..dc577f6 100644
--- a/res/xml/bluetooth_audio_sharing.xml
+++ b/res/xml/bluetooth_audio_sharing.xml
@@ -38,11 +38,17 @@
         android:title="Play a test sound"
         settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingPlaySoundPreferenceController" />
 
-    <com.android.settings.connecteddevice.audiosharing.AudioSharingNamePreference
-        android:key="audio_sharing_stream_name"
-        android:summary="********"
-        android:title="Stream name"
-        settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingNamePreferenceController" />
+    <PreferenceCategory
+        android:key="audio_sharing_stream_settings_category"
+        android:title="Stream settings"
+        settings:controller="com.android.settings.connecteddevice.audiosharing.StreamSettingsCategoryController">
+
+        <com.android.settings.connecteddevice.audiosharing.AudioSharingNamePreference
+            android:key="audio_sharing_stream_name"
+            android:summary="********"
+            android:title="Stream name"
+            settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingNamePreferenceController" />
+    </PreferenceCategory>
 
     <PreferenceCategory
         android:key="audio_streams_settings_category"
diff --git a/res/xml/hearing_device_pairing_fragment.xml b/res/xml/hearing_device_pairing_fragment.xml
new file mode 100644
index 0000000..1ccc1dd
--- /dev/null
+++ b/res/xml/hearing_device_pairing_fragment.xml
@@ -0,0 +1,45 @@
+<?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="more_devices_category"
+        android:title="@string/accessibility_found_all_devices">
+        <com.android.settings.accessibility.ArrowPreference
+            android:key="more_devices"
+            android:title="@string/accessibility_list_all_devices_title"
+            settings:searchable="false"
+            settings:userRestriction="no_config_bluetooth"
+            settings:useAdminDisabledSummary="true"
+            settings:controller="com.android.settings.accessibility.ViewAllBluetoothDevicesPreferenceController"/>
+    </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"
+        settings:controller="com.android.settings.accessibility.PairHearingDeviceFooterPreferenceController"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/SettingsDumpService.java b/src/com/android/settings/SettingsDumpService.java
index 4feb6b2..7e84691 100644
--- a/src/com/android/settings/SettingsDumpService.java
+++ b/src/com/android/settings/SettingsDumpService.java
@@ -38,7 +38,6 @@
 
 import com.android.settings.applications.ProcStatsData;
 import com.android.settings.datausage.lib.DataUsageLib;
-import com.android.settings.fuelgauge.batterytip.AnomalyConfigJobService;
 import com.android.settings.network.MobileNetworkRepository;
 import com.android.settingslib.net.DataUsageController;
 
@@ -99,7 +98,6 @@
                 dump.put(KEY_DATAUSAGE, dumpDataUsage());
                 dump.put(KEY_MEMORY, dumpMemory());
                 dump.put(KEY_DEFAULT_BROWSER_APP, dumpDefaultBrowser());
-                dump.put(KEY_ANOMALY_DETECTION, dumpAnomalyDetection());
             } catch (Exception e) {
                 Log.w(TAG, "exception in dump: ", e);
             }
@@ -197,20 +195,6 @@
         }
     }
 
-    @VisibleForTesting
-    JSONObject dumpAnomalyDetection() throws JSONException {
-        final JSONObject obj = new JSONObject();
-        final SharedPreferences sharedPreferences = getSharedPreferences(
-                AnomalyConfigJobService.PREF_DB,
-                Context.MODE_PRIVATE);
-        final int currentVersion = sharedPreferences.getInt(
-                AnomalyConfigJobService.KEY_ANOMALY_CONFIG_VERSION,
-                0 /* defValue */);
-        obj.put("anomaly_config_version", String.valueOf(currentVersion));
-
-        return obj;
-    }
-
     private void dumpMobileNetworkSettings(IndentingPrintWriter writer) {
         MobileNetworkRepository.getInstance(this).dump(writer);
     }
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
index 33fef62..80a03c6 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
@@ -36,9 +36,9 @@
 /** Accessibility settings for hearing aids. */
 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
 public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPreferenceFragment {
-
     private static final String TAG = "AccessibilityHearingAidsFragment";
     private static final String KEY_HEARING_OPTIONS_CATEGORY = "hearing_options_category";
+    public static final String KEY_HEARING_DEVICE_ADD_BT_DEVICES = "hearing_device_add_bt_devices";
     private static final int SHORTCUT_PREFERENCE_IN_CATEGORY_INDEX = 20;
     private String mFeatureName;
 
diff --git a/src/com/android/settings/accessibility/ArrowPreference.java b/src/com/android/settings/accessibility/ArrowPreference.java
new file mode 100644
index 0000000..32e2bcb
--- /dev/null
+++ b/src/com/android/settings/accessibility/ArrowPreference.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+
+/**
+ * A settings preference with colored rounded rectangle background and an arrow icon on the right
+ */
+public class ArrowPreference extends Preference {
+
+    public ArrowPreference(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public ArrowPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, TypedArrayUtils.getAttr(context,
+                androidx.preference.R.attr.preferenceStyle,
+                android.R.attr.preferenceStyle));
+    }
+
+    public ArrowPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public ArrowPreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.arrow_preference);
+    }
+}
diff --git a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
new file mode 100644
index 0000000..fb79ece
--- /dev/null
+++ b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
@@ -0,0 +1,491 @@
+/*
+ * 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 android.app.Activity.RESULT_OK;
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelUuid;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothDevicePreference;
+import com.android.settings.bluetooth.BluetoothProgressCategory;
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.HearingAidInfo;
+import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This fragment shows all scanned hearing devices through BLE scanning. Users can
+ * pair them in this page.
+ */
+public class HearingDevicePairingFragment extends RestrictedDashboardFragment implements
+        BluetoothCallback {
+
+    private static final boolean DEBUG = true;
+    private static final String TAG = "HearingDevicePairingFragment";
+    private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
+            "persist.bluetooth.showdeviceswithoutnames";
+    private static final String KEY_AVAILABLE_HEARING_DEVICES = "available_hearing_devices";
+
+    LocalBluetoothManager mLocalManager;
+    @Nullable
+    BluetoothAdapter mBluetoothAdapter;
+    @Nullable
+    CachedBluetoothDeviceManager mCachedDeviceManager;
+
+    private boolean mShowDevicesWithoutNames;
+    @Nullable
+    private BluetoothProgressCategory mAvailableHearingDeviceGroup;
+
+    @Nullable
+    BluetoothDevice mSelectedDevice;
+    final List<BluetoothDevice> mSelectedDeviceList = new ArrayList<>();
+    final List<BluetoothGatt> mConnectingGattList = new ArrayList<>();
+    final Map<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
+            new HashMap<>();
+
+    private List<ScanFilter> mLeScanFilters;
+
+    public HearingDevicePairingFragment() {
+        super(DISALLOW_CONFIG_BLUETOOTH);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mLocalManager = Utils.getLocalBtManager(getActivity());
+        if (mLocalManager == null) {
+            Log.e(TAG, "Bluetooth is not supported on this device");
+            return;
+        }
+        mBluetoothAdapter = getSystemService(BluetoothManager.class).getAdapter();
+        mCachedDeviceManager = mLocalManager.getCachedDeviceManager();
+        mShowDevicesWithoutNames = SystemProperties.getBoolean(
+                BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
+
+        initPreferencesFromPreferenceScreen();
+        initHearingDeviceLeScanFilters();
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        use(ViewAllBluetoothDevicesPreferenceController.class).init(this);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mLocalManager == null || mBluetoothAdapter == null || isUiRestricted()) {
+            return;
+        }
+        mLocalManager.setForegroundActivity(getActivity());
+        mLocalManager.getEventManager().registerCallback(this);
+        if (mBluetoothAdapter.isEnabled()) {
+            startScanning();
+        } else {
+            // Turn on bluetooth if it is disabled
+            mBluetoothAdapter.enable();
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mLocalManager == null || isUiRestricted()) {
+            return;
+        }
+        stopScanning();
+        removeAllDevices();
+        for (BluetoothGatt gatt: mConnectingGattList) {
+            gatt.disconnect();
+        }
+        mLocalManager.setForegroundActivity(null);
+        mLocalManager.getEventManager().unregisterCallback(this);
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference instanceof BluetoothDevicePreference) {
+            stopScanning();
+            BluetoothDevicePreference devicePreference = (BluetoothDevicePreference) preference;
+            mSelectedDevice = devicePreference.getCachedDevice().getDevice();
+            if (mSelectedDevice != null) {
+                mSelectedDeviceList.add(mSelectedDevice);
+            }
+            devicePreference.onClicked();
+            return true;
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    @Override
+    public void onDeviceDeleted(@NonNull CachedBluetoothDevice cachedDevice) {
+        removeDevice(cachedDevice);
+    }
+
+    @Override
+    public void onBluetoothStateChanged(int bluetoothState) {
+        switch (bluetoothState) {
+            case BluetoothAdapter.STATE_ON:
+                startScanning();
+                showBluetoothTurnedOnToast();
+                break;
+            case BluetoothAdapter.STATE_OFF:
+                finish();
+                break;
+        }
+    }
+
+    @Override
+    public void onDeviceBondStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
+            int bondState) {
+        if (DEBUG) {
+            Log.d(TAG, "onDeviceBondStateChanged: " + cachedDevice.getName() + ", state = "
+                    + bondState);
+        }
+        if (bondState == BluetoothDevice.BOND_BONDED) {
+            // If one device is connected(bonded), then close this fragment.
+            setResult(RESULT_OK);
+            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.getFeatureFactory().getMetricsFeatureProvider()
+                    .getAttribution(getActivity());
+            final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry(
+                    pageId);
+            HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
+        }
+        if (mSelectedDevice != null) {
+            BluetoothDevice device = cachedDevice.getDevice();
+            if (mSelectedDevice.equals(device) && bondState == BluetoothDevice.BOND_NONE) {
+                // If current selected device failed to bond, restart scanning
+                startScanning();
+            }
+        }
+    }
+
+    @Override
+    public void onProfileConnectionStateChanged(@NonNull 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.isConnected()) {
+            final BluetoothDevice device = cachedDevice.getDevice();
+            if (device != null && mSelectedDeviceList.contains(device)) {
+                setResult(RESULT_OK);
+                finish();
+            } else {
+                removeDevice(cachedDevice);
+            }
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.HEARING_AID_PAIRING;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.hearing_device_pairing_fragment;
+    }
+
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    void addDevice(CachedBluetoothDevice cachedDevice) {
+        if (mBluetoothAdapter == null) {
+            return;
+        }
+        // Do not create new preference while the list shows one of the state messages
+        if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) {
+            return;
+        }
+        if (mDevicePreferenceMap.get(cachedDevice) != null) {
+            return;
+        }
+        String key = cachedDevice.getDevice().getAddress();
+        BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
+        if (preference == null) {
+            preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice,
+                    mShowDevicesWithoutNames, BluetoothDevicePreference.SortType.TYPE_FIFO);
+            preference.setKey(key);
+            preference.hideSecondTarget(true);
+        }
+        if (mAvailableHearingDeviceGroup != null) {
+            mAvailableHearingDeviceGroup.addPreference(preference);
+        }
+        mDevicePreferenceMap.put(cachedDevice, preference);
+        if (DEBUG) {
+            Log.d(TAG, "Add device. device: " + cachedDevice);
+        }
+    }
+
+    void removeDevice(CachedBluetoothDevice cachedDevice) {
+        if (DEBUG) {
+            Log.d(TAG, "removeDevice: " + cachedDevice);
+        }
+        BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
+        if (mAvailableHearingDeviceGroup != null && preference != null) {
+            mAvailableHearingDeviceGroup.removePreference(preference);
+        }
+    }
+
+    void startScanning() {
+        if (mCachedDeviceManager != null) {
+            mCachedDeviceManager.clearNonBondedDevices();
+        }
+        removeAllDevices();
+        startLeScanning();
+    }
+
+    void stopScanning() {
+        stopLeScanning();
+    }
+
+    private final ScanCallback mLeScanCallback = new ScanCallback() {
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            handleLeScanResult(result);
+        }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            for (ScanResult result: results) {
+                handleLeScanResult(result);
+            }
+        }
+
+        @Override
+        public void onScanFailed(int errorCode) {
+            Log.w(TAG, "BLE Scan failed with error code " + errorCode);
+        }
+    };
+
+    void handleLeScanResult(ScanResult result) {
+        if (mCachedDeviceManager == null) {
+            return;
+        }
+        final BluetoothDevice device = result.getDevice();
+        CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
+        if (cachedDevice == null) {
+            cachedDevice = mCachedDeviceManager.addDevice(device);
+        }
+        if (cachedDevice.getHearingAidInfo() == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Set hearing aid info on device: " + cachedDevice);
+            }
+            cachedDevice.setHearingAidInfo(new HearingAidInfo.Builder().build());
+        }
+        // No need to handle the device if the device is already in the list or discovering services
+        if (mDevicePreferenceMap.get(cachedDevice) == null
+                && mConnectingGattList.stream().noneMatch(
+                        gatt -> gatt.getDevice().equals(device))) {
+            if (isAndroidCompatibleHearingAid(result)) {
+                addDevice(cachedDevice);
+            } else {
+                discoverServices(cachedDevice);
+            }
+        }
+    }
+
+    void startLeScanning() {
+        if (mBluetoothAdapter == null) {
+            return;
+        }
+        if (DEBUG) {
+            Log.v(TAG, "startLeScanning");
+        }
+        final BluetoothLeScanner leScanner = mBluetoothAdapter.getBluetoothLeScanner();
+        if (leScanner == null) {
+            Log.w(TAG, "LE scanner not found, cannot start LE scanning");
+        } else {
+            final ScanSettings settings = new ScanSettings.Builder()
+                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+                    .setLegacy(false)
+                    .build();
+            leScanner.startScan(mLeScanFilters, settings, mLeScanCallback);
+            if (mAvailableHearingDeviceGroup != null) {
+                mAvailableHearingDeviceGroup.setProgress(true);
+            }
+        }
+    }
+
+    void stopLeScanning() {
+        if (mBluetoothAdapter == null) {
+            return;
+        }
+        if (DEBUG) {
+            Log.v(TAG, "stopLeScanning");
+        }
+        final BluetoothLeScanner leScanner = mBluetoothAdapter.getBluetoothLeScanner();
+        if (leScanner != null) {
+            leScanner.stopScan(mLeScanCallback);
+            if (mAvailableHearingDeviceGroup != null) {
+                mAvailableHearingDeviceGroup.setProgress(false);
+            }
+        }
+    }
+
+    private void removeAllDevices() {
+        mDevicePreferenceMap.clear();
+        if (mAvailableHearingDeviceGroup != null) {
+            mAvailableHearingDeviceGroup.removeAll();
+        }
+    }
+
+    void initPreferencesFromPreferenceScreen() {
+        mAvailableHearingDeviceGroup = findPreference(KEY_AVAILABLE_HEARING_DEVICES);
+    }
+
+    private void initHearingDeviceLeScanFilters() {
+        mLeScanFilters = new ArrayList<>();
+        // Filters for ASHA hearing aids
+        mLeScanFilters.add(
+                new ScanFilter.Builder().setServiceUuid(BluetoothUuid.HEARING_AID).build());
+        mLeScanFilters.add(new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.HEARING_AID, new byte[0]).build());
+        // Filters for LE audio hearing aids
+        mLeScanFilters.add(new ScanFilter.Builder().setServiceUuid(BluetoothUuid.HAS).build());
+        mLeScanFilters.add(new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.HAS, new byte[0]).build());
+        // Filters for MFi hearing aids
+        mLeScanFilters.add(new ScanFilter.Builder().setServiceUuid(BluetoothUuid.MFI_HAS).build());
+        mLeScanFilters.add(new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.MFI_HAS, new byte[0]).build());
+    }
+
+    boolean isAndroidCompatibleHearingAid(ScanResult scanResult) {
+        ScanRecord scanRecord = scanResult.getScanRecord();
+        if (scanRecord == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Scan record is null, not compatible with Android. device: "
+                        + scanResult.getDevice());
+            }
+            return false;
+        }
+        List<ParcelUuid> uuids = scanRecord.getServiceUuids();
+        if (uuids != null) {
+            if (uuids.contains(BluetoothUuid.HEARING_AID) || uuids.contains(BluetoothUuid.HAS)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Scan record uuid matched, compatible with Android. device: "
+                            + scanResult.getDevice());
+                }
+                return true;
+            }
+        }
+        if (scanRecord.getServiceData(BluetoothUuid.HEARING_AID) != null
+                || scanRecord.getServiceData(BluetoothUuid.HAS) != null) {
+            if (DEBUG) {
+                Log.d(TAG, "Scan record service data matched, compatible with Android. device: "
+                        + scanResult.getDevice());
+            }
+            return true;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Scan record mismatched, not compatible with Android. device: "
+                    + scanResult.getDevice());
+        }
+        return false;
+    }
+
+    void discoverServices(CachedBluetoothDevice cachedDevice) {
+        if (DEBUG) {
+            Log.d(TAG, "connectGattToCheckCompatibility, device: " + cachedDevice);
+        }
+        BluetoothGatt gatt = cachedDevice.getDevice().connectGatt(getContext(), false,
+                new BluetoothGattCallback() {
+                    @Override
+                    public void onConnectionStateChange(BluetoothGatt gatt, int status,
+                            int newState) {
+                        super.onConnectionStateChange(gatt, status, newState);
+                        if (DEBUG) {
+                            Log.d(TAG, "onConnectionStateChange, status: " + status + ", newState: "
+                                    + newState + ", device: " + cachedDevice);
+                        }
+                        if (newState == BluetoothProfile.STATE_CONNECTED) {
+                            gatt.discoverServices();
+                        }
+                    }
+
+                    @Override
+                    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+                        super.onServicesDiscovered(gatt, status);
+                        boolean isCompatible = gatt.getService(BluetoothUuid.HEARING_AID.getUuid())
+                                != null
+                                || gatt.getService(BluetoothUuid.HAS.getUuid()) != null;
+                        if (DEBUG) {
+                            Log.d(TAG,
+                                    "onServicesDiscovered, compatible with Android: " + isCompatible
+                                            + ", device: " + cachedDevice);
+                        }
+                        if (isCompatible) {
+                            addDevice(cachedDevice);
+                        }
+                    }
+                });
+        mConnectingGattList.add(gatt);
+    }
+
+    void showBluetoothTurnedOnToast() {
+        Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
+                Toast.LENGTH_SHORT).show();
+    }
+}
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index c843282..c7d5a73 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -899,8 +899,18 @@
         @Override
         public Dialog onCreateDialog(Bundle savedInstanceState) {
             return new AlertDialog.Builder(getActivity())
-                    .setTitle(getContext().getString(R.string.credman_error_message_title))
-                    .setMessage(getContext().getString(R.string.credman_error_message))
+                    .setTitle(
+                            getContext()
+                                    .getString(
+                                            Flags.newSettingsUi()
+                                                    ? R.string.credman_limit_error_msg_title
+                                                    : R.string.credman_error_message_title))
+                    .setMessage(
+                            getContext()
+                                    .getString(
+                                            Flags.newSettingsUi()
+                                                    ? R.string.credman_limit_error_msg
+                                                    : R.string.credman_error_message))
                     .setPositiveButton(android.R.string.ok, this)
                     .create();
         }
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java
index 2637d83..0bffee9 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java
@@ -25,6 +25,7 @@
 import android.credentials.CredentialManager;
 import android.credentials.CredentialProviderInfo;
 import android.credentials.SetEnabledProvidersException;
+import android.credentials.flags.Flags;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -305,14 +306,21 @@
     protected CharSequence getConfirmationMessage(CandidateInfo appInfo) {
         // If we are selecting none then show a warning label.
         if (appInfo == null) {
-            final String message = getContext().getString(R.string.credman_confirmation_message);
+            final String message =
+                    getContext()
+                            .getString(
+                                    Flags.newSettingsUi()
+                                            ? R.string.credman_confirmation_message_new_ui
+                                            : R.string.credman_confirmation_message);
             return Html.fromHtml(message);
         }
         final CharSequence appName = appInfo.loadLabel();
         final String message =
                 getContext()
                         .getString(
-                                R.string.credman_autofill_confirmation_message,
+                                Flags.newSettingsUi()
+                                        ? R.string.credman_autofill_confirmation_message_new_ui
+                                        : R.string.credman_autofill_confirmation_message,
                                 Html.escapeHtml(appName));
         return Html.fromHtml(message);
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 98d78f2..ac0c63b 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -156,7 +156,7 @@
         return R.layout.preference_widget_gear;
     }
 
-    CachedBluetoothDevice getCachedDevice() {
+    public CachedBluetoothDevice getCachedDevice() {
         return mCachedDevice;
     }
 
@@ -362,7 +362,11 @@
         }
     }
 
-    void onClicked() {
+    /**
+     * Performs different actions according to the device connected and bonded state after
+     * clicking on the preference.
+     */
+    public void onClicked() {
         Context context = getContext();
         int bondState = mCachedDevice.getBondState();
 
diff --git a/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java
index 12cbd58..3a16e3e 100644
--- a/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java
+++ b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java
@@ -29,8 +29,10 @@
 
 import com.android.settings.R;
 import com.android.settings.accessibility.HearingDevicePairingDetail;
+import com.android.settings.accessibility.HearingDevicePairingFragment;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.HearingAidInfo;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -123,8 +125,11 @@
         final int launchPage = getArguments().getInt(KEY_LAUNCH_PAGE);
         final boolean launchFromA11y = (launchPage == SettingsEnums.ACCESSIBILITY)
                 || (launchPage == SettingsEnums.ACCESSIBILITY_HEARING_AID_SETTINGS);
+        final String a11yDestination = Flags.newHearingDevicePairingPage()
+                ? HearingDevicePairingFragment.class.getName()
+                : HearingDevicePairingDetail.class.getName();
         final String destination = launchFromA11y
-                ? HearingDevicePairingDetail.class.getName()
+                ? a11yDestination
                 : BluetoothPairingDetail.class.getName();
         new SubSettingLauncher(getActivity())
                 .setDestination(destination)
diff --git a/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java b/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java
index d2bc319..ef44843 100644
--- a/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/AddDevicePreferenceController.java
@@ -15,18 +15,25 @@
  */
 package com.android.settings.connecteddevice;
 
+import static com.android.settings.accessibility.AccessibilityHearingAidsFragment.KEY_HEARING_DEVICE_ADD_BT_DEVICES;
+
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.text.TextUtils;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
+import com.android.settings.accessibility.HearingDevicePairingDetail;
+import com.android.settings.accessibility.HearingDevicePairingFragment;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.flags.Flags;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -76,6 +83,21 @@
     }
 
     @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (TextUtils.equals(preference.getKey(), KEY_HEARING_DEVICE_ADD_BT_DEVICES)) {
+            String destination = Flags.newHearingDevicePairingPage()
+                    ? HearingDevicePairingFragment.class.getName()
+                    : HearingDevicePairingDetail.class.getName();
+            new SubSettingLauncher(preference.getContext())
+                    .setDestination(destination)
+                    .setSourceMetricsCategory(getMetricsCategory())
+                    .launch();
+            return true;
+        }
+        return super.handlePreferenceTreeClick(preference);
+    }
+
+    @Override
     public int getAvailabilityStatus() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
                 ? AVAILABLE
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
index 9105297..7a7f337 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
@@ -35,7 +35,6 @@
     private AudioSharingDeviceVolumeGroupController mAudioSharingDeviceVolumeGroupController;
     private CallsAndAlarmsPreferenceController mCallsAndAlarmsPreferenceController;
     private AudioSharingPlaySoundPreferenceController mAudioSharingPlaySoundPreferenceController;
-    private AudioSharingNamePreferenceController mAudioSharingNamePreferenceController;
     private AudioStreamsCategoryController mAudioStreamsCategoryController;
 
     public AudioSharingDashboardFragment() {
@@ -77,7 +76,6 @@
         mCallsAndAlarmsPreferenceController.init(this);
         mAudioSharingPlaySoundPreferenceController =
                 use(AudioSharingPlaySoundPreferenceController.class);
-        mAudioSharingNamePreferenceController = use(AudioSharingNamePreferenceController.class);
         mAudioStreamsCategoryController = use(AudioStreamsCategoryController.class);
     }
 
@@ -104,7 +102,6 @@
         mAudioSharingDeviceVolumeGroupController.updateVisibility();
         mCallsAndAlarmsPreferenceController.updateVisibility();
         mAudioSharingPlaySoundPreferenceController.updateVisibility();
-        mAudioSharingNamePreferenceController.updateVisibility();
         mAudioStreamsCategoryController.updateVisibility();
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
index 36f66ff..a3eb188 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
@@ -18,13 +18,12 @@
 
 import android.content.Context;
 
-import androidx.annotation.NonNull;
-import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.widget.ValidatedEditTextPreference;
 
-public class AudioSharingNamePreferenceController extends AudioSharingBasePreferenceController
+public class AudioSharingNamePreferenceController extends BasePreferenceController
         implements ValidatedEditTextPreference.Validator, Preference.OnPreferenceChangeListener {
 
     private static final String TAG = "AudioSharingNamePreferenceController";
@@ -33,12 +32,17 @@
 
     private AudioSharingNameTextValidator mAudioSharingNameTextValidator;
 
-    public AudioSharingNamePreferenceController(Context context) {
-        super(context, PREF_KEY);
+    public AudioSharingNamePreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
         mAudioSharingNameTextValidator = new AudioSharingNameTextValidator();
     }
 
     @Override
+    public int getAvailabilityStatus() {
+        return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
     public String getPreferenceKey() {
         return PREF_KEY;
     }
@@ -53,16 +57,4 @@
     public boolean isTextValid(String value) {
         return mAudioSharingNameTextValidator.isTextValid(value);
     }
-
-    @Override
-    public void onStart(@NonNull LifecycleOwner owner) {
-        super.onStart(owner);
-        // TODO
-    }
-
-    @Override
-    public void onStop(@NonNull LifecycleOwner owner) {
-        super.onStop(owner);
-        // TODO
-    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryController.java
new file mode 100644
index 0000000..f62183d
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryController.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.audiosharing;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+
+public class StreamSettingsCategoryController extends BasePreferenceController
+        implements DefaultLifecycleObserver {
+    private static final String TAG = "StreamSettingsCategoryController";
+    private final BluetoothAdapter mBluetoothAdapter;
+    private final IntentFilter mIntentFilter;
+    private @Nullable Preference mPreference;
+    private BroadcastReceiver mReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) return;
+                    int adapterState =
+                            intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothDevice.ERROR);
+                    mContext.getMainExecutor()
+                            .execute(
+                                    () -> {
+                                        if (mPreference == null) {
+                                            Log.w(
+                                                    TAG,
+                                                    "Skip BT state change due to mPreference "
+                                                            + "is null");
+                                        } else {
+                                            mPreference.setVisible(
+                                                    adapterState == BluetoothAdapter.STATE_ON);
+                                        }
+                                    });
+                }
+            };
+
+    public StreamSettingsCategoryController(Context context, String key) {
+        super(context, key);
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+    }
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        mContext.registerReceiver(mReceiver, mIntentFilter, Context.RECEIVER_EXPORTED_UNAUDITED);
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        mContext.unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+        if (mPreference != null) {
+            mPreference.setVisible(isBluetoothStateOn());
+        }
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    private boolean isBluetoothStateOn() {
+        return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
index d0b57fd..6a65dc0 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
@@ -80,7 +80,8 @@
             return mContext.getString(
                     com.android.settingslib.R.string.battery_info_status_not_charging);
         } else if (BatteryUtils.isBatteryDefenderOn(info)) {
-            return null;
+            return mContext.getString(
+                    com.android.settingslib.R.string.battery_info_status_charging_on_hold);
         } else if (info.remainingLabel == null
                 || info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
             // Present status only if no remaining time or status anomalous
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index f4217b6..f846a6c 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -22,17 +22,14 @@
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.BatteryStatsManager;
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.Build;
-import android.os.Process;
 import android.os.SystemClock;
 import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -47,14 +44,11 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.settings.R;
 import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
-import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
 import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
-import com.android.settings.fuelgauge.batterytip.StatsManagerConfig;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.fuelgauge.Estimate;
 import com.android.settingslib.fuelgauge.EstimateKt;
-import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
 import com.android.settingslib.utils.PowerUtil;
 import com.android.settingslib.utils.StringUtil;
 import com.android.settingslib.utils.ThreadUtils;
@@ -68,7 +62,6 @@
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
-import java.util.List;
 
 /** Utils for battery operation */
 public class BatteryUtils {
@@ -548,74 +541,6 @@
         return false;
     }
 
-    /** Return {@code true} if we should hide anomaly app represented by {@code uid} */
-    public boolean shouldHideAnomaly(
-            PowerAllowlistBackend powerAllowlistBackend, int uid, AnomalyInfo anomalyInfo) {
-        final String[] packageNames = mPackageManager.getPackagesForUid(uid);
-        if (ArrayUtils.isEmpty(packageNames)) {
-            // Don't show it if app has been uninstalled
-            return true;
-        }
-
-        return isSystemUid(uid)
-                || powerAllowlistBackend.isAllowlisted(packageNames, uid)
-                || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames))
-                || (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames));
-    }
-
-    private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) {
-        return anomalyInfo.anomalyType
-                == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE;
-    }
-
-    private boolean isSystemUid(int uid) {
-        final int appUid = UserHandle.getAppId(uid);
-        return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID;
-    }
-
-    private boolean isSystemApp(PackageManager packageManager, String[] packageNames) {
-        for (String packageName : packageNames) {
-            try {
-                final ApplicationInfo info =
-                        packageManager.getApplicationInfo(packageName, 0 /* flags */);
-                if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                    return true;
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "Package not found: " + packageName, e);
-            }
-        }
-
-        return false;
-    }
-
-    private boolean hasLauncherEntry(String[] packageNames) {
-        final Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
-        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
-        // If we do not specify MATCH_DIRECT_BOOT_AWARE or
-        // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
-        // according to the user's lock state. When the user is locked,
-        // components
-        // with ComponentInfo#directBootAware == false will be filtered. We should
-        // explicitly include both direct boot aware and unaware components here.
-        final List<ResolveInfo> resolveInfos =
-                mPackageManager.queryIntentActivities(
-                        launchIntent,
-                        PackageManager.MATCH_DISABLED_COMPONENTS
-                                | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                | PackageManager.MATCH_SYSTEM_ONLY);
-        for (int i = 0, size = resolveInfos.size(); i < size; i++) {
-            final ResolveInfo resolveInfo = resolveInfos.get(i);
-            if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     /**
      * Return version number of an app represented by {@code packageName}, and return -1 if not
      * found.
diff --git a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
index 0f54f3e..08d49f1 100644
--- a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
@@ -151,7 +151,13 @@
     private CharSequence generateLabel(BatteryInfo info) {
         if (Utils.containsIncompatibleChargers(mContext, TAG)) {
             return mContext.getString(
-                    com.android.settingslib.R.string.battery_info_status_not_charging);
+                    com.android.settingslib.R.string.power_incompatible_charging_settings_home_page,
+                    info.batteryPercentString);
+        }
+        if (BatteryUtils.isBatteryDefenderOn(info)) {
+            return mContext.getString(
+                    com.android.settingslib.R.string.power_charging_on_hold_settings_home_page,
+                    info.batteryPercentString);
         }
         if (info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
             // Present status only if no remaining time or status anomalous
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobService.java
deleted file mode 100644
index 0eaed17..0000000
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobService.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.settings.R;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.util.concurrent.TimeUnit;
-
-/** A JobService to clean up obsolete data in anomaly database */
-public class AnomalyCleanupJobService extends JobService {
-    private static final String TAG = "AnomalyCleanUpJobService";
-
-    @VisibleForTesting static final long CLEAN_UP_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1);
-
-    public static void scheduleCleanUp(Context context) {
-        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
-
-        final ComponentName component = new ComponentName(context, AnomalyCleanupJobService.class);
-        final JobInfo.Builder jobBuilder =
-                new JobInfo.Builder(R.integer.job_anomaly_clean_up, component)
-                        .setPeriodic(CLEAN_UP_FREQUENCY_MS)
-                        .setRequiresDeviceIdle(true)
-                        .setRequiresCharging(true)
-                        .setPersisted(true);
-        final JobInfo pending = jobScheduler.getPendingJob(R.integer.job_anomaly_clean_up);
-
-        // Don't schedule it if it already exists, to make sure it runs periodically even after
-        // reboot
-        if (pending == null
-                && jobScheduler.schedule(jobBuilder.build()) != JobScheduler.RESULT_SUCCESS) {
-            Log.i(TAG, "Anomaly clean up job service schedule failed.");
-        }
-    }
-
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        final BatteryDatabaseManager batteryDatabaseManager =
-                BatteryDatabaseManager.getInstance(this);
-        final BatteryTipPolicy policy = new BatteryTipPolicy(this);
-        ThreadUtils.postOnBackgroundThread(
-                () -> {
-                    batteryDatabaseManager.deleteAllAnomaliesBeforeTimeStamp(
-                            System.currentTimeMillis()
-                                    - TimeUnit.DAYS.toMillis(policy.dataHistoryRetainDay));
-                    jobFinished(params, false /* wantsReschedule */);
-                });
-
-        return true;
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters jobParameters) {
-        return false;
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobService.java
deleted file mode 100644
index fe75c8e..0000000
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobService.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import android.app.StatsManager;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.settings.R;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.util.concurrent.TimeUnit;
-
-/** A JobService check whether to update the anomaly config periodically */
-public class AnomalyConfigJobService extends JobService {
-    private static final String TAG = "AnomalyConfigJobService";
-
-    public static final String PREF_DB = "anomaly_pref";
-    public static final String KEY_ANOMALY_CONFIG_VERSION = "anomaly_config_version";
-    private static final int DEFAULT_VERSION = 0;
-
-    @VisibleForTesting static final long CONFIG_UPDATE_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1);
-
-    public static void scheduleConfigUpdate(Context context) {
-        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
-
-        final ComponentName component = new ComponentName(context, AnomalyConfigJobService.class);
-        final JobInfo.Builder jobBuilder =
-                new JobInfo.Builder(R.integer.job_anomaly_config_update, component)
-                        .setPeriodic(CONFIG_UPDATE_FREQUENCY_MS)
-                        .setRequiresDeviceIdle(true)
-                        .setRequiresCharging(true)
-                        .setPersisted(true);
-        final JobInfo pending = jobScheduler.getPendingJob(R.integer.job_anomaly_config_update);
-
-        // Don't schedule it if it already exists, to make sure it runs periodically even after
-        // reboot
-        if (pending == null
-                && jobScheduler.schedule(jobBuilder.build()) != JobScheduler.RESULT_SUCCESS) {
-            Log.i(TAG, "Anomaly config update job service schedule failed.");
-        }
-    }
-
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        ThreadUtils.postOnBackgroundThread(
-                () -> {
-                    final StatsManager statsManager = getSystemService(StatsManager.class);
-                    checkAnomalyConfig(statsManager);
-                    try {
-                        BatteryTipUtils.uploadAnomalyPendingIntent(this, statsManager);
-                    } catch (StatsManager.StatsUnavailableException e) {
-                        Log.w(TAG, "Failed to uploadAnomalyPendingIntent.", e);
-                    }
-                    jobFinished(params, false /* wantsReschedule */);
-                });
-
-        return true;
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters jobParameters) {
-        return false;
-    }
-
-    @VisibleForTesting
-    synchronized void checkAnomalyConfig(StatsManager statsManager) {
-        final SharedPreferences sharedPreferences =
-                getSharedPreferences(PREF_DB, Context.MODE_PRIVATE);
-        final int currentVersion =
-                sharedPreferences.getInt(KEY_ANOMALY_CONFIG_VERSION, DEFAULT_VERSION);
-        final int newVersion =
-                Settings.Global.getInt(
-                        getContentResolver(),
-                        Settings.Global.ANOMALY_CONFIG_VERSION,
-                        DEFAULT_VERSION);
-        final String rawConfig =
-                Settings.Global.getString(getContentResolver(), Settings.Global.ANOMALY_CONFIG);
-        Log.i(TAG, "CurrentVersion: " + currentVersion + " new version: " + newVersion);
-
-        if (newVersion > currentVersion) {
-            try {
-                statsManager.removeConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY);
-            } catch (StatsManager.StatsUnavailableException e) {
-                Log.i(
-                        TAG,
-                        "When updating anomaly config, failed to first remove the old config "
-                                + StatsManagerConfig.ANOMALY_CONFIG_KEY,
-                        e);
-            }
-            if (!TextUtils.isEmpty(rawConfig)) {
-                try {
-                    final byte[] config = Base64.decode(rawConfig, Base64.DEFAULT);
-                    statsManager.addConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY, config);
-                    Log.i(
-                            TAG,
-                            "Upload the anomaly config. configKey: "
-                                    + StatsManagerConfig.ANOMALY_CONFIG_KEY);
-                    SharedPreferences.Editor editor = sharedPreferences.edit();
-                    editor.putInt(KEY_ANOMALY_CONFIG_VERSION, newVersion);
-                    editor.commit();
-                } catch (IllegalArgumentException e) {
-                    Log.e(TAG, "Anomaly raw config is in wrong format", e);
-                } catch (StatsManager.StatsUnavailableException e) {
-                    Log.i(
-                            TAG,
-                            "Upload of anomaly config failed for configKey "
-                                    + StatsManagerConfig.ANOMALY_CONFIG_KEY,
-                            e);
-                }
-            }
-        }
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigReceiver.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigReceiver.java
deleted file mode 100644
index 538b047..0000000
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigReceiver.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import android.app.StatsManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-/**
- * Receive broadcast when {@link StatsManager} restart, then check the anomaly config and prepare
- * info for {@link StatsManager}
- */
-public class AnomalyConfigReceiver extends BroadcastReceiver {
-    private static final String TAG = "AnomalyConfigReceiver";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (StatsManager.ACTION_STATSD_STARTED.equals(intent.getAction())
-                || Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
-            final StatsManager statsManager = context.getSystemService(StatsManager.class);
-
-            // Check whether to update the config
-            AnomalyConfigJobService.scheduleConfigUpdate(context);
-
-            try {
-                BatteryTipUtils.uploadAnomalyPendingIntent(context, statsManager);
-            } catch (StatsManager.StatsUnavailableException e) {
-                Log.w(TAG, "Failed to uploadAnomalyPendingIntent.", e);
-            }
-
-            if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
-                AnomalyCleanupJobService.scheduleCleanUp(context);
-            }
-        }
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
deleted file mode 100644
index a80987d..0000000
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import static android.os.StatsDimensionsValue.INT_VALUE_TYPE;
-import static android.os.StatsDimensionsValue.TUPLE_VALUE_TYPE;
-
-import android.app.AppOpsManager;
-import android.app.StatsManager;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.app.job.JobWorkItem;
-import android.app.settings.SettingsEnums;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.StatsDimensionsValue;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.GuardedBy;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.settings.R;
-import com.android.settings.fuelgauge.BatteryUtils;
-import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** A JobService to store anomaly data to anomaly database */
-public class AnomalyDetectionJobService extends JobService {
-    private static final String TAG = "AnomalyDetectionService";
-    private static final int ON = 1;
-    @VisibleForTesting static final int UID_NULL = -1;
-    @VisibleForTesting static final int STATSD_UID_FILED = 1;
-    @VisibleForTesting static final long MAX_DELAY_MS = Duration.ofDays(1).toMillis();
-
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    @VisibleForTesting
-    boolean mIsJobCanceled = false;
-
-    public static void scheduleAnomalyDetection(Context context, Intent intent) {
-        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
-        final ComponentName component =
-                new ComponentName(context, AnomalyDetectionJobService.class);
-        final JobInfo.Builder jobBuilder =
-                new JobInfo.Builder(R.integer.job_anomaly_detection, component)
-                        .setOverrideDeadline(MAX_DELAY_MS);
-
-        if (jobScheduler.enqueue(jobBuilder.build(), new JobWorkItem(intent))
-                != JobScheduler.RESULT_SUCCESS) {
-            Log.i(TAG, "Anomaly detection job service enqueue failed.");
-        }
-    }
-
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        synchronized (mLock) {
-            mIsJobCanceled = false;
-        }
-        ThreadUtils.postOnBackgroundThread(
-                () -> {
-                    final Context context = AnomalyDetectionJobService.this;
-                    final BatteryDatabaseManager batteryDatabaseManager =
-                            BatteryDatabaseManager.getInstance(this);
-                    final BatteryTipPolicy policy = new BatteryTipPolicy(this);
-                    final BatteryUtils batteryUtils = BatteryUtils.getInstance(this);
-                    final ContentResolver contentResolver = getContentResolver();
-                    final UserManager userManager = getSystemService(UserManager.class);
-                    final PowerAllowlistBackend powerAllowlistBackend =
-                            PowerAllowlistBackend.getInstance(context);
-                    final PowerUsageFeatureProvider powerUsageFeatureProvider =
-                            FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider();
-                    final MetricsFeatureProvider metricsFeatureProvider =
-                            FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
-
-                    for (JobWorkItem item = dequeueWork(params);
-                            item != null;
-                            item = dequeueWork(params)) {
-                        saveAnomalyToDatabase(
-                                context,
-                                userManager,
-                                batteryDatabaseManager,
-                                batteryUtils,
-                                policy,
-                                powerAllowlistBackend,
-                                contentResolver,
-                                powerUsageFeatureProvider,
-                                metricsFeatureProvider,
-                                item.getIntent().getExtras());
-
-                        completeWork(params, item);
-                    }
-                });
-
-        return true;
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters jobParameters) {
-        synchronized (mLock) {
-            mIsJobCanceled = true;
-        }
-        return true; // Need to reschedule
-    }
-
-    @VisibleForTesting
-    void saveAnomalyToDatabase(
-            Context context,
-            UserManager userManager,
-            BatteryDatabaseManager databaseManager,
-            BatteryUtils batteryUtils,
-            BatteryTipPolicy policy,
-            PowerAllowlistBackend powerAllowlistBackend,
-            ContentResolver contentResolver,
-            PowerUsageFeatureProvider powerUsageFeatureProvider,
-            MetricsFeatureProvider metricsFeatureProvider,
-            Bundle bundle) {
-        // The Example of intentDimsValue is: 35:{1:{1:{1:10013|}|}|}
-        final StatsDimensionsValue intentDimsValue =
-                bundle.getParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE);
-        final long timeMs =
-                bundle.getLong(
-                        AnomalyDetectionReceiver.KEY_ANOMALY_TIMESTAMP, System.currentTimeMillis());
-        final ArrayList<String> cookies =
-                bundle.getStringArrayList(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES);
-        final AnomalyInfo anomalyInfo =
-                new AnomalyInfo(!ArrayUtils.isEmpty(cookies) ? cookies.get(0) : "");
-        Log.i(TAG, "Extra stats value: " + intentDimsValue.toString());
-
-        try {
-            final int uid = extractUidFromStatsDimensionsValue(intentDimsValue);
-            final boolean autoFeatureOn =
-                    powerUsageFeatureProvider.isSmartBatterySupported()
-                            ? Settings.Global.getInt(
-                                            contentResolver,
-                                            Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
-                                            ON)
-                                    == ON
-                            : Settings.Global.getInt(
-                                            contentResolver,
-                                            Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
-                                            ON)
-                                    == ON;
-            final String packageName = batteryUtils.getPackageName(uid);
-            final long versionCode = batteryUtils.getAppLongVersionCode(packageName);
-            final String versionedPackage = packageName + "/" + versionCode;
-            if (batteryUtils.shouldHideAnomaly(powerAllowlistBackend, uid, anomalyInfo)) {
-                metricsFeatureProvider.action(
-                        SettingsEnums.PAGE_UNKNOWN,
-                        SettingsEnums.ACTION_ANOMALY_IGNORED,
-                        SettingsEnums.PAGE_UNKNOWN,
-                        versionedPackage,
-                        anomalyInfo.anomalyType);
-            } else {
-                if (autoFeatureOn && anomalyInfo.autoRestriction) {
-                    // Auto restrict this app
-                    batteryUtils.setForceAppStandby(uid, packageName, AppOpsManager.MODE_IGNORED);
-                    databaseManager.insertAnomaly(
-                            uid,
-                            packageName,
-                            anomalyInfo.anomalyType,
-                            AnomalyDatabaseHelper.State.AUTO_HANDLED,
-                            timeMs);
-                } else {
-                    databaseManager.insertAnomaly(
-                            uid,
-                            packageName,
-                            anomalyInfo.anomalyType,
-                            AnomalyDatabaseHelper.State.NEW,
-                            timeMs);
-                }
-                metricsFeatureProvider.action(
-                        SettingsEnums.PAGE_UNKNOWN,
-                        SettingsEnums.ACTION_ANOMALY_TRIGGERED,
-                        SettingsEnums.PAGE_UNKNOWN,
-                        versionedPackage,
-                        anomalyInfo.anomalyType);
-            }
-
-        } catch (NullPointerException | IndexOutOfBoundsException e) {
-            Log.e(TAG, "Parse stats dimensions value error.", e);
-        }
-    }
-
-    /**
-     * Extract the uid from {@link StatsDimensionsValue} <br>
-     * <br>
-     * The uid dimension has the format: {1:int} inside the tuple list. Here are some examples: <br>
-     * 1.Excessive bg anomaly: 27:{1:10089|} <br>
-     * 2.Wakeup alarm anomaly: 35:{1:{1:{1:10013|}|}|} <br>
-     * 3.Bluetooth anomaly: 3:{1:{1:{1:10140|}|}|}
-     */
-    @VisibleForTesting
-    int extractUidFromStatsDimensionsValue(StatsDimensionsValue statsDimensionsValue) {
-        if (statsDimensionsValue == null) {
-            return UID_NULL;
-        }
-        if (statsDimensionsValue.isValueType(INT_VALUE_TYPE)
-                && statsDimensionsValue.getField() == STATSD_UID_FILED) {
-            // Find out the real uid
-            return statsDimensionsValue.getIntValue();
-        }
-        if (statsDimensionsValue.isValueType(TUPLE_VALUE_TYPE)) {
-            final List<StatsDimensionsValue> values = statsDimensionsValue.getTupleValueList();
-            for (int i = 0, size = values.size(); i < size; i++) {
-                int uid = extractUidFromStatsDimensionsValue(values.get(i));
-                if (uid != UID_NULL) {
-                    return uid;
-                }
-            }
-        }
-
-        return UID_NULL;
-    }
-
-    @VisibleForTesting
-    JobWorkItem dequeueWork(JobParameters parameters) {
-        synchronized (mLock) {
-            if (mIsJobCanceled) {
-                return null;
-            }
-
-            return parameters.dequeueWork();
-        }
-    }
-
-    @VisibleForTesting
-    void completeWork(JobParameters parameters, JobWorkItem item) {
-        synchronized (mLock) {
-            if (mIsJobCanceled) {
-                return;
-            }
-
-            parameters.completeWork(item);
-        }
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionReceiver.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionReceiver.java
deleted file mode 100644
index 0d43add..0000000
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionReceiver.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import android.app.StatsManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-/** Receive the anomaly info from {@link StatsManager} */
-public class AnomalyDetectionReceiver extends BroadcastReceiver {
-    private static final String TAG = "SettingsAnomalyReceiver";
-
-    public static final String KEY_ANOMALY_TIMESTAMP = "key_anomaly_timestamp";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        final long configUid = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_UID, -1);
-        final long configKey = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, -1);
-        final long subscriptionId =
-                intent.getLongExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, -1);
-        Log.i(
-                TAG,
-                "Anomaly intent received.  configUid = "
-                        + configUid
-                        + " configKey = "
-                        + configKey
-                        + " subscriptionId = "
-                        + subscriptionId);
-
-        final Bundle bundle = intent.getExtras();
-        if (bundle == null) {
-            return;
-        }
-        bundle.putLong(KEY_ANOMALY_TIMESTAMP, System.currentTimeMillis());
-
-        AnomalyDetectionJobService.scheduleAnomalyDetection(context, intent);
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyInfo.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyInfo.java
deleted file mode 100644
index da277c6..0000000
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyInfo.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import android.util.KeyValueListParser;
-import android.util.Log;
-
-/** Model class to parse and store anomaly info from statsd. */
-public class AnomalyInfo {
-    private static final String TAG = "AnomalyInfo";
-
-    private static final String KEY_ANOMALY_TYPE = "anomaly_type";
-    private static final String KEY_AUTO_RESTRICTION = "auto_restriction";
-    public final Integer anomalyType;
-    public final boolean autoRestriction;
-
-    public AnomalyInfo(String info) {
-        Log.i(TAG, "anomalyInfo: " + info);
-        KeyValueListParser parser = new KeyValueListParser(',');
-        parser.setString(info);
-        anomalyType = parser.getInt(KEY_ANOMALY_TYPE, -1);
-        autoRestriction = parser.getBoolean(KEY_AUTO_RESTRICTION, false);
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index d65bd26..7dc993c 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -17,8 +17,6 @@
 package com.android.settings.fuelgauge.batterytip;
 
 import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.app.StatsManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
@@ -111,36 +109,8 @@
         }
     }
 
-    /**
-     * Upload the {@link PendingIntent} to {@link StatsManager} for anomaly detection
-     *
-     * @throws StatsManager.StatsUnavailableException if failed to communicate with stats service
-     */
-    public static void uploadAnomalyPendingIntent(Context context, StatsManager statsManager)
-            throws StatsManager.StatsUnavailableException {
-        final Intent extraIntent = new Intent(context, AnomalyDetectionReceiver.class);
-        final PendingIntent pendingIntent =
-                PendingIntent.getBroadcast(
-                        context,
-                        REQUEST_CODE,
-                        extraIntent,
-                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
-        statsManager.setBroadcastSubscriber(
-                pendingIntent,
-                StatsManagerConfig.ANOMALY_CONFIG_KEY,
-                StatsManagerConfig.SUBSCRIBER_ID);
-    }
-
-    /** Detect and return anomaly apps after {@code timeAfterMs} */
+   /** Detect and return anomaly apps after {@code timeAfterMs} */
     public static List<AppInfo> detectAnomalies(Context context, long timeAfterMs) {
-        final List<AppInfo> highUsageApps =
-                BatteryDatabaseManager.getInstance(context)
-                        .queryAllAnomalies(timeAfterMs, AnomalyDatabaseHelper.State.NEW);
-        // Remove it if it doesn't have label or been restricted
-        highUsageApps.removeIf(
-                AppLabelPredicate.getInstance(context)
-                        .or(AppRestrictionPredicate.getInstance(context)));
-
-        return highUsageApps;
+        return new ArrayList<>();
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index 002c807..df9f063 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -479,7 +479,9 @@
         }
     }
 
-    private static BatteryUsageDiff convertToBatteryUsageDiff(BatteryDiffEntry batteryDiffEntry) {
+
+    @VisibleForTesting
+    static BatteryUsageDiff convertToBatteryUsageDiff(BatteryDiffEntry batteryDiffEntry) {
         BatteryUsageDiff.Builder builder =
                 BatteryUsageDiff.newBuilder()
                         .setUid(batteryDiffEntry.mUid)
@@ -496,6 +498,8 @@
                                 batteryDiffEntry.mForegroundServiceUsageConsumePower)
                         .setCachedUsageConsumePower(batteryDiffEntry.mCachedUsageConsumePower)
                         .setForegroundUsageTime(batteryDiffEntry.mForegroundUsageTimeInMs)
+                        .setForegroundServiceUsageTime(
+                                batteryDiffEntry.mForegroundServiceUsageTimeInMs)
                         .setBackgroundUsageTime(batteryDiffEntry.mBackgroundUsageTimeInMs)
                         .setScreenOnTime(batteryDiffEntry.mScreenOnTimeInMs);
         if (batteryDiffEntry.mKey != null) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index 8a1cd76..a41e9bd 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -767,7 +767,8 @@
                 BatteryUsageBroadcastReceiver.ACTION_CLEAR_BATTERY_CACHE_DATA);
         writeString(context, writer, "LastLoadFullChargeTime", KEY_LAST_LOAD_FULL_CHARGE_TIME);
         writeString(context, writer, "LastUploadFullChargeTime", KEY_LAST_UPLOAD_FULL_CHARGE_TIME);
-        writeString(context, writer, "DismissedPowerAnomalyKeys", KEY_DISMISSED_POWER_ANOMALY_KEYS);
+        writeStringSet(
+                context, writer, "DismissedPowerAnomalyKeys", KEY_DISMISSED_POWER_ANOMALY_KEYS);
     }
 
     static SharedPreferences getSharedPreferences(Context context) {
@@ -921,9 +922,22 @@
     private static void writeString(
             Context context, PrintWriter writer, String prefix, String key) {
         final SharedPreferences sharedPreferences = getSharedPreferences(context);
-        if (sharedPreferences != null) {
-            final String content = sharedPreferences.getString(key, "");
-            writer.println(String.format("\t\t%s: %s", prefix, content));
+        if (sharedPreferences == null) {
+            return;
+        }
+        final String content = sharedPreferences.getString(key, "");
+        writer.println(String.format("\t\t%s: %s", prefix, content));
+    }
+
+    private static void writeStringSet(
+            Context context, PrintWriter writer, String prefix, String key) {
+        final SharedPreferences sharedPreferences = getSharedPreferences(context);
+        if (sharedPreferences == null) {
+            return;
+        }
+        final Set<String> results = sharedPreferences.getStringSet(key, new ArraySet<>());
+        if (results != null) {
+            writer.println(String.format("\t\t%s: %s", prefix, results.toString()));
         }
     }
 
diff --git a/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
new file mode 100644
index 0000000..b421185
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.network.telephony
+
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import androidx.core.os.persistableBundleOf
+
+/**
+ * Gets the configuration values of the specified config keys applied.
+ */
+fun CarrierConfigManager.safeGetConfig(
+    keys: List<String>,
+    subId: Int = SubscriptionManager.getDefaultSubscriptionId(),
+): PersistableBundle = try {
+    getConfigForSubId(subId, *keys.toTypedArray())
+} catch (e: IllegalStateException) {
+    // The CarrierConfigLoader (the service implemented the CarrierConfigManager) hasn't been
+    // initialized yet. This may occurs during very early phase of phone booting up or when Phone
+    // process has been restarted.
+    // Settings should not assume Carrier config loader (and any other system services as well) are
+    // always available. If not available, use default value instead.
+    persistableBundleOf()
+}
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 663b4e5..db031e7 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -203,6 +203,7 @@
         private boolean mOnlyEnforceDevicePasswordRequirement = false;
         private int mExtraLockScreenTitleResId;
         private int mExtraLockScreenDescriptionResId;
+        private boolean mWaitingForBiometricEnrollment = false;
 
         @Override
         public int getMetricsCategory() {
@@ -250,6 +251,7 @@
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
             mForBiometrics = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
+            mWaitingForBiometricEnrollment = mForBiometrics || mForFingerprint || mForFace;
 
             mExtraLockScreenTitleResId = intent.getIntExtra(EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE, -1);
             mExtraLockScreenDescriptionResId =
@@ -440,6 +442,7 @@
                 return true;
             } else if (KEY_SKIP_FINGERPRINT.equals(key) || KEY_SKIP_FACE.equals(key)
                     || KEY_SKIP_BIOMETRICS.equals(key)) {
+                mWaitingForBiometricEnrollment = false;
                 Intent chooseLockGenericIntent = new Intent(getActivity(),
                     getInternalActivityClass());
                 chooseLockGenericIntent.setAction(getIntent().getAction());
@@ -493,6 +496,7 @@
                 finish();
             } else if (requestCode == CHOOSE_LOCK_BEFORE_BIOMETRIC_REQUEST
                     && resultCode == BiometricEnrollBase.RESULT_FINISHED) {
+                mWaitingForBiometricEnrollment = false;
                 Intent intent = getBiometricEnrollIntent(getActivity());
                 if (data != null) {
                     // ChooseLockGeneric should have requested for a Gatekeeper Password Handle to
@@ -873,7 +877,8 @@
             // Otherwise, bugs would be caused. (e.g. b/278488549, b/278530059)
             final boolean hasCredential = mLockPatternUtils.isSecure(mUserId);
             if (!getActivity().isChangingConfigurations()
-                    && !mWaitingForConfirmation && !mWaitingForActivityResult && hasCredential) {
+                    && !mWaitingForConfirmation && !mWaitingForActivityResult && hasCredential
+                    && !mWaitingForBiometricEnrollment) {
                 getActivity().finish();
             }
         }
diff --git a/src/com/android/settings/system/ClientInitiatedActionRepository.kt b/src/com/android/settings/system/ClientInitiatedActionRepository.kt
index 24c04b4..715acfa 100644
--- a/src/com/android/settings/system/ClientInitiatedActionRepository.kt
+++ b/src/com/android/settings/system/ClientInitiatedActionRepository.kt
@@ -20,6 +20,7 @@
 import android.content.Intent
 import android.telephony.CarrierConfigManager
 import android.util.Log
+import com.android.settings.network.telephony.safeGetConfig
 
 class ClientInitiatedActionRepository(private val context: Context) {
     private val configManager = context.getSystemService(CarrierConfigManager::class.java)!!
@@ -29,11 +30,13 @@
      */
     fun onSystemUpdate() {
         val bundle =
-            configManager.getConfig(
-                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL,
-                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING,
-                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING,
-                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING,
+            configManager.safeGetConfig(
+                keys = listOf(
+                    CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL,
+                    CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING,
+                    CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING,
+                    CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING,
+                ),
             )
 
         if (!bundle.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) return
diff --git a/tests/robotests/src/com/android/settings/SettingsDumpServiceTest.java b/tests/robotests/src/com/android/settings/SettingsDumpServiceTest.java
index 9d8841f..e44d528 100644
--- a/tests/robotests/src/com/android/settings/SettingsDumpServiceTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsDumpServiceTest.java
@@ -29,10 +29,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 
-import com.android.settings.fuelgauge.batterytip.AnomalyConfigJobService;
-
-import org.json.JSONException;
-import org.json.JSONObject;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -84,22 +80,6 @@
     }
 
     @Test
-    public void testDumpAnomalyDetection_returnAnomalyInfo() throws JSONException {
-        final SharedPreferences sharedPreferences =
-                RuntimeEnvironment.application.getSharedPreferences(AnomalyConfigJobService.PREF_DB,
-                        Context.MODE_PRIVATE);
-        SharedPreferences.Editor editor = sharedPreferences.edit();
-        editor.putInt(AnomalyConfigJobService.KEY_ANOMALY_CONFIG_VERSION, ANOMALY_VERSION);
-        editor.commit();
-        doReturn(sharedPreferences).when(mTestService).getSharedPreferences(anyString(), anyInt());
-
-        final JSONObject jsonObject = mTestService.dumpAnomalyDetection();
-
-        assertThat(jsonObject.getInt(AnomalyConfigJobService.KEY_ANOMALY_CONFIG_VERSION)).isEqualTo(
-                ANOMALY_VERSION);
-    }
-
-    @Test
     public void testDump_printServiceAsKey() {
         mResolveInfo.activityInfo = new ActivityInfo();
         mResolveInfo.activityInfo.packageName = PACKAGE_BROWSER;
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java
index 6c1de59..bd57e9d 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidPairingDialogFragmentTest.java
@@ -32,6 +32,10 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.FragmentActivity;
@@ -43,6 +47,7 @@
 import com.android.settings.bluetooth.BluetoothPairingDetail;
 import com.android.settings.bluetooth.HearingAidPairingDialogFragment;
 import com.android.settings.bluetooth.Utils;
+import com.android.settings.flags.Flags;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -77,6 +82,9 @@
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
     private static final int TEST_LAUNCH_PAGE = SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY;
 
@@ -129,7 +137,22 @@
     }
 
     @Test
-    public void dialogPositiveButtonClick_intentToA11yPairingPage() {
+    @RequiresFlagsEnabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
+    public void dialogPositiveButtonClick_intentToNewA11yPairingPage() {
+        setupDialog(SettingsEnums.ACCESSIBILITY);
+        final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY);
+        dialog.show();
+
+        dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
+
+        final Intent intent = shadowOf(mActivity).getNextStartedActivity();
+        assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(HearingDevicePairingFragment.class.getName());
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
+    public void dialogPositiveButtonClick_intentToOldA11yPairingPage() {
         setupDialog(SettingsEnums.ACCESSIBILITY);
         final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY);
         dialog.show();
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java
new file mode 100644
index 0000000..e14686e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java
@@ -0,0 +1,318 @@
+/*
+ * 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.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.Pair;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.bluetooth.BluetoothDevicePreference;
+import com.android.settings.bluetooth.BluetoothProgressCategory;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.HearingAidInfo;
+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;
+
+import java.util.List;
+
+/** Tests for {@link HearingDevicePairingFragment}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class HearingDevicePairingFragmentTest {
+
+    private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
+
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Spy
+    private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+    @Spy
+    private final HearingDevicePairingFragment mFragment = new TestHearingDevicePairingFragment();
+
+    @Mock
+    private LocalBluetoothManager mLocalManager;
+    @Mock
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice;
+    @Mock
+    private BluetoothProgressCategory mAvailableHearingDeviceGroup;
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private BluetoothDevice mDevice;
+    private BluetoothDevicePreference mDevicePreference;
+
+
+    @Before
+    public void setUp() {
+        mFragment.mLocalManager = mLocalManager;
+        mFragment.mCachedDeviceManager = mCachedDeviceManager;
+        mFragment.mBluetoothAdapter = mBluetoothAdapter;
+        doReturn(mContext).when(mFragment).getContext();
+        doReturn(mAvailableHearingDeviceGroup).when(mFragment).findPreference(
+                "available_hearing_devices");
+        mFragment.initPreferencesFromPreferenceScreen();
+
+
+        mDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
+        doReturn(mDevice).when(mCachedDevice).getDevice();
+        final Pair<Drawable, String> pair = new Pair<>(mock(Drawable.class), "test_device");
+        doReturn(pair).when(mCachedDevice).getDrawableWithDescription();
+
+        mDevicePreference = new BluetoothDevicePreference(mContext, mCachedDevice, true,
+                BluetoothDevicePreference.SortType.TYPE_DEFAULT);
+    }
+
+    @Test
+    public void startAndStopScanning_stateIsCorrect() {
+        mFragment.startScanning();
+
+        verify(mFragment).startLeScanning();
+
+        mFragment.stopScanning();
+
+        verify(mFragment).stopLeScanning();
+    }
+
+    @Test
+    public void onDeviceDeleted_stateIsCorrect() {
+        mFragment.mDevicePreferenceMap.put(mCachedDevice, mDevicePreference);
+
+        assertThat(mFragment.mDevicePreferenceMap).isNotEmpty();
+
+        mFragment.onDeviceDeleted(mCachedDevice);
+
+        assertThat(mFragment.mDevicePreferenceMap).isEmpty();
+        verify(mAvailableHearingDeviceGroup).removePreference(mDevicePreference);
+    }
+
+    @Test
+    public void addDevice_bluetoothOff_doNothing() {
+        doReturn(BluetoothAdapter.STATE_OFF).when(mBluetoothAdapter).getState();
+
+        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
+
+        mFragment.addDevice(mCachedDevice);
+
+        verify(mAvailableHearingDeviceGroup, never()).addPreference(mDevicePreference);
+        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
+    }
+
+    @Test
+    public void addDevice_addToAvailableHearingDeviceGroup() {
+        doReturn(BluetoothAdapter.STATE_ON).when(mBluetoothAdapter).getState();
+
+        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
+
+        mFragment.addDevice(mCachedDevice);
+
+        verify(mAvailableHearingDeviceGroup).addPreference(mDevicePreference);
+        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(1);
+    }
+
+    @Test
+    public void handleLeScanResult_markDeviceAsHearingAid() {
+        ScanResult scanResult = mock(ScanResult.class);
+        doReturn(mDevice).when(scanResult).getDevice();
+        doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
+
+        mFragment.handleLeScanResult(scanResult);
+
+        verify(mCachedDevice).setHearingAidInfo(new HearingAidInfo.Builder().build());
+    }
+
+    @Test
+    public void handleLeScanResult_isAndroidCompatible_addDevice() {
+        ScanResult scanResult = mock(ScanResult.class);
+        doReturn(mDevice).when(scanResult).getDevice();
+        doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
+        doReturn(true).when(mFragment).isAndroidCompatibleHearingAid(scanResult);
+
+        mFragment.handleLeScanResult(scanResult);
+
+        verify(mFragment).addDevice(mCachedDevice);
+    }
+
+    @Test
+    public void handleLeScanResult_isNotAndroidCompatible_() {
+        ScanResult scanResult = mock(ScanResult.class);
+        doReturn(mDevice).when(scanResult).getDevice();
+        doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
+        doReturn(false).when(mFragment).isAndroidCompatibleHearingAid(scanResult);
+
+        mFragment.handleLeScanResult(scanResult);
+
+        verify(mFragment).discoverServices(mCachedDevice);
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceConnected_inSelectedList_finish() {
+        doReturn(true).when(mCachedDevice).isConnected();
+        mFragment.mSelectedDeviceList.add(mDevice);
+
+        mFragment.onProfileConnectionStateChanged(mCachedDevice, BluetoothAdapter.STATE_CONNECTED,
+                BluetoothProfile.A2DP);
+
+        verify(mFragment).finish();
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceConnected_notInSelectedList_deleteDevice() {
+        doReturn(true).when(mCachedDevice).isConnected();
+
+        mFragment.onProfileConnectionStateChanged(mCachedDevice, BluetoothAdapter.STATE_CONNECTED,
+                BluetoothProfile.A2DP);
+
+        verify(mFragment).removeDevice(mCachedDevice);
+    }
+
+    @Test
+    public void onProfileConnectionStateChanged_deviceNotConnected_doNothing() {
+        doReturn(false).when(mCachedDevice).isConnected();
+
+        mFragment.onProfileConnectionStateChanged(mCachedDevice, BluetoothAdapter.STATE_CONNECTED,
+                BluetoothProfile.A2DP);
+
+        verify(mFragment, never()).finish();
+        verify(mFragment, never()).removeDevice(mCachedDevice);
+    }
+
+    @Test
+    public void onBluetoothStateChanged_stateOn_startScanningAndShowToast() {
+        mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
+
+        verify(mFragment).startScanning();
+        verify(mFragment).showBluetoothTurnedOnToast();
+    }
+
+    @Test
+    public void onBluetoothStateChanged_stateOff_finish() {
+        mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
+
+        verify(mFragment).finish();
+    }
+
+    @Test
+    public void onDeviceBondStateChanged_bonded_finish() {
+        mFragment.onDeviceBondStateChanged(mCachedDevice, BluetoothDevice.BOND_BONDED);
+
+        verify(mFragment).finish();
+    }
+
+    @Test
+    public void onDeviceBondStateChanged_selectedDeviceNotBonded_startScanning() {
+        mFragment.mSelectedDevice = mDevice;
+
+        mFragment.onDeviceBondStateChanged(mCachedDevice, BluetoothDevice.BOND_NONE);
+
+        verify(mFragment).startScanning();
+    }
+
+    @Test
+    public void isAndroidCompatibleHearingAid_asha_returnTrue() {
+        ScanResult scanResult = createAshaScanResult();
+
+        boolean isCompatible = mFragment.isAndroidCompatibleHearingAid(scanResult);
+
+        assertThat(isCompatible).isTrue();
+    }
+
+    @Test
+    public void isAndroidCompatibleHearingAid_has_returnTrue() {
+        ScanResult scanResult = createHasScanResult();
+
+        boolean isCompatible = mFragment.isAndroidCompatibleHearingAid(scanResult);
+
+        assertThat(isCompatible).isTrue();
+    }
+
+    @Test
+    public void isAndroidCompatibleHearingAid_mfiHas_returnFalse() {
+        ScanResult scanResult = createMfiHasScanResult();
+
+        boolean isCompatible = mFragment.isAndroidCompatibleHearingAid(scanResult);
+
+        assertThat(isCompatible).isFalse();
+    }
+
+    private ScanResult createAshaScanResult() {
+        ScanResult scanResult = mock(ScanResult.class);
+        ScanRecord scanRecord = mock(ScanRecord.class);
+        byte[] fakeAshaServiceData = new byte[] {
+                0x09, 0x16, (byte) 0xf0, (byte) 0xfd, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04};
+        doReturn(scanRecord).when(scanResult).getScanRecord();
+        doReturn(fakeAshaServiceData).when(scanRecord).getServiceData(BluetoothUuid.HEARING_AID);
+        return scanResult;
+    }
+
+    private ScanResult createHasScanResult() {
+        ScanResult scanResult = mock(ScanResult.class);
+        ScanRecord scanRecord = mock(ScanRecord.class);
+        doReturn(scanRecord).when(scanResult).getScanRecord();
+        doReturn(List.of(BluetoothUuid.HAS)).when(scanRecord).getServiceUuids();
+        return scanResult;
+    }
+
+    private ScanResult createMfiHasScanResult() {
+        ScanResult scanResult = mock(ScanResult.class);
+        ScanRecord scanRecord = mock(ScanRecord.class);
+        byte[] fakeMfiServiceData = new byte[] {0x00, 0x00, 0x00, 0x00};
+        doReturn(scanRecord).when(scanResult).getScanRecord();
+        doReturn(fakeMfiServiceData).when(scanRecord).getServiceData(BluetoothUuid.MFI_HAS);
+        return scanResult;
+    }
+
+    private class TestHearingDevicePairingFragment extends HearingDevicePairingFragment {
+        @Override
+        protected Preference getCachedPreference(String key) {
+            if (key.equals(TEST_DEVICE_ADDRESS)) {
+                return mDevicePreference;
+            }
+            return super.getCachedPreference(key);
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java
index 7384d3a..63fa88d 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/AddDevicePreferenceControllerTest.java
@@ -15,34 +15,49 @@
  */
 package com.android.settings.connecteddevice;
 
+import static com.android.settings.accessibility.AccessibilityHearingAidsFragment.KEY_HEARING_DEVICE_ADD_BT_DEVICES;
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
 import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.text.TextUtils;
 
 import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.accessibility.HearingDevicePairingDetail;
+import com.android.settings.accessibility.HearingDevicePairingFragment;
+import com.android.settings.flags.Flags;
 import com.android.settingslib.RestrictedPreference;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplicationPackageManager;
 import org.robolectric.util.ReflectionHelpers;
@@ -51,12 +66,16 @@
 @Config(shadows = ShadowApplicationPackageManager.class)
 public class AddDevicePreferenceControllerTest {
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock
     private PreferenceScreen mScreen;
     @Mock
     private BluetoothAdapter mBluetoothAdapter;
 
-    private Context mContext;
+    @Spy
+    private Context mContext = ApplicationProvider.getApplicationContext();
     private AddDevicePreferenceController mAddDevicePreferenceController;
     private RestrictedPreference mAddDevicePreference;
     private ShadowApplicationPackageManager mPackageManager;
@@ -66,8 +85,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mContext = RuntimeEnvironment.application;
-        mPackageManager = (ShadowApplicationPackageManager) Shadows.shadowOf(
+        mPackageManager = (ShadowApplicationPackageManager) shadowOf(
                 mContext.getPackageManager());
         mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
 
@@ -82,6 +100,8 @@
         when(mBluetoothAdapter.isEnabled()).thenReturn(true);
         when(mScreen.findPreference(key)).thenReturn(mAddDevicePreference);
         mAddDevicePreferenceController.displayPreference(mScreen);
+
+        doNothing().when(mContext).startActivity(any(Intent.class));
     }
 
     @Test
@@ -137,4 +157,30 @@
         assertThat(mAddDevicePreferenceController.getAvailabilityStatus())
                 .isEqualTo(UNSUPPORTED_ON_DEVICE);
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
+    public void handlePreferenceClick_A11yPreference_redirectToNewPairingPage() {
+        mAddDevicePreference.setKey(KEY_HEARING_DEVICE_ADD_BT_DEVICES);
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+        mAddDevicePreferenceController.handlePreferenceTreeClick(mAddDevicePreference);
+
+        verify(mContext).startActivity(intentCaptor.capture());
+        assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(HearingDevicePairingFragment.class.getName());
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_NEW_HEARING_DEVICE_PAIRING_PAGE)
+    public void handlePreferenceClick_A11yPreference_redirectToOldPairingPage() {
+        mAddDevicePreference.setKey(KEY_HEARING_DEVICE_ADD_BT_DEVICES);
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+        mAddDevicePreferenceController.handlePreferenceTreeClick(mAddDevicePreference);
+
+        verify(mContext).startActivity(intentCaptor.capture());
+        assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(HearingDevicePairingDetail.class.getName());
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
index 83ff582..1df8a40 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
@@ -272,7 +272,8 @@
 
         mController.updateHeaderPreference(mBatteryInfo);
 
-        verify(mBatteryUsageProgressBarPref).setBottomSummary(null);
+        verify(mBatteryUsageProgressBarPref).setBottomSummary(mContext.getString(
+                com.android.settingslib.R.string.battery_info_status_charging_on_hold));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index 0d8c669..1a2b246 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -40,20 +40,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
 import android.os.BatteryStatsManager;
 import android.os.BatteryUsageStats;
 import android.os.Build;
-import android.os.Process;
 import android.os.SystemClock;
 
 import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
-import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
 import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowThreadUtils;
@@ -70,9 +66,6 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @RunWith(RobolectricTestRunner.class)
 public class BatteryUtilsTest {
 
@@ -122,7 +115,6 @@
     @Mock private ApplicationInfo mLowApplicationInfo;
     @Mock private PowerAllowlistBackend mPowerAllowlistBackend;
     @Mock private BatteryDatabaseManager mBatteryDatabaseManager;
-    private AnomalyInfo mAnomalyInfo;
     private BatteryUtils mBatteryUtils;
     private FakeFeatureFactory mFeatureFactory;
     private PowerUsageFeatureProvider mProvider;
@@ -169,7 +161,6 @@
         doReturn(0L)
                 .when(mBatteryUtils)
                 .getForegroundServiceTotalTimeUs(any(BatteryStats.Uid.class), anyLong());
-        mAnomalyInfo = new AnomalyInfo(INFO_WAKELOCK);
 
         BatteryDatabaseManager.setUpForTest(mBatteryDatabaseManager);
         ShadowThreadUtils.setIsMainThread(true);
@@ -391,79 +382,6 @@
     }
 
     @Test
-    public void testShouldHideAnomaly_systemAppWithLauncher_returnTrue() {
-        final List<ResolveInfo> resolveInfos = new ArrayList<>();
-        final ResolveInfo resolveInfo = new ResolveInfo();
-        resolveInfo.activityInfo = new ActivityInfo();
-        resolveInfo.activityInfo.packageName = HIGH_SDK_PACKAGE;
-
-        doReturn(resolveInfos).when(mPackageManager).queryIntentActivities(any(), anyInt());
-        doReturn(new String[] {HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
-        mHighApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
-
-        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerAllowlistBackend, UID, mAnomalyInfo))
-                .isTrue();
-    }
-
-    @Test
-    public void testShouldHideAnomaly_systemAppWithoutLauncher_returnTrue() {
-        doReturn(new ArrayList<>()).when(mPackageManager).queryIntentActivities(any(), anyInt());
-        doReturn(new String[] {HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
-        mHighApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
-
-        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerAllowlistBackend, UID, mAnomalyInfo))
-                .isTrue();
-    }
-
-    @Test
-    public void testShouldHideAnomaly_systemUid_returnTrue() {
-        final int systemUid = Process.ROOT_UID;
-        doReturn(new String[] {HIGH_SDK_PACKAGE})
-                .when(mPackageManager)
-                .getPackagesForUid(systemUid);
-
-        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerAllowlistBackend, systemUid, mAnomalyInfo))
-                .isTrue();
-    }
-
-    @Test
-    public void testShouldHideAnomaly_AppInDozeList_returnTrue() {
-        doReturn(new String[] {HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
-        doReturn(true)
-                .when(mPowerAllowlistBackend)
-                .isAllowlisted(new String[] {HIGH_SDK_PACKAGE}, UID);
-
-        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerAllowlistBackend, UID, mAnomalyInfo))
-                .isTrue();
-    }
-
-    @Test
-    public void testShouldHideAnomaly_normalApp_returnFalse() {
-        doReturn(new String[] {HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
-
-        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerAllowlistBackend, UID, mAnomalyInfo))
-                .isFalse();
-    }
-
-    @Test
-    public void testShouldHideAnomaly_excessivePriorOApp_returnFalse() {
-        doReturn(new String[] {LOW_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
-        mAnomalyInfo = new AnomalyInfo(INFO_EXCESSIVE);
-
-        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerAllowlistBackend, UID, mAnomalyInfo))
-                .isFalse();
-    }
-
-    @Test
-    public void testShouldHideAnomaly_excessiveOApp_returnTrue() {
-        doReturn(new String[] {HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
-        mAnomalyInfo = new AnomalyInfo(INFO_EXCESSIVE);
-
-        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerAllowlistBackend, UID, mAnomalyInfo))
-                .isTrue();
-    }
-
-    @Test
     public void clearForceAppStandby_appRestricted_clearAndReturnTrue() {
         when(mBatteryUtils.getPackageUid(HIGH_SDK_PACKAGE)).thenReturn(UID);
         when(mAppOpsManager.checkOpNoThrow(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
index 3435987..f7b5049 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
@@ -130,11 +130,14 @@
         BatteryTestUtils.setupIncompatibleEvent(mUsbPort, mUsbManager, mUsbPortStatus);
         mController.mPreference = new Preference(mContext);
         BatteryInfo info = new BatteryInfo();
+        info.batteryPercentString = "66%";
 
         assertThat(mController.getDashboardLabel(mContext, info, true))
                 .isEqualTo(
                         mContext.getString(
-                                com.android.settingslib.R.string.battery_info_status_not_charging));
+                                com.android.settingslib.R.string
+                                        .power_incompatible_charging_settings_home_page,
+                                info.batteryPercentString));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobServiceTest.java
deleted file mode 100644
index 22c59b0..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobServiceTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doNothing;
-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.app.JobSchedulerImpl;
-import android.app.job.IJobScheduler;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.content.Context;
-import android.os.Binder;
-
-import com.android.settings.R;
-import com.android.settings.testutils.DatabaseTestUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(RobolectricTestRunner.class)
-public class AnomalyCleanupJobServiceTest {
-    private static final int UID = 1234;
-    private static final String PACKAGE_NAME = "com.android.package";
-    private static final String PACKAGE_NAME_OLD = "com.android.package.old";
-    private static final int ANOMALY_TYPE = 1;
-    private static final long TIMESTAMP_NOW = System.currentTimeMillis();
-    private static final long TIMESTAMP_31_DAYS_BEFORE = TIMESTAMP_NOW - TimeUnit.DAYS.toMillis(31);
-
-    private Context mContext;
-    private JobScheduler mJobScheduler;
-    @Mock private JobParameters mParams;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = spy(RuntimeEnvironment.application);
-        mJobScheduler =
-                spy(new JobSchedulerImpl(mContext, IJobScheduler.Stub.asInterface(new Binder())));
-        when(mContext.getSystemService(JobScheduler.class)).thenReturn(mJobScheduler);
-    }
-
-    @After
-    public void cleanUp() {
-        DatabaseTestUtils.clearDb(mContext);
-    }
-
-    @Test
-    public void scheduleCleanUp() {
-        AnomalyCleanupJobService.scheduleCleanUp(mContext);
-
-        JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class);
-        List<JobInfo> pendingJobs = jobScheduler.getAllPendingJobs();
-        assertEquals(1, pendingJobs.size());
-        JobInfo pendingJob = pendingJobs.get(0);
-        assertThat(pendingJob.getId()).isEqualTo(R.integer.job_anomaly_clean_up);
-        assertThat(pendingJob.getIntervalMillis()).isEqualTo(TimeUnit.DAYS.toMillis(1));
-        assertThat(pendingJob.isRequireDeviceIdle()).isTrue();
-        assertThat(pendingJob.isRequireCharging()).isTrue();
-        assertThat(pendingJob.isPersisted()).isTrue();
-    }
-
-    @Test
-    public void scheduleCleanUp_invokeTwice_onlyScheduleOnce() {
-        AnomalyCleanupJobService.scheduleCleanUp(mContext);
-        AnomalyCleanupJobService.scheduleCleanUp(mContext);
-
-        verify(mJobScheduler, times(1)).schedule(any());
-    }
-
-    @Test
-    @Ignore
-    public void onStartJob_cleanUpDataBefore30days() {
-        final BatteryDatabaseManager databaseManager = BatteryDatabaseManager.getInstance(mContext);
-        final AnomalyCleanupJobService service =
-                spy(Robolectric.setupService(AnomalyCleanupJobService.class));
-        doNothing().when(service).jobFinished(any(), anyBoolean());
-
-        // Insert two records, one is current and the other one is 31 days before
-        databaseManager.insertAnomaly(
-                UID, PACKAGE_NAME, ANOMALY_TYPE, AnomalyDatabaseHelper.State.NEW, TIMESTAMP_NOW);
-        databaseManager.insertAnomaly(
-                UID,
-                PACKAGE_NAME_OLD,
-                ANOMALY_TYPE,
-                AnomalyDatabaseHelper.State.NEW,
-                TIMESTAMP_31_DAYS_BEFORE);
-
-        service.onStartJob(mParams);
-
-        // In database, it only contains the current record
-        final List<AppInfo> appInfos =
-                databaseManager.queryAllAnomalies(0, AnomalyDatabaseHelper.State.NEW);
-        assertThat(appInfos)
-                .containsExactly(
-                        new AppInfo.Builder()
-                                .setUid(UID)
-                                .setPackageName(PACKAGE_NAME)
-                                .addAnomalyType(ANOMALY_TYPE)
-                                .build());
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobServiceTest.java
deleted file mode 100644
index 345b8a1..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobServiceTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-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 static org.robolectric.RuntimeEnvironment.application;
-
-import android.app.JobSchedulerImpl;
-import android.app.StatsManager;
-import android.app.job.IJobScheduler;
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.Context;
-import android.os.Binder;
-import android.provider.Settings;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(RobolectricTestRunner.class)
-public class AnomalyConfigJobServiceTest {
-
-    private static final int ANOMALY_CONFIG_VERSION = 1;
-    private static final String ANOMALY_CONFIG = "X64s";
-    @Mock private StatsManager mStatsManager;
-
-    private Context mContext;
-    private JobScheduler mJobScheduler;
-    private AnomalyConfigJobService mJobService;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = spy(RuntimeEnvironment.application);
-        mJobScheduler =
-                spy(new JobSchedulerImpl(mContext, IJobScheduler.Stub.asInterface(new Binder())));
-        when(mContext.getSystemService(JobScheduler.class)).thenReturn(mJobScheduler);
-
-        mJobService = spy(new AnomalyConfigJobService());
-        doReturn(
-                        application.getSharedPreferences(
-                                AnomalyConfigJobService.PREF_DB, Context.MODE_PRIVATE))
-                .when(mJobService)
-                .getSharedPreferences(anyString(), anyInt());
-        doReturn(application.getContentResolver()).when(mJobService).getContentResolver();
-    }
-
-    @Test
-    public void testScheduleConfigUpdate() {
-        AnomalyConfigJobService.scheduleConfigUpdate(mContext);
-
-        JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class);
-        List<JobInfo> pendingJobs = jobScheduler.getAllPendingJobs();
-        assertEquals(1, pendingJobs.size());
-        JobInfo pendingJob = pendingJobs.get(0);
-        assertThat(pendingJob.getId()).isEqualTo(R.integer.job_anomaly_config_update);
-        assertThat(pendingJob.getIntervalMillis()).isEqualTo(TimeUnit.DAYS.toMillis(1));
-        assertThat(pendingJob.isRequireDeviceIdle()).isTrue();
-        assertThat(pendingJob.isRequireCharging()).isTrue();
-        assertThat(pendingJob.isPersisted()).isTrue();
-    }
-
-    @Test
-    public void testScheduleConfigUpdate_invokeTwice_onlyScheduleOnce() {
-        AnomalyConfigJobService.scheduleConfigUpdate(mContext);
-        AnomalyConfigJobService.scheduleConfigUpdate(mContext);
-
-        verify(mJobScheduler, times(1)).schedule(any());
-    }
-
-    @Test
-    public void checkAnomalyConfig_newConfigExist_removeOldConfig()
-            throws StatsManager.StatsUnavailableException {
-        Settings.Global.putInt(
-                application.getContentResolver(),
-                Settings.Global.ANOMALY_CONFIG_VERSION,
-                ANOMALY_CONFIG_VERSION);
-        Settings.Global.putString(
-                application.getContentResolver(), Settings.Global.ANOMALY_CONFIG, ANOMALY_CONFIG);
-
-        mJobService.checkAnomalyConfig(mStatsManager);
-
-        verify(mStatsManager).removeConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY);
-    }
-
-    @Test
-    public void checkAnomalyConfig_newConfigExist_uploadNewConfig()
-            throws StatsManager.StatsUnavailableException {
-        Settings.Global.putInt(
-                application.getContentResolver(),
-                Settings.Global.ANOMALY_CONFIG_VERSION,
-                ANOMALY_CONFIG_VERSION);
-        Settings.Global.putString(
-                application.getContentResolver(), Settings.Global.ANOMALY_CONFIG, ANOMALY_CONFIG);
-
-        mJobService.checkAnomalyConfig(mStatsManager);
-
-        verify(mStatsManager).addConfig(eq(StatsManagerConfig.ANOMALY_CONFIG_KEY), any());
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
deleted file mode 100644
index 482f0d0..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterytip;
-
-import static android.os.StatsDimensionsValue.FLOAT_VALUE_TYPE;
-import static android.os.StatsDimensionsValue.INT_VALUE_TYPE;
-import static android.os.StatsDimensionsValue.TUPLE_VALUE_TYPE;
-
-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.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.JobSchedulerImpl;
-import android.app.StatsManager;
-import android.app.job.IJobScheduler;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobWorkItem;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.StatsDimensionsValue;
-import android.os.UserManager;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.R;
-import com.android.settings.fuelgauge.BatteryUtils;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.testutils.shadow.ShadowConnectivityManager;
-import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ServiceController;
-import org.robolectric.annotation.Config;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowConnectivityManager.class})
-public class AnomalyDetectionJobServiceTest {
-    private static final int UID = 12345;
-    private static final String SYSTEM_PACKAGE = "com.android.system";
-    private static final String SUBSCRIBER_COOKIES_AUTO_RESTRICTION =
-            "anomaly_type=6,auto_restriction=true";
-    private static final String SUBSCRIBER_COOKIES_NOT_AUTO_RESTRICTION =
-            "anomaly_type=6,auto_restriction=false";
-    private static final int ANOMALY_TYPE = 6;
-    private static final long VERSION_CODE = 15;
-    @Mock private UserManager mUserManager;
-    @Mock private BatteryDatabaseManager mBatteryDatabaseManager;
-    @Mock private BatteryUtils mBatteryUtils;
-    @Mock private PowerAllowlistBackend mPowerAllowlistBackend;
-    @Mock private StatsDimensionsValue mStatsDimensionsValue;
-    @Mock private JobParameters mJobParameters;
-    @Mock private JobWorkItem mJobWorkItem;
-
-    private BatteryTipPolicy mPolicy;
-    private Bundle mBundle;
-    private AnomalyDetectionJobService mAnomalyDetectionJobService;
-    private FakeFeatureFactory mFeatureFactory;
-    private Context mContext;
-    private JobScheduler mJobScheduler;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = spy(RuntimeEnvironment.application);
-        mJobScheduler =
-                spy(new JobSchedulerImpl(mContext, IJobScheduler.Stub.asInterface(new Binder())));
-        when(mContext.getSystemService(JobScheduler.class)).thenReturn(mJobScheduler);
-
-        mPolicy = new BatteryTipPolicy(mContext);
-        mBundle = new Bundle();
-        mBundle.putParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, mStatsDimensionsValue);
-        mFeatureFactory = FakeFeatureFactory.setupForTest();
-        when(mBatteryUtils.getAppLongVersionCode(any())).thenReturn(VERSION_CODE);
-
-        final ServiceController<AnomalyDetectionJobService> controller =
-                Robolectric.buildService(AnomalyDetectionJobService.class);
-        mAnomalyDetectionJobService = spy(controller.get());
-        doNothing().when(mAnomalyDetectionJobService).jobFinished(any(), anyBoolean());
-    }
-
-    @Test
-    public void scheduleCleanUp() {
-        AnomalyDetectionJobService.scheduleAnomalyDetection(mContext, new Intent());
-
-        JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class);
-        List<JobInfo> pendingJobs = jobScheduler.getAllPendingJobs();
-        assertThat(pendingJobs).hasSize(1);
-
-        JobInfo pendingJob = pendingJobs.get(0);
-        assertThat(pendingJob.getId()).isEqualTo(R.integer.job_anomaly_detection);
-        assertThat(pendingJob.getMaxExecutionDelayMillis())
-                .isEqualTo(Duration.ofDays(1).toMillis());
-    }
-
-    @Test
-    public void saveAnomalyToDatabase_systemAllowlisted_doNotSave() {
-        doReturn(UID).when(mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
-        doReturn(true)
-                .when(mPowerAllowlistBackend)
-                .isAllowlisted(any(String[].class), any(Integer.class));
-
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(
-                mContext,
-                mUserManager,
-                mBatteryDatabaseManager,
-                mBatteryUtils,
-                mPolicy,
-                mPowerAllowlistBackend,
-                mContext.getContentResolver(),
-                mFeatureFactory.powerUsageFeatureProvider,
-                mFeatureFactory.metricsFeatureProvider,
-                mBundle);
-
-        verify(mBatteryDatabaseManager, never())
-                .insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(), anyLong());
-    }
-
-    @Test
-    public void saveAnomalyToDatabase_systemApp_doNotSaveButLog() {
-        final ArrayList<String> cookies = new ArrayList<>();
-        cookies.add(SUBSCRIBER_COOKIES_AUTO_RESTRICTION);
-        mBundle.putStringArrayList(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookies);
-        doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt());
-        doReturn(false).when(mPowerAllowlistBackend).isSysAllowlisted(SYSTEM_PACKAGE);
-        doReturn(Process.FIRST_APPLICATION_UID)
-                .when(mAnomalyDetectionJobService)
-                .extractUidFromStatsDimensionsValue(any());
-        doReturn(true).when(mBatteryUtils).shouldHideAnomaly(any(), anyInt(), any());
-
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(
-                mContext,
-                mUserManager,
-                mBatteryDatabaseManager,
-                mBatteryUtils,
-                mPolicy,
-                mPowerAllowlistBackend,
-                mContext.getContentResolver(),
-                mFeatureFactory.powerUsageFeatureProvider,
-                mFeatureFactory.metricsFeatureProvider,
-                mBundle);
-
-        verify(mBatteryDatabaseManager, never())
-                .insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(), anyLong());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.PAGE_UNKNOWN,
-                        MetricsProto.MetricsEvent.ACTION_ANOMALY_IGNORED,
-                        SettingsEnums.PAGE_UNKNOWN,
-                        SYSTEM_PACKAGE + "/" + VERSION_CODE,
-                        ANOMALY_TYPE);
-    }
-
-    @Test
-    public void saveAnomalyToDatabase_systemUid_doNotSave() {
-        doReturn(Process.SYSTEM_UID)
-                .when(mAnomalyDetectionJobService)
-                .extractUidFromStatsDimensionsValue(any());
-
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(
-                mContext,
-                mUserManager,
-                mBatteryDatabaseManager,
-                mBatteryUtils,
-                mPolicy,
-                mPowerAllowlistBackend,
-                mContext.getContentResolver(),
-                mFeatureFactory.powerUsageFeatureProvider,
-                mFeatureFactory.metricsFeatureProvider,
-                mBundle);
-
-        verify(mBatteryDatabaseManager, never())
-                .insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(), anyLong());
-    }
-
-    @Test
-    public void saveAnomalyToDatabase_uidNull_doNotSave() {
-        doReturn(AnomalyDetectionJobService.UID_NULL)
-                .when(mAnomalyDetectionJobService)
-                .extractUidFromStatsDimensionsValue(any());
-
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(
-                mContext,
-                mUserManager,
-                mBatteryDatabaseManager,
-                mBatteryUtils,
-                mPolicy,
-                mPowerAllowlistBackend,
-                mContext.getContentResolver(),
-                mFeatureFactory.powerUsageFeatureProvider,
-                mFeatureFactory.metricsFeatureProvider,
-                mBundle);
-
-        verify(mBatteryDatabaseManager, never())
-                .insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(), anyLong());
-    }
-
-    @Test
-    public void saveAnomalyToDatabase_normalAppWithAutoRestriction_save() {
-        final ArrayList<String> cookies = new ArrayList<>();
-        cookies.add(SUBSCRIBER_COOKIES_AUTO_RESTRICTION);
-        mBundle.putStringArrayList(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookies);
-        doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt());
-        doReturn(false).when(mPowerAllowlistBackend).isSysAllowlisted(SYSTEM_PACKAGE);
-        doReturn(Process.FIRST_APPLICATION_UID)
-                .when(mAnomalyDetectionJobService)
-                .extractUidFromStatsDimensionsValue(any());
-
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(
-                mContext,
-                mUserManager,
-                mBatteryDatabaseManager,
-                mBatteryUtils,
-                mPolicy,
-                mPowerAllowlistBackend,
-                mContext.getContentResolver(),
-                mFeatureFactory.powerUsageFeatureProvider,
-                mFeatureFactory.metricsFeatureProvider,
-                mBundle);
-
-        verify(mBatteryDatabaseManager)
-                .insertAnomaly(
-                        anyInt(),
-                        anyString(),
-                        eq(6),
-                        eq(AnomalyDatabaseHelper.State.AUTO_HANDLED),
-                        anyLong());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.PAGE_UNKNOWN,
-                        MetricsProto.MetricsEvent.ACTION_ANOMALY_TRIGGERED,
-                        SettingsEnums.PAGE_UNKNOWN,
-                        SYSTEM_PACKAGE + "/" + VERSION_CODE,
-                        ANOMALY_TYPE);
-    }
-
-    @Test
-    public void saveAnomalyToDatabase_normalAppWithoutAutoRestriction_save() {
-        final ArrayList<String> cookies = new ArrayList<>();
-        cookies.add(SUBSCRIBER_COOKIES_NOT_AUTO_RESTRICTION);
-        mBundle.putStringArrayList(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookies);
-        doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt());
-        doReturn(false).when(mPowerAllowlistBackend).isSysAllowlisted(SYSTEM_PACKAGE);
-        doReturn(Process.FIRST_APPLICATION_UID)
-                .when(mAnomalyDetectionJobService)
-                .extractUidFromStatsDimensionsValue(any());
-
-        mAnomalyDetectionJobService.saveAnomalyToDatabase(
-                mContext,
-                mUserManager,
-                mBatteryDatabaseManager,
-                mBatteryUtils,
-                mPolicy,
-                mPowerAllowlistBackend,
-                mContext.getContentResolver(),
-                mFeatureFactory.powerUsageFeatureProvider,
-                mFeatureFactory.metricsFeatureProvider,
-                mBundle);
-
-        verify(mBatteryDatabaseManager)
-                .insertAnomaly(
-                        anyInt(),
-                        anyString(),
-                        eq(6),
-                        eq(AnomalyDatabaseHelper.State.NEW),
-                        anyLong());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.PAGE_UNKNOWN,
-                        MetricsProto.MetricsEvent.ACTION_ANOMALY_TRIGGERED,
-                        SettingsEnums.PAGE_UNKNOWN,
-                        SYSTEM_PACKAGE + "/" + VERSION_CODE,
-                        ANOMALY_TYPE);
-    }
-
-    @Test
-    public void extractUidFromStatsDimensionsValue_extractCorrectUid() {
-        // Build an integer dimensions value.
-        final StatsDimensionsValue intValue = mock(StatsDimensionsValue.class);
-        when(intValue.isValueType(INT_VALUE_TYPE)).thenReturn(true);
-        when(intValue.getField()).thenReturn(AnomalyDetectionJobService.STATSD_UID_FILED);
-        when(intValue.getIntValue()).thenReturn(UID);
-
-        // Build a tuple dimensions value and put the previous integer dimensions value inside.
-        final StatsDimensionsValue tupleValue = mock(StatsDimensionsValue.class);
-        when(tupleValue.isValueType(TUPLE_VALUE_TYPE)).thenReturn(true);
-        final List<StatsDimensionsValue> statsDimensionsValues = new ArrayList<>();
-        statsDimensionsValues.add(intValue);
-        when(tupleValue.getTupleValueList()).thenReturn(statsDimensionsValues);
-
-        assertThat(mAnomalyDetectionJobService.extractUidFromStatsDimensionsValue(tupleValue))
-                .isEqualTo(UID);
-    }
-
-    @Test
-    public void extractUidFromStatsDimensionsValue_wrongFormat_returnNull() {
-        // Build a float dimensions value
-        final StatsDimensionsValue floatValue = mock(StatsDimensionsValue.class);
-        when(floatValue.isValueType(FLOAT_VALUE_TYPE)).thenReturn(true);
-        when(floatValue.getField()).thenReturn(AnomalyDetectionJobService.STATSD_UID_FILED);
-        when(floatValue.getFloatValue()).thenReturn(0f);
-
-        assertThat(mAnomalyDetectionJobService.extractUidFromStatsDimensionsValue(floatValue))
-                .isEqualTo(AnomalyDetectionJobService.UID_NULL);
-    }
-
-    @Test
-    public void stopJobWhileDequeuingWork_shouldNotCrash() {
-        when(mJobParameters.dequeueWork()).thenThrow(new SecurityException());
-
-        mAnomalyDetectionJobService.onStopJob(mJobParameters);
-
-        // Should not crash even job is stopped
-        mAnomalyDetectionJobService.dequeueWork(mJobParameters);
-    }
-
-    @Test
-    public void stopJobWhileCompletingWork_shouldNotCrash() {
-        doThrow(new SecurityException()).when(mJobParameters).completeWork(any());
-
-        mAnomalyDetectionJobService.onStopJob(mJobParameters);
-
-        // Should not crash even job is stopped
-        mAnomalyDetectionJobService.completeWork(mJobParameters, mJobWorkItem);
-    }
-
-    @Test
-    public void restartWorkAfterBeenStopped_jobStarted() {
-        mAnomalyDetectionJobService.onStopJob(mJobParameters);
-        mAnomalyDetectionJobService.onStartJob(mJobParameters);
-
-        assertThat(mAnomalyDetectionJobService.mIsJobCanceled).isFalse();
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index e68b892..5ce449b 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -361,6 +361,52 @@
     }
 
     @Test
+    public void convertToBatteryUsageDiff_returnsExpectedResult() {
+        final BatteryDiffEntry batteryDiffEntry =
+                new BatteryDiffEntry(
+                        mContext,
+                        /* uid= */ 101L,
+                        /* userId= */ 1001L,
+                        /* key= */ "key",
+                        /* isHidden= */ false,
+                        /* componentId= */ -1,
+                        /* legacyPackageName= */ null,
+                        /* legacyLabel= */ null,
+                        /* consumerType= */ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
+                        /* foregroundUsageTimeInMs= */ 1234L,
+                        /* foregroundServiceUsageTimeInMs= */ 3456L,
+                        /* backgroundUsageTimeInMs= */ 5678L,
+                        /* screenOnTimeInMs= */ 123L,
+                        /* consumePower= */ 1.1,
+                        /* foregroundUsageConsumePower= */ 1.2,
+                        /* foregroundServiceUsageConsumePower= */ 1.3,
+                        /* backgroundUsageConsumePower= */ 1.4,
+                        /* cachedUsageConsumePower= */ 1.5);
+
+        final BatteryUsageDiff batteryUsageDiff =
+                ConvertUtils.convertToBatteryUsageDiff(batteryDiffEntry);
+
+        assertThat(batteryUsageDiff.getUid()).isEqualTo(101L);
+        assertThat(batteryUsageDiff.getUserId()).isEqualTo(1001L);
+        assertThat(batteryUsageDiff.getIsHidden()).isFalse();
+        assertThat(batteryUsageDiff.getComponentId()).isEqualTo(-1);
+        assertThat(batteryUsageDiff.getConsumerType())
+                .isEqualTo(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        assertThat(batteryUsageDiff.getConsumePower()).isEqualTo(1.1);
+        assertThat(batteryUsageDiff.getForegroundUsageConsumePower()).isEqualTo(1.2);
+        assertThat(batteryUsageDiff.getForegroundServiceUsageConsumePower()).isEqualTo(1.3);
+        assertThat(batteryUsageDiff.getBackgroundUsageConsumePower()).isEqualTo(1.4);
+        assertThat(batteryUsageDiff.getCachedUsageConsumePower()).isEqualTo(1.5);
+        assertThat(batteryUsageDiff.getForegroundUsageTime()).isEqualTo(1234L);
+        assertThat(batteryUsageDiff.getForegroundServiceUsageTime()).isEqualTo(3456L);
+        assertThat(batteryUsageDiff.getBackgroundUsageTime()).isEqualTo(5678L);
+        assertThat(batteryUsageDiff.getScreenOnTime()).isEqualTo(123L);
+        assertThat(batteryUsageDiff.getKey()).isEqualTo("key");
+        assertThat(batteryUsageDiff.hasPackageName()).isFalse();
+        assertThat(batteryUsageDiff.hasLabel()).isFalse();
+    }
+
+    @Test
     public void convertToAppUsageEvent_returnsExpectedResult()
             throws PackageManager.NameNotFoundException {
         final Event event = new Event();
diff --git a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
index 0f647aa..a399e17 100644
--- a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
@@ -74,6 +74,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.AdditionalMatchers;
@@ -709,6 +710,7 @@
         verify(mUserManager).getAliveUsers();
     }
 
+    @Ignore
     @Test
     public void updateUserList_userIconMissing_shouldLoadIcon() {
         UserInfo currentUser = getAdminUser(true);
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigManagerExtTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigManagerExtTest.kt
new file mode 100644
index 0000000..5a82f99
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigManagerExtTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 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.network.telephony
+
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import androidx.core.os.persistableBundleOf
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class CarrierConfigManagerExtTest {
+
+    private val mockCarrierConfigManager = mock<CarrierConfigManager>()
+
+    private val context = mock<Context> {
+        on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
+    }
+
+    @Test
+    fun safeGetConfig_managerReturnKeyValue_returnNonEmptyBundle() {
+        mockCarrierConfigManager.stub {
+            on { getConfigForSubId(any(), eq(KEY)) } doReturn persistableBundleOf(KEY to VALUE)
+        }
+        val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
+
+        val bundle = carrierConfigManager.safeGetConfig(listOf(KEY))
+
+        assertThat(bundle.getString(KEY)).isEqualTo(VALUE)
+    }
+
+    @Test
+    fun safeGetConfig_managerThrowIllegalStateException_returnEmptyBundle() {
+        mockCarrierConfigManager.stub {
+            on { getConfigForSubId(any(), eq(KEY)) } doThrow IllegalStateException()
+        }
+        val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
+
+        val bundle = carrierConfigManager.safeGetConfig(listOf(KEY))
+
+        assertThat(bundle.containsKey(KEY)).isFalse()
+    }
+
+    private companion object {
+        const val KEY = "key"
+        const val VALUE = "value"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
index f202668..2f52031 100644
--- a/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
@@ -25,7 +25,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
-import org.mockito.kotlin.anyVararg
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
@@ -47,7 +46,7 @@
     @Test
     fun onSystemUpdate_notEnabled() {
         mockCarrierConfigManager.stub {
-            on { getConfig(anyVararg()) } doReturn persistableBundleOf()
+            on { getConfigForSubId(any(), any()) } doReturn persistableBundleOf()
         }
 
         repository.onSystemUpdate()
@@ -58,7 +57,7 @@
     @Test
     fun onSystemUpdate_enabled() {
         mockCarrierConfigManager.stub {
-            on { getConfig(anyVararg()) } doReturn persistableBundleOf(
+            on { getConfigForSubId(any(), any()) } doReturn persistableBundleOf(
                 CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL to true,
                 CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING to ACTION,
             )