Merge "Migrate About phone" into main
diff --git a/Android.bp b/Android.bp
index 087030b..e81ef9e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -79,6 +79,7 @@
         "BiometricsSharedLib",
         "SystemUIUnfoldLib",
         "WifiTrackerLib",
+        "android.hardware.biometrics.flags-aconfig-java",
         "android.hardware.dumpstate-V1-java",
         "android.hardware.dumpstate-V1.0-java",
         "android.hardware.dumpstate-V1.1-java",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cdd9535..6f89a26 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3365,6 +3365,21 @@
                        android:value="@string/menu_key_battery"/>
         </activity>
 
+        <activity
+            android:name="Settings$PowerUsageAdvancedActivity"
+            android:label="@string/advanced_battery_title"
+            android:exported="true"
+            android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.settings.battery.action.POWER_USAGE_ADVANCED" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.fuelgauge.batteryusage.PowerUsageAdvanced" />
+            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+                android:value="@string/menu_key_battery"/>
+        </activity>
+
         <provider
             android:name=".fuelgauge.batteryusage.BatteryUsageContentProvider"
             android:enabled="true"
diff --git a/aconfig/catalyst/network_and_internet.aconfig b/aconfig/catalyst/network_and_internet.aconfig
index e8943e6..aa26ce4 100644
--- a/aconfig/catalyst/network_and_internet.aconfig
+++ b/aconfig/catalyst/network_and_internet.aconfig
@@ -23,6 +23,13 @@
 }
 
 flag {
+  name: "catalyst_tether_settings"
+  namespace: "android_settings"
+  description: "Flag for Hotspot & tethering"
+  bug: "323791114"
+}
+
+flag {
   name: "catalyst_adaptive_connectivity"
   namespace: "android_settings"
   description: "Flag for Adaptive connectivity"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a58702a..e986ce0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1234,6 +1234,16 @@
     <string name="security_settings_fingerprint_bad_calibration_title">Can\u2019t use fingerprint sensor</string>
     <!-- Text shown during fingerprint enrollment to indicate bad sensor calibration. [CHAR LIMIT=100] -->
     <string name="security_settings_fingerprint_bad_calibration">Visit a repair provider.</string>
+
+    <!-- Key for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_screen_off_unlock_udfps_key" translatable="false">security_settings_screen_off_unlock_udfps</string>
+    <!-- Title for  Key for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_screen_off_unlock_udfps_title">Screen-off Fingerprint Unlock</string>
+    <!-- Description for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_screen_off_unlock_udfps_description">Use Fingerprint Unlock even when the screen is off</string>
+    <!-- Description for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_screen_off_unlock_udfps_keywords">Screen-off, Unlock</string>
+
     <!-- Title for the section that has additional security settings. [CHAR LIMIT=60] -->
     <string name="security_advanced_settings">More security settings</string>
     <!-- String for the "More security settings" summary when a work profile is on the device. [CHAR_LIMIT=NONE] -->
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index 4e52cf4..c7e2967 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -28,6 +28,7 @@
         <com.android.settingslib.RestrictedPreference
             android:key="@string/preference_key_brightness_level"
             android:title="@string/brightness"
+            android:persistent="false"
             settings:keywords="@string/keywords_display_brightness_level"
             settings:useAdminDisabledSummary="true"
             settings:userRestriction="no_config_brightness"/>
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index 292f182..1437db6 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -56,7 +56,7 @@
         settings:controller="com.android.settings.network.MobileNetworkSummaryController" />
 
     <com.android.settingslib.RestrictedSwitchPreference
-        android:key="airplane_mode"
+        android:key="airplane_mode_on"
         android:title="@string/airplane_mode"
         android:icon="@drawable/ic_airplanemode_active"
         android:order="-5"
diff --git a/res/xml/security_settings_fingerprint.xml b/res/xml/security_settings_fingerprint.xml
index 9c8b0a3..32f0924 100644
--- a/res/xml/security_settings_fingerprint.xml
+++ b/res/xml/security_settings_fingerprint.xml
@@ -40,7 +40,16 @@
             android:title="@string/security_settings_require_screen_on_to_auth_title"
             android:summary="@string/security_settings_require_screen_on_to_auth_description"
             settings:keywords="@string/security_settings_require_screen_on_to_auth_keywords"
-            settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController" />
+            settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController"
+            settings:isPreferenceVisible="false"/>
+
+        <com.android.settingslib.RestrictedSwitchPreference
+            android:key="@string/security_settings_screen_off_unlock_udfps_key"
+            android:title="@string/security_settings_screen_off_unlock_udfps_title"
+            android:summary="@string/security_settings_screen_off_unlock_udfps_description"
+            settings:keywords="@string/security_settings_screen_off_unlock_udfps_keywords"
+            settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsScreenOffUnlockUdfpsPreferenceController"
+            settings:isPreferenceVisible="false"/>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/src/com/android/settings/MainClearConfirm.java b/src/com/android/settings/MainClearConfirm.java
index c9887e8..5aee5de 100644
--- a/src/com/android/settings/MainClearConfirm.java
+++ b/src/com/android/settings/MainClearConfirm.java
@@ -17,6 +17,8 @@
 package com.android.settings;
 
 
+import static android.content.Context.MODE_PRIVATE;
+
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import android.app.ProgressDialog;
@@ -25,6 +27,7 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -33,6 +36,7 @@
 import android.os.UserManager;
 import android.service.oemlock.OemLockManager;
 import android.service.persistentdata.PersistentDataBlockManager;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -42,6 +46,7 @@
 
 import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
+import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 
 import com.google.android.setupcompat.template.FooterBarMixin;
@@ -90,7 +95,7 @@
             } else {
                 pdbManager = null;
             }
-
+            setSimDialogProgressState();
             if (shouldWipePersistentDataBlock(pdbManager)) {
 
                 new AsyncTask<Void, Void, Void>() {
@@ -128,6 +133,17 @@
             } else {
                 doMainClear();
             }
+
+        }
+
+        private void setSimDialogProgressState() {
+            if (getActivity() != null) {
+                final SharedPreferences prefs = getActivity().getSharedPreferences(
+                        SubscriptionActionDialogActivity.SIM_ACTION_DIALOG_PREFS, MODE_PRIVATE);
+                prefs.edit().putInt(SubscriptionActionDialogActivity.KEY_PROGRESS_STATE,
+                        SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING).apply();
+                Log.d(TAG, "SIM dialog setProgressState: 1");
+            }
         }
 
         private ProgressDialog getProgressDialog() {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index b04f3af..e3925fe 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -484,6 +484,7 @@
     public static class NetworkDashboardActivity extends SettingsActivity {}
     public static class ConnectedDeviceDashboardActivity extends SettingsActivity {}
     public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
+    public static class PowerUsageAdvancedActivity extends SettingsActivity { /* empty */ }
     public static class StorageDashboardActivity extends SettingsActivity {}
     public static class AccountDashboardActivity extends SettingsActivity {}
     public static class SystemDashboardActivity extends SettingsActivity {}
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 66397c0..609b96a 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.settings;
 
+import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+
 import android.app.Activity;
 import android.app.Dialog;
 import android.app.admin.DevicePolicyManager;
@@ -45,6 +47,7 @@
 
 import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
 import com.android.settings.support.actionbar.HelpResourceProvider;
 import com.android.settings.widget.HighlightablePreferenceGroupAdapter;
 import com.android.settings.widget.LoadingViewController;
@@ -367,9 +370,13 @@
     @Override
     protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
         final Bundle arguments = getArguments();
-        mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen,
-                arguments == null
-                        ? null : arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY),
+        String key = arguments == null ? null : arguments.getString(EXTRA_FRAGMENT_ARG_KEY);
+        if (Flags.catalyst() && key == null) {
+            Activity activity = getActivity();
+            Intent intent = activity != null ? activity.getIntent() : null;
+            key = intent != null ? intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY) : null;
+        }
+        mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen, key,
                 mPreferenceHighlighted);
         return mAdapter;
     }
diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
index 851d763..62c5910 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.applications;
 
-import static android.webkit.Flags.updateServiceV2;
-
 import android.Manifest;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -173,11 +171,9 @@
         }
 
         // Keep WebView default package enabled.
-        if (updateServiceV2()) {
-            String packageName = mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName();
-            if (packageName != null) {
-                keepEnabledPackages.add(packageName);
-            }
+        String packageName = mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName();
+        if (packageName != null) {
+            keepEnabledPackages.add(packageName);
         }
 
         keepEnabledPackages.addAll(getEnabledPackageAllowlist());
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index adb6700..c7f7ad7 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -20,6 +20,7 @@
 import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE;
 import static android.app.admin.DevicePolicyResources.UNDEFINED;
+import static android.hardware.biometrics.Flags.screenOffUnlockUdfps;
 
 import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
 import static com.android.settings.Utils.isPrivateProfile;
@@ -207,6 +208,17 @@
                                 context,
                                 KEY_REQUIRE_SCREEN_ON_TO_AUTH
                         ));
+            } else if (screenOffUnlockUdfps()) {
+                controllers.add(
+                        new FingerprintUnlockCategoryController(
+                                context,
+                                KEY_FINGERPRINT_UNLOCK_CATEGORY
+                        ));
+                controllers.add(
+                        new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(
+                                context,
+                                KEY_SCREEN_OFF_FINGERPRINT_UNLOCK
+                        ));
             }
             controllers.add(new FingerprintsEnrolledCategoryPreferenceController(context,
                     KEY_FINGERPRINTS_ENROLLED_CATEGORY));
@@ -233,6 +245,9 @@
         @VisibleForTesting
         static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH =
                 "security_settings_require_screen_on_to_auth";
+        @VisibleForTesting
+        static final String KEY_SCREEN_OFF_FINGERPRINT_UNLOCK =
+                "security_settings_screen_off_unlock_udfps";
         private static final String KEY_FINGERPRINTS_ENROLLED_CATEGORY =
                 "security_settings_fingerprints_enrolled";
         private static final String KEY_FINGERPRINT_UNLOCK_CATEGORY =
@@ -263,8 +278,11 @@
                 mFingerprintUnlockCategoryPreferenceController;
         private FingerprintSettingsRequireScreenOnToAuthPreferenceController
                 mRequireScreenOnToAuthPreferenceController;
+        private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController
+                mScreenOffUnlockUdfpsPreferenceController;
         private Preference mAddFingerprintPreference;
         private RestrictedSwitchPreference mRequireScreenOnToAuthPreference;
+        private RestrictedSwitchPreference mScreenOffUnlockUdfpsPreference;
         private PreferenceCategory mFingerprintsEnrolledCategory;
         private PreferenceCategory mFingerprintUnlockCategory;
         private PreferenceCategory mFingerprintUnlockFooter;
@@ -621,7 +639,7 @@
             // This needs to be after setting ids, otherwise
             // |mRequireScreenOnToAuthPreferenceController.isChecked| is always checking the primary
             // user instead of the user with |mUserId|.
-            if (isSfps()) {
+            if (isSfps() || screenOffUnlockUdfps()) {
                 scrollToPreference(fpPrefKey);
                 addFingerprintUnlockCategory();
             }
@@ -671,33 +689,46 @@
 
         private void addFingerprintUnlockCategory() {
             mFingerprintUnlockCategory = findPreference(KEY_FINGERPRINT_UNLOCK_CATEGORY);
-            setupFingerprintUnlockCategoryPreferences();
-            final Preference restToUnlockPreference = FeatureFactory.getFeatureFactory()
-                    .getFingerprintFeatureProvider()
-                    .getSfpsRestToUnlockFeature(getContext())
-                    .getRestToUnlockPreference(getContext());
-            if (restToUnlockPreference != null) {
-                // Use custom featured preference if any.
-                mRequireScreenOnToAuthPreference.setTitle(restToUnlockPreference.getTitle());
-                mRequireScreenOnToAuthPreference.setSummary(restToUnlockPreference.getSummary());
-                mRequireScreenOnToAuthPreference.setChecked(
-                        ((TwoStatePreference) restToUnlockPreference).isChecked());
-                mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
-                        restToUnlockPreference.getOnPreferenceChangeListener());
+            if (isSfps()) {
+                // For both SFPS "screen on to auth" and "rest to unlock"
+                final Preference restToUnlockPreference = FeatureFactory.getFeatureFactory()
+                        .getFingerprintFeatureProvider()
+                        .getSfpsRestToUnlockFeature(getContext())
+                        .getRestToUnlockPreference(getContext());
+                if (restToUnlockPreference != null) {
+                    // Use custom featured preference if any.
+                    mRequireScreenOnToAuthPreference.setTitle(restToUnlockPreference.getTitle());
+                    mRequireScreenOnToAuthPreference.setSummary(
+                            restToUnlockPreference.getSummary());
+                    mRequireScreenOnToAuthPreference.setChecked(
+                            ((TwoStatePreference) restToUnlockPreference).isChecked());
+                    mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
+                            restToUnlockPreference.getOnPreferenceChangeListener());
+                }
+                setupFingerprintUnlockCategoryPreferencesForScreenOnToAuth();
+            } else if (screenOffUnlockUdfps()) {
+                setupFingerprintUnlockCategoryPreferencesForScreenOffUnlock();
             }
             updateFingerprintUnlockCategoryVisibility();
         }
 
         private void updateFingerprintUnlockCategoryVisibility() {
-            final boolean mFingerprintUnlockCategoryAvailable =
+            final boolean fingerprintUnlockCategoryAvailable =
                     mFingerprintUnlockCategoryPreferenceController.isAvailable();
-            if (mFingerprintUnlockCategory.isVisible() != mFingerprintUnlockCategoryAvailable) {
-                mFingerprintUnlockCategory.setVisible(
-                        mFingerprintUnlockCategoryAvailable);
+            if (mFingerprintUnlockCategory.isVisible() != fingerprintUnlockCategoryAvailable) {
+                mFingerprintUnlockCategory.setVisible(fingerprintUnlockCategoryAvailable);
+            }
+            if (mRequireScreenOnToAuthPreferenceController != null) {
+                mRequireScreenOnToAuthPreference.setVisible(
+                        mRequireScreenOnToAuthPreferenceController.isAvailable());
+            }
+            if (mScreenOffUnlockUdfpsPreferenceController != null) {
+                mScreenOffUnlockUdfpsPreference.setVisible(
+                        mScreenOffUnlockUdfpsPreferenceController.isAvailable());
             }
         }
 
-        private void setupFingerprintUnlockCategoryPreferences() {
+        private void setupFingerprintUnlockCategoryPreferencesForScreenOnToAuth() {
             mRequireScreenOnToAuthPreference = findPreference(KEY_REQUIRE_SCREEN_ON_TO_AUTH);
             mRequireScreenOnToAuthPreference.setChecked(
                     mRequireScreenOnToAuthPreferenceController.isChecked());
@@ -709,9 +740,21 @@
                     });
         }
 
+        private void setupFingerprintUnlockCategoryPreferencesForScreenOffUnlock() {
+            mScreenOffUnlockUdfpsPreference = findPreference(KEY_SCREEN_OFF_FINGERPRINT_UNLOCK);
+            mScreenOffUnlockUdfpsPreference.setChecked(
+                    mScreenOffUnlockUdfpsPreferenceController.isChecked());
+            mScreenOffUnlockUdfpsPreference.setOnPreferenceChangeListener(
+                    (preference, newValue) -> {
+                        final boolean isChecked = ((TwoStatePreference) preference).isChecked();
+                        mScreenOffUnlockUdfpsPreferenceController.setChecked(!isChecked);
+                        return true;
+                    });
+        }
+
         private void updatePreferencesAfterFingerprintRemoved() {
             updateAddPreference();
-            if (isSfps()) {
+            if (isSfps() || screenOffUnlockUdfps()) {
                 updateFingerprintUnlockCategoryVisibility();
             }
             updatePreferences();
@@ -955,6 +998,18 @@
                     }
 
                 }
+            } else if (screenOffUnlockUdfps()) {
+                for (AbstractPreferenceController controller : controllers) {
+                    if (controller.getPreferenceKey() == KEY_FINGERPRINT_UNLOCK_CATEGORY) {
+                        mFingerprintUnlockCategoryPreferenceController =
+                                (FingerprintUnlockCategoryController) controller;
+                    } else if (controller.getPreferenceKey() == KEY_SCREEN_OFF_FINGERPRINT_UNLOCK) {
+                        mScreenOffUnlockUdfpsPreferenceController =
+                                (FingerprintSettingsScreenOffUnlockUdfpsPreferenceController)
+                                        controller;
+                    }
+
+                }
             }
             return controllers;
         }
@@ -1070,7 +1125,8 @@
             } else if (requestCode == BIOMETRIC_AUTH_REQUEST) {
                 mBiometricsAuthenticationRequested = false;
                 if (resultCode != RESULT_OK) {
-                    if (resultCode == ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT) {
+                    if (resultCode
+                            == ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT) {
                         IdentityCheckBiometricErrorDialog
                                 .showBiometricErrorDialogAndFinishActivityOnDismiss(getActivity(),
                                         Utils.BiometricStatus.LOCKOUT);
@@ -1408,7 +1464,7 @@
                         getContext().getSystemService(DevicePolicyManager.class);
                 String messageId =
                         isProfileChallengeUser ? WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE
-                        : UNDEFINED;
+                                : UNDEFINED;
                 int defaultMessageId = isProfileChallengeUser
                         ? R.string.fingerprint_last_delete_message_profile_challenge
                         : R.string.fingerprint_last_delete_message;
@@ -1417,7 +1473,7 @@
                         .setTitle(title)
                         .setMessage(devicePolicyManager.getResources().getString(
                                 messageId,
-                                () ->  message + "\n\n" + getContext().getString(defaultMessageId)))
+                                () -> message + "\n\n" + getContext().getString(defaultMessageId)))
                         .setPositiveButton(
                                 R.string.security_settings_fingerprint_enroll_dialog_delete,
                                 new DialogInterface.OnClickListener() {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java
new file mode 100644
index 0000000..5c32d90
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import static android.hardware.biometrics.Flags.screenOffUnlockUdfps;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.Utils;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * Preference controller that controls whether show screen off UDFPS unlock toggle for users to
+ * turn this feature ON or OFF
+ */
+@SearchIndexable
+public class FingerprintSettingsScreenOffUnlockUdfpsPreferenceController
+        extends FingerprintSettingsPreferenceController {
+    private static final String TAG =
+            "FingerprintSettingsScreenOffUnlockUdfpsPreferenceController";
+
+    @VisibleForTesting
+    protected FingerprintManager mFingerprintManager;
+
+    public FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(
+            @NonNull Context context, @NonNull String prefKey) {
+        super(context, prefKey);
+        mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+    }
+
+    @Override
+    public boolean isChecked() {
+        if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+            return false;
+        } else if (getRestrictingAdmin() != null) {
+            return false;
+        }
+        final boolean defEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_screen_off_udfps_enabled);
+        final int value = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
+                defEnabled ? 1 : 0 /* config_screen_off_udfps_enabled */,
+                getUserHandle());
+        return value == 1;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
+                isChecked ? 1 : 0,
+                getUserHandle());
+        return true;
+    }
+
+    @Override
+    public void updateState(@NonNull Preference preference) {
+        super.updateState(preference);
+        if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+            preference.setEnabled(false);
+        } else if (!mFingerprintManager.hasEnrolledTemplates(getUserId())) {
+            preference.setEnabled(false);
+        } else if (getRestrictingAdmin() != null) {
+            preference.setEnabled(false);
+        } else {
+            preference.setEnabled(true);
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    @Override
+    public int getAvailabilityStatus() {
+        if (mFingerprintManager != null
+                && mFingerprintManager.isHardwareDetected()
+                && screenOffUnlockUdfps()
+                && !mFingerprintManager.isPowerbuttonFps()) {
+            return mFingerprintManager.hasEnrolledTemplates(getUserId())
+                    ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+        } else {
+            return UNSUPPORTED_ON_DEVICE;
+        }
+    }
+
+    private int getUserHandle() {
+        return UserHandle.of(getUserId()).getIdentifier();
+    }
+
+    /**
+     * This feature is not directly searchable.
+     */
+    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {};
+
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
index 674a0df..c949d3d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.biometrics.fingerprint;
 
+import static android.hardware.biometrics.Flags.screenOffUnlockUdfps;
+
 import android.content.Context;
 import android.hardware.fingerprint.FingerprintManager;
 
@@ -42,7 +44,7 @@
     public int getAvailabilityStatus() {
         if (mFingerprintManager != null
                 && mFingerprintManager.isHardwareDetected()
-                && mFingerprintManager.isPowerbuttonFps()) {
+                && (mFingerprintManager.isPowerbuttonFps() || screenOffUnlockUdfps())) {
             return mFingerprintManager.hasEnrolledTemplates(getUserId())
                     ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
         } else {
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 117364f..1b92436 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -117,6 +117,7 @@
 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
 import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings;
 import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
+import com.android.settings.fuelgauge.batteryusage.PowerUsageAdvanced;
 import com.android.settings.fuelgauge.batteryusage.PowerUsageSummary;
 import com.android.settings.gestures.ButtonNavigationSettingsFragment;
 import com.android.settings.gestures.DoubleTapPowerSettings;
@@ -276,6 +277,7 @@
             DevelopmentSettingsDashboardFragment.class.getName(),
             WifiDisplaySettings.class.getName(),
             PowerUsageSummary.class.getName(),
+            PowerUsageAdvanced.class.getName(),
             AccountSyncSettings.class.getName(),
             FaceSettings.class.getName(),
             FingerprintSettings.FingerprintSettingsFragment.class.getName(),
@@ -415,6 +417,7 @@
             Settings.SoundSettingsActivity.class.getName(),
             Settings.StorageDashboardActivity.class.getName(),
             Settings.PowerUsageSummaryActivity.class.getName(),
+            Settings.PowerUsageAdvancedActivity.class.getName(),
             Settings.AccountDashboardActivity.class.getName(),
             Settings.PrivacySettingsActivity.class.getName(),
             Settings.SecurityDashboardActivity.class.getName(),
diff --git a/src/com/android/settings/display/BrightnessLevelPreferenceController.java b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
index 96043db..9c4af66 100644
--- a/src/com/android/settings/display/BrightnessLevelPreferenceController.java
+++ b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
@@ -57,6 +57,7 @@
 /**
  * The top-level preference controller that updates the adaptive brightness level.
  */
+// LINT.IfChange
 public class BrightnessLevelPreferenceController extends BasePreferenceController implements
         PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
     private static final Uri BRIGHTNESS_ADJ_URI;
@@ -187,3 +188,4 @@
         return (value - min) / (max - min);
     }
 }
+// LINT.ThenChange(BrightnessLevelRestrictedPreference.kt)
diff --git a/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt b/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt
new file mode 100644
index 0000000..a412b8c
--- /dev/null
+++ b/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.display
+
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.ACTION_SHOW_BRIGHTNESS_DIALOG
+import android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH
+import android.hardware.display.BrightnessInfo
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Process
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.Settings.System
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.core.SettingsBaseActivity
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.RestrictedPreference
+import com.android.settingslib.datastore.HandlerExecutor
+import com.android.settingslib.datastore.KeyedObserver
+import com.android.settingslib.datastore.SettingsSystemStore
+import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX
+import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN
+import com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat
+import com.android.settingslib.metadata.PreferenceLifecycleContext
+import com.android.settingslib.metadata.PreferenceLifecycleProvider
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceRestrictionProvider
+import com.android.settingslib.metadata.PreferenceSummaryProvider
+import com.android.settingslib.preference.PreferenceBinding
+import com.android.settingslib.transition.SettingsTransitionHelper
+import java.text.NumberFormat
+
+// LINT.IfChange
+class BrightnessLevelRestrictedPreference :
+    PreferenceMetadata,
+    PreferenceBinding,
+    PreferenceRestrictionProvider,
+    PreferenceSummaryProvider,
+    PreferenceLifecycleProvider,
+    Preference.OnPreferenceClickListener {
+
+    private var brightnessObserver: KeyedObserver<String>? = null
+    private var displayListener: DisplayListener? = null
+
+    override val key: String
+        get() = "brightness"
+
+    override val title: Int
+        get() = R.string.brightness
+
+    override val keywords: Int
+        get() = R.string.keywords_display_brightness_level
+
+    override fun getSummary(context: Context) =
+        NumberFormat.getPercentInstance().format(getCurrentBrightness(context))
+
+    override fun isEnabled(context: Context) =
+        !UserManager.get(context)
+            .hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, Process.myUserHandle())
+
+    override fun isRestricted(context: Context) =
+        RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+            context,
+            UserManager.DISALLOW_CONFIG_BRIGHTNESS,
+            UserHandle.myUserId(),
+        ) != null
+
+    override fun createWidget(context: Context) = RestrictedPreference(context)
+
+    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+        super.bind(preference, metadata)
+        if (preference is RestrictedPreference) preference.useAdminDisabledSummary(true)
+        preference.onPreferenceClickListener = this
+    }
+
+    override fun onStart(context: PreferenceLifecycleContext) {
+        val observer =
+            object : KeyedObserver<String> {
+                override fun onKeyChanged(key: String, reason: Int) {
+                    context.notifyPreferenceChange(this@BrightnessLevelRestrictedPreference)
+                }
+            }
+        brightnessObserver = observer
+        SettingsSystemStore.get(context)
+            .addObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, observer, HandlerExecutor.main)
+
+        val listener =
+            object : DisplayListener {
+                override fun onDisplayAdded(displayId: Int) {}
+
+                override fun onDisplayRemoved(displayId: Int) {}
+
+                override fun onDisplayChanged(displayId: Int) {
+                    context.notifyPreferenceChange(this@BrightnessLevelRestrictedPreference)
+                }
+            }
+        displayListener = listener
+        context
+            .getSystemService(DisplayManager::class.java)
+            .registerDisplayListener(
+                listener,
+                HandlerExecutor.main,
+                DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS,
+            )
+    }
+
+    override fun onStop(context: PreferenceLifecycleContext) {
+        brightnessObserver?.let {
+            SettingsSystemStore.get(context).removeObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, it)
+            brightnessObserver = null
+        }
+
+        displayListener?.let {
+            context.getSystemService(DisplayManager::class.java).unregisterDisplayListener(it)
+            displayListener = null
+        }
+    }
+
+    override fun onPreferenceClick(preference: Preference): Boolean {
+        val context = preference.context
+        val intent =
+            Intent(ACTION_SHOW_BRIGHTNESS_DIALOG)
+                .setPackage(Utils.SYSTEMUI_PACKAGE_NAME)
+                .putExtra(
+                    SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
+                    SettingsTransitionHelper.TransitionType.TRANSITION_NONE,
+                )
+                .putExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, true)
+        val options =
+            ActivityOptions.makeCustomAnimation(
+                context,
+                android.R.anim.fade_in,
+                android.R.anim.fade_out,
+            )
+        context.startActivityForResult(preference.key, intent, 0, options.toBundle())
+        return true
+    }
+
+    private fun getCurrentBrightness(context: Context): Double {
+        val info: BrightnessInfo? = context.display.brightnessInfo
+        val value =
+            info?.run {
+                convertLinearToGammaFloat(brightness, brightnessMinimum, brightnessMaximum)
+            }
+        return getPercentage(value?.toDouble() ?: 0.0)
+    }
+
+    private fun getPercentage(value: Double): Double =
+        when {
+            value > GAMMA_SPACE_MAX -> 1.0
+            value < GAMMA_SPACE_MIN -> 0.0
+            else -> (value - GAMMA_SPACE_MIN) / (GAMMA_SPACE_MAX - GAMMA_SPACE_MIN)
+        }
+}
+// LINT.ThenChange(BrightnessLevelPreferenceController.java)
diff --git a/src/com/android/settings/display/DisplayScreen.kt b/src/com/android/settings/display/DisplayScreen.kt
index bd21e8e..5248367 100644
--- a/src/com/android/settings/display/DisplayScreen.kt
+++ b/src/com/android/settings/display/DisplayScreen.kt
@@ -18,10 +18,13 @@
 import android.content.Context
 import com.android.settings.DisplaySettings
 import com.android.settings.R
+import com.android.settings.Settings.DisplaySettingsActivity
 import com.android.settings.display.darkmode.DarkModeScreen
 import com.android.settings.flags.Flags
+import com.android.settings.utils.makeLaunchIntent
 import com.android.settingslib.metadata.PreferenceAvailabilityProvider
 import com.android.settingslib.metadata.PreferenceIconProvider
+import com.android.settingslib.metadata.PreferenceMetadata
 import com.android.settingslib.metadata.ProvidePreferenceScreen
 import com.android.settingslib.metadata.preferenceHierarchy
 import com.android.settingslib.preference.PreferenceScreenCreator
@@ -48,10 +51,14 @@
     override fun fragmentClass() = DisplaySettings::class.java
 
     override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {
+        +BrightnessLevelRestrictedPreference()
         +DarkModeScreen.KEY
         +PeakRefreshRateSwitchPreference()
     }
 
+    override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
+        makeLaunchIntent(context, DisplaySettingsActivity::class.java, metadata?.key)
+
     override fun isAvailable(context: Context) =
         context.resources.getBoolean(R.bool.config_show_top_level_display)
 
diff --git a/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt b/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
index 793b0e2..5302fce 100644
--- a/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
+++ b/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
@@ -18,53 +18,51 @@
 import android.content.Context
 import android.hardware.display.DisplayManager
 import android.provider.DeviceConfig
-import android.util.Log
+import android.provider.Settings.System.PEAK_REFRESH_RATE
 import com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE
 import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateAmongAllDisplays
 import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay
 import com.android.server.display.feature.flags.Flags
 import com.android.settings.R
 import com.android.settingslib.datastore.HandlerExecutor
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.datastore.KeyedObservableDelegate
+import com.android.settingslib.datastore.SettingsStore
 import com.android.settingslib.datastore.SettingsSystemStore
 import com.android.settingslib.metadata.PreferenceAvailabilityProvider
 import com.android.settingslib.metadata.PreferenceLifecycleContext
 import com.android.settingslib.metadata.PreferenceLifecycleProvider
 import com.android.settingslib.metadata.PreferenceSummaryProvider
 import com.android.settingslib.metadata.SwitchPreference
-import com.android.settingslib.preference.SwitchPreferenceBinding
 import kotlin.math.roundToInt
 
 // LINT.IfChange
 class PeakRefreshRateSwitchPreference :
-    SwitchPreference("peak_refresh_rate", R.string.peak_refresh_rate_title),
-    SwitchPreferenceBinding,
+    SwitchPreference(PEAK_REFRESH_RATE, R.string.peak_refresh_rate_title),
     PreferenceAvailabilityProvider,
     PreferenceSummaryProvider,
     PreferenceLifecycleProvider {
 
     private var propertiesChangedListener: DeviceConfig.OnPropertiesChangedListener? = null
 
-    override fun storage(context: Context) = SettingsSystemStore.get(context)
+    override fun storage(context: Context): KeyValueStore =
+        PeakRefreshRateStore(context, SettingsSystemStore.get(context))
 
     override fun isAvailable(context: Context) =
         context.resources.getBoolean(R.bool.config_show_smooth_display) &&
-            (getPeakRefreshRate(context) > DEFAULT_REFRESH_RATE)
+            context.peakRefreshRate > DEFAULT_REFRESH_RATE
 
-    override fun getSummary(context: Context) =
-        context.getString(
-            R.string.peak_refresh_rate_summary,
-            getPeakRefreshRate(context).roundToInt(),
-        )
+    override fun getSummary(context: Context): CharSequence? =
+        context.getString(R.string.peak_refresh_rate_summary, context.peakRefreshRate.roundToInt())
 
     override fun onStart(context: PreferenceLifecycleContext) {
         val listener =
-            object : DeviceConfig.OnPropertiesChangedListener {
+            DeviceConfig.OnPropertiesChangedListener {
                 // Got notified if any property has been changed in NAMESPACE_DISPLAY_MANAGER. The
                 // KEY_PEAK_REFRESH_RATE_DEFAULT value could be added, changed, removed or
                 // unchanged.
                 // Just force a UI update for any case.
-                override fun onPropertiesChanged(properties: DeviceConfig.Properties) =
-                    context.notifyPreferenceChange(this@PeakRefreshRateSwitchPreference)
+                context.notifyPreferenceChange(this)
             }
 
         propertiesChangedListener = listener
@@ -83,38 +81,74 @@
         }
     }
 
-    private fun getPeakRefreshRate(context: Context): Float =
-        Math.round(
-                when {
-                    Flags.backUpSmoothDisplayAndForcePeakRefreshRate() ->
-                        findHighestRefreshRateAmongAllDisplays(context)
-                    else -> findHighestRefreshRateForDefaultDisplay(context)
-                }
-            )
-            .toFloat()
+    @Suppress("UNCHECKED_CAST")
+    private class PeakRefreshRateStore(
+        private val context: Context,
+        private val settingsStore: SettingsStore,
+    ) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
 
-    private fun getDefaultPeakRefreshRate(context: Context): Float {
-        var defaultPeakRefreshRate =
-            DeviceConfig.getFloat(
-                DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
-                DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT,
-                INVALIDATE_REFRESH_RATE,
-            )
+        override fun contains(key: String) = settingsStore.contains(key)
 
-        if (defaultPeakRefreshRate == INVALIDATE_REFRESH_RATE) {
-            defaultPeakRefreshRate =
-                context.resources
-                    .getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate)
-                    .toFloat()
+        override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>): T? {
+            if (key != PEAK_REFRESH_RATE) return super.getDefaultValue(key, valueType)
+            return context.defaultPeakRefreshRate.refreshRateAsBoolean(context) as T
         }
 
-        Log.d(TAG, "DeviceConfig getDefaultPeakRefreshRate : $defaultPeakRefreshRate")
-        return defaultPeakRefreshRate
+        override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
+            if (key != PEAK_REFRESH_RATE) return null
+            val refreshRate =
+                settingsStore.getFloat(PEAK_REFRESH_RATE) ?: context.defaultPeakRefreshRate
+            return refreshRate.refreshRateAsBoolean(context) as T
+        }
+
+        private fun Float.refreshRateAsBoolean(context: Context) =
+            this.isInfinite() || roundToInt() == context.peakRefreshRate.roundToInt()
+
+        override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
+            when {
+                key != PEAK_REFRESH_RATE -> {}
+                value == null -> settingsStore.setFloat(PEAK_REFRESH_RATE, null)
+                else -> {
+                    val peakRefreshRate =
+                        if (value as Boolean) context.refreshRateIfON() else DEFAULT_REFRESH_RATE
+                    settingsStore.setFloat(PEAK_REFRESH_RATE, peakRefreshRate)
+                }
+            }
+
+        private fun Context.refreshRateIfON() =
+            when {
+                Flags.backUpSmoothDisplayAndForcePeakRefreshRate() -> Float.POSITIVE_INFINITY
+                else -> peakRefreshRate
+            }
     }
 
     companion object {
-        private const val TAG: String = "PeakRefreshRateSwitchPreference"
         private const val INVALIDATE_REFRESH_RATE: Float = -1f
+
+        private val Context.peakRefreshRate: Float
+            get() =
+                Math.round(
+                        when {
+                            Flags.backUpSmoothDisplayAndForcePeakRefreshRate() ->
+                                findHighestRefreshRateAmongAllDisplays(this)
+                            else -> findHighestRefreshRateForDefaultDisplay(this)
+                        }
+                    )
+                    .toFloat()
+
+        private val Context.defaultPeakRefreshRate: Float
+            get() {
+                val defaultPeakRefreshRate =
+                    DeviceConfig.getFloat(
+                        DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                        DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT,
+                        INVALIDATE_REFRESH_RATE,
+                    )
+                if (defaultPeakRefreshRate != INVALIDATE_REFRESH_RATE) return defaultPeakRefreshRate
+                return resources
+                    .getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate)
+                    .toFloat()
+            }
     }
 }
 // LINT.ThenChange(PeakRefreshRatePreferenceController.java)
diff --git a/src/com/android/settings/network/AirplaneModePreference.kt b/src/com/android/settings/network/AirplaneModePreference.kt
new file mode 100644
index 0000000..5b7903c
--- /dev/null
+++ b/src/com/android/settings/network/AirplaneModePreference.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.provider.Settings.Global.AIRPLANE_MODE_ON
+import androidx.annotation.DrawableRes
+import com.android.settings.R
+import com.android.settingslib.datastore.SettingsGlobalStore
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.SwitchPreference
+
+// LINT.IfChange
+class AirplaneModePreference :
+    SwitchPreference(AIRPLANE_MODE_ON, R.string.airplane_mode),
+    PreferenceAvailabilityProvider {
+
+    override val icon: Int
+        @DrawableRes get() = R.drawable.ic_airplanemode_active
+
+    override fun storage(context: Context) = SettingsGlobalStore.get(context)
+
+    override fun isAvailable(context: Context) =
+        (context.resources.getBoolean(R.bool.config_show_toggle_airplane)
+                && !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+}
+// LINT.ThenChange(AirplaneModePreferenceController.java)
diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java
index d4bd4a3..430feb1 100644
--- a/src/com/android/settings/network/AirplaneModePreferenceController.java
+++ b/src/com/android/settings/network/AirplaneModePreferenceController.java
@@ -52,6 +52,7 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+// LINT.IfChange
 public class AirplaneModePreferenceController extends TogglePreferenceController
         implements LifecycleObserver, OnStart, OnResume, OnStop, OnDestroy,
         AirplaneModeEnabler.OnAirplaneModeChangedListener {
@@ -217,3 +218,4 @@
         }
     }
 }
+// LINT.ThenChange(AirplaneModePreference.kt)
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index be07809..660fd14 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -26,7 +26,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.provider.SettingsSlicesContract;
 import android.security.Credentials;
 import android.security.LegacyVpnProfileStore;
 import android.util.Log;
@@ -39,7 +38,6 @@
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.settings.R;
-import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.vpn2.VpnInfoPreference;
 import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -50,7 +48,6 @@
 import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.Arrays;
-import java.util.List;
 import java.util.function.Function;
 
 public class VpnPreferenceController extends AbstractPreferenceController
@@ -87,7 +84,7 @@
                 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
         // Manually set dependencies for Wifi when not toggleable.
         if (toggleable == null || !toggleable.contains(Settings.Global.RADIO_WIFI)) {
-            preference.setDependency(SettingsSlicesContract.KEY_AIRPLANE_MODE);
+            preference.setDependency(Settings.Global.AIRPLANE_MODE_ON);
         }
         return preference;
     }
diff --git a/src/com/android/settings/network/tether/TetherScreen.kt b/src/com/android/settings/network/tether/TetherScreen.kt
new file mode 100644
index 0000000..20dc4b7
--- /dev/null
+++ b/src/com/android/settings/network/tether/TetherScreen.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.tether
+
+import android.content.Context
+import android.net.TetheringManager
+import com.android.settings.R
+import com.android.settings.flags.Flags
+import com.android.settings.network.TetherPreferenceController
+import com.android.settingslib.TetherUtil
+import com.android.settingslib.Utils
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.ProvidePreferenceScreen
+import com.android.settingslib.metadata.preferenceHierarchy
+import com.android.settingslib.preference.PreferenceScreenCreator
+
+@ProvidePreferenceScreen
+class TetherScreen : PreferenceScreenCreator, PreferenceAvailabilityProvider {
+
+    override val key: String
+        get() = KEY
+
+    override val icon: Int
+        get() = R.drawable.ic_wifi_tethering
+
+    override val keywords: Int
+        get() = R.string.keywords_hotspot_tethering
+
+    override fun getPreferenceTitle(context: Context): CharSequence? =
+        if (TetherPreferenceController.isTetherConfigDisallowed(context)) {
+            context.getText(R.string.tether_settings_title_all)
+        } else {
+            val tetheringManager = context.getSystemService(TetheringManager::class.java)!!
+            context.getText(Utils.getTetheringLabel(tetheringManager))
+        }
+
+    override fun isAvailable(context: Context) = TetherUtil.isTetherAvailable(context)
+
+    override fun isFlagEnabled(context: Context) = Flags.catalystTetherSettings()
+
+    override fun hasCompleteHierarchy() = false
+
+    override fun fragmentClass() = TetherSettings::class.java
+
+    override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
+
+    companion object {
+        const val KEY = "tether_settings"
+    }
+}
diff --git a/src/com/android/settings/network/tether/TetherSettings.java b/src/com/android/settings/network/tether/TetherSettings.java
index 7458514..1db1802 100644
--- a/src/com/android/settings/network/tether/TetherSettings.java
+++ b/src/com/android/settings/network/tether/TetherSettings.java
@@ -48,6 +48,7 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.preference.Preference;
@@ -731,4 +732,9 @@
         }
         updateBluetoothAndEthernetState();
     }
+
+    @Override
+    public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
+        return TetherScreen.KEY;
+    }
 }
diff --git a/src/com/android/settings/notification/MediaVolumePreference.kt b/src/com/android/settings/notification/MediaVolumePreference.kt
new file mode 100644
index 0000000..13fd029
--- /dev/null
+++ b/src/com/android/settings/notification/MediaVolumePreference.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.content.Context
+import android.media.AudioManager.STREAM_MUSIC
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.datastore.NoOpKeyedObservable
+import com.android.settingslib.metadata.*
+import com.android.settingslib.preference.PreferenceBinding
+
+// LINT.IfChange
+open class MediaVolumePreference :
+    PreferenceMetadata,
+    PreferenceBinding,
+    PersistentPreference<Int>,
+    RangeValue,
+    PreferenceAvailabilityProvider,
+    PreferenceIconProvider,
+    PreferenceRestrictionProvider {
+    override val key: String
+        get() = KEY
+
+    override val title: Int
+        get() = R.string.media_volume_option_title
+
+    override fun getIcon(context: Context) =
+        when {
+            VolumeHelper.isMuted(context, STREAM_MUSIC) -> R.drawable.ic_media_stream_off
+            else -> R.drawable.ic_media_stream
+        }
+
+    override fun isAvailable(context: Context) =
+        context.resources.getBoolean(R.bool.config_show_media_volume)
+
+    override fun isRestricted(context: Context) =
+        RestrictedLockUtilsInternal.hasBaseUserRestriction(
+            context,
+            UserManager.DISALLOW_ADJUST_VOLUME,
+            UserHandle.myUserId(),
+        ) ||
+                RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+                    context,
+                    UserManager.DISALLOW_ADJUST_VOLUME,
+                    UserHandle.myUserId(),
+                ) != null
+
+    override fun storage(context: Context): KeyValueStore {
+        val helper = createAudioHelper(context)
+        return object : NoOpKeyedObservable<String>(), KeyValueStore {
+            override fun contains(key: String) = key == KEY
+
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+                helper.getStreamVolume(STREAM_MUSIC) as T
+
+            override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+                helper.setStreamVolume(STREAM_MUSIC, value as Int)
+            }
+        }
+    }
+
+    override fun getMinValue(context: Context) =
+        createAudioHelper(context).getMinVolume(STREAM_MUSIC)
+
+    override fun getMaxValue(context: Context) =
+        createAudioHelper(context).getMaxVolume(STREAM_MUSIC)
+
+    override fun createWidget(context: Context) = VolumeSeekBarPreference(context)
+
+    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+        super.bind(preference, metadata)
+        (preference as VolumeSeekBarPreference).apply {
+            setStream(STREAM_MUSIC)
+            setMuteIcon(R.drawable.ic_media_stream_off)
+            setListener { updateContentDescription(this) }
+        }
+    }
+
+    open fun createAudioHelper(context: Context) = AudioHelper(context)
+
+    fun updateContentDescription(preference: VolumeSeekBarPreference) {
+        when {
+            preference.isMuted() ->
+                preference.updateContentDescription(
+                    preference.context.getString(
+                        R.string.volume_content_description_silent_mode,
+                        preference.title,
+                    )
+                )
+            else -> preference.updateContentDescription(preference.title)
+        }
+    }
+
+    companion object {
+        const val KEY = "media_volume"
+    }
+}
+// LINT.ThenChange(MediaVolumePreferenceController.java)
diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java
index e70cf95..431806a 100644
--- a/src/com/android/settings/notification/MediaVolumePreferenceController.java
+++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java
@@ -42,6 +42,7 @@
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.media.MediaOutputConstants;
 
+// LINT.IfChange
 public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceController {
     private static final String TAG = "MediaVolumePreCtrl";
     private static final String KEY_MEDIA_VOLUME = "media_volume";
@@ -204,3 +205,4 @@
         return MediaOutputIndicatorWorker.class;
     }
 }
+// LINT.ThenChange(MediaVolumePreference.kt)
diff --git a/src/com/android/settings/notification/SeparateRingVolumePreference.kt b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
new file mode 100644
index 0000000..2f696b1
--- /dev/null
+++ b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.app.INotificationManager
+import android.app.NotificationManager
+import android.content.Context
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.media.AudioManager.STREAM_RING
+import android.os.ServiceManager
+import android.os.UserHandle
+import android.os.UserManager.DISALLOW_ADJUST_VOLUME
+import android.os.Vibrator
+import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS
+import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.datastore.NoOpKeyedObservable
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.PreferenceIconProvider
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceRestrictionProvider
+import com.android.settingslib.metadata.RangeValue
+import com.android.settingslib.preference.PreferenceBinding
+
+// LINT.IfChange
+open class SeparateRingVolumePreference :
+    PreferenceMetadata,
+    PreferenceBinding,
+    PersistentPreference<Int>,
+    RangeValue,
+    PreferenceAvailabilityProvider,
+    PreferenceIconProvider,
+    PreferenceRestrictionProvider {
+    override val key: String
+        get() = KEY
+
+    override val title: Int
+        get() = R.string.separate_ring_volume_option_title
+
+    override fun getIcon(context: Context) =
+        when {
+            VolumeHelper.isMuted(context, STREAM_RING) -> getMuteIcon(context)
+            else -> R.drawable.ic_ring_volume
+        }
+
+    override fun isAvailable(context: Context) = !createAudioHelper(context).isSingleVolume()
+
+    override fun isEnabled(context: Context) =
+        !RestrictedLockUtilsInternal.hasBaseUserRestriction(
+            context,
+            DISALLOW_ADJUST_VOLUME,
+            UserHandle.myUserId(),
+        )
+
+    override fun isRestricted(context: Context) =
+        RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+            context,
+            DISALLOW_ADJUST_VOLUME,
+            UserHandle.myUserId(),
+        ) != null
+
+    override fun storage(context: Context): KeyValueStore {
+        val helper = createAudioHelper(context)
+        return object : NoOpKeyedObservable<String>(), KeyValueStore {
+            override fun contains(key: String) = key == KEY
+
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+                helper.getStreamVolume(STREAM_RING) as T
+
+            override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+                helper.setStreamVolume(STREAM_RING, value as Int)
+            }
+        }
+    }
+
+    override fun getMinValue(context: Context) =
+        createAudioHelper(context).getMinVolume(STREAM_RING)
+
+    override fun getMaxValue(context: Context) =
+        createAudioHelper(context).getMaxVolume(STREAM_RING)
+
+    override fun createWidget(context: Context) = VolumeSeekBarPreference(context)
+
+    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+        super.bind(preference, metadata)
+        (preference as VolumeSeekBarPreference).apply {
+            setStream(STREAM_RING)
+            setMuteIcon(getMuteIcon(preference.context))
+            setListener { updateContentDescription(this) }
+            setSuppressionText(getSuppressionText(preference.context))
+        }
+    }
+
+    open fun createAudioHelper(context: Context) = AudioHelper(context)
+
+    fun updateContentDescription(preference: VolumeSeekBarPreference) {
+        val context = preference.context
+        val ringerMode = getEffectiveRingerMode(context)
+        when (ringerMode) {
+            RINGER_MODE_VIBRATE ->
+                preference.updateContentDescription(
+                    context.getString(R.string.ringer_content_description_vibrate_mode)
+                )
+            RINGER_MODE_SILENT ->
+                preference.updateContentDescription(
+                    context.getString(R.string.ringer_content_description_silent_mode)
+                )
+            else -> preference.updateContentDescription(preference.title)
+        }
+    }
+
+    fun getMuteIcon(context: Context): Int {
+        val ringerMode = getEffectiveRingerMode(context)
+        return when (ringerMode) {
+            RINGER_MODE_NORMAL -> R.drawable.ic_ring_volume
+            RINGER_MODE_VIBRATE -> R.drawable.ic_volume_ringer_vibrate
+            else -> R.drawable.ic_ring_volume_off
+        }
+    }
+
+    fun getEffectiveRingerMode(context: Context): Int {
+        val hasVibrator = context.getSystemService(Vibrator::class.java)?.hasVibrator() ?: false
+        val ringerMode = createAudioHelper(context).ringerModeInternal
+        return when {
+            !hasVibrator && ringerMode == RINGER_MODE_VIBRATE -> RINGER_MODE_SILENT
+            else -> ringerMode
+        }
+    }
+
+    fun getSuppressionText(context: Context): String? {
+        val suppressor = NotificationManager.from(context).getEffectsSuppressor()
+        val notificationManager =
+            INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE)
+            )
+        val hints = notificationManager.getHintsFromListenerNoToken()
+        return when {
+            hintsMatch(hints) -> SuppressorHelper.getSuppressionText(context, suppressor)
+            else -> null
+        }
+    }
+
+    private fun hintsMatch(hints: Int) =
+        (hints and HINT_HOST_DISABLE_CALL_EFFECTS) != 0 ||
+                (hints and HINT_HOST_DISABLE_EFFECTS) != 0
+
+    companion object {
+        const val KEY = "separate_ring_volume"
+    }
+}
+// LINT.ThenChange(SeparateRingVolumePreferenceController.java)
diff --git a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
index 91926e3..feb976f 100644
--- a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
+++ b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
@@ -35,6 +35,7 @@
 /**
  * This slider is used to represent ring volume when ring is separated from notification
  */
+// LINT.IfChange
 public class SeparateRingVolumePreferenceController extends
         RingerModeAffectedVolumePreferenceController {
 
@@ -149,3 +150,4 @@
     }
 
 }
+// LINT.ThenChange(SeparateRingVolumePreference.kt)
diff --git a/src/com/android/settings/notification/SoundScreen.kt b/src/com/android/settings/notification/SoundScreen.kt
index e36576d..e074301 100644
--- a/src/com/android/settings/notification/SoundScreen.kt
+++ b/src/com/android/settings/notification/SoundScreen.kt
@@ -49,7 +49,9 @@
 
     override fun getPreferenceHierarchy(context: Context) =
         preferenceHierarchy(this) {
+            +MediaVolumePreference() order -180
             +CallVolumePreference() order -170
+            +SeparateRingVolumePreference() order -155
             +DialPadTonePreference() order -50
         }
 
diff --git a/src/com/android/settings/notification/VolumeHelper.kt b/src/com/android/settings/notification/VolumeHelper.kt
new file mode 100644
index 0000000..73e490e
--- /dev/null
+++ b/src/com/android/settings/notification/VolumeHelper.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.app.NotificationManager
+import android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
+import android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+import android.content.Context
+import android.media.AudioManager
+import android.media.AudioManager.*
+import android.provider.Settings.Global.ZEN_MODE_ALARMS
+import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+import android.service.notification.ZenModeConfig
+
+class VolumeHelper {
+    companion object {
+        fun isMuted(context: Context, streamType: Int): Boolean {
+            val audioManager = context.getSystemService(AudioManager::class.java)
+            return audioManager.isStreamMute(streamType) && !isZenMuted(context, streamType)
+        }
+
+        fun isZenMuted(context: Context, streamType: Int): Boolean {
+            val notificationManager = context.getSystemService(NotificationManager::class.java)
+            val zenMode = notificationManager.getZenMode()
+            val notificationPolicy = notificationManager.getConsolidatedNotificationPolicy()
+            val isAllowAlarms =
+                (notificationPolicy.priorityCategories and PRIORITY_CATEGORY_ALARMS) != 0
+            val isAllowMedia =
+                (notificationPolicy.priorityCategories and PRIORITY_CATEGORY_MEDIA) != 0
+            val isAllowRinger =
+                !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(notificationPolicy)
+            return isNotificationOrRingStream(streamType)
+                    && zenMode == ZEN_MODE_ALARMS || zenMode == ZEN_MODE_NO_INTERRUPTIONS
+                    || (zenMode == ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                    && (!isAllowRinger && isNotificationOrRingStream(streamType)
+                    || !isAllowMedia && isMediaStream(streamType)
+                    || !isAllowAlarms && isAlarmStream(streamType)))
+        }
+
+        private fun isNotificationOrRingStream(streamType: Int) =
+            streamType == STREAM_RING || streamType == STREAM_NOTIFICATION
+
+        private fun isAlarmStream(streamType: Int) = streamType == STREAM_ALARM
+
+        private fun isMediaStream(streamType: Int) = streamType == STREAM_MUSIC
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index d656934..302cf1f 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -377,7 +377,14 @@
                 setBiometricPromptPropertiesForPrivateProfile(promptInfo);
                 showBiometricPrompt(promptInfo, effectiveUserId);
                 launchedBiometric = true;
+            } else if (Flags.privateSpaceBp()) {
+                promptInfo.setAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL);
+                setBiometricPromptPropertiesForPrivateProfile(promptInfo);
+                showBiometricPrompt(promptInfo, mUserId);
+                launchedBiometric = true;
             } else {
+                // TODO(b/376328272): Remove custom private space behavior
+                mDetails = Utils.getConfirmCredentialStringForUser(this, mUserId, credentialType);
                 showConfirmCredentials();
                 launchedCDC = true;
             }
diff --git a/src/com/android/settings/utils/IntentUtils.kt b/src/com/android/settings/utils/IntentUtils.kt
new file mode 100644
index 0000000..dbdfa23
--- /dev/null
+++ b/src/com/android/settings/utils/IntentUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.utils
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import com.android.settings.SettingsActivity
+
+/**
+ * Returns the [Intent] to start given settings activity and locate the preference.
+ *
+ * @param context context
+ * @param activityClass activity to start
+ * @param key preference key to locate
+ */
+fun makeLaunchIntent(context: Context, activityClass: Class<out Activity>, key: String?) =
+    Intent(context, activityClass).apply {
+        if (key != null) putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key)
+    }
diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
index 82ef58b..4eb30d2 100644
--- a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
+++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
@@ -30,6 +30,7 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceGroupAdapter;
@@ -64,7 +65,7 @@
 
     private final Context mContext;
     private final int mNormalBackgroundRes;
-    private final String mHighlightKey;
+    private final @Nullable String mHighlightKey;
     private boolean mHighlightRequested;
     private int mHighlightPosition = RecyclerView.NO_POSITION;
 
@@ -101,7 +102,8 @@
         screen.setInitialExpandedChildrenCount(initialCount);
     }
 
-    public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup, String key,
+    public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup,
+            @Nullable String key,
             boolean highlightRequested) {
         super(preferenceGroup);
         mHighlightKey = key;
diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
index a23ca69..3afca4d 100644
--- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
@@ -38,11 +38,8 @@
 import android.os.SystemConfigManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.webkit.Flags;
 
 import com.android.settings.testutils.ApplicationTestUtils;
 import com.android.settings.webview.WebViewUpdateServiceWrapper;
@@ -373,7 +370,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_UPDATE_SERVICE_V2)
     public void getKeepEnabledPackages_shouldContainWebViewPackage() {
         final String testWebViewPackageName = "com.android.webview";
         when(mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName())
@@ -383,16 +379,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_UPDATE_SERVICE_V2)
-    public void getKeepEnabledPackages_shouldNotContainWebViewPackageIfFlagDisabled() {
-        final String testWebViewPackageName = "com.android.webview";
-        when(mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName())
-                .thenReturn(testWebViewPackageName);
-        final Set<String> allowlist = mProvider.getKeepEnabledPackages();
-        assertThat(allowlist).doesNotContain(testWebViewPackageName);
-    }
-
-    @Test
     @Config(shadows = {ShadowSmsApplication.class, ShadowDefaultDialerManager.class})
     public void getKeepEnabledPackages_shouldContainPackageInstaller() {
         final String testDialer = "com.android.test.defaultdialer";
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java
new file mode 100644
index 0000000..7660e88
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUtils.class})
+public class FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Mock
+    private FingerprintManager mFingerprintManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private RestrictedSwitchPreference mPreference;
+
+    private Context mContext;
+    private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getSystemService(eq(Context.FINGERPRINT_SERVICE))).thenReturn(
+                mFingerprintManager);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+        mController = spy(new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(mContext,
+                "test_key"));
+        ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowUtils.reset();
+    }
+
+    @Test
+    public void onPreferenceChange_settingIsUpdated() {
+        boolean state = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, 1) != 0;
+
+        assertThat(mController.isChecked()).isFalse();
+        assertThat(mController.onPreferenceChange(mPreference, !state)).isTrue();
+        boolean newState = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, 1) != 0;
+        assertThat(newState).isEqualTo(!state);
+    }
+
+    @Test
+    @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+    public void isAvailable_isEnabled_whenUdfpsHardwareDetected_AndHasEnrolledFingerprints() {
+        assertThat(mController.isAvailable()).isEqualTo(false);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                false /* isPowerbuttonFps false implies udfps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isEqualTo(true);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+    public void isUnavailable_isDisabled_whenUdfpsHardwareDetected_AndNoEnrolledFingerprints() {
+        assertThat(mController.isAvailable()).isEqualTo(false);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                false /* isPowerbuttonFps false implies udfps */,
+                false /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isEqualTo(false);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+    public void isUnavailable_whenHardwareNotDetected() {
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+                false /* isHardwareDetected */,
+                false /* isPowerbuttonFps false implies udfps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isEqualTo(false);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+    public void isUnavailable_onNonUdfpsDevice() {
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                true /* isPowerbuttonFps false implies udfps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    private void configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+            boolean isHardwareDetected, boolean isPowerbuttonFps, boolean hasEnrolledTemplates) {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(isHardwareDetected);
+        when(mFingerprintManager.isPowerbuttonFps()).thenReturn(isPowerbuttonFps);
+        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(hasEnrolledTemplates);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
index 7b6a70e..59af934 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
@@ -30,12 +30,15 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import com.android.settings.testutils.shadow.ShadowUtils;
 import com.android.settingslib.RestrictedSwitchPreference;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -49,6 +52,8 @@
 @Config(shadows = {ShadowUtils.class})
 public class FingerprintSettingsUnlockCategoryControllerTest {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     @Mock
     private FingerprintManager mFingerprintManager;
     @Mock
@@ -59,6 +64,8 @@
     private Context mContext;
     private FingerprintSettingsRequireScreenOnToAuthPreferenceController mController;
 
+    private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController mScreenOffUnlockController;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -69,7 +76,12 @@
 
         mController = spy(new FingerprintSettingsRequireScreenOnToAuthPreferenceController(mContext,
                 "test_key"));
+        mScreenOffUnlockController = spy(
+            new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(mContext,
+                "screen_off_unlock_test_key"));
         ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager);
+        ReflectionHelpers.setField(mScreenOffUnlockController, "mFingerprintManager",
+                mFingerprintManager);
     }
 
     @After
@@ -90,6 +102,20 @@
     }
 
     @Test
+    @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+    public void isAvailable_isEnabled_whenUdfpsHardwareDetected_AndHasEnrolledFingerprints() {
+        assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+        assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+                UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                false /* isPowerbuttonFps false implies udfps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(true);
+        assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
     public void isUnavailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() {
         assertThat(mController.isAvailable()).isEqualTo(false);
         assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
@@ -102,7 +128,22 @@
     }
 
     @Test
-    public void isUnavailable_whenHardwareNotDetected() {
+    @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+    public void isUnavailable_isDisabled_whenUdfpsHardwareDetected_AndNoEnrolledFingerprints() {
+        assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+        assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+                UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                false /* isPowerbuttonFps false implies udfps */,
+                false /* hasEnrolledTemplates */);
+        assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+        assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+                CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    public void isUnavailable_whenHardwareNotDetected_onSfpsDevice() {
         assertThat(mController.isAvailable()).isFalse();
         assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
         configure_hardwareDetected_isSfps_hasEnrolledTemplates(
@@ -114,6 +155,21 @@
     }
 
     @Test
+    @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+    public void isUnavailable_whenHardwareNotDetected_onUdfpsDevice() {
+        assertThat(mScreenOffUnlockController.isAvailable()).isFalse();
+        assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+                UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+                false /* isHardwareDetected */,
+                false /* isPowerbuttonFps false implies udfps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+        assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+                UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
     public void isUnavailable_onNonSfpsDevice() {
         assertThat(mController.isAvailable()).isFalse();
         assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
diff --git a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt
new file mode 100644
index 0000000..67bcc10
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.ContextWrapper
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.res.Resources
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class AirplaneModePreferenceTest {
+
+    private val mockPackageManager = mock<PackageManager>()
+    private val mockResources = mock<Resources>()
+
+    private val context =
+        object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+            override fun getPackageManager(): PackageManager = mockPackageManager
+
+            override fun getResources(): Resources = mockResources
+        }
+
+    private val airplaneModePreference = AirplaneModePreference()
+
+    @Test
+    fun isAvailable_hasConfigAndNoFeatureLeanback_shouldReturnTrue() {
+        mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+        mockPackageManager.stub { on { hasSystemFeature(FEATURE_LEANBACK) } doReturn false }
+
+        assertThat(airplaneModePreference.isAvailable(context)).isTrue()
+    }
+
+    @Test
+    fun isAvailable_noConfig_shouldReturnFalse() {
+        mockResources.stub { on { getBoolean(anyInt()) } doReturn false }
+        mockPackageManager.stub { on { hasSystemFeature(FEATURE_LEANBACK) } doReturn false }
+
+        assertThat(airplaneModePreference.isAvailable(context)).isFalse()
+    }
+
+    @Test
+    fun isAvailable_hasFeatureLeanback_shouldReturnFalse() {
+        mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+        mockPackageManager.stub { on { hasSystemFeature(FEATURE_LEANBACK) } doReturn true }
+
+        assertThat(airplaneModePreference.isAvailable(context)).isFalse()
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt b/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt
new file mode 100644
index 0000000..0eeac43
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.tether
+
+import android.net.TetheringManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.flags.Flags
+import com.android.settings.testutils.shadow.ShadowConnectivityManager
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal
+import com.android.settingslib.Utils
+import com.android.settingslib.preference.CatalystScreenTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.Implementation
+import org.robolectric.annotation.Implements
+
+@RunWith(AndroidJUnit4::class)
+@Config(shadows = [ShadowConnectivityManager::class, ShadowRestrictedLockUtilsInternal::class,
+    ShadowTetheringManager::class])
+class TetherScreenTest : CatalystScreenTestCase() {
+    override val preferenceScreenCreator = TetherScreen()
+
+    override val flagName: String
+        get() = Flags.FLAG_CATALYST_TETHER_SETTINGS
+
+    @Before
+    fun setUp() {
+        ShadowConnectivityManager.getShadow().setTetheringSupported(true)
+    }
+
+    @Test
+    fun key() {
+        assertThat(preferenceScreenCreator.key).isEqualTo(TetherScreen.KEY)
+    }
+
+    @Test
+    fun getPreferenceTitle_tetherConfigDisallowed_shouldShowAll() {
+        ShadowRestrictedLockUtilsInternal.setRestricted(true)
+
+        assertThat(preferenceScreenCreator.getPreferenceTitle(appContext)).isEqualTo(
+                appContext.getString(R.string.tether_settings_title_all))
+    }
+
+    @Test
+    fun getPreferenceTitle_tetherConfigAllowed_shouldShowTetheringLabel() {
+        ShadowRestrictedLockUtilsInternal.setRestricted(false)
+        val tm = appContext.getSystemService(TetheringManager::class.java)
+
+        assertThat(preferenceScreenCreator.getPreferenceTitle(appContext)).isEqualTo(
+                appContext.getText(Utils.getTetheringLabel(tm)))
+    }
+
+    @Test
+    fun isAvailable_tetherIsAvailable_shouldReturnTrue() {
+        ShadowRestrictedLockUtilsInternal.setRestricted(false)
+
+        assertThat(preferenceScreenCreator.isAvailable(appContext)).isTrue()
+    }
+
+    @Test
+    fun isAvailable_tetherIsUnavailable_shouldReturnFalse() {
+        ShadowRestrictedLockUtilsInternal.setRestricted(true)
+
+        assertThat(preferenceScreenCreator.isAvailable(appContext)).isFalse()
+    }
+}
+
+@Implements(TetheringManager::class)
+class ShadowTetheringManager {
+    private val emptyArray = arrayOf<String>()
+
+    @Implementation
+    fun getTetheredIfaces() = emptyArray
+
+    @Implementation
+    fun getTetherableIfaces() = emptyArray
+
+    @Implementation
+    fun getTetherableWifiRegexs() = emptyArray
+
+    @Implementation
+    fun getTetherableUsbRegexs() = emptyArray
+
+    @Implementation
+    fun getTetherableBluetoothRegexs() = emptyArray
+}
diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java
index a25f472..2965acf 100644
--- a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java
@@ -58,6 +58,7 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
+// LINT.IfChange
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = MediaVolumePreferenceControllerTest.ShadowSliceBackgroundWorker.class)
 public class MediaVolumePreferenceControllerTest {
@@ -269,3 +270,4 @@
                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 }
+// LINT.ThenChange(MediaVolumePreference.kt)
diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt
new file mode 100644
index 0000000..383acfb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.content.ContextWrapper
+import android.content.res.Resources
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+// LINT.IfChange
+@RunWith(AndroidJUnit4::class)
+class MediaVolumePreferenceTest {
+    private val mockResources = mock<Resources>()
+
+    private val mediaVolumePreference = MediaVolumePreference()
+    private val context =
+        object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+            override fun getResources(): Resources = mockResources
+        }
+
+    @Test
+    fun isAvailable_configTrue_shouldReturnTrue() {
+        mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+
+        assertThat(mediaVolumePreference.isAvailable(context)).isTrue()
+    }
+
+    @Test
+    fun isAvailable_configFalse_shouldReturnFalse() {
+        mockResources.stub { on { getBoolean(anyInt()) } doReturn false }
+
+        assertThat(mediaVolumePreference.isAvailable(context)).isFalse()
+    }
+}
+// LINT.ThenChange(MediaVolumePreferenceControllerTest.java)
diff --git a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java
index 2974af6..a243643 100644
--- a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java
@@ -41,6 +41,7 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowApplication;
 
+// LINT.IfChange
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowDeviceConfig.class})
 public class SeparateRingVolumePreferenceControllerTest {
@@ -108,3 +109,4 @@
     }
 
 }
+// LINT.ThenChange(SeparateRingVolumePreferenceTest.kt)
diff --git a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt
new file mode 100644
index 0000000..21ddd75
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.content.ContextWrapper
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.os.Vibrator
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+// LINT.IfChange
+@RunWith(AndroidJUnit4::class)
+class SeparateRingVolumePreferenceTest {
+    private var audioHelper = mock<AudioHelper>()
+    private var vibrator: Vibrator? = null
+    private var ringVolumePreference = SeparateRingVolumePreference()
+
+    private val context =
+        object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+            override fun getSystemService(name: String): Any? =
+                when {
+                    name == getSystemServiceName(Vibrator::class.java) -> vibrator
+                    else -> super.getSystemService(name)
+                }
+        }
+
+    @Test
+    fun isAvailable_singleVolume_shouldReturnFalse() {
+        audioHelper = mock { on { isSingleVolume } doReturn true }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.isAvailable(context)).isFalse()
+    }
+
+    @Test
+    fun isAvailable_noSingleVolume_shouldReturnTrue() {
+        audioHelper = mock { on { isSingleVolume } doReturn false }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.isAvailable(context)).isTrue()
+    }
+
+    @Test
+    fun getEffectiveRingerMode_noVibratorAndVibrateMode_shouldReturnSilentMode() {
+        vibrator = mock { on { hasVibrator() } doReturn false }
+        audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.getEffectiveRingerMode(context))
+            .isEqualTo(RINGER_MODE_SILENT)
+    }
+
+    @Test
+    fun getEffectiveRingerMode_hasVibratorAndVibrateMode_shouldReturnVibrateMode() {
+        vibrator = mock { on { hasVibrator() } doReturn true }
+        audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.getEffectiveRingerMode(context))
+            .isEqualTo(RINGER_MODE_VIBRATE)
+    }
+
+    @Test
+    fun getEffectiveRingerMode_hasVibratorAndNormalMode_shouldReturnNormalMode() {
+        vibrator = mock { on { hasVibrator() } doReturn true }
+        audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_NORMAL }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.getEffectiveRingerMode(context))
+            .isEqualTo(RINGER_MODE_NORMAL)
+    }
+
+    @Test
+    fun getMuteIcon_normalMode_shouldReturnRingVolumeIcon() {
+        vibrator = mock { on { hasVibrator() } doReturn true }
+        audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_NORMAL }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.getMuteIcon(context)).isEqualTo(R.drawable.ic_ring_volume)
+    }
+
+    @Test
+    fun getMuteIcon_vibrateMode_shouldReturnVibrateIcon() {
+        vibrator = mock { on { hasVibrator() } doReturn true }
+        audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.getMuteIcon(context))
+            .isEqualTo(R.drawable.ic_volume_ringer_vibrate)
+    }
+
+    @Test
+    fun getMuteIcon_silentMode_shouldReturnSilentIcon() {
+        vibrator = mock { on { hasVibrator() } doReturn false }
+        audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+        ringVolumePreference =
+            spy(ringVolumePreference).stub {
+                onGeneric { createAudioHelper(context) } doReturn audioHelper
+            }
+
+        assertThat(ringVolumePreference.getMuteIcon(context))
+            .isEqualTo(R.drawable.ic_ring_volume_off)
+    }
+}
+// LINT.ThenChange(SeparateRingVolumePreferenceControllerTest.java)
diff --git a/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
index ee239ba..7a6a99a 100644
--- a/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
@@ -34,7 +34,6 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.SettingsSlicesContract;
 
 import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
@@ -102,7 +101,7 @@
 
         controller.displayPreference(mScreen);
 
-        verify(mPreference).setDependency(SettingsSlicesContract.KEY_AIRPLANE_MODE);
+        verify(mPreference).setDependency(Settings.Global.AIRPLANE_MODE_ON);
     }
 
     @Test