Merge "Add ingress rate limit to developer settings" into tm-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 26f0c4f..214d32a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -9,7 +9,7 @@
     <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_MEDIA_IMAGE" />
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
     <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
     <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
diff --git a/res/layout/support_details_dialog.xml b/res/layout/support_details_dialog.xml
index 0233a1b..2390f90 100644
--- a/res/layout/support_details_dialog.xml
+++ b/res/layout/support_details_dialog.xml
@@ -33,6 +33,8 @@
             android:layout_width="@dimen/admin_details_dialog_icon_size"
             android:layout_height="@dimen/admin_details_dialog_icon_size"
             android:scaleType="fitCenter"
+            android:src="@drawable/ic_lock_closed"
+            android:tint="?android:attr/colorAccent"
             android:contentDescription="@null"/>
         <TextView
             android:id="@+id/admin_support_dialog_title"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d83640b..2c0780f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -329,14 +329,16 @@
 
     <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
     <string name="bluetooth_disable_a2dp_hw_offload">Disable Bluetooth A2DP hardware offload</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_title">Restart Device?</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_message">You need to restart your device to change this setting.</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_confirm">Restart</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_cancel">Cancel</string>
+    <!-- UI debug setting: Disable Bluetooth LE AUDIO hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_le_audio_hw_offload">Disable Bluetooth LE AUDIO hardware offload</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_title">Restart Device?</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_message">You need to restart your device to change this setting.</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_confirm">Restart</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_cancel">Cancel</string>
 
     <!-- Title for Bluetooth device group with media capability group [CHAR LIMIT=none]-->
     <string name="connected_device_media_device_title">Media devices</string>
@@ -5401,9 +5403,9 @@
     <!-- Summary text for the accessibility button preference. [CHAR LIMIT=50] -->
     <string name="accessibility_button_summary">Quickly access accessibility features</string>
     <!-- Description for the accessibility button in gesture navigation. Explain how this page works. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_button_gesture_description"><b>How to use the button or gesture</b>\n\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. To use the feature, tap the accessibility button or gesture</string>
+    <string name="accessibility_button_gesture_description"><b>To get started</b>\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose whether you want to use a button or gesture to access the feature</string>
     <!-- Description for the accessibility button page. Explain how this page works. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_button_description"><b>How to use the button</b>\n\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. To use the feature, tap the accessibility button</string>
+    <string name="accessibility_button_description"><b>To get started</b>\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose the button to use to access the feature</string>
     <!-- Title for the button or gesture of the accessibility button. [CHAR LIMIT=35] -->
     <string name="accessibility_button_or_gesture_title">Use button or gesture</string>
     <!-- Title for the location of the accessibility button. [CHAR LIMIT=35] -->
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index b21d5c9..ef5a990 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -38,11 +38,6 @@
         android:key="action_buttons"
         settings:allowDividerBelow="true"/>
 
-    <!-- Add SpacePreference to draw divider -->
-    <com.android.settings.applications.SpacePreference
-        android:layout_height="0dp"
-        settings:allowDividerAbove="true" />
-
     <com.android.settings.slices.SlicePreference
         android:key="bt_device_slice"
         settings:controller="com.android.settings.slices.BlockingSlicePrefController"
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 4e4d393..75395c0 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -321,14 +321,13 @@
             android:summary="@string/bluetooth_disable_absolute_volume_summary" />
 
         <SwitchPreference
-            android:key="bluetooth_gabeldorsche_enable"
-            android:title="@string/bluetooth_enable_gabeldorsche"
-            android:summary="@string/bluetooth_enable_gabeldorsche_summary" />
-
-        <SwitchPreference
             android:key="bluetooth_disable_a2dp_hw_offload"
             android:title="@string/bluetooth_disable_a2dp_hw_offload" />
 
+        <SwitchPreference
+            android:key="bluetooth_disable_le_audio_hw_offload"
+            android:title="@string/bluetooth_disable_le_audio_hw_offload" />
+
         <ListPreference
             android:key="bluetooth_select_avrcp_version"
             android:title="@string/bluetooth_select_avrcp_version_string"
diff --git a/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
index 0ff960f..930fbe4 100644
--- a/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.accessibility;
 
+import android.app.settings.SettingsEnums;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -57,7 +58,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return super.getMetricsCategory();
+        return SettingsEnums.SUW_ACCESSIBILITY_TEXT_READING_OPTIONS;
     }
 
     @Override
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
index f1e312a..f5ad76b 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
@@ -56,6 +56,13 @@
     }
 
     /**
+     * Returns whether at least one face template or fingerprint has been enrolled.
+     */
+    public boolean hasEnrolled() {
+        return hasEnrolledFingerprints() || hasEnrolledFace();
+    }
+
+    /**
      * Returns the {@link EnforcedAdmin} in case parental consent is required to change both
      * face and fingerprint settings.
      *
@@ -84,8 +91,7 @@
     public String getSummary() {
         final int numFingerprintsEnrolled = mFingerprintManager != null
                 ? mFingerprintManager.getEnrolledFingerprints(mUserId).size() : 0;
-        final boolean faceEnrolled = mFaceManager != null
-                && mFaceManager.hasEnrolledTemplates(mUserId);
+        final boolean faceEnrolled = hasEnrolledFace();
 
         if (faceEnrolled && numFingerprintsEnrolled > 1) {
             return mContext.getString(
@@ -105,6 +111,14 @@
         }
     }
 
+    private boolean hasEnrolledFingerprints() {
+        return mFingerprintManager != null && mFingerprintManager.hasEnrolledFingerprints(mUserId);
+    }
+
+    private boolean hasEnrolledFace() {
+        return mFaceManager != null && mFaceManager.hasEnrolledTemplates(mUserId);
+    }
+
     /**
      * Returns the class name of the Settings page corresponding to combined biometric settings.
      */
diff --git a/src/com/android/settings/biometrics/face/FaceStatusUtils.java b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
index 4e25543..1749aca 100644
--- a/src/com/android/settings/biometrics/face/FaceStatusUtils.java
+++ b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
@@ -75,7 +75,10 @@
                 : FaceEnrollIntroduction.class.getName();
     }
 
-    private boolean hasEnrolled() {
+    /**
+     * Returns whether at least one face template has been enrolled.
+     */
+    public boolean hasEnrolled() {
         return mFaceManager.hasEnrolledTemplates(mUserId);
     }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index 2ef5446..23595fb 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -35,6 +35,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricEnrollIntroduction;
@@ -55,6 +56,7 @@
 
     private static final String TAG = "FingerprintIntro";
 
+    @VisibleForTesting
     private FingerprintManager mFingerprintManager;
     @Nullable private FooterButton mPrimaryFooterButton;
     @Nullable private FooterButton mSecondaryFooterButton;
@@ -213,6 +215,8 @@
     @Override
     protected int checkMaxEnrolled() {
         final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
+        final boolean isDeferredSetupWizard =
+                WizardManagerHelper.isDeferredSetupWizard(getIntent());
         if (mFingerprintManager != null) {
             final List<FingerprintSensorPropertiesInternal> props =
                     mFingerprintManager.getSensorPropertiesInternal();
@@ -220,9 +224,11 @@
             final int max = props.get(0).maxEnrollmentsPerUser;
             final int numEnrolledFingerprints =
                     mFingerprintManager.getEnrolledFingerprints(mUserId).size();
-            final int maxFingerprintsEnrollableIfSUW = getApplicationContext().getResources()
-                    .getInteger(R.integer.suw_max_fingerprints_enrollable);
-            if (isSetupWizard) {
+            final int maxFingerprintsEnrollableIfSUW =
+                    getApplicationContext()
+                            .getResources()
+                            .getInteger(R.integer.suw_max_fingerprints_enrollable);
+            if (isSetupWizard && !isDeferredSetupWizard) {
                 if (numEnrolledFingerprints >= maxFingerprintsEnrollableIfSUW) {
                     return R.string.fingerprint_intro_error_max;
                 } else {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
index d23195d..5707f32 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
@@ -82,7 +82,10 @@
                 : FingerprintEnrollIntroduction.class.getName();
     }
 
-    private boolean hasEnrolled() {
+    /**
+     * Returns whether at least one fingerprint has been enrolled.
+     */
+    public boolean hasEnrolled() {
         return mFingerprintManager.hasEnrolledFingerprints(mUserId);
     }
 }
diff --git a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
index 95e663b..2ffa11b 100644
--- a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
@@ -16,9 +16,12 @@
 
 package com.android.settings.development;
 
+import static com.android.settings.development.BluetoothLeAudioHwOffloadPreferenceController.LE_AUDIO_OFFLOAD_DISABLED_PROPERTY;
+
 import android.content.Context;
 import android.os.SystemProperties;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.SwitchPreference;
 
@@ -34,6 +37,9 @@
     static final String A2DP_OFFLOAD_DISABLED_PROPERTY = "persist.bluetooth.a2dp_offload.disabled";
     static final String A2DP_OFFLOAD_SUPPORTED_PROPERTY = "ro.bluetooth.a2dp_offload.supported";
 
+    @VisibleForTesting
+    boolean mChanged = false;
+
     public BluetoothA2dpHwOffloadPreferenceController(Context context,
             DevelopmentSettingsDashboardFragment fragment) {
         super(context);
@@ -47,7 +53,8 @@
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
-        BluetoothA2dpHwOffloadRebootDialog.show(mFragment, this);
+        BluetoothHwOffloadRebootDialog.show(mFragment);
+        mChanged = true;
         return false;
     }
 
@@ -85,10 +92,26 @@
         return offloadSupported ? !offloadDisabled : true;
     }
 
-    public void onA2dpHwDialogConfirmed() {
+    /**
+     * Called when the HwOffloadDialog confirm is clicked.
+     */
+    public void onHwOffloadDialogConfirmed() {
+        if (!mChanged) {
+            return;
+        }
         final boolean offloadDisabled =
                 SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
         SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(!offloadDisabled));
+        if (offloadDisabled) {
+            SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY,
+                    Boolean.toString(!offloadDisabled));
+        }
     }
 
+    /**
+     * Called when the HwOffloadDialog cancel is clicked.
+     */
+    public void onHwOffloadDialogCanceled() {
+        mChanged = false;
+    }
 }
diff --git a/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java b/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java
deleted file mode 100644
index f5c30f5..0000000
--- a/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.SwitchPreference;
-
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-/**
- * Preference controller for Bluetooth Gabeldorche feature
- */
-public class BluetoothGabeldorschePreferenceController extends
-        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
-        PreferenceControllerMixin {
-
-    private static final String BLUETOOTH_GABELDORSCHE_KEY =
-            "bluetooth_gabeldorsche_enable";
-
-    @VisibleForTesting
-    static final String CURRENT_GD_FLAG = "INIT_gd_scanning";
-
-    public BluetoothGabeldorschePreferenceController(Context context) {
-        super(context);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return BLUETOOTH_GABELDORSCHE_KEY;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean isEnabled = (Boolean) newValue;
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, isEnabled ? "true" : "false", false /* makeDefault */);
-        return true;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        final boolean isEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_BLUETOOTH, CURRENT_GD_FLAG, false /* default */);
-        ((SwitchPreference) mPreference).setChecked(isEnabled);
-    }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        super.onDeveloperOptionsSwitchDisabled();
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, null, false /* makeDefault */);
-        ((SwitchPreference) mPreference).setChecked(false);
-    }
-}
diff --git a/src/com/android/settings/development/BluetoothA2dpHwOffloadRebootDialog.java b/src/com/android/settings/development/BluetoothHwOffloadRebootDialog.java
similarity index 60%
rename from src/com/android/settings/development/BluetoothA2dpHwOffloadRebootDialog.java
rename to src/com/android/settings/development/BluetoothHwOffloadRebootDialog.java
index 95ef019..389103e 100644
--- a/src/com/android/settings/development/BluetoothA2dpHwOffloadRebootDialog.java
+++ b/src/com/android/settings/development/BluetoothHwOffloadRebootDialog.java
@@ -28,17 +28,23 @@
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 
-public class BluetoothA2dpHwOffloadRebootDialog extends InstrumentedDialogFragment
+/**
+ * The a2dp and LE audio offload switch should reboot the device to take effect, the dialog is
+ * to ask the user to reboot the device after a2dp or LE audio offload user preference changed
+ */
+public class BluetoothHwOffloadRebootDialog extends InstrumentedDialogFragment
         implements DialogInterface.OnClickListener {
 
-    public static final String TAG = "BluetoothA2dpHwOffloadReboot";
+    public static final String TAG = "BluetoothHwOffloadReboot";
 
-    public static void show(DevelopmentSettingsDashboardFragment host,
-            BluetoothA2dpHwOffloadPreferenceController controller) {
+    /**
+     * The function to show the HwOffloadReboot Dialog.
+     */
+    public static void show(DevelopmentSettingsDashboardFragment host) {
         final FragmentManager manager = host.getActivity().getSupportFragmentManager();
         if (manager.findFragmentByTag(TAG) == null) {
-            final BluetoothA2dpHwOffloadRebootDialog dialog =
-                    new BluetoothA2dpHwOffloadRebootDialog();
+            final BluetoothHwOffloadRebootDialog dialog =
+                    new BluetoothHwOffloadRebootDialog();
             dialog.setTargetFragment(host, 0 /* requestCode */);
             dialog.show(manager, TAG);
         }
@@ -52,33 +58,44 @@
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         return new AlertDialog.Builder(getActivity())
-                .setMessage(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message)
-                .setTitle(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title)
+                .setMessage(R.string.bluetooth_disable_hw_offload_dialog_message)
+                .setTitle(R.string.bluetooth_disable_hw_offload_dialog_title)
                 .setPositiveButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_confirm, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_confirm, this)
                 .setNegativeButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_cancel, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_cancel, this)
                 .create();
     }
 
     @Override
     public void onClick(DialogInterface dialog, int which) {
-        final OnA2dpHwDialogConfirmedListener host =
-                (OnA2dpHwDialogConfirmedListener) getTargetFragment();
+        final OnHwOffloadDialogListener host =
+                (OnHwOffloadDialogListener) getTargetFragment();
         if (host == null) {
             return;
         }
         if (which == DialogInterface.BUTTON_POSITIVE) {
-            host.onA2dpHwDialogConfirmed();
+            host.onHwOffloadDialogConfirmed();
             PowerManager pm = getContext().getSystemService(PowerManager.class);
             pm.reboot(null);
+        } else {
+            host.onHwOffloadDialogCanceled();
         }
     }
 
-    public interface OnA2dpHwDialogConfirmedListener {
+    /**
+     * The interface for the HsOffloadDialogListener to provide the action as the
+     * confirmed or canceled clicked.
+     */
+    public interface OnHwOffloadDialogListener {
         /**
          * Called when the user presses reboot on the warning dialog.
          */
-        void onA2dpHwDialogConfirmed();
+        void onHwOffloadDialogConfirmed();
+
+        /**
+         * Called when the user presses cancel on the warning dialog.
+         */
+        void onHwOffloadDialogCanceled();
     }
 }
diff --git a/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceController.java
new file mode 100644
index 0000000..911b62d
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceController.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2022 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.development;
+
+import static com.android.settings.development.BluetoothA2dpHwOffloadPreferenceController.A2DP_OFFLOAD_SUPPORTED_PROPERTY;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Preference controller to control Bluetooth LE audio offload
+ */
+public class BluetoothLeAudioHwOffloadPreferenceController
+        extends DeveloperOptionsPreferenceController
+        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+    private static final String PREFERENCE_KEY = "bluetooth_disable_le_audio_hw_offload";
+    private final DevelopmentSettingsDashboardFragment mFragment;
+
+    static final String LE_AUDIO_OFFLOAD_DISABLED_PROPERTY =
+            "persist.bluetooth.leaudio_offload.disabled";
+    static final String LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY =
+            "ro.bluetooth.leaudio_offload.supported";
+
+    @VisibleForTesting
+    boolean mChanged = false;
+
+    public BluetoothLeAudioHwOffloadPreferenceController(Context context,
+            DevelopmentSettingsDashboardFragment fragment) {
+        super(context);
+        mFragment = fragment;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREFERENCE_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        BluetoothHwOffloadRebootDialog.show(mFragment);
+        mChanged = true;
+        return false;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final boolean offloadSupported =
+                SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
+                && SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY, false);
+        if (offloadSupported) {
+            final boolean offloadDisabled =
+                    SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, true);
+            ((SwitchPreference) mPreference).setChecked(offloadDisabled);
+        } else {
+            mPreference.setEnabled(false);
+            ((SwitchPreference) mPreference).setChecked(true);
+        }
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        final boolean offloadSupported =
+                SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
+                && SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY, false);
+        if (offloadSupported) {
+            ((SwitchPreference) mPreference).setChecked(true);
+            SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, "true");
+        }
+    }
+
+    /**
+     * Check if the le audio offload setting is default value.
+     */
+    public boolean isDefaultValue() {
+        final boolean offloadSupported =
+                SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
+                && SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY, false);
+        final boolean offloadDisabled =
+                    SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, false);
+        return offloadSupported ? offloadDisabled : true;
+    }
+
+    /**
+     * Called when the HwOffloadDialog confirm is clicked.
+     */
+    public void onHwOffloadDialogConfirmed() {
+        if (!mChanged) {
+            return;
+        }
+
+        final boolean offloadDisabled =
+                SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY,
+                false);
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY,
+                Boolean.toString(!offloadDisabled));
+    }
+
+    /**
+     * Called when the HwOffloadDialog cancel is clicked.
+     */
+    public void onHwOffloadDialogCanceled() {
+        mChanged = false;
+    }
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 64e7f3a..636404e 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -80,7 +80,7 @@
 public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFragment
         implements OnMainSwitchChangeListener, OemUnlockDialogHost, AdbDialogHost,
         AdbClearKeysDialogHost, LogPersistDialogHost,
-        BluetoothA2dpHwOffloadRebootDialog.OnA2dpHwDialogConfirmedListener,
+        BluetoothHwOffloadRebootDialog.OnHwOffloadDialogListener,
         AbstractBluetoothPreferenceController.Callback {
 
     private static final String TAG = "DevSettingsDashboard";
@@ -293,12 +293,16 @@
             if (isChecked) {
                 EnableDevelopmentSettingWarningDialog.show(this /* host */);
             } else {
-                final BluetoothA2dpHwOffloadPreferenceController controller =
+                final BluetoothA2dpHwOffloadPreferenceController a2dpController =
                         getDevelopmentOptionsController(
                                 BluetoothA2dpHwOffloadPreferenceController.class);
-                // If A2DP hardware offload isn't default value, we must reboot after disable
+                final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
+                        getDevelopmentOptionsController(
+                                BluetoothLeAudioHwOffloadPreferenceController.class);
+                // If hardware offload isn't default value, we must reboot after disable
                 // developer options. Show a dialog for the user to confirm.
-                if (controller == null || controller.isDefaultValue()) {
+                if ((a2dpController == null || a2dpController.isDefaultValue())
+                        && (leAudioController == null || leAudioController.isDefaultValue())) {
                     disableDeveloperOptions();
                 } else {
                     DisableDevSettingsDialogFragment.show(this /* host */);
@@ -358,10 +362,27 @@
     }
 
     @Override
-    public void onA2dpHwDialogConfirmed() {
-        final BluetoothA2dpHwOffloadPreferenceController controller =
+    public void onHwOffloadDialogConfirmed() {
+        final BluetoothA2dpHwOffloadPreferenceController a2dpController =
                 getDevelopmentOptionsController(BluetoothA2dpHwOffloadPreferenceController.class);
-        controller.onA2dpHwDialogConfirmed();
+        a2dpController.onHwOffloadDialogConfirmed();
+
+        final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
+                getDevelopmentOptionsController(
+                    BluetoothLeAudioHwOffloadPreferenceController.class);
+        leAudioController.onHwOffloadDialogConfirmed();
+    }
+
+    @Override
+    public void onHwOffloadDialogCanceled() {
+        final BluetoothA2dpHwOffloadPreferenceController a2dpController =
+                getDevelopmentOptionsController(BluetoothA2dpHwOffloadPreferenceController.class);
+        a2dpController.onHwOffloadDialogCanceled();
+
+        final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
+                getDevelopmentOptionsController(
+                    BluetoothLeAudioHwOffloadPreferenceController.class);
+        leAudioController.onHwOffloadDialogCanceled();
     }
 
     @Override
@@ -517,10 +538,10 @@
         controllers.add(new TetheringHardwareAccelPreferenceController(context));
         controllers.add(new BluetoothDeviceNoNamePreferenceController(context));
         controllers.add(new BluetoothAbsoluteVolumePreferenceController(context));
-        controllers.add(new BluetoothGabeldorschePreferenceController(context));
         controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
         controllers.add(new BluetoothMapVersionPreferenceController(context));
         controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
+        controllers.add(new BluetoothLeAudioHwOffloadPreferenceController(context, fragment));
         controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
         controllers.add(new NfcStackDebugLogPreferenceController(context));
         controllers.add(new ShowTapsPreferenceController(context));
diff --git a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
index 803030e..5db2ed4 100644
--- a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
+++ b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
@@ -56,15 +56,15 @@
 
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Reuse the same text of disable_a2dp_hw_offload_dialog.
+        // Reuse the same text of disable_hw_offload_dialog.
         // The text is generic enough to be used for turning off Dev options.
         return new AlertDialog.Builder(getActivity())
-                .setMessage(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message)
-                .setTitle(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title)
+                .setMessage(R.string.bluetooth_disable_hw_offload_dialog_message)
+                .setTitle(R.string.bluetooth_disable_hw_offload_dialog_title)
                 .setPositiveButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_confirm, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_confirm, this)
                 .setNegativeButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_cancel, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_cancel, this)
                 .create();
     }
 
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index 9a157d6..8fdc370 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -369,6 +369,7 @@
                     }
                     return true;
                 })
+                .sorted(Comparator.comparingInt(UiccSlotMapping::getLogicalSlotIndex))
                 .mapToInt(uiccSlotMapping -> uiccSlotMapping.getLogicalSlotIndex())
                 .findFirst()
                 .orElse(INVALID_LOGICAL_SLOT_ID);
diff --git a/src/com/android/settings/safetycenter/BiometricsSafetySource.java b/src/com/android/settings/safetycenter/BiometricsSafetySource.java
index c302915..74d8e01 100644
--- a/src/com/android/settings/safetycenter/BiometricsSafetySource.java
+++ b/src/com/android/settings/safetycenter/BiometricsSafetySource.java
@@ -65,6 +65,7 @@
                             combinedBiometricStatusUtils.getSettingsClassName(), disablingAdmin,
                             Bundle.EMPTY),
                     disablingAdmin == null /* enabled */,
+                    combinedBiometricStatusUtils.hasEnrolled(),
                     safetyEvent);
             return;
         }
@@ -82,6 +83,7 @@
                             faceStatusUtils.getSettingsClassName(), disablingAdmin,
                             Bundle.EMPTY),
                     disablingAdmin == null /* enabled */,
+                    faceStatusUtils.hasEnrolled(),
                     safetyEvent);
 
             return;
@@ -101,6 +103,7 @@
                             fingerprintStatusUtils.getSettingsClassName(), disablingAdmin,
                             Bundle.EMPTY),
                     disablingAdmin == null /* enabled */,
+                    fingerprintStatusUtils.hasEnrolled(),
                     safetyEvent);
         }
     }
@@ -114,12 +117,14 @@
     }
 
     private static void setBiometricSafetySourceData(Context context, String title, String summary,
-            Intent clickIntent, boolean enabled, SafetyEvent safetyEvent) {
+            Intent clickIntent, boolean enabled, boolean hasEnrolled, SafetyEvent safetyEvent) {
         final PendingIntent pendingIntent = createPendingIntent(context, clickIntent);
+        final int statusLevel =
+                enabled && hasEnrolled ? SafetySourceStatus.STATUS_LEVEL_OK
+                        : SafetySourceStatus.STATUS_LEVEL_NONE;
 
-        final SafetySourceStatus status = new SafetySourceStatus.Builder(title, summary,
-                SafetySourceStatus.STATUS_LEVEL_NONE, pendingIntent)
-                .setEnabled(enabled).build();
+        final SafetySourceStatus status = new SafetySourceStatus.Builder(
+                title, summary, statusLevel, pendingIntent).setEnabled(enabled).build();
         final SafetySourceData safetySourceData =
                 new SafetySourceData.Builder().setStatus(status).build();
 
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
new file mode 100644
index 0000000..d8852db
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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.biometrics.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorProperties;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+
+import com.android.settings.R;
+
+import com.google.android.setupcompat.util.WizardManagerHelper;
+
+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.ActivityController;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class FingerprintEnrollIntroductionTest {
+
+    @Mock private FingerprintManager mFingerprintManager;
+
+    private Context mContext;
+
+    private FingerprintEnrollIntroduction mFingerprintEnrollIntroduction;
+
+    private static final int MAX_ENROLLMENTS = 5;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application.getApplicationContext());
+
+        final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+        final FingerprintSensorPropertiesInternal prop =
+                new FingerprintSensorPropertiesInternal(
+                        0 /* sensorId */,
+                        SensorProperties.STRENGTH_STRONG,
+                        MAX_ENROLLMENTS /* maxEnrollmentsPerUser */,
+                        componentInfo,
+                        FingerprintSensorProperties.TYPE_REAR,
+                        true /* resetLockoutRequiresHardwareAuthToken */);
+        final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+        props.add(prop);
+        when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
+    }
+
+    void setupFingerprintEnrollIntroWith(Intent intent) {
+        ActivityController<FingerprintEnrollIntroduction> controller =
+                Robolectric.buildActivity(FingerprintEnrollIntroduction.class, intent);
+        mFingerprintEnrollIntroduction = spy(controller.get());
+        ReflectionHelpers.setField(
+                mFingerprintEnrollIntroduction, "mFingerprintManager", mFingerprintManager);
+        controller.create();
+    }
+
+    void setFingerprintManagerToHave(int numEnrollments) {
+        List<Fingerprint> fingerprints = new ArrayList<>();
+        for (int i = 0; i < numEnrollments; i++) {
+            fingerprints.add(
+                    new Fingerprint(
+                            "Fingerprint " + i /* name */, 1 /*fingerId */, 1 /* deviceId */));
+        }
+        when(mFingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprints);
+    }
+
+    @Test
+    public void intro_CheckCanEnrollNormal() {
+        setupFingerprintEnrollIntroWith(new Intent());
+        setFingerprintManagerToHave(3 /* numEnrollments */);
+        int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void intro_CheckMaxEnrolledNormal() {
+        setupFingerprintEnrollIntroWith(new Intent());
+        setFingerprintManagerToHave(7 /* numEnrollments */);
+        int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
+
+        assertThat(result).isEqualTo(R.string.fingerprint_intro_error_max);
+    }
+
+    @Test
+    public void intro_CheckCanEnrollDuringSUW() {
+        // This code path should depend on suw_max_fingerprints_enrollable versus
+        // FingerprintManager.getSensorProperties...maxEnrollmentsPerUser()
+        Resources resources = mock(Resources.class);
+        when(resources.getInteger(anyInt())).thenReturn(5);
+        when(mContext.getResources()).thenReturn(resources);
+
+        setupFingerprintEnrollIntroWith(
+                new Intent()
+                        .putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
+                        .putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true));
+        setFingerprintManagerToHave(0 /* numEnrollments */);
+        int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void intro_CheckMaxEnrolledDuringSUW() {
+        // This code path should depend on suw_max_fingerprints_enrollable versus
+        // FingerprintManager.getSensorProperties...maxEnrollmentsPerUser()
+        Resources resources = mock(Resources.class);
+        when(mContext.getResources()).thenReturn(resources);
+        when(resources.getInteger(anyInt())).thenReturn(1);
+
+        setupFingerprintEnrollIntroWith(
+                new Intent()
+                        .putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
+                        .putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true));
+        setFingerprintManagerToHave(1 /* numEnrollments */);
+        int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
+
+        assertThat(result).isEqualTo(R.string.fingerprint_intro_error_max);
+    }
+
+    @Test
+    public void intro_CheckCanEnrollDuringDeferred() {
+        setupFingerprintEnrollIntroWith(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true));
+        setFingerprintManagerToHave(2 /* numEnrollments */);
+        int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void intro_CheckMaxEnrolledDuringDeferred() {
+        setupFingerprintEnrollIntroWith(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true));
+        setFingerprintManagerToHave(6 /* numEnrollments */);
+        int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
+
+        assertThat(result).isEqualTo(R.string.fingerprint_intro_error_max);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java
index a1d479f..fcb3ea9 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.settings.development.BluetoothA2dpHwOffloadPreferenceController
         .A2DP_OFFLOAD_DISABLED_PROPERTY;
+import static com.android.settings.development.BluetoothLeAudioHwOffloadPreferenceController
+        .LE_AUDIO_OFFLOAD_DISABLED_PROPERTY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -62,15 +64,37 @@
     }
 
     @Test
-    public void onA2dpHwDialogConfirmed_shouldChangeProperty() {
+    public void onA2dpHwDialogConfirmedAsA2dpOffloadDisabled_shouldChangeProperty() {
         SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
 
-        mController.onA2dpHwDialogConfirmed();
+        mController.onHwOffloadDialogConfirmed();
         final boolean mode = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
         assertThat(mode).isTrue();
+    }
 
-        mController.onA2dpHwDialogConfirmed();
-        final boolean mode2 = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
-        assertThat(mode2).isFalse();
+    @Test
+    public void onA2dpHwDialogConfirmedAsA2dpOffloadEnabled_shouldChangeProperty() {
+        SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(true));
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(true));
+
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogConfirmed();
+        final boolean a2dpMode = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, true);
+        final boolean leAudioMode = SystemProperties
+                .getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, true);
+        assertThat(a2dpMode).isFalse();
+        assertThat(leAudioMode).isFalse();
+    }
+
+    @Test
+    public void onA2dpHwDialogCanceled_shouldNotChangeProperty() {
+        SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogCanceled();
+        final boolean mode = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
+        assertThat(mode).isFalse();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java
deleted file mode 100644
index 1916bf2..0000000
--- a/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2021 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.development;
-
-import static com.android.settings.development.BluetoothGabeldorschePreferenceController
-        .CURRENT_GD_FLAG;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
-
-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.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class BluetoothGabeldorschePreferenceControllerTest {
-
-    @Mock
-    private SwitchPreference mPreference;
-    @Mock
-    private PreferenceScreen mPreferenceScreen;
-
-    private BluetoothGabeldorschePreferenceController mController;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        Context context = RuntimeEnvironment.application;
-        mController = new BluetoothGabeldorschePreferenceController(context);
-        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
-                .thenReturn(mPreference);
-        mController.displayPreference(mPreferenceScreen);
-    }
-
-    @Test
-    @Ignore
-    public void onPreferenceChanged_settingEnabled_shouldTurnOnBluetoothGabeldorsche() {
-        mController.onPreferenceChange(mPreference, true /* new value */);
-
-        boolean enabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, false /* defaultValue */);
-
-        assertThat(enabled).isTrue();
-    }
-
-    @Test
-    @Ignore
-    public void onPreferenceChanged_settingDisabled_shouldTurnOffBluetoothGabeldorsche() {
-        mController.onPreferenceChange(mPreference, false /* new value */);
-
-        boolean enabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, false /* defaultValue */);
-
-        assertThat(enabled).isFalse();
-    }
-
-    @Test
-    @Ignore
-    public void updateState_settingEnabled_preferenceShouldBeChecked() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, "true", false /* makeDefault */);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setChecked(true);
-    }
-
-    @Test
-    @Ignore
-    public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, "false", false /* makeDefault */);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setChecked(false);
-    }
-
-    @Test
-    @Ignore
-    public void onDeveloperOptionsDisabled_shouldDisablePreference() {
-        mController.onDeveloperOptionsDisabled();
-
-        String configStr = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG);
-
-        assertThat(configStr).isNull();
-        verify(mPreference).setEnabled(false);
-        verify(mPreference).setChecked(false);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceControllerTest.java
new file mode 100644
index 0000000..c82df40e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceControllerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 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.development;
+
+import static com.android.settings.development.BluetoothLeAudioHwOffloadPreferenceController
+        .LE_AUDIO_OFFLOAD_DISABLED_PROPERTY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothLeAudioHwOffloadPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private DevelopmentSettingsDashboardFragment mFragment;
+
+    private Context mContext;
+    private SwitchPreference mPreference;
+    private BluetoothLeAudioHwOffloadPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mPreference = new SwitchPreference(mContext);
+        mController = spy(new BluetoothLeAudioHwOffloadPreferenceController(mContext, mFragment));
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+            .thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void onLeAudioHwDialogConfirmedAsLeAudioOffloadDisabled_shouldChangeProperty() {
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogConfirmed();
+        final boolean mode = SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, false);
+        assertThat(mode).isTrue();
+    }
+
+    @Test
+    public void onLeAudioHwDialogConfirmedAsLeAudioOffloadEnabled_shouldChangeProperty() {
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(true));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogConfirmed();
+        final boolean mode2 = SystemProperties.getBoolean(
+                LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, true);
+        assertThat(mode2).isFalse();
+    }
+
+    @Test
+    public void onLeAudioHwDialogCanceled_shouldNotChangeProperty() {
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogCanceled();
+        final boolean mode = SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, false);
+        assertThat(mode).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
index 5e4be68..7db7141 100644
--- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
@@ -208,9 +208,9 @@
         assertThat(dialog).isNotNull();
         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
         assertThat(shadowDialog.getTitle()).isEqualTo(
-                mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title));
+                mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_title));
         assertThat(shadowDialog.getMessage()).isEqualTo(
-                mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message));
+                mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_message));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
index a0188f8..3305cde 100644
--- a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
@@ -23,26 +23,23 @@
 import android.content.Context;
 
 import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settingslib.dream.DreamBackend;
 import com.android.settingslib.dream.DreamBackend.WhenToDream;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(RobolectricTestRunner.class)
-@Ignore
 public class WhenToDreamPreferenceControllerTest {
 
     private WhenToDreamPreferenceController mController;
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
     @Mock
     private DreamBackend mBackend;
@@ -50,6 +47,7 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mContext = ApplicationProvider.getApplicationContext();
         mController = new WhenToDreamPreferenceController(mContext);
         ReflectionHelpers.setField(mController, "mBackend", mBackend);
     }
diff --git a/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
index 7d89079..10f1110 100644
--- a/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
@@ -57,8 +57,7 @@
     private static final ComponentName COMPONENT_NAME =
             new ComponentName("package", "class");
     private static final int USER_ID = UserHandle.myUserId();
-    private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);;
-
+    private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
 
     @Mock
     private PackageManager mPackageManager;
@@ -123,6 +122,54 @@
     }
 
     @Test
+    public void hasEnrolled_withoutFingerprintHardware_withoutFaceHardware_returnsFalse() {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+        when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+        assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isFalse();
+    }
+
+    @Test
+    public void hasEnrolled_withoutFingerprintEnroll_withoutFaceEnroll_returnsFalse() {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+        assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isFalse();
+    }
+
+    @Test
+    public void hasEnrolled_withoutFingerprintEnroll_withFaceEnroll_returnsTrue() {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+        assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
+    }
+
+    @Test
+    public void hasEnrolled_withFingerprintEnroll_withoutFaceEnroll_returnsTrue() {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+        assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
+    }
+
+    @Test
+    public void hasEnrolled_withFingerprintEnroll_withFaceEnroll_returnsTrue() {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+        assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
+    }
+
+    @Test
     public void getDisabledAdmin_whenFingerprintDisabled_whenFaceDisabled_returnsEnforcedAdmin() {
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
                 .thenReturn(KEYGUARD_DISABLE_FACE | KEYGUARD_DISABLE_FINGERPRINT);
diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java
index 8697da7..9f9dd93 100644
--- a/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java
@@ -115,6 +115,20 @@
     }
 
     @Test
+    public void hasEnrolled_withEnrolledTemplates_returnsTrue() {
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+        assertThat(mFaceStatusUtils.hasEnrolled()).isTrue();
+    }
+
+    @Test
+    public void hasEnrolled_withoutEnrolledTemplates_returnsFalse() {
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+        assertThat(mFaceStatusUtils.hasEnrolled()).isFalse();
+    }
+
+    @Test
     public void getDisabledAdmin_whenFaceDisabled_returnsEnforcedAdmin() {
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
index cbe4298..4ecf82e 100644
--- a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
@@ -119,6 +119,20 @@
     }
 
     @Test
+    public void hasEnrolled_withEnrolledFingerprints_returnsTrue() {
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+
+        assertThat(mFingerprintStatusUtils.hasEnrolled()).isTrue();
+    }
+
+    @Test
+    public void hasEnrolled_withoutEnrolledFingerprints_returnsFalse() {
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+
+        assertThat(mFingerprintStatusUtils.hasEnrolled()).isFalse();
+    }
+
+    @Test
     public void getDisabledAdmin_whenFingerprintDisabled_returnsEnforcedAdmin() {
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
diff --git a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
index cc9bdfc..2cf9845 100644
--- a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
+++ b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
@@ -454,7 +454,7 @@
     @Test
     public void getExcludedLogicalSlotIndex_oneEsimAndFromDualPortsAToPsimAndPort0_logicalSlot1() {
         // There is only one enabled esimPort0 before user enables the psim.
-        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingPsimAndPort1();
+        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingDualPortsA();
         Collection<SubscriptionInfo> activeSubscriptionInfoList =
                 createActiveSubscriptionInfoListOneSim(0, 0);
         SubscriptionInfo removedSubInfo = null;
@@ -469,7 +469,7 @@
     @Test
     public void getExcludedLogicalSlotIndex_oneEsimAndFromDualPortsBToPsimAndPort1_logicalSlot1() {
         // There is only one enabled esimPort1 before user enables the psim.
-        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingPsimAndPort1();
+        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingDualPortsB();
         Collection<SubscriptionInfo> activeSubscriptionInfoList =
                 createActiveSubscriptionInfoListOneSim(0, 1);
         SubscriptionInfo removedSubInfo = null;
@@ -484,7 +484,7 @@
     @Test
     public void getExcludedLogicalSlotIndex_oneEsimAndFromDualPortsBToPsimAndPort0_logicalSlot0() {
         // There is only one enabled esimPort0 before user enables the psim.
-        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingPsimAndPort1();
+        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingDualPortsB();
         Collection<SubscriptionInfo> activeSubscriptionInfoList =
                 createActiveSubscriptionInfoListOneSim(1, 0);
         SubscriptionInfo removedSubInfo = null;
@@ -552,6 +552,48 @@
         assertThat(testExcludedLogicalSlotIndex).isEqualTo(verifyExcludedLogicalSlotIndex);
     }
 
+    @Test
+    public void getExcludedLogicalSlotIndex_noEsimAndFromDualPortsAToPsimAndPort1_logicalSlot0() {
+        // There is no profiles enabled on either esim port before user enables the psim.
+        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingDualPortsA();
+        Collection<SubscriptionInfo> activeSubscriptionInfoList = new ArrayList<>();
+        SubscriptionInfo removedSubInfo = null;
+        int verifyExcludedLogicalSlotIndex = 0;
+
+        int testExcludedLogicalSlotIndex = UiccSlotUtil.getExcludedLogicalSlotIndex(
+                uiccSlotMappings, activeSubscriptionInfoList, removedSubInfo, true);
+
+        assertThat(testExcludedLogicalSlotIndex).isEqualTo(verifyExcludedLogicalSlotIndex);
+    }
+
+    @Test
+    public void getExcludedLogicalSlotIndex_noEsimAndFromDualPortsBToPsimAndPort0_logicalSlot0() {
+        // There is no profiles enabled on either esim port before user enables the psim.
+        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingDualPortsB();
+        Collection<SubscriptionInfo> activeSubscriptionInfoList = new ArrayList<>();
+        SubscriptionInfo removedSubInfo = null;
+        int verifyExcludedLogicalSlotIndex = 0;
+
+        int testExcludedLogicalSlotIndex = UiccSlotUtil.getExcludedLogicalSlotIndex(
+                uiccSlotMappings, activeSubscriptionInfoList, removedSubInfo, true);
+
+        assertThat(testExcludedLogicalSlotIndex).isEqualTo(verifyExcludedLogicalSlotIndex);
+    }
+
+    @Test
+    public void getExcludedLogicalSlotIndex_noEsimNoOrdingFromDualPortsBToPsimAndPort1_logical0() {
+        // There is no profiles enabled on either esim port before user enables the psim.
+        Collection<UiccSlotMapping> uiccSlotMappings = createUiccSlotMappingDualPortsBNoOrding();
+        Collection<SubscriptionInfo> activeSubscriptionInfoList = new ArrayList<>();
+        SubscriptionInfo removedSubInfo = null;
+        int verifyExcludedLogicalSlotIndex = 0;
+
+        int testExcludedLogicalSlotIndex = UiccSlotUtil.getExcludedLogicalSlotIndex(
+                uiccSlotMappings, activeSubscriptionInfoList, removedSubInfo, true);
+
+        assertThat(testExcludedLogicalSlotIndex).isEqualTo(verifyExcludedLogicalSlotIndex);
+    }
+
     private void compareTwoUiccSlotMappings(Collection<UiccSlotMapping> testUiccSlotMappings,
             Collection<UiccSlotMapping> verifyUiccSlotMappings) {
         assertThat(testUiccSlotMappings.size()).isEqualTo(verifyUiccSlotMappings.size());
@@ -656,7 +698,13 @@
 
         return slotMap;
     }
+    private List<UiccSlotMapping> createUiccSlotMappingDualPortsBNoOrding() {
+        List<UiccSlotMapping> slotMap = new ArrayList<>();
+        slotMap.add(new UiccSlotMapping(0, ESIM_PHYSICAL_SLOT, 1));
+        slotMap.add(new UiccSlotMapping(1, ESIM_PHYSICAL_SLOT, 0));
 
+        return slotMap;
+    }
     /**
      * The "oneSimSlotDevice" has below cases
      * 1) The device is one psim slot and no esim slot
diff --git a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
index 13778f7..3030f0c 100644
--- a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
@@ -23,6 +23,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -112,8 +113,8 @@
 
         BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
 
-        verify(mSafetyCenterManagerWrapper, never()).setSafetySourceData(
-                any(), any(), any(), any());
+        verify(mSafetyCenterManagerWrapper, never())
+                .setSafetySourceData(any(), any(), any(), any());
     }
 
     @Test
@@ -124,8 +125,8 @@
 
         BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
 
-        verify(mSafetyCenterManagerWrapper, never()).setSafetySourceData(
-                any(), any(), any(), any());
+        verify(mSafetyCenterManagerWrapper, never())
+                .setSafetySourceData(any(), any(), any(), any());
     }
 
     @Test
@@ -137,11 +138,9 @@
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
 
         BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
-        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), captor.capture(), any(), any());
 
-        assertThat(captor.getValue()).isEqualTo(BiometricsSafetySource.SAFETY_SOURCE_ID);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), any(), any());
     }
 
     @Test
@@ -153,11 +152,9 @@
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
 
         BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
-        ArgumentCaptor<SafetyEvent> captor = ArgumentCaptor.forClass(SafetyEvent.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), any(), any(), captor.capture());
 
-        assertThat(captor.getValue()).isEqualTo(EVENT_SOURCE_STATE_CHANGED);
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED));
     }
 
     @Test
@@ -429,6 +426,128 @@
                 Settings.CombinedBiometricSettingsActivity.class.getName());
     }
 
+    @Test
+    public void setSafetySourceData_faceAndFingerprint_whenFaceEnrolled_setsOkStatus() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+
+        BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        SafetySourceStatus safetySourceStatus = captor.getValue().getStatus();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_OK);
+    }
+
+    @Test
+    public void setSafetySourceData_faceAndFingerprint_whenFingerprintEnrolled_setsOkStatus() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+
+        BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        SafetySourceStatus safetySourceStatus = captor.getValue().getStatus();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_OK);
+    }
+
+    @Test
+    public void setSafetySourceData_faceAndFingerprint_whenNotEnrolled_setsNoneStatus() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+
+        BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        SafetySourceStatus safetySourceStatus = captor.getValue().getStatus();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_NONE);
+    }
+
+    @Test
+    public void setSafetySourceData_fingerprint_whenEnrolled_setsOKStatus() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+
+        BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        SafetySourceStatus safetySourceStatus = captor.getValue().getStatus();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_OK);
+    }
+
+    @Test
+    public void setSafetySourceData_fingerprint_whenNotEnrolled_setsNoneStatus() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+
+        BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        SafetySourceStatus safetySourceStatus = captor.getValue().getStatus();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_NONE);
+    }
+
+    @Test
+    public void setSafetySourceData_face_whenEnrolled_setsOKStatus() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+        BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        SafetySourceStatus safetySourceStatus = captor.getValue().getStatus();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_OK);
+    }
+
+    @Test
+    public void setSafetySourceData_face_whenNotEnrolled_setsNoneStatus() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+        BiometricsSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        SafetySourceStatus safetySourceStatus = captor.getValue().getStatus();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_NONE);
+    }
+
     private void assertSafetySourceDisabledDataSetWithSingularSummary(String expectedTitleResName,
             String expectedSummaryResName) {
         assertSafetySourceDisabledDataSet(
@@ -478,13 +597,16 @@
     private void assertSafetySourceDisabledDataSet(String expectedTitle, String expectedSummary) {
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
         verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), any(), captor.capture(), any());
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
         assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle);
         assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary);
         assertThat(safetySourceStatus.isEnabled()).isFalse();
+        assertThat(safetySourceStatus.getStatusLevel())
+                .isEqualTo(SafetySourceStatus.STATUS_LEVEL_NONE);
+
         final Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent();
         assertThat(clickIntent).isNotNull();
         assertThat(clickIntent.getAction()).isEqualTo(ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
@@ -494,7 +616,7 @@
             String expectedSettingsClassName) {
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
         verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), any(), captor.capture(), any());
+                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
@@ -509,7 +631,6 @@
                 .isEqualTo(expectedSettingsClassName);
     }
 
-
     private List<Fingerprint> createFingerprintList(int size) {
         final List<Fingerprint> fingerprintList = new ArrayList<>(size);
         for (int i = 0; i < size; i++) {