Merge "Add LE audio hardware offload development option" into tm-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 56792a7..45fac0a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5403,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/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/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/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/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/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