Merge "Reset the dynamic set denylist after reboot the device" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 75c6fbb..26386c5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3389,16 +3389,6 @@
                 <action android:name="android.settings.USER_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-            <intent-filter>
-                <action android:name="com.android.settings.action.SETTINGS" />
-            </intent-filter>
-            <meta-data android:name="com.android.settings.order" android:value="-45"/>
-            <meta-data android:name="com.android.settings.category"
-                       android:value="com.android.settings.category.ia.system" />
-            <meta-data android:name="com.android.settings.summary_uri"
-                       android:value="content://com.android.settings.dashboard.SummaryProvider/user" />
-            <meta-data android:name="com.android.settings.icon"
-                       android:resource="@drawable/ic_settings_multiuser" />
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                        android:value="com.android.settings.users.UserSettings" />
             <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml
index 1f5559f..54dc195 100644
--- a/res/xml/system_dashboard_fragment.xml
+++ b/res/xml/system_dashboard_fragment.xml
@@ -93,6 +93,14 @@
     </Preference>
 
     <Preference
+        android:key="system_multiuser"
+        android:title="@string/user_settings_title"
+        android:icon="@drawable/ic_settings_multiuser"
+        android:order="-45"
+        android:fragment="com.android.settings.users.UserSettings"
+        settings:controller="com.android.settings.users.MultiUserPreferenceController"/>
+
+    <Preference
         android:key="reset_dashboard"
         android:title="@string/reset_dashboard_title"
         android:icon="@drawable/ic_restore"
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 4c20231..3b0dd40 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -781,12 +781,6 @@
                 Utils.isBandwidthControlEnabled(), isAdmin)
                 || somethingChanged;
 
-        somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
-                        Settings.UserSettingsActivity.class.getName()),
-                UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
-                        && !Utils.isMonkeyRunning(), isAdmin)
-                || somethingChanged;
-
         final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
                 && !Utils.isMonkeyRunning();
         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index eeca139..f7e341b 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1209,7 +1209,9 @@
         for (UserHandle userHandle : profiles) {
             UserProperties userProperties = userManager.getUserProperties(userHandle);
             if (userProperties.getShowInSettings() == UserProperties.SHOW_IN_SETTINGS_SEPARATE) {
-                if (Flags.allowPrivateProfile() && userProperties.getHideInSettingsInQuietMode()) {
+                if (Flags.allowPrivateProfile()
+                        && userProperties.getShowInQuietMode()
+                        == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
                     if (!userManager.isQuietModeEnabled(userHandle)) {
                         return true;
                     } else {
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index b2cdb77..3e6feb7 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -42,6 +42,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -312,7 +313,8 @@
                             && !(Flags.allowPrivateProfile() && profile.isPrivateProfile())
                             && (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0))
                         && !(mUm.getUserProperties(profile.getUserHandle())
-                            .getHideInSettingsInQuietMode() && profile.isQuietModeEnabled())) {
+                            .getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
+                            && profile.isQuietModeEnabled())) {
                     updateProfileUi(profile);
                 }
             }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
index 9ebe26d..21f1c0e 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
@@ -64,7 +64,7 @@
         mPreference.setVisible(isVisible);
     }
 
-    private boolean isBroadcasting() {
+    protected boolean isBroadcasting() {
         return mBroadcast != null && mBroadcast.isEnabled(null);
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
index cf5881b..7f90ceb 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
@@ -22,6 +22,7 @@
 
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.widget.SettingsMainSwitchBar;
 
@@ -34,6 +35,7 @@
     private AudioSharingDeviceVolumeGroupController mAudioSharingDeviceVolumeGroupController;
     private CallsAndAlarmsPreferenceController mCallsAndAlarmsPreferenceController;
     private AudioSharingNamePreferenceController mAudioSharingNamePreferenceController;
+    private AudioStreamsCategoryController mAudioStreamsCategoryController;
 
     public AudioSharingDashboardFragment() {
         super();
@@ -73,6 +75,7 @@
         mCallsAndAlarmsPreferenceController = use(CallsAndAlarmsPreferenceController.class);
         mCallsAndAlarmsPreferenceController.init(this);
         mAudioSharingNamePreferenceController = use(AudioSharingNamePreferenceController.class);
+        mAudioStreamsCategoryController = use(AudioStreamsCategoryController.class);
     }
 
     @Override
@@ -98,5 +101,6 @@
         mAudioSharingDeviceVolumeGroupController.updateVisibility(isVisible);
         mCallsAndAlarmsPreferenceController.updateVisibility(isVisible);
         mAudioSharingNamePreferenceController.updateVisibility(isVisible);
+        mAudioStreamsCategoryController.updateVisibility(isVisible);
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index 1855667..7a2f26f 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -160,6 +160,7 @@
                                     + ", reason = "
                                     + reason);
                     mBluetoothDeviceUpdater.forceUpdate();
+                    AudioSharingUtils.updateActiveDeviceIfNeeded(mLocalBtManager);
                 }
 
                 @Override
@@ -204,6 +205,7 @@
                                     + ", reason = "
                                     + reason);
                     mBluetoothDeviceUpdater.forceUpdate();
+                    AudioSharingUtils.updateActiveDeviceIfNeeded(mLocalBtManager);
                 }
 
                 @Override
@@ -299,7 +301,7 @@
         mPreferenceGroup.setVisible(false);
         mAudioSharingSettingsPreference.setVisible(false);
 
-        if (isAvailable() && mBluetoothDeviceUpdater != null) {
+        if (isAvailable()) {
             mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
             mBluetoothDeviceUpdater.forceUpdate();
         }
@@ -309,6 +311,7 @@
     public int getAvailabilityStatus() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
                         && Flags.enableLeAudioSharing()
+                        && mBluetoothDeviceUpdater != null
                 ? AVAILABLE_UNSEARCHABLE
                 : UNSUPPORTED_ON_DEVICE;
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 58ff322..be02519 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -192,6 +192,7 @@
                                     + sourceId
                                     + ", reason = "
                                     + reason);
+                    AudioSharingUtils.updateActiveDeviceIfNeeded(mBtManager);
                 }
 
                 @Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
index 53e095b..a43e098 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.connecteddevice.audiosharing;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
@@ -34,6 +35,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 public class AudioSharingUtils {
@@ -89,11 +91,11 @@
      * @return A list of ordered connected devices eligible for the audio sharing. The active device
      *     is placed in the first place if it exists.
      */
-    public static ArrayList<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
+    public static List<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
             LocalBluetoothManager localBtManager,
             Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
             boolean filterByInSharing) {
-        ArrayList<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
+        List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
         LocalBluetoothLeBroadcastAssistant assistant =
                 localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
         if (assistant == null) return orderedDevices;
@@ -229,9 +231,62 @@
         return false;
     }
 
+    /**
+     * Retrieves the one and only active Bluetooth LE Audio sink device, regardless if the device is
+     * currently in an audio sharing session.
+     *
+     * @param manager The LocalBluetoothManager instance used to fetch connected devices.
+     * @return An Optional containing the active LE Audio device, or an empty Optional if not found.
+     */
+    public static Optional<CachedBluetoothDevice> getActiveSinkOnAssistant(
+            LocalBluetoothManager manager) {
+        if (manager == null) {
+            Log.w(TAG, "getActiveSinksOnAssistant(): LocalBluetoothManager is null!");
+            return Optional.empty();
+        }
+        var groupedDevices = AudioSharingUtils.fetchConnectedDevicesByGroupId(manager);
+        var leadDevices =
+                AudioSharingUtils.buildOrderedConnectedLeadDevices(manager, groupedDevices, false);
+
+        if (!leadDevices.isEmpty() && AudioSharingUtils.isActiveLeAudioDevice(leadDevices.get(0))) {
+            return Optional.of(leadDevices.get(0));
+        } else {
+            Log.w(TAG, "getActiveSinksOnAssistant(): No active lead device!");
+        }
+        return Optional.empty();
+    }
+
     /** Toast message on main thread. */
     public static void toastMessage(Context context, String message) {
         ThreadUtils.postOnMainThread(
                 () -> Toast.makeText(context, message, Toast.LENGTH_LONG).show());
     }
+
+    /** Automatically update active device if needed. */
+    public static void updateActiveDeviceIfNeeded(LocalBluetoothManager localBtManager) {
+        if (localBtManager == null) return;
+        Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices =
+                fetchConnectedDevicesByGroupId(localBtManager);
+        List<CachedBluetoothDevice> devicesInSharing =
+                buildOrderedConnectedLeadDevices(
+                        localBtManager, groupedConnectedDevices, /* filterByInSharing= */ true);
+        if (devicesInSharing.isEmpty()) return;
+        List<BluetoothDevice> devices =
+                BluetoothAdapter.getDefaultAdapter().getMostRecentlyConnectedDevices();
+        CachedBluetoothDevice targetDevice = null;
+        int targetDeviceIdx = -1;
+        for (CachedBluetoothDevice device : devicesInSharing) {
+            if (devices.contains(device.getDevice())) {
+                int idx = devices.indexOf(device.getDevice());
+                if (idx > targetDeviceIdx) {
+                    targetDeviceIdx = idx;
+                    targetDevice = device;
+                }
+            }
+        }
+        if (targetDevice != null && !isActiveLeAudioDevice(targetDevice)) {
+            Log.d(TAG, "Set active device: " + targetDevice.getDevice().getAnonymizedAddress());
+            targetDevice.setActive();
+        }
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java
index f1119fc..d001409 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceController.java
@@ -25,10 +25,10 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.core.BasePreferenceController;
-import com.android.settings.widget.SummaryUpdater;
 
 public class AudioStreamsActiveDeviceController extends BasePreferenceController
-        implements SummaryUpdater.OnSummaryChangeListener, DefaultLifecycleObserver {
+        implements AudioStreamsActiveDeviceSummaryUpdater.OnSummaryChangeListener,
+                DefaultLifecycleObserver {
 
     public static final String KEY = "audio_streams_active_device";
     private final AudioStreamsActiveDeviceSummaryUpdater mSummaryHelper;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
index 66101f7..0d3b1b1 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
@@ -19,28 +19,28 @@
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
-import com.android.settings.widget.SummaryUpdater;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.Optional;
-
-public class AudioStreamsActiveDeviceSummaryUpdater extends SummaryUpdater
-        implements BluetoothCallback {
-    private static final String TAG = "AudioStreamsListenWithSummaryUpdater";
+public class AudioStreamsActiveDeviceSummaryUpdater implements BluetoothCallback {
+    private static final String TAG = "AudioStreamsActiveDeviceSummaryUpdater";
     private static final boolean DEBUG = BluetoothUtils.D;
     private final LocalBluetoothManager mBluetoothManager;
+    private String mSummary;
+    private OnSummaryChangeListener mListener;
 
     public AudioStreamsActiveDeviceSummaryUpdater(
             Context context, OnSummaryChangeListener listener) {
-        super(context, listener);
         mBluetoothManager = Utils.getLocalBluetoothManager(context);
+        mListener = listener;
     }
 
     @Override
@@ -59,8 +59,7 @@
         }
     }
 
-    @Override
-    public void register(boolean register) {
+    void register(boolean register) {
         if (register) {
             notifyChangeIfNeeded();
             mBluetoothManager.getEventManager().registerCallback(this);
@@ -69,30 +68,32 @@
         }
     }
 
-    @Override
-    protected String getSummary() {
-        var activeSink = getActiveSinkOnAssistant(mBluetoothManager);
+    private void notifyChangeIfNeeded() {
+        ThreadUtils.postOnBackgroundThread(
+                () -> {
+                    String summary = getSummary();
+                    if (!TextUtils.equals(mSummary, summary)) {
+                        mSummary = summary;
+                        ThreadUtils.postOnMainThread(() -> mListener.onSummaryChanged(summary));
+                    }
+                });
+    }
+
+    private String getSummary() {
+        var activeSink = AudioSharingUtils.getActiveSinkOnAssistant(mBluetoothManager);
         if (activeSink.isEmpty()) {
             return "No active LE Audio device";
         }
         return activeSink.get().getName();
     }
 
-    private static Optional<CachedBluetoothDevice> getActiveSinkOnAssistant(
-            LocalBluetoothManager manager) {
-        if (manager == null) {
-            Log.w(TAG, "getActiveSinksOnAssistant(): LocalBluetoothManager is null!");
-            return Optional.empty();
-        }
-        var groupedDevices = AudioSharingUtils.fetchConnectedDevicesByGroupId(manager);
-        var leadDevices =
-                AudioSharingUtils.buildOrderedConnectedLeadDevices(manager, groupedDevices, false);
-
-        if (!leadDevices.isEmpty() && AudioSharingUtils.isActiveLeAudioDevice(leadDevices.get(0))) {
-            return Optional.of(leadDevices.get(0));
-        } else {
-            Log.w(TAG, "getActiveSinksOnAssistant(): No active lead device!");
-        }
-        return Optional.empty();
+    /** Interface definition for a callback to be invoked when the summary has been changed. */
+    interface OnSummaryChangeListener {
+        /**
+         * Called when summary has changed.
+         *
+         * @param summary The new summary.
+         */
+        void onSummaryChanged(String summary);
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
index 84a7be9..f80fdab 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
@@ -16,15 +16,64 @@
 
 package com.android.settings.connecteddevice.audiosharing.audiostreams;
 
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingBasePreferenceController;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
 import com.android.settings.flags.Flags;
-import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.utils.ThreadUtils;
 
-public class AudioStreamsCategoryController extends PreferenceCategoryController {
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class AudioStreamsCategoryController extends AudioSharingBasePreferenceController
+        implements DefaultLifecycleObserver {
+    private static final String TAG = "AudioStreamsCategoryController";
+    private static final boolean DEBUG = BluetoothUtils.D;
+    private final LocalBluetoothManager mLocalBtManager;
+    private final Executor mExecutor;
+    private final BluetoothCallback mBluetoothCallback =
+            new BluetoothCallback() {
+                @Override
+                public void onActiveDeviceChanged(
+                        @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+                    if (bluetoothProfile == BluetoothProfile.LE_AUDIO) {
+                        updateVisibility(isBroadcasting());
+                    }
+                }
+            };
 
     public AudioStreamsCategoryController(Context context, String key) {
         super(context, key);
+        mLocalBtManager = Utils.getLocalBtManager(mContext);
+        mExecutor = Executors.newSingleThreadExecutor();
+    }
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        if (mLocalBtManager != null) {
+            mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
+        }
+        updateVisibility(isBroadcasting());
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        if (mLocalBtManager != null) {
+            mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
+        }
     }
 
     @Override
@@ -33,4 +82,23 @@
                 ? AVAILABLE
                 : UNSUPPORTED_ON_DEVICE;
     }
+
+    @Override
+    public void updateVisibility(boolean isBroadcasting) {
+        mExecutor.execute(
+                () -> {
+                    boolean hasActiveLe =
+                            AudioSharingUtils.getActiveSinkOnAssistant(mLocalBtManager).isPresent();
+                    if (DEBUG) {
+                        Log.d(
+                                TAG,
+                                "updateVisibility() isBroadcasting : "
+                                        + isBroadcasting
+                                        + " hasActiveLe : "
+                                        + hasActiveLe);
+                    }
+                    ThreadUtils.postOnMainThread(
+                            () -> super.updateVisibility(hasActiveLe && !isBroadcasting));
+                });
+    }
 }
diff --git a/src/com/android/settings/dashboard/SummaryProvider.java b/src/com/android/settings/dashboard/SummaryProvider.java
index 6acc663..cd388b9 100644
--- a/src/com/android/settings/dashboard/SummaryProvider.java
+++ b/src/com/android/settings/dashboard/SummaryProvider.java
@@ -20,15 +20,10 @@
 
 import android.content.ContentProvider;
 import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
 
-import com.android.settings.R;
 import com.android.settings.backup.BackupSettingsHelper;
 
 /** Provide preference summary for injected items. */
@@ -44,14 +39,6 @@
                 bundle.putString(META_DATA_PREFERENCE_SUMMARY,
                         new BackupSettingsHelper(getContext()).getSummary());
                 break;
-            case USER:
-                final Context context = getContext();
-                final UserInfo info = context.getSystemService(UserManager.class).getUserInfo(
-                        UserHandle.myUserId());
-                bundle.putString(META_DATA_PREFERENCE_SUMMARY,
-                        context.getString(R.string.users_summary,
-                                info.name));
-                break;
             default:
                 throw new IllegalArgumentException("Unknown Uri format: " + uri);
         }
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
index 561a51a..eaad7f3 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
@@ -235,7 +235,7 @@
     private static boolean shouldHideUserInQuietMode(
             UserHandle userHandle, UserManager userManager) {
         UserProperties userProperties = userManager.getUserProperties(userHandle);
-        return userProperties.getHideInSettingsInQuietMode()
+        return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
                 && userManager.isQuietModeEnabled(userHandle);
     }
 }
diff --git a/src/com/android/settings/inputmethod/SpellCheckerPreference.java b/src/com/android/settings/inputmethod/SpellCheckerPreference.java
index 8c8942a..b8028fe 100644
--- a/src/com/android/settings/inputmethod/SpellCheckerPreference.java
+++ b/src/com/android/settings/inputmethod/SpellCheckerPreference.java
@@ -25,6 +25,7 @@
 import android.view.View.OnClickListener;
 import android.view.textservice.SpellCheckerInfo;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog.Builder;
 import androidx.preference.PreferenceViewHolder;
 
@@ -41,11 +42,15 @@
 class SpellCheckerPreference extends CustomListPreference {
 
     private final SpellCheckerInfo[] mScis;
-    private Intent mIntent;
+    @VisibleForTesting
+    Intent mIntent;
 
     public SpellCheckerPreference(final Context context, final SpellCheckerInfo[] scis) {
         super(context, null);
         mScis = scis;
+        setLayoutResource(
+                com.android.settingslib.widget.preference.twotarget.R.layout.preference_two_target);
+
         setWidgetLayoutResource(R.layout.preference_widget_gear);
         if (scis == null) {
             return;
@@ -108,14 +113,26 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder view) {
         super.onBindViewHolder(view);
+        final View divider = view.findViewById(
+                com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider);
+        final View widgetFrame = view.findViewById(android.R.id.widget_frame);
+        if (divider != null) {
+            divider.setVisibility(mIntent != null ? View.VISIBLE : View.GONE);
+        }
+        if (widgetFrame != null) {
+            widgetFrame.setVisibility(mIntent != null ? View.VISIBLE : View.GONE);
+        }
+
         View settingsButton = view.findViewById(R.id.settings_button);
-        settingsButton.setVisibility(mIntent != null ? View.VISIBLE : View.INVISIBLE);
-        settingsButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onSettingsButtonClicked();
-            }
-        });
+        if (settingsButton != null) {
+            settingsButton.setVisibility(mIntent != null ? View.VISIBLE : View.INVISIBLE);
+            settingsButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    onSettingsButtonClicked();
+                }
+            });
+        }
     }
 
     private void onSettingsButtonClicked() {
diff --git a/src/com/android/settings/users/MultiUserPreferenceController.java b/src/com/android/settings/users/MultiUserPreferenceController.java
new file mode 100644
index 0000000..2eb57ec
--- /dev/null
+++ b/src/com/android/settings/users/MultiUserPreferenceController.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.users;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+
+public class MultiUserPreferenceController extends BasePreferenceController {
+
+    public MultiUserPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return (UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
+                && !Utils.isMonkeyRunning()) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        UserManager um = mContext.getSystemService(UserManager.class);
+        UserInfo info = um.getUserInfo(UserHandle.myUserId());
+        return mContext.getString(R.string.users_summary, info.name);
+    }
+}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
index aa77dc1..010607f 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
@@ -37,6 +37,8 @@
  * Sharing a Wi-Fi network by QR code after unlocking. Used by {@code InternetDialog} in QS.
  */
 public class WifiDppConfiguratorAuthActivity extends InstrumentedActivity {
+    private static final String WIFI_SHARING_KEY_ALIAS = "wifi_sharing_auth_key";
+    private static final int MAX_UNLOCK_SECONDS = 60;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -46,7 +48,9 @@
         Intent authIntent = getSystemService(KeyguardManager.class)
                 .createConfirmDeviceCredentialIntent(
                         getText(R.string.wifi_dpp_lockscreen_title), null, getUserId());
-        if (authIntent == null) {
+        if (authIntent == null
+                || WifiDppUtils.isUnlockedWithinSeconds(
+                        WIFI_SHARING_KEY_ALIAS, MAX_UNLOCK_SECONDS)) {
             startQrCodeActivity();
             finish();
         } else {
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 83a1571..23a6a54 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -30,6 +30,8 @@
 import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
 import android.text.TextUtils;
 
 import com.android.settings.R;
@@ -37,9 +39,19 @@
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.wifitrackerlib.WifiEntry;
 
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.time.Duration;
 import java.util.List;
 
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
 /**
  * Here are the items shared by both WifiDppConfiguratorActivity & WifiDppEnrolleeActivity
  *
@@ -97,6 +109,8 @@
 
     private static final Duration VIBRATE_DURATION_QR_CODE_RECOGNITION = Duration.ofMillis(3);
 
+    private static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
+
     /**
      * Returns whether the device support WiFi DPP.
      */
@@ -367,6 +381,48 @@
     }
 
     /**
+     * Checks whether the device is unlocked recently.
+     *
+     * @param keyStoreAlias key
+     * @param seconds how many seconds since the device is unlocked
+     * @return whether the device is unlocked within the time
+     */
+    public static boolean isUnlockedWithinSeconds(String keyStoreAlias, int seconds) {
+        try {
+            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS7_PADDING);
+            cipher.init(Cipher.ENCRYPT_MODE, generateSecretKey(keyStoreAlias, seconds));
+            cipher.doFinal();
+            return true;
+        } catch (NoSuchPaddingException
+                 | IllegalBlockSizeException
+                 | NoSuchAlgorithmException
+                 | BadPaddingException
+                 | InvalidKeyException e) {
+            return false;
+        }
+    }
+
+    private static SecretKey generateSecretKey(String keyStoreAlias, int seconds) {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec
+                .Builder(keyStoreAlias, KeyProperties.PURPOSE_ENCRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationParameters(
+                        seconds,
+                        KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG)
+                .build();
+        try {
+            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
+            keyGenerator.init(spec);
+            return keyGenerator.generateKey();
+        } catch (NoSuchAlgorithmException
+                 | InvalidAlgorithmParameterException e) {
+            return null;
+        }
+    }
+
+    /**
      * Shows authentication screen to confirm credentials (pin, pattern or password) for the current
      * user of the device.
      *
diff --git a/tests/robotests/src/com/android/settings/inputmethod/SpellCheckerPreferenceTest.java b/tests/robotests/src/com/android/settings/inputmethod/SpellCheckerPreferenceTest.java
new file mode 100644
index 0000000..d8e54b0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/inputmethod/SpellCheckerPreferenceTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.view.View;
+import android.view.textservice.SpellCheckerInfo;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SpellCheckerPreferenceTest {
+
+    private Context mContext;
+    private PreferenceViewHolder mViewHolder;
+    private View mDivider;
+    private SpellCheckerPreference mPreference;
+    private final SpellCheckerInfo[] mScis = new SpellCheckerInfo[]{};
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mPreference = new SpellCheckerPreference(mContext, mScis);
+    }
+
+    @Test
+    public void onBindViewHolder_withIntent_DividerIsVisible() {
+        final View view = spy(View.inflate(mContext, mPreference.getLayoutResource(), null));
+        mViewHolder = PreferenceViewHolder.createInstanceForTests(view);
+        mDivider = view.findViewById(
+                com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider);
+        mPreference.mIntent = new Intent(Intent.ACTION_MAIN);
+
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void onBindViewHolder_withoutIntent_DividerIsNotExist() {
+        final View view = spy(View.inflate(mContext, mPreference.getLayoutResource(), null));
+        mViewHolder = PreferenceViewHolder.createInstanceForTests(view);
+        mDivider = view.findViewById(
+                com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider);
+
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mDivider.getVisibility()).isEqualTo(View.GONE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java
new file mode 100644
index 0000000..fd73eab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.users;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class})
+public class MultiUserPreferenceControllerTest {
+
+    private Context mContext;
+    private ShadowUserManager mUserManager;
+    private MultiUserPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mUserManager = ShadowUserManager.getShadow();
+        mController = new MultiUserPreferenceController(mContext, "test_key");
+    }
+
+    @After
+    public void tearDown() {
+        ShadowUserManager.reset();
+    }
+
+    @Test
+    public void getAvailabilityStatus_multiuserEnabled_shouldReturnAvailable() {
+        mUserManager.setSupportsMultipleUsers(true);
+
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_multiuserDisabled_shouldReturnNotAvailable() {
+        mUserManager.setSupportsMultipleUsers(false);
+
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+    }
+}