Merge "Add SettingsLintDefaults to Settings-core" into main
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/values/strings.xml b/res/values/strings.xml
index ff59f6a..35c1759 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1231,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] -->
@@ -1281,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] -->
@@ -9607,6 +9613,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/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/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/specialaccess/VoiceActivationApps.kt b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
index 1225806..a7f9714 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
@@ -53,6 +65,84 @@
     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
+        val 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/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());
     }