Merge "Fix a CHAR LIMIT mistake for a BT message as requested by linguists" into main
diff --git a/Android.bp b/Android.bp
index 0777aa7..2699c38 100644
--- a/Android.bp
+++ b/Android.bp
@@ -89,6 +89,7 @@
         "SettingsLib",
         "SettingsLibActivityEmbedding",
         "aconfig_settings_flags_lib",
+        "accessibility_settings_flags_lib",
         "app-usage-event-protos-lite",
         "battery-event-protos-lite",
         "battery-usage-slot-protos-lite",
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 37b03ba..de6d0af 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -35,4 +35,15 @@
 java_aconfig_library {
     name: "MediaDrmSettingsFlagsLib",
     aconfig_declarations: "media_drm_flags",
-}
\ No newline at end of file
+}
+
+aconfig_declarations {
+    name: "accessibility_flags",
+    package: "com.android.settings.accessibility",
+    srcs: ["accessibility/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "accessibility_settings_flags_lib",
+    aconfig_declarations: "accessibility_flags",
+}
diff --git a/aconfig/accessibility/OWNERS b/aconfig/accessibility/OWNERS
new file mode 100644
index 0000000..7a76c21
--- /dev/null
+++ b/aconfig/accessibility/OWNERS
@@ -0,0 +1 @@
+include /src/com/android/settings/accessibility/OWNERS
diff --git a/aconfig/accessibility/accessibility_flags.aconfig b/aconfig/accessibility/accessibility_flags.aconfig
new file mode 100644
index 0000000..7f1f909
--- /dev/null
+++ b/aconfig/accessibility/accessibility_flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.settings.accessibility"
+
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+flag {
+  name: "remove_qs_tooltip_in_suw"
+  namespace: "accessibility"
+  description: "Don't show quick settings tooltip in SUW, since the user can't use quick settings there."
+  bug: "294560581"
+}
\ No newline at end of file
diff --git a/aconfig/settings_accessibility_flag_declarations.aconfig b/aconfig/settings_accessibility_flag_declarations_legacy.aconfig
similarity index 86%
rename from aconfig/settings_accessibility_flag_declarations.aconfig
rename to aconfig/settings_accessibility_flag_declarations_legacy.aconfig
index 246f983..acdce96 100644
--- a/aconfig/settings_accessibility_flag_declarations.aconfig
+++ b/aconfig/settings_accessibility_flag_declarations_legacy.aconfig
@@ -1,5 +1,8 @@
 package: "com.android.settings.flags"
 
+# NOTE: Don't add new accessibility flags here, since the package name doesn't follow
+# the best practice for setting's feature flag go/settings-trunk-stable
+
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 # NOTE: All Settings flags share the same Flags class, so prefix our
diff --git a/res/layout/content_protection_preference_fragment.xml b/res/layout/content_protection_preference_fragment.xml
index 4c7352e..8bf6582 100644
--- a/res/layout/content_protection_preference_fragment.xml
+++ b/res/layout/content_protection_preference_fragment.xml
@@ -17,6 +17,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:key="content_protection_preference_subpage"
     android:title="@string/content_protection_preference_title">
 
     <com.android.settingslib.widget.TopIntroPreference
diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
index 6bd8747..f268a40 100644
--- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
+++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.accessibility;
 
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Bundle;
@@ -33,6 +34,8 @@
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
 import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
 
+import com.google.android.setupcompat.util.WizardManagerHelper;
+
 import java.util.Optional;
 
 /**
@@ -207,6 +210,13 @@
             return;
         }
 
+        if (Flags.removeQsTooltipInSuw()
+                && mContext instanceof Activity
+                && WizardManagerHelper.isAnySetupWizard(((Activity) mContext).getIntent())) {
+            // Don't show QuickSettingsTooltip in Setup Wizard
+            return;
+        }
+
         if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
                 mContext, tileComponentName)) {
             // Returns if quick settings tooltip only show once.
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 427cad9..c76bb8b 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -905,6 +905,14 @@
             return;
         }
 
+        Activity activity = getActivity();
+        if (com.android.settings.accessibility.Flags.removeQsTooltipInSuw()
+                && activity != null
+                && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+            // Don't show QuickSettingsTooltip in Setup Wizard
+            return;
+        }
+
         if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
                 getContext(), tileComponentName)) {
             // Returns if quick settings tooltip only show once.
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 807f043..e45657f 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -53,6 +53,7 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.deviceinfo.StorageWizardMoveConfirm;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.applications.ApplicationsState.Callbacks;
@@ -359,6 +360,8 @@
         mButtonsPref.setButton1Enabled(false);
         // Invoke uninstall or clear user data based on sysPackage
         String packageName = mAppEntry.info.packageName;
+        DynamicDenylistManager.getInstance(getContext())
+                .resetDenylistIfNeeded(packageName, /* force= */ false);
         Log.i(TAG, "Clearing user data for package : " + packageName);
         if (mClearDataObserver == null) {
             mClearDataObserver = new ClearUserDataObserver();
diff --git a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
index 6da3e52..b2b7512 100644
--- a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
+++ b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
@@ -39,6 +39,7 @@
 
 import com.android.settings.R;
 import com.android.settings.fuelgauge.BatteryOptimizeUtils;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
 
 import java.util.Arrays;
 import java.util.List;
@@ -155,6 +156,8 @@
             }
             mAom.resetAllModes();
             BatteryOptimizeUtils.resetAppOptimizationMode(mContext, mIPm, mAom);
+            DynamicDenylistManager.getInstance(mContext)
+                    .resetDenylistIfNeeded(/* packageName= */ null, /* force= */ true);
             final int[] restrictedUids = mNpm.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
             final int currentUserId = ActivityManager.getCurrentUser();
             for (int uid : restrictedUids) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index b0f8b8f..0d2b53a 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -17,7 +17,6 @@
 package com.android.settings.connecteddevice.audiosharing;
 
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcast;
 import android.bluetooth.BluetoothLeBroadcastAssistant;
@@ -43,16 +42,12 @@
 import com.android.settings.flags.Flags;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LeAudioProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
-import com.google.common.collect.ImmutableList;
-
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
@@ -378,13 +373,14 @@
             // Do nothing for ineligible (non LE audio) remote device when no sharing session.
         } else {
             Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
-                    fetchConnectedDevicesByGroupId();
+                    AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
             // Handle connected eligible (LE audio) remote device
             if (isBroadcasting()) {
                 // Show audio sharing switch or join dialog according to device count in the sharing
                 // session.
                 ArrayList<AudioSharingDeviceItem> deviceItemsInSharingSession =
-                        buildDeviceItemsInSharingSession(groupedDevices);
+                        AudioSharingUtils.buildOrderedDeviceItemsInSharingSession(
+                                groupedDevices, mLocalBtManager);
                 // Show audio sharing switch dialog when the third eligible (LE audio) remote device
                 // connected during a sharing session.
                 if (deviceItemsInSharingSession.size() >= 2) {
@@ -432,8 +428,7 @@
                     if (device.getGroupId() == cachedDevice.getGroupId()) {
                         continue;
                     }
-                    deviceItems.add(
-                            new AudioSharingDeviceItem(device.getName(), device.getGroupId()));
+                    deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(device));
                 }
                 // Show audio sharing join dialog when the second eligible (LE audio) remote device
                 // connect and no sharing session.
@@ -494,52 +489,6 @@
         return mBroadcast != null && mBroadcast.isEnabled(null);
     }
 
-    private Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId() {
-        // TODO: filter out devices with le audio disabled.
-        List<BluetoothDevice> connectedDevices =
-                mAssistant == null ? ImmutableList.of() : mAssistant.getConnectedDevices();
-        Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
-        CachedBluetoothDeviceManager cacheManager = mLocalBtManager.getCachedDeviceManager();
-        for (BluetoothDevice device : connectedDevices) {
-            CachedBluetoothDevice cachedDevice = cacheManager.findDevice(device);
-            if (cachedDevice == null) {
-                Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
-                continue;
-            }
-            int groupId = cachedDevice.getGroupId();
-            if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
-                Log.d(
-                        TAG,
-                        "Skip device due to no valid group id: " + device.getAnonymizedAddress());
-                continue;
-            }
-            if (!groupedDevices.containsKey(groupId)) {
-                groupedDevices.put(groupId, new ArrayList<>());
-            }
-            groupedDevices.get(groupId).add(cachedDevice);
-        }
-        return groupedDevices;
-    }
-
-    private ArrayList<AudioSharingDeviceItem> buildDeviceItemsInSharingSession(
-            Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
-        ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
-        for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
-            for (CachedBluetoothDevice device : devices) {
-                List<BluetoothLeBroadcastReceiveState> sourceList =
-                        mAssistant.getAllSources(device.getDevice());
-                if (!sourceList.isEmpty()) {
-                    // Use random device in the group within the sharing session to
-                    // represent the group.
-                    deviceItems.add(
-                            new AudioSharingDeviceItem(device.getName(), device.getGroupId()));
-                    break;
-                }
-            }
-        }
-        return deviceItems;
-    }
-
     private void addSourceToTargetDevices(List<BluetoothDevice> sinks) {
         if (sinks.isEmpty() || mBroadcast == null || mAssistant == null) {
             Log.d(TAG, "Skip adding source to target.");
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 3f9f48e..8b82fe9 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -16,13 +16,11 @@
 
 package com.android.settings.connecteddevice.audiosharing;
 
-import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcast;
 import android.bluetooth.BluetoothLeBroadcastAssistant;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.util.Log;
 import android.widget.CompoundButton;
@@ -37,17 +35,14 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.flags.Flags;
 import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.utils.ThreadUtils;
 
-import com.google.common.collect.ImmutableList;
-
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -273,18 +268,17 @@
             mSwitchBar.setEnabled(true);
             return;
         }
-        Map<Integer, List<CachedBluetoothDevice>> groupedDevices = fetchConnectedDevicesByGroupId();
+        Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
+                AudioSharingUtils.fetchConnectedDevicesByGroupId(mBtManager);
         ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
         Optional<Integer> activeGroupId = Optional.empty();
         for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
             // Use random device in the group to represent the group.
             CachedBluetoothDevice device = devices.get(0);
-            // TODO: add BluetoothUtils.isActiveLeAudioDevice to avoid directly using isActiveDevice
-            if (device.isActiveDevice(BluetoothProfile.LE_AUDIO)) {
+            if (BluetoothUtils.isActiveLeAudioDevice(device)) {
                 activeGroupId = Optional.of(device.getGroupId());
             } else {
-                AudioSharingDeviceItem item =
-                        new AudioSharingDeviceItem(device.getName(), device.getGroupId());
+                AudioSharingDeviceItem item = AudioSharingUtils.buildAudioSharingDeviceItem(device);
                 deviceItems.add(item);
             }
         }
@@ -347,31 +341,6 @@
         return mBroadcast != null && mBroadcast.isEnabled(null);
     }
 
-    private Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId() {
-        // TODO: filter out devices with le audio disabled.
-        List<BluetoothDevice> connectedDevices =
-                mAssistant == null ? ImmutableList.of() : mAssistant.getConnectedDevices();
-        Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
-        CachedBluetoothDeviceManager cacheManager = mBtManager.getCachedDeviceManager();
-        for (BluetoothDevice device : connectedDevices) {
-            CachedBluetoothDevice cachedDevice = cacheManager.findDevice(device);
-            if (cachedDevice == null) {
-                Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
-                continue;
-            }
-            int groupId = cachedDevice.getGroupId();
-            if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
-                Log.d(TAG, "Skip device due to no valid group id");
-                continue;
-            }
-            if (!groupedDevices.containsKey(groupId)) {
-                groupedDevices.put(groupId, new ArrayList<>());
-            }
-            groupedDevices.get(groupId).add(cachedDevice);
-        }
-        return groupedDevices;
-    }
-
     private void addSourceToTargetDevices(List<BluetoothDevice> sinks) {
         if (sinks.isEmpty() || mBroadcast == null || mAssistant == null) {
             Log.d(TAG, "Skip adding source to target.");
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
new file mode 100644
index 0000000..4ece70e
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -0,0 +1,125 @@
+/*
+ * 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.connecteddevice.audiosharing;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.util.Log;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AudioSharingUtils {
+    private static final String TAG = "AudioSharingUtils";
+
+    /**
+     * Fetch {@link CachedBluetoothDevice}s connected to the broadcast assistant. The devices are
+     * grouped by CSIP group id.
+     *
+     * @param localBtManager The BT manager to provide BT functions.
+     * @return A map of connected devices grouped by CSIP group id.
+     */
+    public static Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId(
+            LocalBluetoothManager localBtManager) {
+        Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
+        LocalBluetoothLeBroadcastAssistant assistant =
+                localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        if (assistant == null) return groupedDevices;
+        // TODO: filter out devices with le audio disabled.
+        List<BluetoothDevice> connectedDevices = assistant.getConnectedDevices();
+        CachedBluetoothDeviceManager cacheManager = localBtManager.getCachedDeviceManager();
+        for (BluetoothDevice device : connectedDevices) {
+            CachedBluetoothDevice cachedDevice = cacheManager.findDevice(device);
+            if (cachedDevice == null) {
+                Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
+                continue;
+            }
+            int groupId = cachedDevice.getGroupId();
+            if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+                Log.d(
+                        TAG,
+                        "Skip device due to no valid group id: " + device.getAnonymizedAddress());
+                continue;
+            }
+            if (!groupedDevices.containsKey(groupId)) {
+                groupedDevices.put(groupId, new ArrayList<>());
+            }
+            groupedDevices.get(groupId).add(cachedDevice);
+        }
+        return groupedDevices;
+    }
+
+    /**
+     * Fetch a list of {@link AudioSharingDeviceItem}s in the audio sharing session.
+     *
+     * @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
+     *     id.
+     * @param localBtManager The BT manager to provide BT functions.
+     * @return A list of connected devices in the audio sharing session.
+     */
+    public static ArrayList<AudioSharingDeviceItem> buildOrderedDeviceItemsInSharingSession(
+            Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
+            LocalBluetoothManager localBtManager) {
+        ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
+        LocalBluetoothLeBroadcastAssistant assistant =
+                localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        if (assistant == null) return deviceItems;
+        CachedBluetoothDevice activeDevice = null;
+        List<CachedBluetoothDevice> inactiveDevices = new ArrayList<>();
+        for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
+            for (CachedBluetoothDevice device : devices) {
+                List<BluetoothLeBroadcastReceiveState> sourceList =
+                        assistant.getAllSources(device.getDevice());
+                if (!sourceList.isEmpty()) {
+                    // Use random device in the group within the sharing session to
+                    // represent the group.
+                    if (BluetoothUtils.isActiveLeAudioDevice(device)) {
+                        activeDevice = device;
+                    } else {
+                        inactiveDevices.add(device);
+                    }
+                    break;
+                }
+            }
+        }
+        if (activeDevice != null) {
+            deviceItems.add(buildAudioSharingDeviceItem(activeDevice));
+        }
+        inactiveDevices.stream()
+                .sorted(CachedBluetoothDevice::compareTo)
+                .forEach(
+                        device -> {
+                            deviceItems.add(buildAudioSharingDeviceItem(device));
+                        });
+        return deviceItems;
+    }
+
+    /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
+    public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
+            CachedBluetoothDevice cachedDevice) {
+        return new AudioSharingDeviceItem(cachedDevice.getName(), cachedDevice.getGroupId());
+    }
+}
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index 38f09f4..fb28d68 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -44,6 +44,7 @@
 import com.android.settings.datausage.lib.AppDataUsageDetailsRepository;
 import com.android.settings.datausage.lib.NetworkTemplates;
 import com.android.settings.datausage.lib.NetworkUsageDetailsData;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
 import com.android.settings.network.SubscriptionUtil;
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settingslib.AppItem;
@@ -325,7 +326,8 @@
     private boolean getAppRestrictBackground() {
         final int uid = mAppItem.key;
         final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
-        return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+        return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0
+                && DynamicDenylistManager.getInstance(mContext).isInManualDenylist(uid);
     }
 
     private boolean getUnrestrictData() {
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index b4b6b8c..6e99453 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -23,6 +23,7 @@
 import android.net.NetworkPolicyManager;
 import android.util.SparseIntArray;
 
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
@@ -39,6 +40,7 @@
     private final MetricsFeatureProvider mMetricsFeatureProvider;
 
     private final NetworkPolicyManager mPolicyManager;
+    private final DynamicDenylistManager mDynamicDenylistManager;
     private final ArrayList<Listener> mListeners = new ArrayList<>();
     private SparseIntArray mUidPolicies = new SparseIntArray();
     private boolean mAllowlistInitialized;
@@ -50,6 +52,7 @@
         mContext = context.getApplicationContext();
         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
         mPolicyManager = NetworkPolicyManager.from(mContext);
+        mDynamicDenylistManager = DynamicDenylistManager.getInstance(mContext);
     }
 
     public void addListener(Listener listener) {
@@ -83,7 +86,7 @@
 
     public void setIsAllowlisted(int uid, String packageName, boolean allowlisted) {
         final int policy = allowlisted ? POLICY_ALLOW_METERED_BACKGROUND : POLICY_NONE;
-        mPolicyManager.setUidPolicy(uid, policy);
+        mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
         mUidPolicies.put(uid, policy);
         if (allowlisted) {
             mMetricsFeatureProvider.action(
@@ -113,7 +116,7 @@
 
     public void setIsDenylisted(int uid, String packageName, boolean denylisted) {
         final int policy = denylisted ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE;
-        mPolicyManager.setUidPolicy(uid, policy);
+        mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
         mUidPolicies.put(uid, policy);
         if (denylisted) {
             mMetricsFeatureProvider.action(
@@ -123,7 +126,8 @@
 
     public boolean isDenylisted(int uid) {
         loadDenylist();
-        return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND;
+        return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND
+                && mDynamicDenylistManager.isInManualDenylist(uid);
     }
 
     private void loadDenylist() {
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
index 5d9d047..dd49c8b 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
@@ -26,6 +26,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 
 import java.util.List;
@@ -50,6 +51,8 @@
         context = context.getApplicationContext();
         verifySaverConfiguration(context);
         verifyBatteryOptimizeModes(context);
+        // Initialize and sync settings into SharedPreferences for migration.
+        DynamicDenylistManager.getInstance(context);
     }
 
     /** Avoid users set important apps into the unexpected battery optimize modes */
diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
index be72e56..7eae7eb 100644
--- a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
+++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
@@ -16,12 +16,24 @@
 
 package com.android.settings.fuelgauge.datasaver;
 
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+import static com.android.settings.fuelgauge.BatteryUtils.UID_ZERO;
+
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.net.NetworkPolicyManager;
+import android.util.ArraySet;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
+import java.util.List;
+import java.util.Set;
+
 /** A class to dynamically manage per apps {@link NetworkPolicyManager} POLICY_ flags. */
 public final class DynamicDenylistManager {
 
@@ -29,47 +41,157 @@
     private static final String PREF_KEY_MANUAL_DENY = "manual_denylist_preference";
     private static final String PREF_KEY_DYNAMIC_DENY = "dynamic_denylist_preference";
 
+    private static DynamicDenylistManager sInstance;
+
     private final Context mContext;
     private final NetworkPolicyManager mNetworkPolicyManager;
+    private final Object mLock = new Object();
 
-    private static DynamicDenylistManager sInstance;
+    @VisibleForTesting
+    static final String PREF_KEY_MANUAL_DENYLIST_SYNCED = "manual_denylist_synced";
 
     /** @return a DynamicDenylistManager object */
     public static DynamicDenylistManager getInstance(Context context) {
         synchronized (DynamicDenylistManager.class) {
             if (sInstance == null) {
-                sInstance = new DynamicDenylistManager(context);
+                sInstance = new DynamicDenylistManager(
+                        context, NetworkPolicyManager.from(context));
             }
             return sInstance;
         }
     }
 
-    DynamicDenylistManager(Context context) {
+    @VisibleForTesting
+    DynamicDenylistManager(Context context, NetworkPolicyManager networkPolicyManager) {
         mContext = context.getApplicationContext();
-        mNetworkPolicyManager = NetworkPolicyManager.from(mContext);
+        mNetworkPolicyManager = networkPolicyManager;
+        syncPolicyIfNeeded();
     }
 
-    /** Update the target uid policy in {@link #getManualDenylistPref()}. */
-    public void updateManualDenylist(String uid, int policy) {
-        if (policy != NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND) {
-            getManualDenylistPref().edit().remove(uid).apply();
-        } else {
-            getManualDenylistPref().edit().putInt(uid, policy).apply();
+    /** Sync the policy from {@link NetworkPolicyManager} if needed. */
+    private void syncPolicyIfNeeded() {
+        if (getManualDenylistPref().contains(PREF_KEY_MANUAL_DENYLIST_SYNCED)) {
+            Log.i(TAG, "syncPolicyIfNeeded() ignore synced manual denylist");
+            return;
+        }
+
+        final SharedPreferences.Editor editor = getManualDenylistPref().edit();
+        final int[] existedUids = mNetworkPolicyManager
+                .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
+        if (existedUids != null && existedUids.length != 0) {
+            for (int uid : existedUids) {
+                editor.putInt(String.valueOf(uid), POLICY_REJECT_METERED_BACKGROUND);
+            }
+        }
+        editor.putInt(PREF_KEY_MANUAL_DENYLIST_SYNCED, POLICY_NONE).apply();
+    }
+
+    /** Set policy flags for specific UID. */
+    public void setUidPolicyLocked(int uid, int policy) {
+        synchronized (mLock) {
+            mNetworkPolicyManager.setUidPolicy(uid, policy);
+        }
+        updateDenylistPref(uid, policy);
+    }
+
+    /** Suggest a list of package to set as POLICY_REJECT. */
+    public void setDenylist(List<String> packageNameList) {
+        final Set<Integer> denylistTargetUids = new ArraySet<>(packageNameList.size());
+        for (String packageName : packageNameList) {
+            try {
+                final int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+                if (uid == UID_ZERO) {
+                    continue;
+                }
+                denylistTargetUids.add(uid);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Unknown package name: " + packageName, e);
+            }
+        }
+
+        final Set<Integer> manualDenylistUids = getDenylistAllUids(getManualDenylistPref());
+        denylistTargetUids.removeAll(manualDenylistUids);
+
+        final Set<Integer> lastDynamicDenylistUids = getDenylistAllUids(getDynamicDenylistPref());
+        if (lastDynamicDenylistUids.equals(denylistTargetUids)) {
+            Log.i(TAG, "setDenylist() ignore the same denylist with size: "
+                    + lastDynamicDenylistUids.size());
+            return;
+        }
+
+        // Store target denied uids into DynamicDenylistPref.
+        final SharedPreferences.Editor editor = getDynamicDenylistPref().edit();
+        editor.clear();
+        denylistTargetUids.forEach(
+                uid -> editor.putInt(String.valueOf(uid), POLICY_REJECT_METERED_BACKGROUND));
+        editor.apply();
+
+        // Set new added UIDs into REJECT policy.
+        synchronized (mLock) {
+            for (int uid : denylistTargetUids) {
+                if (!lastDynamicDenylistUids.contains(uid)) {
+                    mNetworkPolicyManager.setUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
+                }
+            }
+        }
+        // Unset removed UIDs back to NONE policy.
+        synchronized (mLock) {
+            for (int uid : lastDynamicDenylistUids) {
+                if (!denylistTargetUids.contains(uid)) {
+                    mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE);
+                }
+            }
         }
     }
 
     /** Return true if the target uid is in {@link #getManualDenylistPref()}. */
-    public boolean isInManualDenylist(String uid) {
-        return getManualDenylistPref().contains(uid);
+    public boolean isInManualDenylist(int uid) {
+        return getManualDenylistPref().contains(String.valueOf(uid));
     }
 
-    /** Clear all data in {@link #getManualDenylistPref()} */
-    public void clearManualDenylistPref() {
+    /** Reset the UIDs in the denylist if needed. */
+    public void resetDenylistIfNeeded(String packageName, boolean force) {
+        if (!force && !SETTINGS_PACKAGE_NAME.equals(packageName)) {
+            return;
+        }
+        synchronized (mLock) {
+            for (int uid : mNetworkPolicyManager
+                    .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
+                if (!getDenylistAllUids(getManualDenylistPref()).contains(uid)) {
+                    mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE);
+                }
+            }
+        }
+        clearSharedPreferences();
+    }
+
+    private Set<Integer> getDenylistAllUids(SharedPreferences sharedPreferences) {
+        final ArraySet<Integer> uids = new ArraySet<>();
+        for (String key : sharedPreferences.getAll().keySet()) {
+            if (PREF_KEY_MANUAL_DENYLIST_SYNCED.equals(key)) {
+                continue;
+            }
+            try {
+                uids.add(Integer.parseInt(key));
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "getDenylistAllUids() unexpected format for " + key);
+            }
+        }
+        return uids;
+    }
+
+    void updateDenylistPref(int uid, int policy) {
+        final String uidString = String.valueOf(uid);
+        if (policy != POLICY_REJECT_METERED_BACKGROUND) {
+            getManualDenylistPref().edit().remove(uidString).apply();
+        } else {
+            getManualDenylistPref().edit().putInt(uidString, policy).apply();
+        }
+        getDynamicDenylistPref().edit().remove(uidString).apply();
+    }
+
+    void clearSharedPreferences() {
         getManualDenylistPref().edit().clear().apply();
-    }
-
-    /** Clear all data in {@link #getDynamicDenylistPref()} */
-    public void clearDynamicDenylistPref() {
         getDynamicDenylistPref().edit().clear().apply();
     }
 
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 70d4d7d..a0db4ce 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -31,6 +31,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserProperties;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.hardware.biometrics.BiometricConstants;
@@ -61,11 +62,6 @@
 public class ConfirmDeviceCredentialActivity extends FragmentActivity {
     public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
 
-    // The normal flow that apps go through
-    private static final int CREDENTIAL_NORMAL = 1;
-    // Unlocks the managed profile when the primary profile is unlocked
-    private static final int CREDENTIAL_MANAGED = 2;
-
     private static final String TAG_BIOMETRIC_FRAGMENT = "fragment";
 
     public static class InternalActivity extends ConfirmDeviceCredentialActivity {
@@ -84,7 +80,9 @@
     private String mTitle;
     private CharSequence mDetails;
     private int mUserId;
-    private int mCredentialMode;
+    // Used to force the verification path required to unlock profile that shares credentials with
+    // with parent
+    private boolean mForceVerifyPath = false;
     private boolean mGoingToBackground;
     private boolean mWaitingForBiometricCallback;
 
@@ -189,7 +187,9 @@
         }
         final int effectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
         final boolean isEffectiveUserManagedProfile =
-                UserManager.get(this).isManagedProfile(effectiveUserId);
+                mUserManager.isManagedProfile(effectiveUserId);
+        final UserProperties userProperties =
+                mUserManager.getUserProperties(UserHandle.of(mUserId));
         // if the client app did not hand in a title and we are about to show the work challenge,
         // check whether there is a policy setting the organization name and use that as title
         if ((mTitle == null) && isEffectiveUserManagedProfile) {
@@ -278,7 +278,19 @@
                     .setForceVerifyPath(true)
                     .show();
         } else if (isEffectiveUserManagedProfile && isInternalActivity()) {
-            mCredentialMode = CREDENTIAL_MANAGED;
+            // When the mForceVerifyPath is set to true, we launch the real confirm credential
+            // activity with an explicit but fake challenge value (0L). This will result in
+            // ConfirmLockPassword calling verifyTiedProfileChallenge() (if it's a profile with
+            // unified challenge), due to the difference between
+            // ConfirmLockPassword.startVerifyPassword() and
+            // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here
+            // is necessary when this is part of the turning on work profile flow, because it forces
+            // unlocking the work profile even before the profile is running.
+            // TODO: Remove the duplication of checkPassword and verifyPassword in
+            //  ConfirmLockPassword,
+            // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to
+            // use, which optionally accepts a challenge.
+            mForceVerifyPath = true;
             if (isBiometricAllowed(effectiveUserId, mUserId)) {
                 showBiometricPrompt(promptInfo);
                 launchedBiometric = true;
@@ -286,8 +298,19 @@
                 showConfirmCredentials();
                 launchedCDC = true;
             }
+        } else if (android.os.Flags.allowPrivateProfile()
+                && userProperties != null
+                && userProperties.isAuthAlwaysRequiredToDisableQuietMode()
+                && isInternalActivity()) {
+            // Force verification path is required to be invoked as we might need to verify the tied
+            // profile challenge if the profile is using the unified challenge mode. This would
+            // result in ConfirmLockPassword.startVerifyPassword/
+            // ConfirmLockPattern.startVerifyPattern being called instead of the
+            // startCheckPassword/startCheckPattern
+            mForceVerifyPath = userProperties.isCredentialShareableWithParent();
+            showConfirmCredentials();
+            launchedCDC = true;
         } else {
-            mCredentialMode = CREDENTIAL_NORMAL;
             if (isBiometricAllowed(effectiveUserId, mUserId)) {
                 // Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
                 // onAuthenticationError and do the right thing automatically.
@@ -313,11 +336,8 @@
 
     private String getTitleFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
             boolean isEffectiveUserManagedProfile) {
-        int overrideStringId;
-        int defaultStringId;
         switch (credentialType) {
             case LockPatternUtils.CREDENTIAL_TYPE_PIN:
-
                 if (isEffectiveUserManagedProfile) {
                     return mDevicePolicyManager.getResources().getString(
                             CONFIRM_WORK_PROFILE_PIN_HEADER,
@@ -410,29 +430,15 @@
      * Shows ConfirmDeviceCredentials for normal apps.
      */
     private void showConfirmCredentials() {
-        boolean launched = false;
-        ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this)
+        boolean launched = new ChooseLockSettingsHelper.Builder(this)
                 .setHeader(mTitle)
                 .setDescription(mDetails)
                 .setExternal(true)
                 .setUserId(mUserId)
-                .setTaskOverlay(mTaskOverlay);
-        // The only difference between CREDENTIAL_MANAGED and CREDENTIAL_NORMAL is that for
-        // CREDENTIAL_MANAGED, we launch the real confirm credential activity with an explicit
-        // but fake challenge value (0L). This will result in ConfirmLockPassword calling
-        // verifyTiedProfileChallenge() (if it's a profile with unified challenge), due to the
-        // difference between ConfirmLockPassword.startVerifyPassword() and
-        // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here is
-        // necessary when this is part of the turning on work profile flow, because it forces
-        // unlocking the work profile even before the profile is running.
-        // TODO: Remove the duplication of checkPassword and verifyPassword in ConfirmLockPassword,
-        // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to use,
-        // which optionally accepts a challenge.
-        if (mCredentialMode == CREDENTIAL_MANAGED) {
-            launched = builder.setForceVerifyPath(true).show();
-        } else if (mCredentialMode == CREDENTIAL_NORMAL) {
-            launched = builder.show();
-        }
+                .setTaskOverlay(mTaskOverlay)
+                .setForceVerifyPath(mForceVerifyPath)
+                .show();
+
         if (!launched) {
             Log.d(TAG, "No pin/pattern/pass set");
             setResult(Activity.RESULT_OK);
diff --git a/src/com/android/settings/privatespace/HidePrivateSpaceController.java b/src/com/android/settings/privatespace/HidePrivateSpaceController.java
index b972a3f..8a0f167 100644
--- a/src/com/android/settings/privatespace/HidePrivateSpaceController.java
+++ b/src/com/android/settings/privatespace/HidePrivateSpaceController.java
@@ -16,10 +16,10 @@
 
 package com.android.settings.privatespace;
 
-import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL;
 
 import android.content.Context;
-import android.provider.Settings;
 
 import com.android.settings.core.TogglePreferenceController;
 
@@ -28,11 +28,11 @@
  *  in All Apps.
  */
 public class HidePrivateSpaceController extends TogglePreferenceController {
-    private static final int DISABLED_VALUE = 0;
-    private static final int ENABLED_VALUE = 1;
+    private final PrivateSpaceMaintainer mPrivateSpaceMaintainer;
 
     public HidePrivateSpaceController(Context context, String key) {
         super(context, key);
+        mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(context);
     }
 
     @Override
@@ -43,14 +43,15 @@
 
     @Override
     public boolean isChecked() {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                HIDE_PRIVATESPACE_ENTRY_POINT, DISABLED_VALUE) != DISABLED_VALUE;
+        return mPrivateSpaceMaintainer.getHidePrivateSpaceEntryPointSetting()
+                != HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        Settings.Secure.putInt(mContext.getContentResolver(), HIDE_PRIVATESPACE_ENTRY_POINT,
-                isChecked ? ENABLED_VALUE : DISABLED_VALUE);
+        mPrivateSpaceMaintainer.setHidePrivateSpaceEntryPointSetting(
+                isChecked ? HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL
+                        : HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
         return true;
     }
 
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index e6094ce..341110b 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -17,6 +17,7 @@
 package com.android.settings.privatespace;
 
 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
 
 import android.app.ActivityManager;
 import android.app.IActivityManager;
@@ -27,6 +28,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -40,6 +42,7 @@
 /** A class to help with the creation / deletion of Private Space */
 public class PrivateSpaceMaintainer {
     private static final String TAG = "PrivateSpaceMaintainer";
+
     @GuardedBy("this")
     private static PrivateSpaceMaintainer sPrivateSpaceMaintainer;
 
@@ -49,6 +52,10 @@
     private UserHandle mUserHandle;
     private final KeyguardManager mKeyguardManager;
 
+    /** This is the default value for the hide private space entry point settings. */
+    public static final int HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL = 0;
+    public static final int HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL = 1;
+
     public enum ErrorDeletingPrivateSpace {
             DELETE_PS_ERROR_NONE,
             DELETE_PS_ERROR_NO_PRIVATE_SPACE,
@@ -91,6 +98,7 @@
             }
 
             Log.i(TAG, "Private space created with id: " + mUserHandle.getIdentifier());
+            resetPrivateSpaceSettings();
         }
         return true;
     }
@@ -197,4 +205,21 @@
         return doesPrivateSpaceExist()
                 && mKeyguardManager.isDeviceSecure(mUserHandle.getIdentifier());
     }
+
+    /** Sets the setting to show PS entry point to the provided value. */
+    public void setHidePrivateSpaceEntryPointSetting(int value) {
+        Settings.Secure.putInt(mContext.getContentResolver(), HIDE_PRIVATESPACE_ENTRY_POINT, value);
+    }
+
+    /** @return the setting to show PS entry point. */
+    public int getHidePrivateSpaceEntryPointSetting() {
+        return Settings.Secure.getInt(
+                mContext.getContentResolver(),
+                HIDE_PRIVATESPACE_ENTRY_POINT,
+                HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
+    }
+
+    private void resetPrivateSpaceSettings() {
+        setHidePrivateSpaceEntryPointSetting(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
+    }
 }
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 64699ff..29d136f 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -1724,6 +1724,9 @@
                 public List<SearchIndexableRaw> getRawDataToIndex(Context context,
                         boolean enabled) {
                     final List<SearchIndexableRaw> rawData = new ArrayList<>();
+                    if (!UserManager.supportsMultipleUsers()) {
+                        return rawData;
+                    }
 
                     SearchIndexableRaw allowMultipleUsersResult = new SearchIndexableRaw(context);
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
index 87cd544..14306e0 100644
--- a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
@@ -26,31 +26,39 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Activity;
 import android.content.ComponentName;
-import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.LayoutInflater;
 import android.widget.PopupWindow;
 import android.widget.SeekBar;
 
+import androidx.fragment.app.testing.EmptyFragmentActivity;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.preference.PreferenceViewHolder;
 import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.testutils.shadow.ShadowFragment;
-import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
 import com.android.settings.widget.LabeledSeekBarPreference;
+import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
+
+import com.google.android.setupcompat.util.WizardManagerHelper;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.LooperMode;
@@ -64,10 +72,16 @@
 @LooperMode(LooperMode.Mode.LEGACY)
 @Config(shadows = {ShadowInteractionJankMonitor.class})
 public class PreviewSizeSeekBarControllerTest {
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Rule
+    public ActivityScenarioRule<EmptyFragmentActivity> rule =
+            new ActivityScenarioRule<>(EmptyFragmentActivity.class);
     private static final String FONT_SIZE_KEY = "font_size";
     private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
-    @Spy
-    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private Activity mContext;
     private PreviewSizeSeekBarController mSeekBarController;
     private FontSizeData mFontSizeData;
     private LabeledSeekBarPreference mSeekBarPreference;
@@ -91,7 +105,9 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        ShadowInteractionJankMonitor.reset();
 
+        rule.getScenario().onActivity(activity -> mContext = activity);
         mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
         mFragment = spy(new TestFragment());
         when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
@@ -197,6 +213,24 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_REMOVE_QS_TOOLTIP_IN_SUW)
+    public void onProgressChanged_inSuw_toolTipShouldNotShown() {
+        Intent intent = mContext.getIntent();
+        intent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
+        mContext.setIntent(intent);
+        mSeekBarController.displayPreference(mPreferenceScreen);
+
+        // Simulate changing the progress for the first time
+        int newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
+        mSeekBarPreference.setProgress(newProgress);
+        mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
+                newProgress,
+                /* fromUser= */ false);
+
+        assertThat(getLatestPopupWindow()).isNull();
+    }
+
+    @Test
     public void onProgressChanged_tooltipViewHasBeenShown_notShowTooltipView() {
         mSeekBarController.displayPreference(mPreferenceScreen);
         // Simulate changing the progress for the first time
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 66211a2..2c59c26 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -60,6 +60,8 @@
 import com.android.settings.testutils.shadow.ShadowFragment;
 import com.android.settingslib.widget.TopIntroPreference;
 
+import com.google.android.setupcompat.util.WizardManagerHelper;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -302,6 +304,20 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.settings.accessibility.Flags.FLAG_REMOVE_QS_TOOLTIP_IN_SUW)
+    @Config(shadows = ShadowFragment.class)
+    public void onPreferenceToggledOnEnabledService_inSuw_toolTipViewShouldNotShow() {
+        Intent suwIntent = new Intent();
+        suwIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
+        when(mActivity.getIntent()).thenReturn(suwIntent);
+
+        mFragment.onPreferenceToggled(
+                ToggleFeaturePreferenceFragment.KEY_USE_SERVICE_PREFERENCE, /* enabled= */ true);
+
+        assertThat(getLatestPopupWindow()).isNull();
+    }
+
+    @Test
     @Config(shadows = ShadowFragment.class)
     public void onPreferenceToggledOnEnabledService_tooltipViewShown_notShowTooltipView() {
         mFragment.onPreferenceToggled(
diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
index 7b7c7a6..1d841fa 100644
--- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
@@ -239,6 +239,7 @@
         ReflectionHelpers.setField(mFragment, "mUnrestrictedData", unrestrictedDataPref);
         ReflectionHelpers.setField(mFragment, "mDataSaverBackend", dataSaverBackend);
         ReflectionHelpers.setField(mFragment.services, "mPolicyManager", networkPolicyManager);
+        ReflectionHelpers.setField(mFragment, "mContext", RuntimeEnvironment.application);
         when(mFragment.getListView()).thenReturn(mock(RecyclerView.class));
 
         ShadowRestrictedLockUtilsInternal.setRestricted(true);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
index cdf1514..bfa7cfa 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
@@ -19,138 +19,373 @@
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+import static com.android.settings.fuelgauge.datasaver.DynamicDenylistManager.PREF_KEY_MANUAL_DENYLIST_SYNCED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.NetworkPolicyManager;
 
 import org.junit.After;
 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 java.util.Collections;
+import java.util.List;
+
 @RunWith(RobolectricTestRunner.class)
 public class DynamicDenylistManagerTest {
 
-    private static final String FAKE_UID_1 = "package_uid_1";
-    private static final String FAKE_UID_2 = "package_uid_2";
+    private static final int[] EMPTY_ARRAY = new int[]{};
+    private static final String FAKE_UID_1 = "1001";
+    private static final String FAKE_UID_2 = "1002";
+    private static final int FAKE_UID_1_INT = Integer.parseInt(FAKE_UID_1);
+    private static final int FAKE_UID_2_INT = Integer.parseInt(FAKE_UID_2);
 
     private SharedPreferences mManualDenyListPref;
     private SharedPreferences mDynamicDenyListPref;
     private DynamicDenylistManager mDynamicDenylistManager;
-    private Context mContext;
+
+    @Mock
+    private NetworkPolicyManager mNetworkPolicyManager;
+    @Mock
+    private PackageManager mPackageManager;
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application.getApplicationContext();
-        mDynamicDenylistManager = new DynamicDenylistManager(mContext);
-        mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref();
-        mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref();
+        MockitoAnnotations.initMocks(this);
     }
 
     @After
     public void tearDown() {
-        mDynamicDenylistManager.clearManualDenylistPref();
-        mDynamicDenylistManager.clearDynamicDenylistPref();
+        mDynamicDenylistManager.clearSharedPreferences();
     }
 
     @Test
-    public void getManualDenylistPref_isEmpty() {
-        assertThat(mManualDenyListPref.getAll()).isEmpty();
+    public void init_withoutExistedRejectPolicy_createWithExpectedValue() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+
+        assertThat(mManualDenyListPref.getAll()).hasSize(1);
+        assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
     }
 
     @Test
-    public void getDynamicDenylistPref_isEmpty() {
-        assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+    public void init_withExistedRejectPolicy_createWithExpectedValue() {
+        initDynamicDenylistManager(new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+        assertThat(mManualDenyListPref.getAll()).hasSize(3);
+        assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_2));
     }
 
     @Test
     public void getManualDenylistPref_initiated_containsExpectedValue() {
-        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        initDynamicDenylistManager(EMPTY_ARRAY);
 
-        assertThat(mManualDenyListPref.getAll().size()).isEqualTo(1);
+        setupPreference(mManualDenyListPref, FAKE_UID_1);
+
         assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
     }
 
     @Test
     public void getDynamicDenylistPref_initiated_containsExpectedValue() {
-        mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        initDynamicDenylistManager(EMPTY_ARRAY);
 
-        assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+        setupPreference(mDynamicDenyListPref, FAKE_UID_1);
+
         assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
     }
 
     @Test
     public void updateManualDenylist_policyReject_addsUid() {
-        mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+        initDynamicDenylistManager(EMPTY_ARRAY);
 
-        assertThat(mManualDenyListPref.getAll()).hasSize(1);
+        mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT,
+                POLICY_REJECT_METERED_BACKGROUND);
+
         assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
     }
 
     @Test
     public void updateManualDenylist_policyNone_removesUid() {
-        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        initDynamicDenylistManager(EMPTY_ARRAY);
+        setupPreference(mManualDenyListPref, FAKE_UID_1);
         assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
 
-        mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_NONE);
+        mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT, POLICY_NONE);
 
-        assertThat(mManualDenyListPref.getAll()).isEmpty();
+        assertFalse(mManualDenyListPref.contains(FAKE_UID_1));
     }
 
     @Test
     public void updateManualDenylist_samePolicy_doNothing() {
-        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        initDynamicDenylistManager(EMPTY_ARRAY);
+        setupPreference(mManualDenyListPref, FAKE_UID_1);
         assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+        assertThat(mManualDenyListPref.getAll()).hasSize(2);
 
-        mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+        mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT,
+                POLICY_REJECT_METERED_BACKGROUND);
 
-        assertThat(mManualDenyListPref.getAll()).hasSize(1);
+        assertThat(mManualDenyListPref.getAll()).hasSize(2);
     }
 
     @Test
-    public void isManualDenylist_returnsFalse() {
-        assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+    public void setUidPolicyLocked_invokeSetUidPolicy() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+
+        mDynamicDenylistManager.setUidPolicyLocked(FAKE_UID_1_INT,
+                POLICY_REJECT_METERED_BACKGROUND);
+
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+        verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_1_INT),
+                eq(POLICY_REJECT_METERED_BACKGROUND));
     }
 
     @Test
-    public void isManualDenylist_incorrectUid_returnsFalse() {
+    public void setDenylist_emptyListAndNoData_doNothing() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+
+        mDynamicDenylistManager.setDenylist(Collections.emptyList());
+
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
+    }
+
+    @Test
+    public void setDenylist_uidDeniedAlready_doNothing()
+            throws PackageManager.NameNotFoundException {
+        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(FAKE_UID_1_INT);
+        initDynamicDenylistManager(new int[]{FAKE_UID_1_INT});
+
+        mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1));
+
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
+    }
+
+    @Test
+    public void setDenylist_sameList_doNothing() throws PackageManager.NameNotFoundException {
+        when(mPackageManager.getPackageUid(eq(FAKE_UID_1), eq(0))).thenReturn(FAKE_UID_1_INT);
+        when(mPackageManager.getPackageUid(eq(FAKE_UID_2), eq(0))).thenReturn(FAKE_UID_2_INT);
+        initDynamicDenylistManager(EMPTY_ARRAY);
+        setupPreference(mDynamicDenyListPref, FAKE_UID_2, FAKE_UID_1);
+
+        mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1, FAKE_UID_2));
+
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
+    }
+
+    @Test
+    public void setDenylist_newListWithOldData_modifyPolicyNoneAndReject()
+            throws PackageManager.NameNotFoundException {
+        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(
+                Integer.parseInt(FAKE_UID_1));
+        initDynamicDenylistManager(EMPTY_ARRAY);
+        setupPreference(mDynamicDenyListPref, FAKE_UID_2);
+
+        mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1));
+
+        verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE);
+        verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT,
+                POLICY_REJECT_METERED_BACKGROUND);
+        assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+        assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+    }
+
+    @Test
+    public void setDenylist_newListWithoutOldData_modifyPolicyReject()
+            throws PackageManager.NameNotFoundException {
+        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(
+                Integer.parseInt(FAKE_UID_1));
+        initDynamicDenylistManager(EMPTY_ARRAY);
+
+        mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1));
+
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+        verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT,
+                POLICY_REJECT_METERED_BACKGROUND);
+        assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+        assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+    }
+
+    @Test
+    public void setDenylist_emptyListWithOldData_modifyPolicyNone() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+        setupPreference(mDynamicDenyListPref, FAKE_UID_2);
+
+        mDynamicDenylistManager.setDenylist(Collections.emptyList());
+
+        verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE);
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(),
+                eq(POLICY_REJECT_METERED_BACKGROUND));
+        assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+    }
+
+    @Test
+    public void isInManualDenylist_returnsFalse() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+
+        assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
+    }
+
+    @Test
+    public void isInManualDenylist_incorrectUid_returnsFalse() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+
         mManualDenyListPref.edit().putInt(FAKE_UID_2, POLICY_REJECT_METERED_BACKGROUND).apply();
 
-        assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+        assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
     }
 
     @Test
-    public void isManualDenylist_initiated_returnsTrue() {
+    public void isInManualDenylist_initiated_returnsTrue() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+
         mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
 
-        assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+        assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
     }
 
     @Test
-    public void clearManualDenylistPref_isEmpty() {
-        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+    public void resetDenylistIfNeeded_nullPackageName_doNothing() {
+        initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded(null, false);
+
         assertThat(mManualDenyListPref.getAll()).hasSize(1);
-        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+    }
 
-        mDynamicDenylistManager.clearManualDenylistPref();
+    @Test
+    public void resetDenylistIfNeeded_invalidPackageName_doNothing() {
+        initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", false);
+
+        assertThat(mManualDenyListPref.getAll()).hasSize(1);
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+    }
+
+    @Test
+    public void resetDenylistIfNeeded_denylistUnchanged_doNothingWithPolicy() {
+        initDynamicDenylistManager(new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false);
+
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+    }
+
+    @Test
+    public void resetDenylistIfNeeded_denylistChanged_resetAndClear() {
+        initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false);
+
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+        verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
+    }
+
+    @Test
+    public void resetDenylistIfNeeded_forceResetWithNullPackageName_resetAndClear() {
+        initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_2_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded(null, true);
+
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+        verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_2_INT), eq(POLICY_NONE));
+    }
+
+    @Test// 4
+    public void resetDenylistIfNeeded_forceResetWithInvalidPackageName_resetAndClear() {
+        initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", true);
+
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+        verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
+    }
+
+    @Test
+    public void resetDenylistIfNeeded_forceResetButDenylistUnchanged_doNothingWithPolicy() {
+        initDynamicDenylistManager(new int[]{FAKE_UID_1_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true);
+
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+        verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+    }
+
+    @Test
+    public void resetDenylistIfNeeded_forceResetWithDenylistChanged_resetAndClear() {
+        initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+        mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true);
+
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+        verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
+    }
+
+    @Test
+    public void clearSharedPreferences_manualDenyListPrefIsEmpty() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
+        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        assertThat(mManualDenyListPref.getAll()).hasSize(2);
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+        assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
+
+        mDynamicDenylistManager.clearSharedPreferences();
 
         assertThat(mManualDenyListPref.getAll()).isEmpty();
     }
 
     @Test
-    public void clearDynamicDenylistPref_isEmpty() {
+    public void clearSharedPreferences_dynamicDenyListPrefIsEmpty() {
+        initDynamicDenylistManager(EMPTY_ARRAY);
         mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
         assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
         assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
 
-        mDynamicDenylistManager.clearDynamicDenylistPref();
+        mDynamicDenylistManager.clearSharedPreferences();
 
         assertThat(mDynamicDenyListPref.getAll()).isEmpty();
     }
+
+    private void initDynamicDenylistManager(int[] preload) {
+        initDynamicDenylistManager(preload, preload);
+    }
+    private void initDynamicDenylistManager(int[] preload1, int[] preload2) {
+        final Context context = spy(RuntimeEnvironment.application.getApplicationContext());
+        when(context.getApplicationContext()).thenReturn(context);
+        when(context.getPackageManager()).thenReturn(mPackageManager);
+        when(mNetworkPolicyManager.getUidsWithPolicy(anyInt()))
+                .thenReturn(preload1).thenReturn(preload2);
+        mDynamicDenylistManager = new DynamicDenylistManager(context, mNetworkPolicyManager);
+        mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref();
+        mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref();
+    }
+
+    private void setupPreference(SharedPreferences sharedPreferences, String... uids) {
+        for (String uid : uids) {
+            sharedPreferences.edit().putInt(uid, POLICY_REJECT_METERED_BACKGROUND).apply();
+        }
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
index 3b5dbd6..d394582 100644
--- a/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
@@ -113,8 +113,14 @@
         final List<String> allKeys =
                 XmlTestUtils.getKeysFromPreferenceXml(
                         mContext, R.layout.content_protection_preference_fragment);
+        final List<String> nonIndexableKeysExpected =
+                List.of(
+                        "content_protection_preference_top_intro",
+                        "content_protection_preference_subpage_illustration",
+                        "content_protection_preference_user_consent_work_profile_switch");
 
         assertThat(allKeys).containsAtLeastElementsIn(nonIndexableKeys);
+        assertThat(nonIndexableKeys).isEqualTo(nonIndexableKeysExpected);
     }
 
     @Test
@@ -132,7 +138,7 @@
                 XmlTestUtils.getKeysFromPreferenceXml(
                         mContext, R.layout.content_protection_preference_fragment);
 
-        assertThat(nonIndexableKeys).containsAnyIn(allKeys);
+        assertThat(nonIndexableKeys).isEqualTo(allKeys);
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
new file mode 100644
index 0000000..0a2f3d1
--- /dev/null
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.privatespace;
+
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PrivateSpaceMaintainerTest {
+    private Context mContext;
+    private ContentResolver mContentResolver;
+
+    /** Required setup before a test. */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = ApplicationProvider.getApplicationContext();
+        mContentResolver = mContext.getContentResolver();
+    }
+
+    /** Tests that {@link PrivateSpaceMaintainer#deletePrivateSpace()} deletes PS when PS exists. */
+    @Test
+    public void deletePrivateSpace_psExists_deletesPS() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.createPrivateSpace();
+        ErrorDeletingPrivateSpace errorDeletingPrivateSpace =
+                privateSpaceMaintainer.deletePrivateSpace();
+        assertThat(errorDeletingPrivateSpace)
+                .isEqualTo(ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE);
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isFalse();
+    }
+
+    /**
+     * Tests that {@link PrivateSpaceMaintainer#deletePrivateSpace()} returns error when PS does
+     * not exist.
+     */
+    @Test
+    public void deletePrivateSpace_psDoesNotExist_returnsNoPSError() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        ErrorDeletingPrivateSpace errorDeletingPrivateSpace =
+                privateSpaceMaintainer.deletePrivateSpace();
+        assertThat(errorDeletingPrivateSpace)
+                .isEqualTo(ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NO_PRIVATE_SPACE);
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isFalse();
+    }
+
+    /** Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exists creates PS. */
+    @Test
+    public void createPrivateSpace_psDoesNotExist_createsPS() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.deletePrivateSpace();
+        assertThat(privateSpaceMaintainer.createPrivateSpace()).isTrue();
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+    }
+
+    /**
+     * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exists still
+     * returns true.
+     */
+    @Test
+    public void createPrivateSpace_psExists_returnsFalse() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.deletePrivateSpace();
+        assertThat(privateSpaceMaintainer.createPrivateSpace()).isTrue();
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+        assertThat(privateSpaceMaintainer.createPrivateSpace()).isTrue();
+    }
+
+    /**
+     * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when no PS exists resets PS
+     * Settings.
+     */
+    @Test
+    public void createPrivateSpace_psDoesNotExist_resetsPSSettings() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        Settings.Secure.putInt(
+                mContentResolver,
+                Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT,
+                HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL);
+
+        privateSpaceMaintainer.deletePrivateSpace();
+        privateSpaceMaintainer.createPrivateSpace();
+        assertThat(privateSpaceMaintainer.getHidePrivateSpaceEntryPointSetting())
+                .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
+    }
+
+    /**
+     * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exist does not reset
+     * PS Settings.
+     */
+    @Test
+    public void createPrivateSpace_psExists_doesNotResetPSSettings() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.createPrivateSpace();
+        Settings.Secure.putInt(
+                mContentResolver,
+                Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT,
+                HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL);
+
+        privateSpaceMaintainer.createPrivateSpace();
+        assertThat(privateSpaceMaintainer.getHidePrivateSpaceEntryPointSetting())
+                .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL);
+    }
+}