Merge "Don't show quick settings tooltip if the user is in the Setup Wizard, since the user can't access the Quick Settings Panel." into main
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/res/layout/vpn_dialog.xml b/res/layout/vpn_dialog.xml
index 892a176..062772e 100644
--- a/res/layout/vpn_dialog.xml
+++ b/res/layout/vpn_dialog.xml
@@ -66,25 +66,6 @@
                 <EditText style="@style/vpn_value"
                         android:id="@+id/server"/>
 
-                <CheckBox style="@style/vpn_value"
-                        android:id="@+id/mppe"
-                        android:text="@string/vpn_mppe"
-                        android:visibility="gone"/>
-
-                <LinearLayout android:id="@+id/l2tp"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:orientation="vertical"
-                        android:visibility="gone">
-                    <TextView style="@style/vpn_label"
-                            android:text="@string/vpn_l2tp_secret"
-                            android:labelFor="@+id/l2tp_secret"/>
-                    <EditText style="@style/vpn_value"
-                            android:id="@+id/l2tp_secret"
-                            android:password="true"
-                            android:hint="@string/vpn_not_used"/>
-                </LinearLayout>
-
                 <LinearLayout android:id="@+id/options_ipsec_identity"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
@@ -154,31 +135,6 @@
                     android:layout_height="wrap_content"
                     android:orientation="vertical"
                     android:visibility="gone">
-                <LinearLayout android:id="@+id/network_options"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:orientation="vertical">
-                    <TextView style="@style/vpn_label"
-                            android:text="@string/vpn_search_domains"
-                            android:labelFor="@+id/search_domains"/>
-                    <EditText style="@style/vpn_value"
-                            android:id="@+id/search_domains"
-                            android:hint="@string/vpn_not_used"/>
-
-                    <TextView style="@style/vpn_label"
-                            android:text="@string/vpn_dns_servers"
-                            android:labelFor="@+id/dns_servers"/>
-                    <EditText style="@style/vpn_value"
-                            android:id="@+id/dns_servers"
-                            android:hint="@string/vpn_not_used"/>
-
-                    <TextView style="@style/vpn_label"
-                            android:text="@string/vpn_routes"
-                            android:labelFor="@+id/routes"/>
-                    <EditText style="@style/vpn_value"
-                            android:id="@+id/routes"
-                            android:hint="@string/vpn_not_used"/>
-                </LinearLayout>
 
                 <TextView android:id="@+id/vpn_proxy_settings_title"
                           style="@style/vpn_label"
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 7283be2..f84afec 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -872,15 +872,9 @@
         <item>1</item>
     </string-array>
 
-    <!-- Match this with the constants in VpnProfile. --> <skip />
+    <!-- Match this with the array VPN_TYPES in ConfigDialog. --> <skip />
     <!-- Short names for each VPN type, not really translatable. [CHAR LIMIT=20] -->
     <string-array name="vpn_types" translatable="false">
-        <item>PPTP</item>
-        <item>L2TP/IPSec PSK</item>
-        <item>L2TP/IPSec RSA</item>
-        <item>IPSec Xauth PSK</item>
-        <item>IPSec Xauth RSA</item>
-        <item>IPSec Hybrid RSA</item>
         <item>IKEv2/IPSec MSCHAPv2</item>
         <item>IKEv2/IPSec PSK</item>
         <item>IKEv2/IPSec RSA</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7406295..cffaaa8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6440,10 +6440,6 @@
     <string name="vpn_type">Type</string>
     <!-- Input label for the server address of a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_server">Server address</string>
-    <!-- Checkbox label to enable PPP encryption for a VPN profile. [CHAR LIMIT=40] -->
-    <string name="vpn_mppe">PPP encryption (MPPE)</string>
-    <!-- Input label for the L2TP secret of a VPN profile. [CHAR LIMIT=40] -->
-    <string name="vpn_l2tp_secret">L2TP secret</string>
     <!-- Input label for the IPSec identifier of a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_ipsec_identifier">IPSec identifier</string>
     <!-- Input label for the IPSec pre-shared key of a VPN profile. [CHAR LIMIT=40] -->
@@ -6456,12 +6452,6 @@
     <string name="vpn_ipsec_server_cert">IPSec server certificate</string>
     <!-- Checkbox label to show advanced options of a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_show_options">Show advanced options</string>
-    <!-- Input label for the DNS search domains of a VPN profile. [CHAR LIMIT=40] -->
-    <string name="vpn_search_domains">DNS search domains</string>
-    <!-- Input label for the DNS servers of a VPN profile. [CHAR LIMIT=40] -->
-    <string name="vpn_dns_servers">DNS servers (e.g. 8.8.8.8)</string>
-    <!-- Input label for the forwarding routes of a VPN profile. [CHAR LIMIT=40] -->
-    <string name="vpn_routes">Forwarding routes (e.g. 10.0.0.0/8)</string>
     <!-- Input label for the username of a VPN profile. [CHAR LIMIT=40] -->
     <string name="vpn_username">Username</string>
     <!-- Input label for the password of a VPN profile. [CHAR LIMIT=40] -->
@@ -6475,22 +6465,6 @@
     <!-- Option to use the server certificate received from the VPN server. [CHAR LIMIT=40] -->
     <string name="vpn_no_server_cert">(received from server)</string>
     <!-- Error message displayed below the always-on VPN checkbox when the checkbox is disabled:
-        the selected VPN type doesn't support always-on. [CHAR LIMIT=120] -->
-    <string name="vpn_always_on_invalid_reason_type">This VPN type can\'t stay connected at all
-        times</string>
-    <!-- Error message displayed below the always-on VPN checkbox when the checkbox is disabled:
-        the server address is not in numeric form (e.g. 8.8.8.8). [CHAR LIMIT=120] -->
-    <string name="vpn_always_on_invalid_reason_server">Always-on VPN only supports numeric server
-        addresses</string>
-    <!-- Error message displayed below the always-on VPN checkbox when the checkbox is disabled:
-        no DNS is found. [CHAR LIMIT=120] -->
-    <string name="vpn_always_on_invalid_reason_no_dns">A DNS server must be specified for always-on
-        VPN</string>
-    <!-- Error message displayed below the always-on VPN checkbox when the checkbox is disabled:
-        DNS server addresses are not in numeric form (e.g. 8.8.8.8). [CHAR LIMIT=120] -->
-    <string name="vpn_always_on_invalid_reason_dns">DNS server addresses must be numeric for
-        always-on VPN</string>
-    <!-- Error message displayed below the always-on VPN checkbox when the checkbox is disabled:
         generic error. [CHAR LIMIT=120] -->
     <string name="vpn_always_on_invalid_reason_other">The information entered doesn\'t support
         always-on VPN</string>
diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java
index 0aba5ca..7b96d42 100644
--- a/src/com/android/settings/MainClear.java
+++ b/src/com/android/settings/MainClear.java
@@ -26,11 +26,13 @@
 import android.accounts.AuthenticatorDescription;
 import android.app.ActionBar;
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
 import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -43,6 +45,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.image.DynamicSystemManager;
 import android.provider.Settings;
 import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
@@ -266,6 +269,19 @@
                 return;
             }
 
+            final DynamicSystemManager dsuManager = (DynamicSystemManager)
+                    getActivity().getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
+            if (dsuManager.isInUse()) {
+                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+                builder.setTitle(R.string.dsu_is_running);
+                builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int id) {}
+                });
+                AlertDialog dsuAlertdialog = builder.create();
+                dsuAlertdialog.show();
+                return;
+            }
+
             if (runKeyguardConfirmation(KEYGUARD_REQUEST)) {
                 return;
             }
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/AudioSharingBasePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
new file mode 100644
index 0000000..9ebe26d
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
@@ -0,0 +1,70 @@
+/*
+ * 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.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.flags.Flags;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+public abstract class AudioSharingBasePreferenceController extends BasePreferenceController {
+    private final LocalBluetoothManager mBtManager;
+    protected final LocalBluetoothLeBroadcast mBroadcast;
+    protected Preference mPreference;
+
+    public AudioSharingBasePreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mBtManager = Utils.getLocalBtManager(context);
+        mBroadcast =
+                mBtManager == null
+                        ? null
+                        : mBtManager.getProfileManager().getLeAudioBroadcastProfile();
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mBtManager != null && Flags.enableLeAudioSharing()
+                ? AVAILABLE
+                : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+        updateVisibility(isBroadcasting());
+    }
+
+    /**
+     * Update the visibility of the preference.
+     *
+     * @param isVisible the latest visibility state for the preference.
+     */
+    public void updateVisibility(boolean isVisible) {
+        mPreference.setVisible(isVisible);
+    }
+
+    private 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 b3b7a2c..40207be 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
@@ -25,11 +25,14 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.widget.SettingsMainSwitchBar;
 
-public class AudioSharingDashboardFragment extends DashboardFragment {
+public class AudioSharingDashboardFragment extends DashboardFragment
+        implements AudioSharingSwitchBarController.OnSwitchBarChangedListener {
     private static final String TAG = "AudioSharingDashboardFrag";
 
     SettingsMainSwitchBar mMainSwitchBar;
     private AudioSharingSwitchBarController mSwitchBarController;
+    private CallsAndAlarmsPreferenceController mCallsAndAlarmsPreferenceController;
+    private AudioSharingNamePreferenceController mAudioSharingNamePreferenceController;
 
     public AudioSharingDashboardFragment() {
         super();
@@ -63,7 +66,9 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        use(CallsAndAlarmsPreferenceController.class).init(this);
+        mCallsAndAlarmsPreferenceController = use(CallsAndAlarmsPreferenceController.class);
+        mCallsAndAlarmsPreferenceController.init(this);
+        mAudioSharingNamePreferenceController = use(AudioSharingNamePreferenceController.class);
     }
 
     @Override
@@ -74,9 +79,19 @@
         final SettingsActivity activity = (SettingsActivity) getActivity();
         mMainSwitchBar = activity.getSwitchBar();
         mMainSwitchBar.setTitle(getText(R.string.audio_sharing_switch_title));
-        mSwitchBarController = new AudioSharingSwitchBarController(activity, mMainSwitchBar);
+        mSwitchBarController = new AudioSharingSwitchBarController(activity, mMainSwitchBar, this);
         mSwitchBarController.init(this);
         getSettingsLifecycle().addObserver(mSwitchBarController);
         mMainSwitchBar.show();
     }
+
+    @Override
+    public void onSwitchBarChanged(boolean newState) {
+        updateVisibilityForAttachedPreferences(newState);
+    }
+
+    private void updateVisibilityForAttachedPreferences(boolean isVisible) {
+        mCallsAndAlarmsPreferenceController.updateVisibility(isVisible);
+        mAudioSharingNamePreferenceController.updateVisibility(isVisible);
+    }
 }
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/AudioSharingNamePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
index 18c9bfd..8336691 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
@@ -22,13 +22,10 @@
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
 
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.flags.Flags;
 import com.android.settings.widget.ValidatedEditTextPreference;
 
-public class AudioSharingNamePreferenceController extends BasePreferenceController
+public class AudioSharingNamePreferenceController extends AudioSharingBasePreferenceController
         implements ValidatedEditTextPreference.Validator,
                 Preference.OnPreferenceChangeListener,
                 DefaultLifecycleObserver {
@@ -37,8 +34,6 @@
 
     private static final String PREF_KEY = "audio_sharing_stream_name";
 
-    protected Preference mPreference;
-
     private AudioSharingNameTextValidator mAudioSharingNameTextValidator;
 
     public AudioSharingNamePreferenceController(Context context) {
@@ -47,11 +42,6 @@
     }
 
     @Override
-    public int getAvailabilityStatus() {
-        return Flags.enableLeAudioSharing() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
-    }
-
-    @Override
     public String getPreferenceKey() {
         return PREF_KEY;
     }
@@ -63,12 +53,6 @@
     }
 
     @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        mPreference = screen.findPreference(getPreferenceKey());
-    }
-
-    @Override
     public boolean isTextValid(String value) {
         return mAudioSharingNameTextValidator.isTextValid(value);
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 83367ae..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;
@@ -58,11 +53,17 @@
         implements DefaultLifecycleObserver, OnCheckedChangeListener {
     private static final String TAG = "AudioSharingSwitchBarCtl";
     private static final String PREF_KEY = "audio_sharing_main_switch";
+
+    interface OnSwitchBarChangedListener {
+        void onSwitchBarChanged(boolean newState);
+    }
+
     private final SettingsMainSwitchBar mSwitchBar;
     private final LocalBluetoothManager mBtManager;
     private final LocalBluetoothLeBroadcast mBroadcast;
     private final LocalBluetoothLeBroadcastAssistant mAssistant;
     private final Executor mExecutor;
+    private final OnSwitchBarChangedListener mListener;
     private DashboardFragment mFragment;
     private List<BluetoothDevice> mTargetSinks = new ArrayList<>();
 
@@ -196,9 +197,11 @@
                         BluetoothLeBroadcastReceiveState state) {}
             };
 
-    AudioSharingSwitchBarController(Context context, SettingsMainSwitchBar switchBar) {
+    AudioSharingSwitchBarController(
+            Context context, SettingsMainSwitchBar switchBar, OnSwitchBarChangedListener listener) {
         super(context, PREF_KEY);
         mSwitchBar = switchBar;
+        mListener = listener;
         mBtManager = Utils.getLocalBtManager(context);
         mBroadcast = mBtManager.getProfileManager().getLeAudioBroadcastProfile();
         mAssistant = mBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
@@ -265,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);
             }
         }
@@ -326,8 +328,12 @@
     private void updateSwitch() {
         ThreadUtils.postOnMainThread(
                 () -> {
-                    mSwitchBar.setChecked(isBroadcasting());
+                    boolean isBroadcasting = isBroadcasting();
+                    if (mSwitchBar.isChecked() != isBroadcasting) {
+                        mSwitchBar.setChecked(isBroadcasting);
+                    }
                     mSwitchBar.setEnabled(true);
+                    mListener.onSwitchBarChanged(isBroadcasting);
                 });
     }
 
@@ -335,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/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
index 480b257..44e75ec 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
@@ -19,21 +19,16 @@
 import android.content.Context;
 import android.util.Log;
 
-import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settings.core.BasePreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.flags.Flags;
 
 /** PreferenceController to control the dialog to choose the active device for calls and alarms */
-public class CallsAndAlarmsPreferenceController extends BasePreferenceController {
+public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferenceController {
 
     private static final String TAG = "CallsAndAlarmsPreferenceController";
 
     private static final String PREF_KEY = "calls_and_alarms";
-
-    private Preference mPreference;
     private DashboardFragment mFragment;
 
     public CallsAndAlarmsPreferenceController(Context context) {
@@ -41,11 +36,6 @@
     }
 
     @Override
-    public int getAvailabilityStatus() {
-        return Flags.enableLeAudioSharing() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
-    }
-
-    @Override
     public String getPreferenceKey() {
         return PREF_KEY;
     }
@@ -53,7 +43,6 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
-        mPreference = screen.findPreference(getPreferenceKey());
         mPreference.setOnPreferenceClickListener(
                 preference -> {
                     if (mFragment != null) {
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/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java
index 482858f..e75ab1a 100644
--- a/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java
+++ b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java
@@ -52,9 +52,6 @@
     private boolean mShouldToggleSwitchBackOnRebootDialogDismiss;
 
     @VisibleForTesting
-    static final String PROPERTY_RO_GFX_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
-
-    @VisibleForTesting
     static final String PROPERTY_PERSISTENT_GRAPHICS_EGL = "persist.graphics.egl";
 
     @VisibleForTesting
@@ -97,11 +94,6 @@
         return mSystemProperties.getBoolean(PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION, false);
     }
 
-    private boolean isAngleSupported() {
-        return TextUtils.equals(
-                        mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
-    }
-
     @VisibleForTesting
     GraphicsDriverEnableAngleAsSystemDriverController(
             Context context, DevelopmentSettingsDashboardFragment fragment, Injector injector) {
@@ -145,10 +137,6 @@
 
     /** Return the default value of "persist.graphics.egl" */
     public boolean isDefaultValue() {
-        if (!isAngleSupported()) {
-            return true;
-        }
-
         final String currentGlesDriver =
                 mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
         // default value of "persist.graphics.egl" is ""
@@ -158,17 +146,11 @@
     @Override
     public void updateState(Preference preference) {
         super.updateState(preference);
-        if (isAngleSupported()) {
-            // set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor
-            // set switch off otherwise.
-            final String currentGlesDriver =
-                    mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
-            final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver);
-            ((TwoStatePreference) mPreference).setChecked(isAngle);
-        } else {
-            mPreference.setEnabled(false);
-            ((TwoStatePreference) mPreference).setChecked(false);
-        }
+        // set switch on if "persist.graphics.egl" is "angle".
+        final String currentGlesDriver =
+                mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
+        final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver);
+        ((TwoStatePreference) mPreference).setChecked(isAngle);
 
         // Disable the developer option toggle UI if ANGLE is disabled, this means next time the
         // debug property needs to be set to true again to enable ANGLE. If ANGLE is enabled, don't
@@ -182,12 +164,10 @@
     protected void onDeveloperOptionsSwitchDisabled() {
         // 1) disable the switch
         super.onDeveloperOptionsSwitchDisabled();
-        if (isAngleSupported()) {
-            // 2) set the persist.graphics.egl empty string
-            GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
-            // 3) reset the switch
-            ((TwoStatePreference) mPreference).setChecked(false);
-        }
+        // 2) set the persist.graphics.egl empty string
+        GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
+        // 3) reset the switch
+        ((TwoStatePreference) mPreference).setChecked(false);
     }
 
     void toggleSwitchBack() {
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/batteryusage/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
index a8be398..bad1b76 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
@@ -27,10 +27,12 @@
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
 import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batteryusage.BatteryEntry.NameAndIcon;
 import com.android.settingslib.utils.StringUtil;
 
 import java.util.Comparator;
@@ -40,16 +42,23 @@
 /** A container class to carry battery data in a specific time slot. */
 public class BatteryDiffEntry {
     private static final String TAG = "BatteryDiffEntry";
+    private static final Object sResourceCacheLock = new Object();
+    private static final Object sPackageNameAndUidCacheLock = new Object();
+    private static final Object sValidForRestrictionLock = new Object();
 
     static Locale sCurrentLocale = null;
+
     // Caches app label and icon to improve loading performance.
-    static final Map<String, BatteryEntry.NameAndIcon> sResourceCache = new ArrayMap<>();
+    @GuardedBy("sResourceCacheLock")
+    static final Map<String, NameAndIcon> sResourceCache = new ArrayMap<>();
 
     // Caches package name and uid to improve loading performance.
+    @GuardedBy("sPackageNameAndUidCacheLock")
     static final Map<String, Integer> sPackageNameAndUidCache = new ArrayMap<>();
 
     // Whether a specific item is valid to launch restriction page?
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    @GuardedBy("sValidForRestrictionLock")
     static final Map<String, Boolean> sValidForRestriction = new ArrayMap<>();
 
     /** A comparator for {@link BatteryDiffEntry} based on the sorting key. */
@@ -304,12 +313,16 @@
     }
 
     private int getPackageUid(String packageName) {
-        if (sPackageNameAndUidCache.containsKey(packageName)) {
-            return sPackageNameAndUidCache.get(packageName);
+        synchronized (sPackageNameAndUidCacheLock) {
+            if (sPackageNameAndUidCache.containsKey(packageName)) {
+                return sPackageNameAndUidCache.get(packageName);
+            }
         }
 
         int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName);
-        sPackageNameAndUidCache.put(packageName, uid);
+        synchronized (sPackageNameAndUidCacheLock) {
+            sPackageNameAndUidCache.put(packageName, uid);
+        }
         return uid;
     }
 
@@ -318,13 +331,16 @@
             return;
         }
         // Checks whether we have cached data or not first before fetching.
-        final BatteryEntry.NameAndIcon nameAndIcon = getCache();
+        final NameAndIcon nameAndIcon = getCache();
         if (nameAndIcon != null) {
             mAppLabel = nameAndIcon.mName;
             mAppIcon = nameAndIcon.mIcon;
             mAppIconId = nameAndIcon.mIconId;
         }
-        final Boolean validForRestriction = sValidForRestriction.get(getKey());
+        Boolean validForRestriction = null;
+        synchronized (sValidForRestrictionLock) {
+            validForRestriction = sValidForRestriction.get(getKey());
+        }
         if (validForRestriction != null) {
             mValidForRestriction = validForRestriction;
         }
@@ -336,33 +352,34 @@
 
         // Configures whether we can launch restriction page or not.
         updateRestrictionFlagState();
-        sValidForRestriction.put(getKey(), Boolean.valueOf(mValidForRestriction));
+        synchronized (sValidForRestrictionLock) {
+            sValidForRestriction.put(getKey(), Boolean.valueOf(mValidForRestriction));
+        }
 
         if (getKey() != null && SPECIAL_ENTRY_MAP.containsKey(getKey())) {
             Pair<Integer, Integer> pair = SPECIAL_ENTRY_MAP.get(getKey());
             mAppLabel = mContext.getString(pair.first);
             mAppIconId = pair.second;
             mAppIcon = mContext.getDrawable(mAppIconId);
-            sResourceCache.put(
-                    getKey(), new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, mAppIconId));
+            putResourceCache(getKey(), new NameAndIcon(mAppLabel, mAppIcon, mAppIconId));
             return;
         }
 
         // Loads application icon and label based on consumer type.
         switch (mConsumerType) {
             case ConvertUtils.CONSUMER_TYPE_USER_BATTERY:
-                final BatteryEntry.NameAndIcon nameAndIconForUser =
+                final NameAndIcon nameAndIconForUser =
                         BatteryEntry.getNameAndIconFromUserId(mContext, (int) mUserId);
                 if (nameAndIconForUser != null) {
                     mAppIcon = nameAndIconForUser.mIcon;
                     mAppLabel = nameAndIconForUser.mName;
-                    sResourceCache.put(
+                    putResourceCache(
                             getKey(),
-                            new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, /* iconId= */ 0));
+                            new NameAndIcon(mAppLabel, mAppIcon, /* iconId= */ 0));
                 }
                 break;
             case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
-                final BatteryEntry.NameAndIcon nameAndIconForSystem =
+                final NameAndIcon nameAndIconForSystem =
                         BatteryEntry.getNameAndIconFromPowerComponent(mContext, mComponentId);
                 if (nameAndIconForSystem != null) {
                     mAppLabel = nameAndIconForSystem.mName;
@@ -370,9 +387,8 @@
                         mAppIconId = nameAndIconForSystem.mIconId;
                         mAppIcon = mContext.getDrawable(nameAndIconForSystem.mIconId);
                     }
-                    sResourceCache.put(
-                            getKey(),
-                            new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, mAppIconId));
+                    putResourceCache(
+                            getKey(), new NameAndIcon(mAppLabel, mAppIcon, mAppIconId));
                 }
                 break;
             case ConvertUtils.CONSUMER_TYPE_UID_BATTERY:
@@ -384,9 +400,9 @@
                 // Adds badge icon into app icon for work profile.
                 mAppIcon = getBadgeIconForUser(mAppIcon);
                 if (mAppLabel != null || mAppIcon != null) {
-                    sResourceCache.put(
+                    putResourceCache(
                             getKey(),
-                            new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, /* iconId= */ 0));
+                            new NameAndIcon(mAppLabel, mAppIcon, /* iconId= */ 0));
                 }
                 break;
         }
@@ -429,7 +445,7 @@
         }
     }
 
-    private BatteryEntry.NameAndIcon getCache() {
+    private NameAndIcon getCache() {
         final Locale locale = Locale.getDefault();
         if (sCurrentLocale != locale) {
             Log.d(
@@ -440,7 +456,9 @@
             sCurrentLocale = locale;
             clearCache();
         }
-        return sResourceCache.get(getKey());
+        synchronized (sResourceCacheLock) {
+            return sResourceCache.get(getKey());
+        }
     }
 
     private void loadNameAndIconForUid() {
@@ -469,13 +487,13 @@
         final String[] packages = packageManager.getPackagesForUid(uid);
         // Loads special defined application label and icon if available.
         if (packages == null || packages.length == 0) {
-            final BatteryEntry.NameAndIcon nameAndIcon =
+            final NameAndIcon nameAndIcon =
                     BatteryEntry.getNameAndIconFromUid(mContext, mAppLabel, uid);
             mAppLabel = nameAndIcon.mName;
             mAppIcon = nameAndIcon.mIcon;
         }
 
-        final BatteryEntry.NameAndIcon nameAndIcon =
+        final NameAndIcon nameAndIcon =
                 BatteryEntry.loadNameAndIcon(
                         mContext, uid, /* batteryEntry= */ null, packageName, mAppLabel, mAppIcon);
         // Clears BatteryEntry internal cache since we will have another one.
@@ -544,9 +562,21 @@
 
     /** Clears all cache data. */
     public static void clearCache() {
-        sResourceCache.clear();
-        sValidForRestriction.clear();
-        sPackageNameAndUidCache.clear();
+        synchronized (sResourceCacheLock) {
+            sResourceCache.clear();
+        }
+        synchronized (sValidForRestrictionLock) {
+            sValidForRestriction.clear();
+        }
+        synchronized (sPackageNameAndUidCacheLock) {
+            sPackageNameAndUidCache.clear();
+        }
+    }
+
+    private static void putResourceCache(String key, NameAndIcon nameAndIcon) {
+        synchronized (sResourceCacheLock) {
+            sResourceCache.put(key, nameAndIcon);
+        }
     }
 
     private Drawable getBadgeIconForUser(Drawable icon) {
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/localepicker/LocaleLinearLayoutManager.java b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
index 8795bf9..59a5fb0 100644
--- a/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
+++ b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
@@ -43,6 +43,7 @@
 public class LocaleLinearLayoutManager extends LinearLayoutManager {
     private final LocaleDragAndDropAdapter mAdapter;
     private final Context mContext;
+    private LocaleListEditor mLocaleListEditor;
 
     private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveUp;
     private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveDown;
@@ -147,8 +148,12 @@
         }
 
         if (result) {
-            mAdapter.doTheUpdate();
+            mLocaleListEditor.showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
         }
         return result;
     }
+
+    public void setLocaleListEditor(LocaleListEditor localeListEditor) {
+        mLocaleListEditor = localeListEditor;
+    }
 }
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 28f066a..59a39c8 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -472,6 +472,7 @@
     private void configureDragAndDrop(LayoutPreference layout) {
         final RecyclerView list = layout.findViewById(R.id.dragList);
         final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter);
+        llm.setLocaleListEditor(this);
         llm.setAutoMeasureEnabled(true);
         list.setLayoutManager(llm);
         list.setHasFixedSize(true);
@@ -505,7 +506,7 @@
         return false;
     }
 
-    private void showConfirmDialog(boolean isFirstRemoved, LocaleStore.LocaleInfo localeInfo) {
+    public void showConfirmDialog(boolean isFirstRemoved, LocaleStore.LocaleInfo localeInfo) {
         Locale currentSystemLocale = LocalePicker.getLocales().get(0);
         if (!localeInfo.getLocale().equals(currentSystemLocale)) {
             final LocaleDialogFragment localeDialogFragment =
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/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
index 036487d..1c001cb 100644
--- a/src/com/android/settings/vpn2/ConfigDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.vpn2;
 
-import static com.android.internal.net.VpnProfile.isLegacyType;
-
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
@@ -43,9 +41,6 @@
 import com.android.settings.R;
 import com.android.settings.utils.AndroidKeystoreAliasLoader;
 
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
@@ -60,13 +55,18 @@
         View.OnClickListener, AdapterView.OnItemSelectedListener,
         CompoundButton.OnCheckedChangeListener {
     private static final String TAG = "ConfigDialog";
+    // Vpn profile constants to match with R.array.vpn_types.
+    private static final List<Integer> VPN_TYPES = List.of(
+            VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS,
+            VpnProfile.TYPE_IKEV2_IPSEC_PSK,
+            VpnProfile.TYPE_IKEV2_IPSEC_RSA
+    );
+
     private final DialogInterface.OnClickListener mListener;
     private final VpnProfile mProfile;
 
     private boolean mEditing;
     private boolean mExists;
-    private List<String> mTotalTypes;
-    private List<String> mAllowedTypes;
 
     private View mView;
 
@@ -75,14 +75,9 @@
     private TextView mServer;
     private TextView mUsername;
     private TextView mPassword;
-    private TextView mSearchDomains;
-    private TextView mDnsServers;
-    private TextView mRoutes;
     private Spinner mProxySettings;
     private TextView mProxyHost;
     private TextView mProxyPort;
-    private CheckBox mMppe;
-    private TextView mL2tpSecret;
     private TextView mIpsecIdentifier;
     private TextView mIpsecSecret;
     private Spinner mIpsecUserCert;
@@ -116,14 +111,9 @@
         mServer = (TextView) mView.findViewById(R.id.server);
         mUsername = (TextView) mView.findViewById(R.id.username);
         mPassword = (TextView) mView.findViewById(R.id.password);
-        mSearchDomains = (TextView) mView.findViewById(R.id.search_domains);
-        mDnsServers = (TextView) mView.findViewById(R.id.dns_servers);
-        mRoutes = (TextView) mView.findViewById(R.id.routes);
         mProxySettings = (Spinner) mView.findViewById(R.id.vpn_proxy_settings);
         mProxyHost = (TextView) mView.findViewById(R.id.vpn_proxy_host);
         mProxyPort = (TextView) mView.findViewById(R.id.vpn_proxy_port);
-        mMppe = (CheckBox) mView.findViewById(R.id.mppe);
-        mL2tpSecret = (TextView) mView.findViewById(R.id.l2tp_secret);
         mIpsecIdentifier = (TextView) mView.findViewById(R.id.ipsec_identifier);
         mIpsecSecret = (TextView) mView.findViewById(R.id.ipsec_secret);
         mIpsecUserCert = (Spinner) mView.findViewById(R.id.ipsec_user_cert);
@@ -137,29 +127,17 @@
         // Second, copy values from the profile.
         mName.setText(mProfile.name);
         setTypesByFeature(mType);
-        // Not all types will be available to the user. Find the index corresponding to the
-        // string of the profile's type.
-        if (mAllowedTypes != null && mTotalTypes != null) {
-            mType.setSelection(mAllowedTypes.indexOf(mTotalTypes.get(mProfile.type)));
-        } else {
-            Log.w(TAG, "Allowed or Total vpn types not initialized when setting initial selection");
-        }
+        mType.setSelection(convertVpnProfileConstantToTypeIndex(mProfile.type));
         mServer.setText(mProfile.server);
         if (mProfile.saveLogin) {
             mUsername.setText(mProfile.username);
             mPassword.setText(mProfile.password);
         }
-        mSearchDomains.setText(mProfile.searchDomains);
-        mDnsServers.setText(mProfile.dnsServers);
-        mRoutes.setText(mProfile.routes);
         if (mProfile.proxy != null) {
             mProxyHost.setText(mProfile.proxy.getHost());
             int port = mProfile.proxy.getPort();
             mProxyPort.setText(port == 0 ? "" : Integer.toString(port));
         }
-        mMppe.setChecked(mProfile.mppe);
-        mL2tpSecret.setText(mProfile.l2tpSecret);
-        mL2tpSecret.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium);
         mIpsecIdentifier.setText(mProfile.ipsecIdentifier);
         mIpsecSecret.setText(mProfile.ipsecSecret);
         final AndroidKeystoreAliasLoader androidKeystoreAliasLoader =
@@ -185,8 +163,6 @@
         mServer.addTextChangedListener(this);
         mUsername.addTextChangedListener(this);
         mPassword.addTextChangedListener(this);
-        mDnsServers.addTextChangedListener(this);
-        mRoutes.addTextChangedListener(this);
         mProxySettings.setOnItemSelectedListener(this);
         mProxyHost.addTextChangedListener(this);
         mProxyPort.addTextChangedListener(this);
@@ -217,12 +193,6 @@
                 // Create a button to forget the profile if it has already been saved..
                 setButton(DialogInterface.BUTTON_NEUTRAL,
                         context.getString(R.string.vpn_forget), mListener);
-
-                // Display warning subtitle if the existing VPN is an insecure type...
-                if (VpnProfile.isLegacyType(mProfile.type)) {
-                    TextView subtitle = mView.findViewById(R.id.dialog_alert_subtitle);
-                    subtitle.setVisibility(View.VISIBLE);
-                }
             }
 
             // Create a button to save the profile.
@@ -285,10 +255,7 @@
     @Override
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (parent == mType) {
-            // Because the spinner may not display all available types,
-            // convert the selected position into the actual vpn profile type integer.
-            final int profileType = convertAllowedIndexToProfileType(position);
-            changeType(profileType);
+            changeType(VPN_TYPES.get(position));
         } else if (parent == mProxySettings) {
             updateProxyFieldsVisibility(position);
         }
@@ -330,17 +297,7 @@
         } else {
             mAlwaysOnVpn.setChecked(false);
             mAlwaysOnVpn.setEnabled(false);
-            if (!profile.isTypeValidForLockdown()) {
-                mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_type);
-            } else if (isLegacyType(profile.type) && !profile.isServerAddressNumeric()) {
-                mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_server);
-            } else if (isLegacyType(profile.type) && !profile.hasDns()) {
-                mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_no_dns);
-            } else if (isLegacyType(profile.type) && !profile.areDnsAddressesNumeric()) {
-                mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_dns);
-            } else {
-                mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_other);
-            }
+            mAlwaysOnInvalidReason.setText(R.string.vpn_always_on_invalid_reason_other);
             mAlwaysOnInvalidReason.setVisibility(View.VISIBLE);
         }
 
@@ -370,21 +327,14 @@
     }
 
     private boolean isAdvancedOptionsEnabled() {
-        return mSearchDomains.getText().length() > 0 || mDnsServers.getText().length() > 0 ||
-                    mRoutes.getText().length() > 0 || mProxyHost.getText().length() > 0
-                    || mProxyPort.getText().length() > 0;
+        return mProxyHost.getText().length() > 0 || mProxyPort.getText().length() > 0;
     }
 
     private void configureAdvancedOptionsVisibility() {
         if (mShowOptions.isChecked() || isAdvancedOptionsEnabled()) {
             mView.findViewById(R.id.options).setVisibility(View.VISIBLE);
             mShowOptions.setVisibility(View.GONE);
-
-            // Configure networking option visibility
             // TODO(b/149070123): Add ability for platform VPNs to support DNS & routes
-            final int visibility =
-                    isLegacyType(getSelectedVpnType()) ? View.VISIBLE : View.GONE;
-            mView.findViewById(R.id.network_options).setVisibility(visibility);
         } else {
             mView.findViewById(R.id.options).setVisibility(View.GONE);
             mShowOptions.setVisibility(View.VISIBLE);
@@ -393,8 +343,6 @@
 
     private void changeType(int type) {
         // First, hide everything.
-        mMppe.setVisibility(View.GONE);
-        mView.findViewById(R.id.l2tp).setVisibility(View.GONE);
         mView.findViewById(R.id.ipsec_psk).setVisibility(View.GONE);
         mView.findViewById(R.id.ipsec_user).setVisibility(View.GONE);
         mView.findViewById(R.id.ipsec_peer).setVisibility(View.GONE);
@@ -403,34 +351,18 @@
         setUsernamePasswordVisibility(type);
 
         // Always enable identity for IKEv2/IPsec profiles.
-        if (!isLegacyType(type)) {
-            mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.VISIBLE);
-        }
+        mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.VISIBLE);
 
         // Then, unhide type-specific fields.
         switch (type) {
-            case VpnProfile.TYPE_PPTP:
-                mMppe.setVisibility(View.VISIBLE);
-                break;
-
-            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
-                mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE);
-                // fall through
-            case VpnProfile.TYPE_IKEV2_IPSEC_PSK: // fall through
-            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+            case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
                 mView.findViewById(R.id.ipsec_psk).setVisibility(View.VISIBLE);
                 mView.findViewById(R.id.options_ipsec_identity).setVisibility(View.VISIBLE);
                 break;
-
-            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
-                mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE);
-                // fall through
-            case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through
-            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+            case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
                 mView.findViewById(R.id.ipsec_user).setVisibility(View.VISIBLE);
                 // fall through
-            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: // fall through
-            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
                 mView.findViewById(R.id.ipsec_peer).setVisibility(View.VISIBLE);
                 break;
         }
@@ -443,7 +375,8 @@
             return false;
         }
 
-        final int type = getSelectedVpnType();
+        final int position = mType.getSelectedItemPosition();
+        final int type = VPN_TYPES.get(position);
         if (!editing && requiresUsernamePassword(type)) {
             return mUsername.getText().length() != 0 && mPassword.getText().length() != 0;
         }
@@ -451,15 +384,8 @@
             return false;
         }
 
-        // TODO(b/149070123): Add ability for platform VPNs to support DNS & routes
-        if (isLegacyType(mProfile.type)
-                && (!validateAddresses(mDnsServers.getText().toString(), false)
-                        || !validateAddresses(mRoutes.getText().toString(), true))) {
-            return false;
-        }
-
         // All IKEv2 methods require an identifier
-        if (!isLegacyType(mProfile.type) && mIpsecIdentifier.getText().length() == 0) {
+        if (mIpsecIdentifier.getText().length() == 0) {
             return false;
         }
 
@@ -468,56 +394,23 @@
         }
 
         switch (type) {
-            case VpnProfile.TYPE_PPTP: // fall through
-            case VpnProfile.TYPE_IPSEC_HYBRID_RSA: // fall through
             case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
                 return true;
 
-            case VpnProfile.TYPE_IKEV2_IPSEC_PSK: // fall through
-            case VpnProfile.TYPE_L2TP_IPSEC_PSK: // fall through
-            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+            case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
                 return mIpsecSecret.getText().length() != 0;
 
-            case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through
-            case VpnProfile.TYPE_L2TP_IPSEC_RSA: // fall through
-            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+            case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
                 return mIpsecUserCert.getSelectedItemPosition() != 0;
         }
         return false;
     }
 
-    private boolean validateAddresses(String addresses, boolean cidr) {
-        try {
-            for (String address : addresses.split(" ")) {
-                if (address.isEmpty()) {
-                    continue;
-                }
-                // Legacy VPN currently only supports IPv4.
-                int prefixLength = 32;
-                if (cidr) {
-                    String[] parts = address.split("/", 2);
-                    address = parts[0];
-                    prefixLength = Integer.parseInt(parts[1]);
-                }
-                byte[] bytes = InetAddress.parseNumericAddress(address).getAddress();
-                int integer = (bytes[3] & 0xFF) | (bytes[2] & 0xFF) << 8 |
-                        (bytes[1] & 0xFF) << 16 | (bytes[0] & 0xFF) << 24;
-                if (bytes.length != 4 || prefixLength < 0 || prefixLength > 32 ||
-                        (prefixLength < 32 && (integer << prefixLength) != 0)) {
-                    return false;
-                }
-            }
-        } catch (Exception e) {
-            return false;
-        }
-        return true;
-    }
-
     private void setTypesByFeature(Spinner typeSpinner) {
         String[] types = getContext().getResources().getStringArray(R.array.vpn_types);
-        mTotalTypes = new ArrayList<>(Arrays.asList(types));
-        mAllowedTypes = new ArrayList<>(Arrays.asList(types));
-
+        if (types.length != VPN_TYPES.size()) {
+            Log.wtf(TAG, "VPN_TYPES array length does not match string array");
+        }
         // Although FEATURE_IPSEC_TUNNELS should always be present in android S and beyond,
         // keep this check here just to be safe.
         if (!getContext().getPackageManager().hasSystemFeature(
@@ -532,17 +425,6 @@
             mProfile.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
         }
 
-        // Remove all types which are legacy types from the typesList
-        if (!VpnProfile.isLegacyType(mProfile.type)) {
-            for (int i = mAllowedTypes.size() - 1; i >= 0; i--) {
-                // This must be removed from back to front in order to ensure index consistency
-                if (VpnProfile.isLegacyType(i)) {
-                    mAllowedTypes.remove(i);
-                }
-            }
-
-            types = mAllowedTypes.toArray(new String[0]);
-        }
         final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                 getContext(), android.R.layout.simple_spinner_item, types);
         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@@ -606,20 +488,14 @@
         // First, save common fields.
         VpnProfile profile = new VpnProfile(mProfile.key);
         profile.name = mName.getText().toString();
-        profile.type = getSelectedVpnType();
+        final int position = mType.getSelectedItemPosition();
+        profile.type = VPN_TYPES.get(position);
         profile.server = mServer.getText().toString().trim();
         profile.username = mUsername.getText().toString();
         profile.password = mPassword.getText().toString();
 
         // Save fields based on VPN type.
-        if (isLegacyType(profile.type)) {
-            // TODO(b/149070123): Add ability for platform VPNs to support DNS & routes
-            profile.searchDomains = mSearchDomains.getText().toString().trim();
-            profile.dnsServers = mDnsServers.getText().toString().trim();
-            profile.routes = mRoutes.getText().toString().trim();
-        } else {
-            profile.ipsecIdentifier = mIpsecIdentifier.getText().toString();
-        }
+        profile.ipsecIdentifier = mIpsecIdentifier.getText().toString();
 
         if (hasProxy()) {
             String proxyHost = mProxyHost.getText().toString().trim();
@@ -640,34 +516,17 @@
         }
         // Then, save type-specific fields.
         switch (profile.type) {
-            case VpnProfile.TYPE_PPTP:
-                profile.mppe = mMppe.isChecked();
-                break;
-
-            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
-                profile.l2tpSecret = mL2tpSecret.getText().toString();
-                // fall through
-            case VpnProfile.TYPE_IKEV2_IPSEC_PSK: // fall through
-            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
-                profile.ipsecIdentifier = mIpsecIdentifier.getText().toString();
+            case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
                 profile.ipsecSecret = mIpsecSecret.getText().toString();
                 break;
 
             case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
                 if (mIpsecUserCert.getSelectedItemPosition() != 0) {
-                    profile.ipsecSecret = (String) mIpsecUserCert.getSelectedItem();
-                }
-                // fall through
-            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
-                profile.l2tpSecret = mL2tpSecret.getText().toString();
-                // fall through
-            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
-                if (mIpsecUserCert.getSelectedItemPosition() != 0) {
                     profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem();
+                    profile.ipsecSecret = profile.ipsecUserCert;
                 }
                 // fall through
-            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: // fall through
-            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
                 if (mIpsecCaCert.getSelectedItemPosition() != 0) {
                     profile.ipsecCaCert = (String) mIpsecCaCert.getSelectedItem();
                 }
@@ -692,19 +551,13 @@
         return ProxyUtils.validate(host, port, "") == ProxyUtils.PROXY_VALID;
     }
 
-    private int getSelectedVpnType() {
-        return convertAllowedIndexToProfileType(mType.getSelectedItemPosition());
-    }
-
-    private int convertAllowedIndexToProfileType(int allowedSelectedPosition) {
-        if (mAllowedTypes != null && mTotalTypes != null) {
-            final String typeString = mAllowedTypes.get(allowedSelectedPosition);
-            final int profileType = mTotalTypes.indexOf(typeString);
-            return profileType;
-        } else {
-            Log.w(TAG, "Allowed or Total vpn types not initialized when converting protileType");
-            return allowedSelectedPosition;
+    private int convertVpnProfileConstantToTypeIndex(int vpnType) {
+        final int typeIndex = VPN_TYPES.indexOf(vpnType);
+        if (typeIndex == -1) {
+            // Existing legacy profile type
+            Log.wtf(TAG, "Invalid existing profile type");
+            return 0;
         }
+        return typeIndex;
     }
-
 }
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/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java
index 686df7a..e623eb8 100644
--- a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java
@@ -19,7 +19,6 @@
 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.ANGLE_DRIVER_SUFFIX;
 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION;
 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_PERSISTENT_GRAPHICS_EGL;
-import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_RO_GFX_ANGLE_SUPPORTED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -83,7 +82,6 @@
 
     @Test
     public void onPreferenceChange_switchOn_shouldEnableAngleAsSystemDriver() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
         // since GraphicsEnvironment is mocked in Robolectric test environment,
         // we will override the system property persist.graphics.egl as if it is changed by
         // mGraphicsEnvironment.toggleAngleAsSystemDriver(true).
@@ -100,7 +98,6 @@
 
     @Test
     public void onPreferenceChange_switchOff_shouldDisableAngleAsSystemDriver() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
         // since GraphicsEnvironment is mocked in Robolectric test environment,
         // we will override the system property persist.graphics.egl as if it is changed by
         // mGraphicsEnvironment.toggleAngleAsSystemDriver(false).
@@ -116,30 +113,14 @@
     }
 
     @Test
-    public void updateState_angleNotSupported_preferenceShouldNotBeChecked() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "");
-        mController.updateState(mPreference);
-        verify(mPreference).setChecked(false);
-    }
-
-    @Test
-    public void updateState_angleNotSupported_preferenceShouldNotBeEnabled() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "");
-        mController.updateState(mPreference);
-        verify(mPreference).setEnabled(false);
-    }
-
-    @Test
-    public void updateState_angleSupported_angleUsed_preferenceShouldBeChecked() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
+    public void updateState_angleUsed_preferenceShouldBeChecked() {
         ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, ANGLE_DRIVER_SUFFIX);
         mController.updateState(mPreference);
         verify(mPreference).setChecked(true);
     }
 
     @Test
-    public void updateState_angleSupported_angleNotUsed_preferenceShouldNotBeChecked() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
+    public void updateState_angleNotUsed_preferenceShouldNotBeChecked() {
         ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
         mController.updateState(mPreference);
         verify(mPreference).setChecked(false);
@@ -147,7 +128,6 @@
 
     @Test
     public void onDeveloperOptionSwitchDisabled_shouldDisableAngleAsSystemDriver() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
         mController.onDeveloperOptionsSwitchDisabled();
         final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
         assertThat(systemEGLDriver).isEqualTo("");
@@ -155,14 +135,12 @@
 
     @Test
     public void onDeveloperOptionSwitchDisabled_preferenceShouldNotBeChecked() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
         mController.onDeveloperOptionsSwitchDisabled();
         verify(mPreference).setChecked(false);
     }
 
     @Test
     public void onDeveloperOptionsSwitchDisabled_preferenceShouldNotBeEnabled() {
-        ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
         mController.onDeveloperOptionsSwitchDisabled();
         verify(mPreference).setEnabled(false);
     }
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/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java b/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java
index a402d91..8304e5d 100644
--- a/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java
+++ b/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java
@@ -20,7 +20,6 @@
 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.Injector;
 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION;
 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_PERSISTENT_GRAPHICS_EGL;
-import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_RO_GFX_ANGLE_SUPPORTED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -181,31 +180,13 @@
     }
 
     @Test
-    public void updateState_angleNotSupported_PreferenceShouldDisabled() {
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())).thenReturn("");
-        mController.updateState(mPreference);
-        assertThat(mPreference.isEnabled()).isFalse();
-    }
-
-    @Test
-    public void updateState_angleNotSupported_PreferenceShouldNotBeChecked() {
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())).thenReturn("");
-        mController.updateState(mPreference);
-        assertThat(mPreference.isChecked()).isFalse();
-    }
-
-    @Test
-    public void updateState_angleSupported_PreferenceShouldEnabled() {
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
-                .thenReturn("true");
+    public void updateState_PreferenceShouldEnabled() {
         mController.updateState(mPreference);
         assertThat(mPreference.isEnabled()).isTrue();
     }
 
     @Test
-    public void updateState_angleSupported_angleIsSystemGLESDriver_PreferenceShouldBeChecked() {
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
-                .thenReturn("true");
+    public void updateState_angleIsSystemGLESDriver_PreferenceShouldBeChecked() {
         when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
                 .thenReturn(ANGLE_DRIVER_SUFFIX);
         mController.updateState(mPreference);
@@ -213,10 +194,7 @@
     }
 
     @Test
-    public void
-            updateState_angleSupported_angleIsNotSystemGLESDriver_PreferenceShouldNotBeChecked() {
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
-                .thenReturn("true");
+    public void updateState_angleIsNotSystemGLESDriver_PreferenceShouldNotBeChecked() {
         when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any())).thenReturn("");
         mController.updateState(mPreference);
         assertThat(mPreference.isChecked()).isFalse();
@@ -232,8 +210,6 @@
 
         // Test that onDeveloperOptionSwitchDisabled,
         // persist.graphics.egl updates to ""
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
-                .thenReturn("true");
         mController.onDeveloperOptionsSwitchDisabled();
         propertyChangeSignal1.wait(100);
         final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
@@ -245,16 +221,12 @@
 
     @Test
     public void onDeveloperOptionSwitchDisabled_PreferenceShouldNotBeChecked() {
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
-                .thenReturn("true");
         mController.onDeveloperOptionsSwitchDisabled();
         assertThat(mPreference.isChecked()).isFalse();
     }
 
     @Test
     public void onDeveloperOptionSwitchDisabled_PreferenceShouldDisabled() {
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
-                .thenReturn("true");
         mController.onDeveloperOptionsSwitchDisabled();
         assertThat(mPreference.isEnabled()).isFalse();
     }
@@ -480,8 +452,6 @@
         // Test that when debug.graphics.angle.developeroption.enable is false:
         when(mSystemPropertiesMock.getBoolean(eq(PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION),
                                               anyBoolean())).thenReturn(false);
-        when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
-                .thenReturn("true");
 
         // 1. "Enable ANGLE" switch is on, the switch should be enabled.
         when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
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);
+    }
+}