Merge "Add factory reset wizard test coverage to factory reset preference controller" into main
diff --git a/Android.bp b/Android.bp
index 48e0d2d..eef73a4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,8 +23,8 @@
 genrule {
     name: "statslog-settings-java-gen",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --java $(out) --module settings"
-        + " --javaPackage com.android.settings.core.instrumentation --javaClass SettingsStatsLog",
+    cmd: "$(location stats-log-api-gen) --java $(out) --module settings" +
+        " --javaPackage com.android.settings.core.instrumentation --javaClass SettingsStatsLog",
     out: ["com/android/settings/core/instrumentation/SettingsStatsLog.java"],
 }
 
@@ -48,10 +48,14 @@
     platform_apis: true,
     defaults: [
         "SettingsLib-search-defaults",
+        "SettingsLintDefaults",
         "SpaPrivilegedLib-defaults",
     ],
 
-    srcs: ["src/**/*.java", "src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     exclude_srcs: [
         "src/com/android/settings/biometrics/fingerprint2/shared/**/*.kt",
     ],
@@ -120,10 +124,6 @@
         "telephony-common",
         "ims-common",
     ],
-
-    lint: {
-        extra_check_modules: ["SettingsLibLintChecker"],
-    },
 }
 
 platform_compat_config {
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2ba87d7..02dd9cd 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -5036,6 +5036,12 @@
             android:authorities="${applicationId}.androidx-startup"
             tools:node="remove" />
 
+        <activity
+            android:name="com.android.settings.network.WepNetworkDialogActivity"
+            android:exported="false"
+            android:theme="@style/Theme.SpaLib.Dialog">
+        </activity>
+
         <!-- This is the longest AndroidManifest.xml ever. -->
     </application>
 </manifest>
diff --git a/res/layout/dialog_mobile_network_rename.xml b/res/layout/dialog_mobile_network_rename.xml
index 921ab86..2c583d3 100644
--- a/res/layout/dialog_mobile_network_rename.xml
+++ b/res/layout/dialog_mobile_network_rename.xml
@@ -80,13 +80,15 @@
                 android:paddingTop="@dimen/sim_label_padding"
                 android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"
                 android:textColor="?android:attr/textColorPrimary"
-                android:text="@string/status_operator"/>
+                android:text="@string/status_operator"
+                android:visibility="gone"/>
 
             <TextView
                 android:id="@+id/operator_name_value"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/device_info_not_available"/>
+                android:text="@string/device_info_not_available"
+                android:visibility="gone"/>
 
             <TextView
                 android:id="@+id/number_label"
@@ -95,13 +97,15 @@
                 android:paddingTop="@dimen/sim_label_padding"
                 android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"
                 android:textColor="?android:attr/textColorPrimary"
-                android:text="@string/status_number_sim_status"/>
+                android:text="@string/status_number_sim_status"
+                android:visibility="gone"/>
 
             <TextView
                 android:id="@+id/number_value"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/device_info_not_available"/>
+                android:text="@string/device_info_not_available"
+                android:visibility="gone"/>
 
         </LinearLayout>
 
diff --git a/res/layout/sfps_enroll_finish_base.xml b/res/layout/sfps_enroll_finish_base.xml
index f442e90..9e65c83 100644
--- a/res/layout/sfps_enroll_finish_base.xml
+++ b/res/layout/sfps_enroll_finish_base.xml
@@ -24,6 +24,7 @@
 
     <LinearLayout
         style="@style/SudContentFrame"
+        android:id="@+id/sfps_enrollment_finish_content_frame"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
@@ -31,6 +32,7 @@
         android:clipChildren="false">
 
             <com.google.android.setupdesign.view.FillContentLayout
+                android:id="@+id/sfps_enrollment_finish_content_layout"
                 android:layout_width="@dimen/sfps_enrollment_finished_icon_max_size"
                 android:layout_height="@dimen/sfps_enrollment_finished_icon_max_size"
                 android:layout_marginTop="24dp"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c93455c..776e3f4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1114,6 +1114,8 @@
     <string name="security_settings_udfps_enroll_progress_a11y_message">Enrolling fingerprint <xliff:g id="percentage" example="10">%d</xliff:g> percent</string>
     <!-- Title shown in fingerprint enrollment dialog once enrollment is completed [CHAR LIMIT=29] -->
     <string name="security_settings_fingerprint_enroll_finish_title">Fingerprint added</string>
+    <!-- Key for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_require_screen_on_to_auth_key" translatable="false">security_settings_require_screen_on_to_auth</string>
     <!-- Title for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
     <string name="security_settings_require_screen_on_to_auth_title">Touch to unlock anytime</string>
     <!-- Description for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
@@ -1229,6 +1231,10 @@
     <string name="private_space_screen_lock_title">Use device screen lock</string>
     <!-- Title for the Face and Fingerprint preference. [CHAR LIMIT=60] -->
     <string name="private_space_biometric_title">Face &amp; Fingerprint Unlock</string>
+    <!-- Title for the Fingerprint preference when face hardware is not supported on device. [CHAR LIMIT=40] -->
+    <string name="private_space_fingerprint_title">Fingerprint Unlock</string>
+    <!-- Title for the Face preference when fingerprint unlock is not supported on device. [CHAR LIMIT=40] -->
+    <string name="private_space_face_title">Face Unlock</string>
     <!-- Summary for the Face and Fingerprint preference when no biometric is set. [CHAR LIMIT=60] -->
     <string name="private_space_biometric_summary">Tap to set up</string>
     <!-- Title for the Fingerprint unlock for private space preference. [CHAR LIMIT=60] -->
@@ -1279,6 +1285,8 @@
     <string name="private_space_deleted">Private Space successfully deleted</string>
     <!-- Toast to show when the private space could not be deleted. [CHAR LIMIT=NONE] -->
     <string name="private_space_delete_failed">Private Space could not be deleted</string>
+    <!-- Toast to show when the private space is unlocked from settings entry point. [CHAR LIMIT=40] -->
+    <string name="private_space_unlocked">Private space unlocked</string>
     <!-- Title of the Alert Dialog when no screen lock is set [CHAR LIMIT=30] -->
     <string name="no_device_lock_title">Set a screen lock</string>
     <!-- Summary of the alert when no screen lock is set [CHAR LIMIT=90] -->
@@ -3315,6 +3323,8 @@
     <string name="menu_cancel">Cancel</string>
     <!-- APN error dialog title -->
     <string name="error_title"></string>
+    <!-- APN error dialog messages when the new apn is a duplicate: -->
+    <string name="error_duplicate_apn_entry">Duplicate apn entry.</string>
     <!-- APN error dialog messages: -->
     <string name="error_name_empty">The Name field can\u2019t be empty.</string>
     <!-- APN error dialog messages: -->
@@ -9605,6 +9615,10 @@
     <string name="permit_voice_activation_apps">Allow voice activation</string>
     <!-- Description for a setting which controls whether an app can be voice activated [CHAR LIMIT=NONE] -->
     <string name ="allow_voice_activation_apps_description">Voice activation turns-on approved apps, hands-free, using voice command. Built-in adaptive sensing ensures data stays private only to you.\n\n<a href="">More about protected adaptive sensing</a></string>
+    <!-- Label for a setting which controls whether an app can receive sandboxed detection training data [CHAR LIMIT=NONE] -->
+    <string name = "permit_receive_sandboxed_detection_training_data">Improve voice activation</string>
+    <!-- Description for a setting which controls whether an app can receive sandboxed detection training data [CHAR LIMIT=NONE] -->
+    <string name= "receive_sandboxed_detection_training_data_description">This device uses private intelligence to improve the voice activation model. Apps can receive summarized updates that are aggregated across many users to maintain privacy while improving the model for everyone.\n\n<a href="">More about private intelligence</a></string>
 
     <!-- Manage full screen intent permission title [CHAR LIMIT=40] -->
     <string name="full_screen_intent_title">Full screen notifications</string>
diff --git a/res/xml/bluetooth_audio_sharing.xml b/res/xml/bluetooth_audio_sharing.xml
index dc577f6..d3aad22 100644
--- a/res/xml/bluetooth_audio_sharing.xml
+++ b/res/xml/bluetooth_audio_sharing.xml
@@ -48,6 +48,10 @@
             android:summary="********"
             android:title="Stream name"
             settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingNamePreferenceController" />
+        <SwitchPreferenceCompat
+            android:key="audio_sharing_stream_compatibility"
+            android:title="Improve compatibility"
+            settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingCompatibilityPreferenceController" />
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index 12ed8eb..d260554 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -81,6 +81,9 @@
         android:key="bluetooth_profiles"/>
 
     <PreferenceCategory
+        android:key="bt_extra_options"/>
+
+    <PreferenceCategory
         android:key="bluetooth_related_tools"
         android:title="@string/bluetooth_screen_related">
         <Preference
diff --git a/res/xml/security_settings_fingerprint.xml b/res/xml/security_settings_fingerprint.xml
index 0156ef9..9c8b0a3 100644
--- a/res/xml/security_settings_fingerprint.xml
+++ b/res/xml/security_settings_fingerprint.xml
@@ -36,7 +36,7 @@
         settings:isPreferenceVisible="false">
 
         <com.android.settingslib.RestrictedSwitchPreference
-            android:key="security_settings_require_screen_on_to_auth"
+            android:key="@string/security_settings_require_screen_on_to_auth_key"
             android:title="@string/security_settings_require_screen_on_to_auth_title"
             android:summary="@string/security_settings_require_screen_on_to_auth_description"
             settings:keywords="@string/security_settings_require_screen_on_to_auth_keywords"
diff --git a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
index fb79ece..78f5b4c 100644
--- a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
+++ b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
@@ -149,6 +149,7 @@
         for (BluetoothGatt gatt: mConnectingGattList) {
             gatt.disconnect();
         }
+        mConnectingGattList.clear();
         mLocalManager.setForegroundActivity(null);
         mLocalManager.getEventManager().unregisterCallback(this);
     }
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
index 722f213..9c89f24 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
@@ -23,8 +23,10 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -32,6 +34,8 @@
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.biometrics.BiometricUtils;
+import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeature;
+import com.android.settings.overlay.FeatureFactory;
 
 import com.google.android.setupcompat.template.FooterBarMixin;
 import com.google.android.setupcompat.template.FooterButton;
@@ -56,6 +60,8 @@
 
     private boolean mIsAddAnotherOrFinish;
 
+    private SfpsRestToUnlockFeature mSfpsRestToUnlockFeature;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -64,14 +70,20 @@
                 mFingerprintManager.getSensorPropertiesInternal();
         mCanAssumeSfps = props != null && props.size() == 1 && props.get(0).isAnySidefpsType();
         if (mCanAssumeSfps) {
+            mSfpsRestToUnlockFeature = FeatureFactory.getFeatureFactory()
+                    .getFingerprintFeatureProvider().getSfpsRestToUnlockFeature(this);
             setContentView(R.layout.sfps_enroll_finish);
+            setUpRestToUnlockLayout();
         } else {
             setContentView(R.layout.fingerprint_enroll_finish);
         }
         setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title);
         setDescriptionText(R.string.security_settings_fingerprint_enroll_finish_v2_message);
-        if (mCanAssumeSfps) {
-            setDescriptionForSfps();
+        final String sfpsDescription = mSfpsRestToUnlockFeature != null
+                ? mSfpsRestToUnlockFeature.getDescriptionForSfps(this)
+                : null;
+        if (mCanAssumeSfps && !TextUtils.isEmpty(sfpsDescription)) {
+            setDescriptionForSfps(sfpsDescription);
         }
 
         mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
@@ -93,7 +105,7 @@
         );
     }
 
-    private void setDescriptionForSfps() {
+    private void setDescriptionForSfps(String sfpsDescription) {
         final FingerprintManager fpm = Utils.getFingerprintManagerOrNull(this);
         if (fpm != null) {
             final List<FingerprintSensorPropertiesInternal> props =
@@ -101,12 +113,19 @@
             final int maxEnrollments = props.get(0).maxEnrollmentsPerUser;
             final int enrolled = fpm.getEnrolledFingerprints(mUserId).size();
             if (enrolled < maxEnrollments) {
-                setDescriptionText(R.string
-                        .security_settings_fingerprint_enroll_finish_v2_add_fingerprint_message);
+                setDescriptionText(sfpsDescription);
             }
         }
     }
 
+    private void setUpRestToUnlockLayout() {
+        final ViewGroup contentFrame = findViewById(R.id.sfps_enrollment_finish_content_frame);
+        final View restToUnlockLayout = mSfpsRestToUnlockFeature.getRestToUnlockLayout(this);
+        if (restToUnlockLayout == null) return;
+        contentFrame.removeAllViews();
+        contentFrame.addView(restToUnlockLayout);
+    }
+
     @Override
     public void onBackPressed() {
         updateFingerprintSuggestionEnableState();
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
index e770220..c1e34a5 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
@@ -24,6 +24,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
+import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeature;
 
 public interface FingerprintFeatureProvider {
     /**
@@ -44,4 +45,11 @@
             @Nullable Bundle activitySavedInstanceState, @Nullable Intent activityIntent) {
         return null;
     }
+
+    /**
+     * Gets the feature implementation of SFPS rest to unlock.
+     * @param context context
+     * @return the feature implementation
+     */
+    SfpsRestToUnlockFeature getSfpsRestToUnlockFeature(@NonNull Context context);
 }
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
index 9745ca3..8a8df98 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
@@ -16,16 +16,24 @@
 
 package com.android.settings.biometrics.fingerprint;
 
+import android.content.Context;
+
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
 import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl;
+import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeature;
+import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeatureImpl;
 
 public class FingerprintFeatureProviderImpl implements FingerprintFeatureProvider {
 
     @Nullable
     private SfpsEnrollmentFeature mSfpsEnrollmentFeatureImpl = null;
 
+    @Nullable
+    private SfpsRestToUnlockFeature mSfpsRestToUnlockFeature = null;
+
     @Override
     public SfpsEnrollmentFeature getSfpsEnrollmentFeature() {
         if (mSfpsEnrollmentFeatureImpl == null) {
@@ -33,4 +41,12 @@
         }
         return mSfpsEnrollmentFeatureImpl;
     }
+
+    @Override
+    public SfpsRestToUnlockFeature getSfpsRestToUnlockFeature(@NonNull Context context) {
+        if (mSfpsRestToUnlockFeature == null) {
+            mSfpsRestToUnlockFeature = new SfpsRestToUnlockFeatureImpl();
+        }
+        return mSfpsRestToUnlockFeature;
+    }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 6904342..4d1d499 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -75,6 +75,7 @@
 import com.android.settings.core.SettingsBaseActivity;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockGeneric;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.search.BaseSearchIndexProvider;
@@ -635,6 +636,19 @@
         private void addFingerprintUnlockCategory() {
             mFingerprintUnlockCategory = findPreference(KEY_FINGERPRINT_UNLOCK_CATEGORY);
             setupFingerprintUnlockCategoryPreferences();
+            final Preference restToUnlockPreference = FeatureFactory.getFeatureFactory()
+                    .getFingerprintFeatureProvider()
+                    .getSfpsRestToUnlockFeature(getContext())
+                    .getRestToUnlockPreference(getContext());
+            if (restToUnlockPreference != null) {
+                // Use custom featured preference if any.
+                mRequireScreenOnToAuthPreference.setTitle(restToUnlockPreference.getTitle());
+                mRequireScreenOnToAuthPreference.setSummary(restToUnlockPreference.getSummary());
+                mRequireScreenOnToAuthPreference.setChecked(
+                        ((TwoStatePreference) restToUnlockPreference).isChecked());
+                mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
+                        restToUnlockPreference.getOnPreferenceChangeListener());
+            }
             updateFingerprintUnlockCategoryVisibility();
         }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeature.kt b/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeature.kt
new file mode 100644
index 0000000..840926c
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeature.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint.feature
+
+import android.content.Context
+import android.view.View
+import androidx.preference.Preference
+
+/**
+ * Defines the feature provided by rest to unlock.
+ */
+interface SfpsRestToUnlockFeature {
+    /**
+     * Gets the content view hierarchy for SFPS rest to unlock feature which is used by
+     * [com.android.settings.biometrics.fingerprint.FingerprintEnrollFinish].
+     * @param context the context of
+     * [com.android.settings.biometrics.fingerprint.FingerprintEnrollFinish].
+     */
+    fun getRestToUnlockLayout(context: Context) : View? = null
+
+    /**
+     * Gets the SFPS rest to unlock preference which is used in
+     * [com.android.settings.biometrics.fingerprint.FingerprintSettings].
+     * @param context the context of
+     * [com.android.settings.biometrics.fingerprint.FingerprintSettings].
+     */
+    fun getRestToUnlockPreference(context: Context) : Preference? = null
+
+    /**
+     * Gets the specific description used in
+     * [com.android.settings.biometrics.fingerprint.FingerprintEnrollFinish] for SFPS devices.
+     * @return the description text for SFPS devices.
+     */
+    fun getDescriptionForSfps(context: Context) : String
+}
\ No newline at end of file
diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeatureImpl.kt b/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeatureImpl.kt
new file mode 100644
index 0000000..de78a2a
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeatureImpl.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint.feature
+
+import android.content.Context
+
+class SfpsRestToUnlockFeatureImpl : SfpsRestToUnlockFeature {
+    override fun getDescriptionForSfps(context: Context) : String {
+        return context.getString(
+            com.android.settings.R
+                .string.security_settings_fingerprint_enroll_finish_v2_add_fingerprint_message
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsExtraOptionsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsExtraOptionsController.java
new file mode 100644
index 0000000..ddaf5e5
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsExtraOptionsController.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.utils.ThreadUtils;
+
+import dagger.internal.Preconditions;
+
+import java.util.List;
+
+public class BluetoothDetailsExtraOptionsController extends BluetoothDetailsController {
+
+    private static final String KEY_BLUETOOTH_EXTRA_OPTIONS = "bt_extra_options";
+
+    @VisibleForTesting @Nullable
+    PreferenceCategory mOptionsContainer;
+    @Nullable PreferenceScreen mPreferenceScreen;
+
+    public BluetoothDetailsExtraOptionsController(
+            Context context,
+            PreferenceFragmentCompat fragment,
+            CachedBluetoothDevice device,
+            Lifecycle lifecycle) {
+        super(context, fragment, device, lifecycle);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BLUETOOTH_EXTRA_OPTIONS;
+    }
+
+    @Override
+    protected void init(PreferenceScreen screen) {
+        mPreferenceScreen = screen;
+        mOptionsContainer = screen.findPreference(getPreferenceKey());
+        refresh();
+    }
+
+    @Override
+    protected void refresh() {
+        ThreadUtils.postOnBackgroundThread(
+                () -> {
+                    List<Preference> options =
+                            FeatureFactory.getFeatureFactory()
+                                    .getBluetoothFeatureProvider()
+                                    .getBluetoothExtraOptions(mContext, mCachedDevice);
+                    ThreadUtils.postOnMainThread(
+                            () -> {
+                                if (mOptionsContainer != null) {
+                                    mOptionsContainer.removeAll();
+                                    for (Preference option : options) {
+                                        mOptionsContainer.addPreference(option);
+                                    }
+                                    setVisible(
+                                            Preconditions.checkNotNull(mPreferenceScreen),
+                                            getPreferenceKey(),
+                                            !options.isEmpty());
+                                }
+                            });
+                });
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index cfe7962..5e41a20 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -328,6 +328,9 @@
                     mCachedDevice, lifecycle));
             controllers.add(new BluetoothDetailsDataSyncController(context, this,
                     mCachedDevice, lifecycle));
+            controllers.add(
+                    new BluetoothDetailsExtraOptionsController(
+                            context, this, mCachedDevice, lifecycle));
         }
         return controllers;
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java b/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java
index 1bc7184..d44e4b2 100644
--- a/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java
+++ b/src/com/android/settings/bluetooth/BluetoothFeatureProvider.java
@@ -22,6 +22,10 @@
 import android.media.Spatializer;
 import android.net.Uri;
 
+import androidx.preference.Preference;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
 import java.util.List;
 
 /**
@@ -60,4 +64,13 @@
      * @return the Spatializer instance
      */
     Spatializer getSpatializer(Context context);
+
+    /**
+     * Gets bluetooth device extra options
+     *
+     * @param context Context
+     * @param device the bluetooth device
+     * @return the extra bluetooth preference list
+     */
+    List<Preference> getBluetoothExtraOptions(Context context, CachedBluetoothDevice device);
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java b/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java
index ba02020..014a0f3 100644
--- a/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java
+++ b/src/com/android/settings/bluetooth/BluetoothFeatureProviderImpl.java
@@ -23,7 +23,12 @@
 import android.media.Spatializer;
 import android.net.Uri;
 
+import androidx.preference.Preference;
+
 import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 
@@ -54,4 +59,10 @@
         AudioManager audioManager = context.getSystemService(AudioManager.class);
         return audioManager.getSpatializer();
     }
+
+    @Override
+    public List<Preference> getBluetoothExtraOptions(Context context,
+            CachedBluetoothDevice device) {
+        return ImmutableList.of();
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java
new file mode 100644
index 0000000..9dda0a1
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.audiosharing;
+
+import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class AudioSharingCompatibilityPreferenceController extends TogglePreferenceController
+        implements DefaultLifecycleObserver {
+
+    private static final String TAG = "AudioSharingCompatibilityPrefController";
+
+    private static final String PREF_KEY = "audio_sharing_stream_compatibility";
+    private static final String SHARING_OFF_SUMMARY =
+            "Helps some devices like hearing aids connect by reducing audio quality";
+    private static final String SHARING_ON_SUMMARY =
+            "Turns off the audio sharing to config the compatibility";
+
+    private final LocalBluetoothManager mBtManager;
+    private final Executor mExecutor;
+    private final LocalBluetoothLeBroadcast mBroadcast;
+    @Nullable private TwoStatePreference mPreference;
+
+    private final BluetoothLeBroadcast.Callback mBroadcastCallback =
+            new BluetoothLeBroadcast.Callback() {
+                @Override
+                public void onBroadcastStarted(int reason, int broadcastId) {
+                    Log.d(
+                            TAG,
+                            "onBroadcastStarted(), reason = "
+                                    + reason
+                                    + ", broadcastId = "
+                                    + broadcastId);
+                    updateEnabled();
+                }
+
+                @Override
+                public void onBroadcastStartFailed(int reason) {}
+
+                @Override
+                public void onBroadcastMetadataChanged(
+                        int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) {}
+
+                @Override
+                public void onBroadcastStopped(int reason, int broadcastId) {
+                    Log.d(
+                            TAG,
+                            "onBroadcastStopped(), reason = "
+                                    + reason
+                                    + ", broadcastId = "
+                                    + broadcastId);
+                    updateEnabled();
+                }
+
+                @Override
+                public void onBroadcastStopFailed(int reason) {}
+
+                @Override
+                public void onBroadcastUpdated(int reason, int broadcastId) {}
+
+                @Override
+                public void onBroadcastUpdateFailed(int reason, int broadcastId) {}
+
+                @Override
+                public void onPlaybackStarted(int reason, int broadcastId) {}
+
+                @Override
+                public void onPlaybackStopped(int reason, int broadcastId) {}
+            };
+
+    public AudioSharingCompatibilityPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mBtManager = Utils.getLocalBtManager(context);
+        mBroadcast = mBtManager.getProfileManager().getLeAudioBroadcastProfile();
+        mExecutor = Executors.newSingleThreadExecutor();
+    }
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        if (mBroadcast != null) {
+            mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
+        }
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        if (mBroadcast != null) {
+            mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
+        }
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+        updateEnabled();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public boolean isChecked() {
+        // TODO: return real compatibility config.
+        return false;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        if (mBroadcast == null) {
+            return false;
+        }
+        // TODO: set real compatibility config.
+        return true;
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return 0;
+    }
+
+    private void updateEnabled() {
+        mContext.getMainExecutor()
+                .execute(
+                        () -> {
+                            if (mPreference != null) {
+                                boolean isBroadcasting =
+                                        AudioSharingUtils.isBroadcasting(mBtManager);
+                                mPreference.setEnabled(!isBroadcasting);
+                                mPreference.setSummary(
+                                        isBroadcasting ? SHARING_ON_SUMMARY : SHARING_OFF_SUMMARY);
+                            }
+                        });
+    }
+}
diff --git a/src/com/android/settings/development/BackAnimationPreferenceController.java b/src/com/android/settings/development/BackAnimationPreferenceController.java
index ed63896..09af27a 100644
--- a/src/com/android/settings/development/BackAnimationPreferenceController.java
+++ b/src/com/android/settings/development/BackAnimationPreferenceController.java
@@ -16,7 +16,7 @@
 
 package com.android.settings.development;
 
-import static com.android.window.flags.Flags.predictiveBackSystemAnimations;
+import static com.android.window.flags.Flags.predictiveBackSystemAnims;
 
 import android.content.Context;
 import android.provider.Settings;
@@ -57,7 +57,7 @@
 
     @Override
     public boolean isAvailable() {
-        return !predictiveBackSystemAnimations();
+        return !predictiveBackSystemAnims();
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java b/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java
index ab9a9e0..52cec795 100644
--- a/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java
@@ -49,9 +49,18 @@
         }
     }
 
+    private void setEnabled(Preference preference, boolean enabled) {
+        if (preference instanceof PrimarySwitchPreference) {
+            ((PrimarySwitchPreference) preference).setEnabled(enabled);
+            ((PrimarySwitchPreference) preference).setSwitchEnabled(enabled);
+        } else if (preference instanceof MainSwitchPreference) {
+            ((MainSwitchPreference) preference).setEnabled(enabled);
+        }
+    }
+
     @Override
     public void updateState(Preference preference) {
-        preference.setEnabled(mBatteryOptimizeUtils.isOptimizeModeMutable());
+        setEnabled(preference, mBatteryOptimizeUtils.isOptimizeModeMutable());
 
         final boolean isAllowBackground =
                 mBatteryOptimizeUtils.getAppOptimizationMode()
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index f14c32c..0da1034 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -89,6 +89,7 @@
 import com.android.settingslib.widget.LayoutPreference;
 import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
 import com.android.settingslib.wifi.WifiSavedConfigUtils;
+import com.android.wifi.flags.Flags;
 import com.android.wifitrackerlib.WifiEntry;
 import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
 import com.android.wifitrackerlib.WifiPickerTracker;
@@ -1257,8 +1258,19 @@
 
         // If it's an unsaved secure WifiEntry, it will callback
         // ConnectCallback#onConnectResult with ConnectCallback#CONNECT_STATUS_FAILURE_NO_CONFIG
-        wifiEntry.connect(new WifiEntryConnectCallback(wifiEntry, editIfNoConfig,
-                fullScreenEdit));
+        WifiEntryConnectCallback callback =
+                new WifiEntryConnectCallback(wifiEntry, editIfNoConfig, fullScreenEdit);
+
+        if (Flags.wepUsage() && wifiEntry.getSecurityTypes().contains(WifiEntry.SECURITY_WEP)) {
+            WepNetworkDialogActivity.checkWepAllowed(
+                    getContext(), getViewLifecycleOwner(), wifiEntry.getSsid(), () -> {
+                        wifiEntry.connect(callback);
+                        return null;
+                    });
+            return;
+        }
+
+        wifiEntry.connect(callback);
     }
 
     private class WifiConnectActionListener implements WifiManager.ActionListener {
diff --git a/src/com/android/settings/network/WepNetworkDialogActivity.kt b/src/com/android/settings/network/WepNetworkDialogActivity.kt
new file mode 100644
index 0000000..2fa8784
--- /dev/null
+++ b/src/com/android/settings/network/WepNetworkDialogActivity.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.Intent
+import android.net.wifi.WifiManager
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import com.android.settings.R
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.wifi.ConfigureWifiSettings
+import com.android.settingslib.spa.SpaBaseDialogActivity
+import com.android.settingslib.spa.widget.dialog.AlertDialogButton
+import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogWithIcon
+import kotlin.coroutines.resume
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+
+class WepNetworkDialogActivity : SpaBaseDialogActivity() {
+    @Composable
+    override fun Content() {
+        val context = LocalContext.current
+        val wifiManager = context.getSystemService(WifiManager::class.java)
+        SettingsAlertDialogWithIcon(
+            onDismissRequest = { finish() },
+            confirmButton = AlertDialogButton(
+                getString(R.string.wifi_settings_ssid_block_button_close)
+            ) { finish() },
+            dismissButton = AlertDialogButton(
+                getString(R.string.wifi_settings_wep_networks_button_allow)
+            ) {
+                SubSettingLauncher(context)
+                    .setTitleText(context.getText(R.string.network_and_internet_preferences_title))
+                    .setSourceMetricsCategory(SettingsEnums.CONFIGURE_WIFI)
+                    .setDestination(ConfigureWifiSettings::class.java.getName())
+                    .launch()
+                finish()
+            },
+            title = String.format(
+                getString(R.string.wifi_settings_wep_networks_blocked_title),
+                intent.getStringExtra(SSID) ?: SSID
+            ),
+            text = {
+                Text(
+                    if (wifiManager?.isWepSupported == false)
+                        getString(R.string.wifi_settings_wep_networks_summary_toggle_off)
+                    else getString(R.string.wifi_settings_wep_networks_summary_blocked_by_carrier),
+                    modifier = Modifier.fillMaxWidth(),
+                    textAlign = TextAlign.Center
+                )
+            })
+    }
+
+    companion object {
+        @JvmStatic
+        fun checkWepAllowed(
+            context: Context,
+            lifecycleOwner: LifecycleOwner,
+            ssid: String,
+            onAllowed: () -> Unit,
+        ) {
+            lifecycleOwner.lifecycleScope.launch {
+                val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
+                if (wifiManager.queryWepAllowed()) {
+                    onAllowed()
+                } else {
+                    val intent = Intent(context, WepNetworkDialogActivity::class.java).apply {
+                        putExtra(SSID, ssid)
+                    }
+                    context.startActivity(intent)
+                }
+            }
+        }
+
+        private suspend fun WifiManager.queryWepAllowed(): Boolean =
+            withContext(Dispatchers.Default) {
+                suspendCancellableCoroutine { continuation ->
+                    queryWepAllowed(Dispatchers.Default.asExecutor()) {
+                        continuation.resume(it)
+                    }
+                }
+            }
+
+        const val SSID = "ssid"
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 2600618..924e311 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -19,16 +19,19 @@
 import android.net.Uri
 import android.os.Bundle
 import androidx.compose.foundation.layout.Column
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Done
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringArrayResource
 import androidx.compose.ui.res.stringResource
@@ -39,14 +42,14 @@
 import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.compose.LocalNavController
+import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
 import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
 import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
 import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 import java.util.Base64
 
@@ -98,25 +101,59 @@
         getNetworkTypeSelectedOptionsState(apnData.networkType)
     }
     val navController = LocalNavController.current
+    var valid: String?
     RegularScaffold(
         title = if (apnDataInit.newApn) stringResource(id = R.string.apn_add) else stringResource(id = R.string.apn_edit),
         actions = {
             if (!apnData.customizedConfig.readOnlyApn) {
-                IconButton(onClick = {
-                    if (!apnData.validEnabled) apnData = apnData.copy(validEnabled = true)
-                    val valid = validateAndSaveApnData(
+                Button(onClick = {
+                    apnData = apnData.copy(
+                        networkType = ApnNetworkTypes.getNetworkType(
+                            networkTypeSelectedOptionsState
+                        )
+                    )
+                    valid = validateAndSaveApnData(
                         apnDataInit,
                         apnData,
                         context,
-                        uriInit,
-                        networkTypeSelectedOptionsState
+                        uriInit
                     )
-                    if (valid) navController.navigateBack()
-                }) { Icon(imageVector = Icons.Outlined.Done, contentDescription = null) }
+                    if (valid == null) navController.navigateBack()
+                    else if (!apnData.validEnabled) apnData = apnData.copy(validEnabled = true)
+                }) { Text(text = stringResource(id = R.string.save)) }
+            }
+            if (!apnData.newApn && !apnData.customizedConfig.readOnlyApn
+                && apnData.customizedConfig.isAddApnAllowed
+            ) {
+                MoreOptionsAction {
+                    DropdownMenuItem(
+                        text = { Text(stringResource(R.string.menu_delete)) },
+                        onClick = {
+                            deleteApn(uriInit, context)
+                            navController.navigateBack()
+                        })
+                }
             }
         },
     ) {
         Column {
+            if (apnData.validEnabled) {
+                apnData = apnData.copy(
+                    networkType = ApnNetworkTypes.getNetworkType(
+                        networkTypeSelectedOptionsState
+                    )
+                )
+                valid = validateApnData(uriInit, apnData, context)
+                valid?.let {
+                    Text(
+                        text = it,
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .padding(SettingsDimension.menuFieldPadding),
+                        color = MaterialTheme.colorScheme.primary
+                    )
+                }
+            }
             SettingsOutlinedTextField(
                 value = apnData.name,
                 label = stringResource(R.string.apn_name),
@@ -214,19 +251,6 @@
                 emptyVal = stringResource(R.string.network_type_unspecified),
                 enabled = apnData.networkTypeEnabled
             ) {}
-            if (!apnData.newApn && !apnData.customizedConfig.readOnlyApn
-                && apnData.customizedConfig.isAddApnAllowed
-            ) {
-                Preference(
-                    object : PreferenceModel {
-                        override val title = stringResource(R.string.menu_delete)
-                        override val onClick = {
-                            deleteApn(uriInit, context)
-                            navController.navigateBack()
-                        }
-                    }
-                )
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index e0121b4..2f16e69 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.net.Uri
 import android.provider.Telephony
+import android.telephony.TelephonyManager
 import android.util.Log
 import com.android.settings.R
 import com.android.settingslib.utils.ThreadUtils
@@ -150,7 +151,6 @@
 private fun convertProtocol2Options(raw: String, context: Context): String {
     val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
     val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
-
     var uRaw = raw.uppercase(Locale.getDefault())
     uRaw = if (uRaw == "IPV4") "IP" else uRaw
     val protocolIndex = apnProtocolValues.indexOf(uRaw)
@@ -167,7 +167,6 @@
 
 fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
     val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
-
     return if (protocolIndex == -1) {
         ""
     } else {
@@ -179,7 +178,12 @@
     }
 }
 
-fun updateApnDataToDatabase(newApn: Boolean, values: ContentValues, context: Context, uriInit: Uri) {
+fun updateApnDataToDatabase(
+    newApn: Boolean,
+    values: ContentValues,
+    context: Context,
+    uriInit: Uri
+) {
     ThreadUtils.postOnBackgroundThread {
         if (newApn) {
             // Add a new apn to the database
@@ -194,4 +198,24 @@
             )
         }
     }
+}
+
+fun isItemExist(uri: Uri, apnData: ApnData, context: Context): String? {
+    val contentValueMap = apnData.getContentValueMap(context)
+    contentValueMap.remove(Telephony.Carriers.CARRIER_ENABLED)
+    val list = contentValueMap.entries.toList()
+    val selection = list.joinToString(" AND ") { "${it.key} = ?" }
+    val selectionArgs: Array<String> = list.map { it.value.toString() }.toTypedArray()
+    context.contentResolver.query(
+        uri,
+        sProjection,
+        selection /* selection */,
+        selectionArgs /* selectionArgs */,
+        null /* sortOrder */
+    )?.use { cursor ->
+        if (cursor.count > 0) {
+            return context.resources.getString(R.string.error_duplicate_apn_entry)
+        }
+    }
+    return null
 }
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index e4cb603..6f39305 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -72,41 +72,38 @@
     val validEnabled: Boolean = false,
     val customizedConfig: CustomizedConfig = CustomizedConfig()
 ) {
+    fun getContentValueMap(context: Context): MutableMap<String, Any> {
+        val simCarrierId =
+            context.getSystemService(TelephonyManager::class.java)!!
+                .createForSubscriptionId(subId)
+                .getSimCarrierId()
+        return mutableMapOf(
+            Telephony.Carriers.NAME to name, Telephony.Carriers.APN to apn,
+            Telephony.Carriers.PROXY to proxy, Telephony.Carriers.PORT to port,
+            Telephony.Carriers.MMSPROXY to mmsProxy, Telephony.Carriers.MMSPORT to mmsPort,
+            Telephony.Carriers.USER to userName, Telephony.Carriers.SERVER to server,
+            Telephony.Carriers.PASSWORD to passWord, Telephony.Carriers.MMSC to mmsc,
+            Telephony.Carriers.AUTH_TYPE to authType,
+            Telephony.Carriers.PROTOCOL to convertOptions2Protocol(apnProtocol, context),
+            Telephony.Carriers.ROAMING_PROTOCOL to convertOptions2Protocol(apnRoaming, context),
+            Telephony.Carriers.TYPE to apnType,
+            Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
+            Telephony.Carriers.CARRIER_ENABLED to apnEnable,
+            Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
+            Telephony.Carriers.CARRIER_ID to simCarrierId
+        )
+    }
+
     fun getContentValues(context: Context): ContentValues {
         val values = ContentValues()
-        values.put(Telephony.Carriers.NAME, name)
-        values.put(Telephony.Carriers.APN, apn)
-        values.put(Telephony.Carriers.PROXY, proxy)
-        values.put(Telephony.Carriers.PORT, port)
-        values.put(Telephony.Carriers.MMSPROXY, mmsProxy)
-        values.put(Telephony.Carriers.MMSPORT, mmsPort)
-        values.put(Telephony.Carriers.USER, userName)
-        values.put(Telephony.Carriers.SERVER, server)
-        values.put(Telephony.Carriers.PASSWORD, passWord)
-        values.put(Telephony.Carriers.MMSC, mmsc)
-        values.put(Telephony.Carriers.AUTH_TYPE, authType)
-        values.put(Telephony.Carriers.PROTOCOL, convertOptions2Protocol(apnProtocol, context))
-        values.put(
-            Telephony.Carriers.ROAMING_PROTOCOL,
-            convertOptions2Protocol(apnRoaming, context)
-        )
-        values.put(Telephony.Carriers.TYPE, apnType)
-        values.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, networkType)
-        values.put(Telephony.Carriers.CARRIER_ENABLED, apnEnable)
-        values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED)
-        if (newApn) {
-            val simCarrierId =
-                context.getSystemService(TelephonyManager::class.java)!!
-                    .createForSubscriptionId(subId)
-                    .getSimCarrierId()
-            values.put(Telephony.Carriers.CARRIER_ID, simCarrierId)
-        }
+        val contentValueMap = getContentValueMap(context)
+        if (!newApn) contentValueMap.remove(Telephony.Carriers.CARRIER_ID)
+        contentValueMap.forEach { (key, value) -> values.putObject(key, value) }
         return values
     }
 }
 
 data class CustomizedConfig(
-    val newApn: Boolean = false,
     val readOnlyApn: Boolean = false,
     val isAddApnAllowed: Boolean = true,
     val readOnlyApnTypes: List<String> = emptyList(),
@@ -227,20 +224,14 @@
  */
 fun validateAndSaveApnData(
     apnDataInit: ApnData,
-    apnData: ApnData,
+    newApnData: ApnData,
     context: Context,
-    uriInit: Uri,
-    networkTypeSelectedOptionsState: SnapshotStateList<Int>
-): Boolean {
-    // Nothing to do if it's a read only APN
-    if (apnData.customizedConfig.readOnlyApn) {
-        return true
-    }
-    val errorMsg = validateApnData(apnData, context)
+    uriInit: Uri
+): String? {
+    val errorMsg = validateApnData(uriInit, newApnData, context)
     if (errorMsg != null) {
-        return false
+        return errorMsg
     }
-    val newApnData = apnData.copy(networkType = getNetworkType(networkTypeSelectedOptionsState))
     if (newApnData.newApn || (newApnData != apnDataInit)) {
         Log.d(TAG, "[validateAndSaveApnData] newApnData.networkType: ${newApnData.networkType}")
         updateApnDataToDatabase(
@@ -250,7 +241,7 @@
             uriInit
         )
     }
-    return true
+    return null
 }
 
 /**
@@ -258,7 +249,7 @@
  *
  * @return An error message if the apn data is invalid, otherwise return null.
  */
-fun validateApnData(apnData: ApnData, context: Context): String? {
+fun validateApnData(uri: Uri, apnData: ApnData, context: Context): String? {
     var errorMsg: String?
     val name = apnData.name
     val apn = apnData.apn
@@ -267,11 +258,14 @@
     } else if (apn == "") {
         context.resources.getString(R.string.error_apn_empty)
     } else {
-        validateMMSC(apnData.validEnabled, apnData.mmsc, context)
+        validateMMSC(true, apnData.mmsc, context)
+    }
+    if (errorMsg == null) {
+        errorMsg = isItemExist(uri, apnData, context)
     }
     if (errorMsg == null) {
         errorMsg = validateAPNType(
-            apnData.validEnabled,
+            true,
             apnData.apnType,
             apnData.customizedConfig.readOnlyApnTypes,
             context
diff --git a/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java b/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java
index 8823353..ebc5575 100644
--- a/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java
+++ b/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java
@@ -46,6 +46,7 @@
 
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
 import com.android.settings.network.SubscriptionUtil;
 
 import com.google.common.collect.ImmutableMap;
@@ -131,7 +132,7 @@
                 LayoutInflater.class);
         final View view = layoutInflater.inflate(R.layout.dialog_mobile_network_rename, null);
         populateView(view);
-        builder.setTitle(R.string.mobile_network_sim_name)
+        builder.setTitle(R.string.mobile_network_sim_label_color_title)
                 .setView(view)
                 .setPositiveButton(R.string.mobile_network_sim_name_rename, (dialog, which) -> {
                     mSubscriptionManager.setDisplayName(mNameView.getText().toString(), mSubId,
@@ -175,14 +176,23 @@
         mColorSpinner.setAdapter(adapter);
         mColorSpinner.setSelection(getSimColorIndex(info.getIconTint()));
 
+        if(Flags.isDualSimOnboardingEnabled()){
+            return;
+        }
+
+        final TextView operatorTitle = view.findViewById(R.id.operator_name_label);
+        operatorTitle.setVisibility(View.VISIBLE);
+
         final TextView operatorName = view.findViewById(R.id.operator_name_value);
         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
+        operatorName.setVisibility(View.VISIBLE);
         operatorName.setText(info.getCarrierName());
 
         final TextView phoneTitle = view.findViewById(R.id.number_label);
         phoneTitle.setVisibility(info.isOpportunistic() ? View.GONE : View.VISIBLE);
 
         final TextView phoneNumber = view.findViewById(R.id.number_value);
+        phoneNumber.setVisibility(View.VISIBLE);
         final String pn = SubscriptionUtil.getBidiFormattedPhoneNumber(getContext(), info);
         if (!TextUtils.isEmpty(pn)) {
             phoneNumber.setText(pn);
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index 69c4d9d..e9cce12 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -18,6 +18,7 @@
 
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
 
+import android.app.ActivityOptions;
 import android.app.AlertDialog;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
@@ -50,6 +51,8 @@
  */
 public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
     private static final String TAG = "PrivateSpaceAuthCheck";
+    public static final String EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED =
+            "extra_show_private_space_unlocked";
     private PrivateSpaceMaintainer mPrivateSpaceMaintainer;
     private KeyguardManager mKeyguardManager;
 
@@ -158,12 +161,19 @@
                         .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
                         .setSourceMetricsCategory(SettingsEnums.PRIVATE_SPACE_SETTINGS);
         if (mPrivateSpaceMaintainer.isPrivateSpaceLocked()) {
+            ActivityOptions options =
+                    ActivityOptions.makeBasic()
+                            .setPendingIntentCreatorBackgroundActivityStartMode(
+                                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
             mPrivateSpaceMaintainer.unlockPrivateSpace(
                     PendingIntent.getActivity(
                                     context, /* requestCode */
                                     0,
-                                    privateSpaceSettings.toIntent(),
-                                    PendingIntent.FLAG_IMMUTABLE)
+                                    privateSpaceSettings
+                                            .toIntent()
+                                            .putExtra(EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED, true),
+                                    PendingIntent.FLAG_IMMUTABLE,
+                                    options.toBundle())
                             .getIntentSender());
         } else {
             privateSpaceSettings.launch();
diff --git a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
index ed4df97..e426477 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
@@ -16,20 +16,29 @@
 
 package com.android.settings.privatespace;
 
+import static com.android.settings.privatespace.PrivateSpaceAuthenticationActivity.EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED;
+
 import android.app.settings.SettingsEnums;
 import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 
 /** Fragment representing the Private Space dashboard in Settings. */
 public class PrivateSpaceDashboardFragment extends DashboardFragment {
-    private static final String TAG = "PrivateSpaceDashboardFragment";
+    private static final String TAG = "PSDashboardFragment";
 
     @Override
     public void onCreate(Bundle icicle) {
         if (android.os.Flags.allowPrivateProfile()) {
             super.onCreate(icicle);
+            if (getIntent().getBooleanExtra(EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED, false)) {
+                Log.i(TAG, "Private space unlocked showing toast");
+                Toast.makeText(getContext(), R.string.private_space_unlocked, Toast.LENGTH_SHORT)
+                        .show();
+            }
         }
     }
 
diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java
index cc22b87..2a5ff88 100644
--- a/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java
+++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java
@@ -20,8 +20,13 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
 
+import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.biometrics.combination.BiometricFaceStatusPreferenceController;
 import com.android.settings.privatespace.PrivateSpaceMaintainer;
 
@@ -62,4 +67,25 @@
                 ? AVAILABLE
                 : UNSUPPORTED_ON_DEVICE;
     }
+
+    @Override
+    public void updateState(@NonNull Preference preference) {
+        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(getUserId())) {
+            super.updateState(preference);
+            preference.setEnabled(true);
+        } else {
+            preference.setSummary(
+                    mContext.getString(R.string.lock_settings_profile_unified_summary));
+            preference.setEnabled(false);
+        }
+    }
+
+    @Override
+    public void displayPreference(@NonNull PreferenceScreen screen) {
+        super.displayPreference(screen);
+        Preference preference = screen.findPreference(getPreferenceKey());
+        if (!Utils.isMultipleBiometricsSupported(mContext)) {
+            preference.setTitle(R.string.private_space_face_title);
+        }
+    }
 }
diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java
index f2f0801..b6c4457 100644
--- a/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java
+++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java
@@ -20,8 +20,13 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
 
+import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.biometrics.combination.BiometricFingerprintStatusPreferenceController;
 import com.android.settings.privatespace.PrivateSpaceMaintainer;
 
@@ -64,4 +69,25 @@
                 ? AVAILABLE
                 : UNSUPPORTED_ON_DEVICE;
     }
+
+    @Override
+    public void updateState(@NonNull Preference preference) {
+        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(getUserId())) {
+            super.updateState(preference);
+            preference.setEnabled(true);
+        } else {
+            preference.setSummary(
+                    mContext.getString(R.string.lock_settings_profile_unified_summary));
+            preference.setEnabled(false);
+        }
+    }
+
+    @Override
+    public void displayPreference(@NonNull PreferenceScreen screen) {
+        super.displayPreference(screen);
+        Preference preference = screen.findPreference(getPreferenceKey());
+        if (!Utils.isMultipleBiometricsSupported(mContext)) {
+            preference.setTitle(R.string.private_space_fingerprint_title);
+        }
+    }
 }
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java
index 6af6c38..413e02a 100644
--- a/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java
@@ -24,6 +24,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.privatespace.PrivateSpaceMaintainer;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -71,7 +72,17 @@
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new UseOneLockControllerSwitch(context, this));
         controllers.add(new PrivateSpaceLockController(context, this));
-        controllers.add(new FaceFingerprintUnlockController(context, getSettingsLifecycle()));
+        if (Utils.isMultipleBiometricsSupported(context)) {
+            controllers.add(new FaceFingerprintUnlockController(context, getSettingsLifecycle()));
+        } else if (Utils.hasFingerprintHardware(context)) {
+            controllers.add(
+                    new PrivateSpaceFingerprintPreferenceController(
+                            context, "private_space_biometrics", getSettingsLifecycle()));
+        } else if (Utils.hasFaceHardware(context)) {
+            controllers.add(
+                    new PrivateSpaceFacePreferenceController(
+                            context, "private_space_biometrics", getSettingsLifecycle()));
+        }
         return controllers;
     }
 
diff --git a/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
index e4fb1ea..38a8499 100644
--- a/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
@@ -104,7 +104,7 @@
             userHandle
         )
         try {
-            packageInstaller.requestArchive(app.packageName, pendingIntent.intentSender, 0)
+            packageInstaller.requestArchive(app.packageName, pendingIntent.intentSender)
         } catch (e: Exception) {
             Log.e(LOG_TAG, "Request archive failed", e)
             Toast.makeText(
diff --git a/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
index 1225806..aafe493 100644
--- a/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
+++ b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
@@ -20,12 +20,24 @@
 import android.app.AppOpsManager
 import android.app.settings.SettingsEnums
 import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalContext
 import com.android.settings.R
 import com.android.settings.overlay.FeatureFactory
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spaprivileged.model.app.AppOpsController
 import com.android.settingslib.spaprivileged.model.app.PackageManagers.hasGrantPermission
 import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
 import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
 import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+import kotlinx.coroutines.Dispatchers
 
 /**
  * This class builds an App List under voice activation apps and the individual page which
@@ -44,15 +56,97 @@
     override val appOp = AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
     override val permission = Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO
     override val setModeByUid = true
-
+    private var receiveDetectionTrainingDataOpController:AppOpsController? = null
     override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
         super.setAllowed(record, newAllowed)
+        if (!newAllowed && receiveDetectionTrainingDataOpController != null) {
+            receiveDetectionTrainingDataOpController!!.setAllowed(false)
+        }
         logPermissionChange(newAllowed)
     }
 
     override fun isChangeable(record: AppOpPermissionRecord): Boolean =
         super.isChangeable(record) && record.app.hasGrantPermission(permission)
 
+    @Composable
+    override fun InfoPageAdditionalContent(
+        record: AppOpPermissionRecord,
+        isAllowed: () -> Boolean?,
+    ) {
+        SwitchPreference(createReceiveDetectionTrainingDataOpSwitchModel(record, isAllowed))
+    }
+
+    @Composable
+    private fun createReceiveDetectionTrainingDataOpSwitchModel(
+        record: AppOpPermissionRecord,
+        isReceiveSandBoxTriggerAudioOpAllowed: () -> Boolean?
+    ): ReceiveDetectionTrainingDataOpSwitchModel {
+        val context = LocalContext.current
+        receiveDetectionTrainingDataOpController = remember {
+            AppOpsController(
+                context = context,
+                app = record.app,
+                op = AppOpsManager.OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+            )
+        }
+        val isReceiveDetectionTrainingDataOpAllowed = isReceiveDetectionTrainingDataOpAllowed(record, receiveDetectionTrainingDataOpController!!)
+
+        return remember(record) {
+            ReceiveDetectionTrainingDataOpSwitchModel(
+                context,
+                record,
+                isReceiveSandBoxTriggerAudioOpAllowed,
+                receiveDetectionTrainingDataOpController!!,
+                isReceiveDetectionTrainingDataOpAllowed,
+            )
+        }.also { model -> LaunchedEffect(model, Dispatchers.Default) { model.initState() } }
+    }
+
+    private inner class ReceiveDetectionTrainingDataOpSwitchModel(
+        context: Context,
+        private val record: AppOpPermissionRecord,
+        isReceiveSandBoxTriggerAudioOpAllowed: () -> Boolean?,
+        receiveDetectionTrainingDataOpController: AppOpsController,
+        isReceiveDetectionTrainingDataOpAllowed: () -> Boolean?,
+    ) : SwitchPreferenceModel {
+        private var appChangeable by mutableStateOf(true)
+
+        override val title: String = context.getString(R.string.permit_receive_sandboxed_detection_training_data)
+        override val summary: () -> String = { context.getString(R.string.receive_sandboxed_detection_training_data_description) }
+        override val checked = { isReceiveDetectionTrainingDataOpAllowed() == true && isReceiveSandBoxTriggerAudioOpAllowed() == true }
+        override val changeable = { appChangeable && isReceiveSandBoxTriggerAudioOpAllowed() == true }
+
+        fun initState() {
+            appChangeable = isChangeable(record)
+        }
+
+        override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
+            receiveDetectionTrainingDataOpController.setAllowed(newChecked)
+        }
+    }
+
+    @Composable
+    private fun isReceiveDetectionTrainingDataOpAllowed(
+        record: AppOpPermissionRecord,
+        controller: AppOpsController
+    ): () -> Boolean? {
+        if (record.hasRequestBroaderPermission) {
+            // Broader permission trumps the specific permission.
+            return { true }
+        }
+
+        val mode = controller.mode.observeAsState()
+        return {
+            when (mode.value) {
+                null -> null
+                AppOpsManager.MODE_ALLOWED -> true
+                AppOpsManager.MODE_DEFAULT -> record.app.hasGrantPermission(
+                    Manifest.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA)
+                else -> false
+            }
+        }
+    }
+
     private fun logPermissionChange(newAllowed: Boolean) {
         val category = when {
             newAllowed -> SettingsEnums.APP_SPECIAL_PERMISSION_RECEIVE_SANDBOX_TRIGGER_AUDIO_ALLOW
diff --git a/tests/robotests/src/com/android/settings/DisplaySettingsTest.java b/tests/robotests/src/com/android/settings/DisplaySettingsTest.java
index e251669..1259821 100644
--- a/tests/robotests/src/com/android/settings/DisplaySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/DisplaySettingsTest.java
@@ -4,15 +4,14 @@
 
 import android.content.Context;
 
+import androidx.test.core.app.ApplicationProvider;
+
 import com.android.settings.testutils.XmlTestUtils;
-import com.android.settings.testutils.shadow.ShadowPowerManager;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -21,9 +20,8 @@
 public class DisplaySettingsTest {
 
     @Test
-    @Config(shadows = ShadowPowerManager.class)
     public void testPreferenceControllers_getPreferenceKeys_existInPreferenceScreen() {
-        final Context context = RuntimeEnvironment.application;
+        final Context context = ApplicationProvider.getApplicationContext();
         final DisplaySettings fragment = new DisplaySettings();
         final List<String> preferenceScreenKeys = XmlTestUtils.getKeysFromPreferenceXml(context,
                 fragment.getPreferenceScreenResId());
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
index 85d1b78..1d76806 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
@@ -43,6 +43,7 @@
 import android.os.UserManager;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
@@ -189,11 +190,13 @@
                 mPackageManager, mApp6)).isFalse();
     }
 
+    @Ignore("b/313578776")
     @Test
     public void testCountInstalledAppsAcrossAllUsersSync() {
         testCountInstalledAppsAcrossAllUsers(false /* async */);
     }
 
+    @Ignore("b/313578776")
     @Test
     public void testCountInstalledAppsAcrossAllUsersAsync() {
         testCountInstalledAppsAcrossAllUsers(true /* async */);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsExtraOptionsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsExtraOptionsControllerTest.java
new file mode 100644
index 0000000..f063020
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsExtraOptionsControllerTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreferenceCompat;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowLooper;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothDetailsExtraOptionsControllerTest extends BluetoothDetailsControllerTestBase {
+
+    private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
+    @Mock private BluetoothDevice mBluetoothDevice;
+    @Mock private Lifecycle mExtraOptionsLifecycle;
+    @Mock private PreferenceCategory mOptionsContainer;
+    @Mock private PreferenceScreen mPreferenceScreen;
+
+    private BluetoothDetailsExtraOptionsController mController;
+    private BluetoothFeatureProvider mFeatureProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
+        when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
+
+        final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mFeatureProvider = fakeFeatureFactory.getBluetoothFeatureProvider();
+
+        mController =
+                new BluetoothDetailsExtraOptionsController(
+                        mContext, mFragment, mCachedDevice, mExtraOptionsLifecycle);
+    }
+
+    @Test
+    public void displayPreference_removeAndAddPreferences() {
+        Preference preference1 = new SwitchPreferenceCompat(mContext);
+        Preference preference2 = new SwitchPreferenceCompat(mContext);
+        when(mFeatureProvider.getBluetoothExtraOptions(mContext, mCachedDevice))
+                .thenReturn(ImmutableList.of(preference1, preference2));
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+                .thenReturn(mOptionsContainer);
+
+        mController.displayPreference(mPreferenceScreen);
+        ShadowLooper.idleMainLooper();
+
+        verify(mOptionsContainer).removeAll();
+        verify(mOptionsContainer).addPreference(preference1);
+        verify(mOptionsContainer).addPreference(preference2);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
index 78fb23f..7d2a7af 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
@@ -27,7 +27,7 @@
 import java.util.Set;
 
 @Implements(NotificationManager.class)
-public class ShadowNotificationManager {
+public class ShadowNotificationManager extends org.robolectric.shadows.ShadowNotificationManager {
 
     private int mZenMode;
     private ZenModeConfig mZenModeConfig;
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowParcel.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowParcel.java
index 665e415..0a9d20d 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowParcel.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowParcel.java
@@ -9,7 +9,7 @@
  * This class provides helpers to test logic that reads from parcels.
  */
 @Implements(Parcel.class)
-public class ShadowParcel {
+public class ShadowParcel extends org.robolectric.shadows.ShadowParcel {
 
     public static int sReadIntResult;
     public static int sWriteIntResult;
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerManager.java
deleted file mode 100644
index 9a4993e..0000000
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPowerManager.java
+++ /dev/null
@@ -1,38 +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.testutils.shadow;
-
-import android.os.PowerManager;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-@Implements(PowerManager.class)
-public class ShadowPowerManager {
-
-    @Implementation
-    protected int getMinimumScreenBrightnessSetting() {
-        return 0;
-    }
-
-    @Implementation
-    protected int getMaximumScreenBrightnessSetting() {
-        return 0;
-    }
-
-}
diff --git a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java
index dfd0988..acb1dd8 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.testutils.shadow;
 
-import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.annotation.Resetter;
@@ -25,7 +24,7 @@
 import java.util.Map;
 
 @Implements(android.provider.DeviceConfig.class)
-public class ShadowDeviceConfig {
+public class ShadowDeviceConfig extends org.robolectric.shadows.ShadowDeviceConfig {
 
     private static Map<String, String> sPropertyMaps = new HashMap<>();
 
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
index 6b4cc0d..2afb3f1 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
@@ -136,8 +136,7 @@
 
         verify(packageInstaller).requestArchive(
             eq(PACKAGE_NAME),
-            any(),
-            eq(0)
+            any()
         )
     }
 
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImplTest.kt b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImplTest.kt
new file mode 100644
index 0000000..788c907
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImplTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl
+import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeatureImpl
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidJUnit4::class)
+class FingerprintFeatureProviderImplTest {
+
+    @Mock
+    private lateinit var mContext: Context
+
+    private lateinit var mFingerprintFeatureProviderImpl: FingerprintFeatureProviderImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mFingerprintFeatureProviderImpl = FingerprintFeatureProviderImpl()
+    }
+
+    @Test
+    fun getSfpsEnrollmentFeature_returnDefaultImpl() {
+        assertThat(mFingerprintFeatureProviderImpl.sfpsEnrollmentFeature)
+            .isInstanceOf(SfpsEnrollmentFeatureImpl::class.java)
+    }
+
+    @Test
+    fun getSfpsRestToUnlockFeature_returnDefaultImpl() {
+        assertThat(mFingerprintFeatureProviderImpl.getSfpsRestToUnlockFeature(mContext))
+            .isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
+    }
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeatureImplTest.kt b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeatureImplTest.kt
new file mode 100644
index 0000000..1f390d4
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsRestToUnlockFeatureImplTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint.feature
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.RestrictedSwitchPreference
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@RunWith(AndroidJUnit4::class)
+class SfpsRestToUnlockFeatureImplTest {
+
+    private lateinit var mContext: Context
+
+    private lateinit var mSfpsRestToUnlockFeatureImpl: SfpsRestToUnlockFeatureImpl
+
+    private lateinit var mRestrictedSwitchPreferenceSpy: RestrictedSwitchPreference
+
+    @Before
+    fun setUp() {
+        mContext = ApplicationProvider.getApplicationContext()
+        mSfpsRestToUnlockFeatureImpl = SfpsRestToUnlockFeatureImpl()
+        mRestrictedSwitchPreferenceSpy = Mockito.spy(RestrictedSwitchPreference(mContext))
+    }
+
+    @Test
+    fun getDescriptionForSfps_isNotNull() {
+        assertThat(mSfpsRestToUnlockFeatureImpl)
+            .isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
+        assertThat(mSfpsRestToUnlockFeatureImpl.getDescriptionForSfps(mContext))
+            .isNotNull()
+    }
+
+    @Test
+    fun getRestToUnlockLayout_isNull() {
+        assertThat(mSfpsRestToUnlockFeatureImpl)
+            .isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
+        assertThat(mSfpsRestToUnlockFeatureImpl.getRestToUnlockLayout(mContext))
+            .isNull()
+    }
+
+    @Test
+    fun fingerprint_settings_setupFingerprintUnlockCategoryPreferences() {
+        assertThat(mSfpsRestToUnlockFeatureImpl.getRestToUnlockPreference(mContext))
+            .isNull()
+    }
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java b/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java
index dc4f56a..1d93e88 100644
--- a/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java
@@ -91,13 +91,13 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_ANIMATIONS)
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_ANIMS)
     public void controllerNotAvailable_whenAconfigFlagEnabled() {
         assertFalse(mController.isAvailable());
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_ANIMATIONS)
+    @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_ANIMS)
     public void controllerAvailable_whenAconfigFlagDisabled() {
         assertTrue(mController.isAvailable());
     }