Merge "Re-enable failed test in FactoryResetPreferenceControllerTest" into main
diff --git a/res/values/strings.xml b/res/values/strings.xml
index de733b8..fbfaebf 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2319,6 +2319,14 @@
     <!-- Wi-Fi settings screen, advanced, title of the item to show the randomized Wi-Fi MAC address when disconnected. [CHAR LIMIT=69] -->
     <string name="wifi_advanced_randomized_mac_address_disconnected_title">Randomized MAC address (last used)</string>
 
+    <!-- Wifi Network Certificates -->
+    <!-- Wifi certificates summary: More than one certificate -->
+    <string name="wifi_certificate_summary_Certificates"><xliff:g id="number" example="2">%d</xliff:g> certificates</string>
+    <!-- Wifi certificates summary: System certificate -->
+    <string name="wifi_certificate_summary_system">System certificate</string>
+    <!-- Wifi certificates summary: TOFU certificate -->
+    <string name="wifi_certificate_summary_pinning">Certificate pinning</string>
+
     <!-- Wifi Network Details -->
     <!-- Wifi details title-->
     <string name="wifi_details_title">Network details</string>
@@ -5838,9 +5846,6 @@
       other   {# apps restricted}
     }</string>
 
-    <!-- Title to display the battery percentage. [CHAR LIMIT=24] -->
-    <string name="battery_header_title_alternate"><xliff:g id="number" example="88">^1</xliff:g><small> <font size="20"><xliff:g id="unit" example="%">%</xliff:g></font></small></string>
-
     <!-- Summary for top level battery tile if battery is not present. [CHAR LIMIT=NONE] -->
     <string name="battery_missing_message">Problem reading the battery meter.</string>
     <!-- Help text if battery is not present. [CHAR LIMIT=NONE] -->
@@ -6000,13 +6005,13 @@
     <!-- [CHAR_LIMIT=NONE] Battery app usage section header since last full charge to slot_timestamp. Please use similar text with tc/7309909074935858949 -->
     <string name="battery_app_usage_since_last_full_charge_to">App usage since last full charge to <xliff:g id="slot_timestamp" example="Friday 10 am">%s</xliff:g></string>
     <!-- [CHAR_LIMIT=NONE] Battery usage item for total usage time less than a minute -->
-    <string name="battery_usage_total_less_than_one_minute">Total: less than a min</string>
+    <string name="battery_usage_total_less_than_one_minute">Total: less than a minute</string>
     <!-- [CHAR_LIMIT=NONE] Battery usage item for total background time less than a minute -->
-    <string name="battery_usage_background_less_than_one_minute">Background: less than a min</string>
+    <string name="battery_usage_background_less_than_one_minute">Background: less than a minute</string>
     <!-- [CHAR_LIMIT=NONE] Device screen on time less than a minute -->
-    <string name="battery_usage_screen_time_less_than_one_minute">Screen time: less than a min</string>
+    <string name="battery_usage_screen_time_less_than_one_minute">Screen time: less than a minute</string>
     <!-- [CHAR_LIMIT=NONE] Power usage time less than a minute -->
-    <string name="power_usage_time_less_than_one_minute">Less than a min</string>
+    <string name="power_usage_time_less_than_one_minute">Less than a minute</string>
     <!-- [CHAR_LIMIT=NONE] Battery usage item for total usage time -->
     <string name="battery_usage_for_total_time">Total: <xliff:g id="time">%s</xliff:g></string>
     <!-- [CHAR_LIMIT=NONE] Battery usage item for background usage time -->
@@ -6263,6 +6268,10 @@
     <string name="credential_for_vpn_and_apps">Installed for VPN and apps</string>
     <!-- Sub-heading for a user credential installed for Wi-Fi configuration. [CHAR LIMIT=NONE]. -->
     <string name="credential_for_wifi">Installed for Wi\u2011Fi</string>
+    <!-- Sub-heading for a user credential installed for particular Wi-Fi configuration. [CHAR LIMIT=NONE]. -->
+    <string name="credential_installed_for_wifi">Installed for <xliff:g id="ssid" example="SSID">%s</xliff:g></string>
+    <!-- Sub-heading for a user credential using for Wi-Fi configuration. [CHAR LIMIT=NONE]. -->
+    <string name="credential_using_for_wifi">Using for <xliff:g id="ssid" example="SSID">%s</xliff:g></string>
     <!-- Sub-heading for a user credential installed to be used as part of a Wi-Fi configuration. [CHAR LIMIT=NONE]. -->
     <string name="credential_for_wifi_in_use">Installed for Wi\u2011Fi (In use)</string>
     <!-- Description of dialog to reset credential storage [CHAR LIMIT=NONE] -->
diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java
index c0d2445..169e046 100644
--- a/src/com/android/settings/SettingsApplication.java
+++ b/src/com/android/settings/SettingsApplication.java
@@ -28,6 +28,7 @@
 import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
 import com.android.settings.activityembedding.ActivityEmbeddingUtils;
 import com.android.settings.core.instrumentation.ElapsedTimeUtils;
+import com.android.settings.development.DeveloperOptionsActivityLifecycle;
 import com.android.settings.fuelgauge.BatterySettingsStorage;
 import com.android.settings.homepage.SettingsHomepageActivity;
 import com.android.settings.localepicker.LocaleNotificationDataManager;
@@ -79,6 +80,8 @@
                 new DeviceProvisionedObserver().registerContentObserver();
             }
         }
+
+        registerActivityLifecycleCallbacks(new DeveloperOptionsActivityLifecycle());
     }
 
     @Override
diff --git a/src/com/android/settings/applications/ProcessStatsSummary.java b/src/com/android/settings/applications/ProcessStatsSummary.java
index ef76cd5..cdec926 100644
--- a/src/com/android/settings/applications/ProcessStatsSummary.java
+++ b/src/com/android/settings/applications/ProcessStatsSummary.java
@@ -35,13 +35,15 @@
 import com.android.settings.Utils;
 import com.android.settings.applications.ProcStatsData.MemInfo;
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.development.DisableDevSettingsDialogFragment;
 
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 
-public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenceClickListener {
+public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenceClickListener,
+        DeveloperOptionAwareMixin {
 
     private static final String KEY_PREF_SCREEN = "app_list";
     private static final String KEY_MEMORY_INFO_PREF_GROUP = "memory_info";
diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java
index c75fe06..4d1c166 100644
--- a/src/com/android/settings/applications/RunningServices.java
+++ b/src/com/android/settings/applications/RunningServices.java
@@ -26,9 +26,11 @@
 
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.widget.LoadingViewController;
 
-public class RunningServices extends SettingsPreferenceFragment {
+public class RunningServices extends SettingsPreferenceFragment implements
+        DeveloperOptionAwareMixin {
 
     private static final int SHOW_RUNNING_SERVICES = 1;
     private static final int SHOW_BACKGROUND_PROCESSES = 2;
diff --git a/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java
index f6f62e8..3aca85e 100644
--- a/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java
@@ -16,11 +16,8 @@
 
 package com.android.settings.bluetooth;
 
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -30,39 +27,20 @@
 import androidx.preference.TwoStatePreference;
 
 import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
-import com.android.settingslib.flags.Flags;
 import com.android.settingslib.utils.ThreadUtils;
 
 public class BluetoothAutoOnPreferenceController extends TogglePreferenceController
-        implements LifecycleObserver, OnStart, OnStop {
-    private static final String TAG = "BluetoothAutoOnPreferenceController";
+        implements BluetoothCallback, LifecycleObserver, OnStart, OnStop {
+    private static final String TAG = "BluetoothAutoOnPrefCtlr";
     @VisibleForTesting static final String PREF_KEY = "bluetooth_auto_on_settings_toggle";
-    static final String SETTING_NAME = "bluetooth_automatic_turn_on";
-    static final int UNSET = -1;
-    @VisibleForTesting static final int ENABLED = 1;
-    @VisibleForTesting static final int DISABLED = 0;
-    private final ContentObserver mContentObserver =
-            new ContentObserver(new Handler(/* async= */ true)) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    var unused =
-                            ThreadUtils.postOnBackgroundThread(
-                                    () -> {
-                                        updateValue();
-                                        mContext.getMainExecutor()
-                                                .execute(
-                                                        () -> {
-                                                            if (mPreference != null) {
-                                                                updateState(mPreference);
-                                                            }
-                                                        });
-                                    });
-                }
-            };
-    private int mAutoOnValue = UNSET;
+    @VisibleForTesting BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+    private final LocalBluetoothManager mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+    private boolean mAutoOnValue = false;
     @Nullable private TwoStatePreference mPreference;
 
     public BluetoothAutoOnPreferenceController(
@@ -71,26 +49,54 @@
     }
 
     @Override
+    public void onAutoOnStateChanged(int state) {
+        var unused =
+                ThreadUtils.postOnBackgroundThread(
+                        () -> {
+                            Log.i(TAG, "onAutoOnStateChanged() state: " + state);
+                            updateValue();
+                            mContext.getMainExecutor()
+                                    .execute(
+                                            () -> {
+                                                if (mPreference != null) {
+                                                    updateState(mPreference);
+                                                }
+                                            });
+                        });
+    }
+
+    @Override
     public void onStart() {
-        mContext.getContentResolver()
-                .registerContentObserver(
-                        Settings.Secure.getUriFor(SETTING_NAME),
-                        /* notifyForDescendants= */ false,
-                        mContentObserver);
+        if (mLocalBluetoothManager == null) {
+            return;
+        }
+        mLocalBluetoothManager.getEventManager().registerCallback(this);
     }
 
     @Override
     public void onStop() {
-        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+        if (mLocalBluetoothManager == null) {
+            return;
+        }
+        mLocalBluetoothManager.getEventManager().unregisterCallback(this);
     }
 
     @Override
     public int getAvailabilityStatus() {
-        if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
+        if (mBluetoothAdapter == null) {
             return UNSUPPORTED_ON_DEVICE;
         }
-        updateValue();
-        return mAutoOnValue != UNSET ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+        try {
+            boolean isSupported = mBluetoothAdapter.isAutoOnSupported();
+            Log.i(TAG, "getAvailabilityStatus() isSupported: " + isSupported);
+            if (isSupported) {
+                var unused = ThreadUtils.postOnBackgroundThread(this::updateValue);
+            }
+            return isSupported ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+        } catch (Exception e) {
+            // Server could throw TimeoutException, InterruptedException or ExecutionException
+            return UNSUPPORTED_ON_DEVICE;
+        }
     }
 
     @Override
@@ -106,26 +112,20 @@
 
     @Override
     public boolean isChecked() {
-        return mAutoOnValue == ENABLED;
+        return mAutoOnValue;
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        if (getAvailabilityStatus() != AVAILABLE) {
-            Log.w(TAG, "Trying to set toggle value while feature not available.");
-            return false;
-        }
         var unused =
                 ThreadUtils.postOnBackgroundThread(
                         () -> {
-                            boolean updated =
-                                    Settings.Secure.putIntForUser(
-                                            mContext.getContentResolver(),
-                                            SETTING_NAME,
-                                            isChecked ? ENABLED : DISABLED,
-                                            UserHandle.myUserId());
-                            if (updated) {
-                                updateValue();
+                            try {
+                                mBluetoothAdapter.setAutoOnEnabled(isChecked);
+                            } catch (Exception e) {
+                                // Server could throw IllegalStateException, TimeoutException,
+                                // InterruptedException or ExecutionException
+                                Log.e(TAG, "Error calling setAutoOnEnabled()", e);
                             }
                         });
         return true;
@@ -137,8 +137,14 @@
     }
 
     private void updateValue() {
-        mAutoOnValue =
-                Settings.Secure.getIntForUser(
-                        mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId());
+        if (mBluetoothAdapter == null) {
+            return;
+        }
+        try {
+            mAutoOnValue = mBluetoothAdapter.isAutoOnEnabled();
+        } catch (Exception e) {
+            // Server could throw TimeoutException, InterruptedException or ExecutionException
+            Log.e(TAG, "Error calling isAutoOnEnabled()", e);
+        }
     }
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
index ac55758..fc8365d 100644
--- a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
@@ -15,13 +15,10 @@
  */
 package com.android.settings.bluetooth;
 
-import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
-import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
-
 import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.util.Log;
 import android.view.View;
 
 import androidx.annotation.VisibleForTesting;
@@ -34,7 +31,6 @@
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
-import com.android.settingslib.flags.Flags;
 import com.android.settingslib.widget.FooterPreference;
 
 /**
@@ -47,11 +43,13 @@
                 OnStop,
                 SwitchWidgetController.OnSwitchChangeListener,
                 View.OnClickListener {
+    private static final String TAG = "BluetoothSwitchPrefCtrl";
 
     private BluetoothEnabler mBluetoothEnabler;
     private RestrictionUtils mRestrictionUtils;
     private SwitchWidgetController mSwitch;
     private Context mContext;
+    private BluetoothAdapter mBluetoothAdapter;
     private FooterPreference mFooterPreference;
     private boolean mIsAlwaysDiscoverable;
 
@@ -87,6 +85,7 @@
                         mRestrictionUtils);
         mBluetoothEnabler.setToggleCallback(this);
         mAlwaysDiscoverable = new AlwaysDiscoverable(context);
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     @Override
@@ -157,11 +156,15 @@
     }
 
     private boolean isAutoOnFeatureAvailable() {
-        if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
+        if (mBluetoothAdapter == null) {
             return false;
         }
-        return Settings.Secure.getIntForUser(
-                        mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId())
-                != UNSET;
+        try {
+            return mBluetoothAdapter.isAutoOnSupported();
+        } catch (Exception e) {
+            // Server could throw TimeoutException, InterruptedException or ExecutionException
+            Log.e(TAG, "Error calling isAutoOnFeatureAvailable()", e);
+            return false;
+        }
     }
 }
diff --git a/src/com/android/settings/bugreporthandler/BugReportHandlerPicker.java b/src/com/android/settings/bugreporthandler/BugReportHandlerPicker.java
index 276e7aa..88cb819 100644
--- a/src/com/android/settings/bugreporthandler/BugReportHandlerPicker.java
+++ b/src/com/android/settings/bugreporthandler/BugReportHandlerPicker.java
@@ -38,6 +38,7 @@
 
 import com.android.settings.R;
 import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settingslib.applications.DefaultAppInfo;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.widget.CandidateInfo;
@@ -50,7 +51,8 @@
 /**
  * Picker for BugReportHandler.
  */
-public class BugReportHandlerPicker extends DefaultAppPickerFragment {
+public class BugReportHandlerPicker extends DefaultAppPickerFragment implements
+        DeveloperOptionAwareMixin {
     private static final String TAG = "BugReportHandlerPicker";
 
     private BugReportHandlerUtil mBugReportHandlerUtil;
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java b/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java
index b5a9e5a..5f3e4cd 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java
@@ -33,6 +33,7 @@
 
 import com.android.settings.R;
 import com.android.settings.Utils;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.widget.RadioButtonPickerFragment;
 import com.android.settingslib.widget.CandidateInfo;
 import com.android.settingslib.widget.FooterPreference;
@@ -45,7 +46,8 @@
 /**
  * Provides options for selecting the default USB mode.
  */
-public class UsbDefaultFragment extends RadioButtonPickerFragment {
+public class UsbDefaultFragment extends RadioButtonPickerFragment implements
+        DeveloperOptionAwareMixin {
 
     private static final String TAG = "UsbDefaultFragment";
 
diff --git a/src/com/android/settings/development/AdbDeviceDetailsFragment.java b/src/com/android/settings/development/AdbDeviceDetailsFragment.java
index 9861345..f5c71b6 100644
--- a/src/com/android/settings/development/AdbDeviceDetailsFragment.java
+++ b/src/com/android/settings/development/AdbDeviceDetailsFragment.java
@@ -32,7 +32,8 @@
  * Fragment shown when clicking on a paired device in the Wireless
  * Debugging fragment.
  */
-public class AdbDeviceDetailsFragment extends DashboardFragment {
+public class AdbDeviceDetailsFragment extends DashboardFragment implements
+        DeveloperOptionAwareMixin {
     private static final String TAG = "AdbDeviceDetailsFrag";
     private PairDevice mPairedDevice;
 
diff --git a/src/com/android/settings/development/DeveloperOptionAwareMixin.java b/src/com/android/settings/development/DeveloperOptionAwareMixin.java
new file mode 100644
index 0000000..f5a05af
--- /dev/null
+++ b/src/com/android/settings/development/DeveloperOptionAwareMixin.java
@@ -0,0 +1,20 @@
+/*
+ * 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.development;
+
+/** A fragment mixin that should aware the state of developer options */
+public interface DeveloperOptionAwareMixin {}
diff --git a/src/com/android/settings/development/DeveloperOptionsActivityLifecycle.java b/src/com/android/settings/development/DeveloperOptionsActivityLifecycle.java
new file mode 100644
index 0000000..de5c52b
--- /dev/null
+++ b/src/com/android/settings/development/DeveloperOptionsActivityLifecycle.java
@@ -0,0 +1,87 @@
+/*
+ * 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.development;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.SettingsActivity;
+import com.android.settingslib.development.DevelopmentSettingsEnabler;
+
+public class DeveloperOptionsActivityLifecycle implements Application.ActivityLifecycleCallbacks {
+
+    private FragmentManager.FragmentLifecycleCallbacks mFragmentCallback =
+            new FragmentManager.FragmentLifecycleCallbacks() {
+                @Override
+                public void onFragmentResumed(@NonNull FragmentManager fm, @NonNull Fragment f) {
+                    if (!(f instanceof DeveloperOptionAwareMixin)) {
+                        return;
+                    }
+
+                    Activity activity = f.getActivity();
+                    if (activity == null) {
+                        return;
+                    }
+
+                    if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(activity)) {
+                        return;
+                    }
+
+                    if (fm.getBackStackEntryCount() > 0) {
+                        fm.popBackStack();
+                    } else {
+                        activity.finish();
+                    }
+                }
+            };
+
+    public DeveloperOptionsActivityLifecycle() {}
+
+    @Override
+    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
+        if (!(activity instanceof SettingsActivity)) {
+            return;
+        }
+
+        FragmentManager fm = ((SettingsActivity) activity).getSupportFragmentManager();
+        fm.registerFragmentLifecycleCallbacks(mFragmentCallback, /* recursive= */ true);
+    }
+
+    @Override
+    public void onActivityStarted(@NonNull Activity activity) {}
+
+    @Override
+    public void onActivityResumed(@NonNull Activity activity) {}
+
+    @Override
+    public void onActivityPaused(@NonNull Activity activity) {}
+
+    @Override
+    public void onActivityStopped(@NonNull Activity activity) {}
+
+    @Override
+    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {}
+
+    @Override
+    public void onActivityDestroyed(@NonNull Activity activity) {}
+}
diff --git a/src/com/android/settings/development/DevelopmentAppPicker.java b/src/com/android/settings/development/DevelopmentAppPicker.java
index 7ef0869..5ac8d2f 100644
--- a/src/com/android/settings/development/DevelopmentAppPicker.java
+++ b/src/com/android/settings/development/DevelopmentAppPicker.java
@@ -39,7 +39,8 @@
 import java.util.Comparator;
 import java.util.List;
 
-public class DevelopmentAppPicker extends DefaultAppPickerFragment {
+public class DevelopmentAppPicker extends DefaultAppPickerFragment implements
+        DeveloperOptionAwareMixin {
     public static final String EXTRA_REQUESTING_PERMISSION = "REQUESTING_PERMISSION";
     public static final String EXTRA_DEBUGGABLE = "DEBUGGABLE";
     public static final String EXTRA_SELECTING_APP = "SELECTING_APP";
diff --git a/src/com/android/settings/development/DevelopmentMemtagPage.java b/src/com/android/settings/development/DevelopmentMemtagPage.java
index df983f3..230e5db 100644
--- a/src/com/android/settings/development/DevelopmentMemtagPage.java
+++ b/src/com/android/settings/development/DevelopmentMemtagPage.java
@@ -25,7 +25,7 @@
 import com.android.settingslib.search.SearchIndexable;
 
 @SearchIndexable
-public class DevelopmentMemtagPage extends DashboardFragment {
+public class DevelopmentMemtagPage extends DashboardFragment implements DeveloperOptionAwareMixin {
     private static final String TAG = "DevelopmentMemtagPage";
 
     @Override
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 43aa783..d3bf8d8 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -218,6 +218,14 @@
         super.onStart();
         final ContentResolver cr = getContext().getContentResolver();
         cr.registerContentObserver(mDevelopEnabled, false, mDeveloperSettingsObserver);
+
+        // Restore UI state based on whether developer options is enabled
+        if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) {
+            enableDeveloperOptions();
+            handleQsTileLongPressActionIfAny();
+        } else {
+            disableDeveloperOptions();
+        }
     }
 
     @Override
@@ -274,14 +282,6 @@
         mSwitchBarController = new DevelopmentSwitchBarController(
                 this /* DevelopmentSettings */, mSwitchBar, mIsAvailable,
                 getSettingsLifecycle());
-
-        // Restore UI state based on whether developer options is enabled
-        if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) {
-            enableDeveloperOptions();
-            handleQsTileLongPressActionIfAny();
-        } else {
-            disableDeveloperOptions();
-        }
     }
 
     @Override
diff --git a/src/com/android/settings/development/WirelessDebuggingFragment.java b/src/com/android/settings/development/WirelessDebuggingFragment.java
index 8be8d91..fede1f1 100644
--- a/src/com/android/settings/development/WirelessDebuggingFragment.java
+++ b/src/com/android/settings/development/WirelessDebuggingFragment.java
@@ -61,7 +61,7 @@
  */
 @SearchIndexable
 public class WirelessDebuggingFragment extends DashboardFragment
-        implements WirelessDebuggingEnabler.OnEnabledListener {
+        implements WirelessDebuggingEnabler.OnEnabledListener, DeveloperOptionAwareMixin {
 
     private static final String TAG = "WirelessDebuggingFrag";
 
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java b/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java
index 0483dce..def449b 100644
--- a/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java
+++ b/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java
@@ -22,8 +22,8 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.Indexable;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.search.SearchIndexable;
 
@@ -31,7 +31,8 @@
 import java.util.List;
 
 @SearchIndexable
-public class FeatureFlagsDashboard extends DashboardFragment {
+public class FeatureFlagsDashboard extends DashboardFragment implements
+        DeveloperOptionAwareMixin {
 
     private static final String TAG = "FeatureFlagsDashboard";
 
@@ -51,11 +52,6 @@
     }
 
     @Override
-    public void onAttach(Context context) {
-        super.onAttach(context);
-    }
-
-    @Override
     public int getHelpResource() {
         return 0;
     }
diff --git a/src/com/android/settings/development/graphicsdriver/GraphicsDriverDashboard.java b/src/com/android/settings/development/graphicsdriver/GraphicsDriverDashboard.java
index 87678f9..364a3b0 100644
--- a/src/com/android/settings/development/graphicsdriver/GraphicsDriverDashboard.java
+++ b/src/com/android/settings/development/graphicsdriver/GraphicsDriverDashboard.java
@@ -23,6 +23,7 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.widget.MainSwitchBarController;
 import com.android.settings.widget.SettingsMainSwitchBar;
@@ -33,7 +34,8 @@
  * Dashboard for Graphics Driver preferences.
  */
 @SearchIndexable
-public class GraphicsDriverDashboard extends DashboardFragment {
+public class GraphicsDriverDashboard extends DashboardFragment implements
+        DeveloperOptionAwareMixin {
 
     private static final String TAG = "GraphicsDriverDashboard";
 
diff --git a/src/com/android/settings/development/qstile/DevelopmentTileConfigFragment.java b/src/com/android/settings/development/qstile/DevelopmentTileConfigFragment.java
index 0ca189d..076930a 100644
--- a/src/com/android/settings/development/qstile/DevelopmentTileConfigFragment.java
+++ b/src/com/android/settings/development/qstile/DevelopmentTileConfigFragment.java
@@ -25,6 +25,7 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.search.SearchIndexable;
@@ -35,7 +36,8 @@
 import java.util.Map;
 
 @SearchIndexable
-public class DevelopmentTileConfigFragment extends DashboardFragment {
+public class DevelopmentTileConfigFragment extends DashboardFragment implements
+        DeveloperOptionAwareMixin {
     private static final String TAG = "DevelopmentTileConfig";
     private static final String QS_TILE_PERF = "develop_qs_tile";
 
diff --git a/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersDashboard.java b/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersDashboard.java
index 6f1ede2..50e00dc 100644
--- a/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersDashboard.java
+++ b/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersDashboard.java
@@ -22,6 +22,7 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.search.SearchIndexable;
@@ -32,7 +33,8 @@
  * Bluetooth Snoop Logger Filters Dashboard
  */
 @SearchIndexable
-public class SnoopLoggerFiltersDashboard extends DashboardFragment {
+public class SnoopLoggerFiltersDashboard extends DashboardFragment implements
+        DeveloperOptionAwareMixin {
 
     private static final String TAG = "SnoopLoggerFiltersDashboard";
 
diff --git a/src/com/android/settings/development/transcode/TranscodeSettingsFragment.java b/src/com/android/settings/development/transcode/TranscodeSettingsFragment.java
index e3dc9bf..8abe424 100644
--- a/src/com/android/settings/development/transcode/TranscodeSettingsFragment.java
+++ b/src/com/android/settings/development/transcode/TranscodeSettingsFragment.java
@@ -21,6 +21,7 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.search.SearchIndexable;
@@ -29,7 +30,8 @@
  * Fragment for native transcode settings in Developer options.
  */
 @SearchIndexable
-public class TranscodeSettingsFragment extends DashboardFragment {
+public class TranscodeSettingsFragment extends DashboardFragment implements
+        DeveloperOptionAwareMixin {
     private static final String TAG = "TranscodeSettings";
 
     @Override
diff --git a/src/com/android/settings/development/widevine/WidevineSettingsFragment.java b/src/com/android/settings/development/widevine/WidevineSettingsFragment.java
index 058aa40..9eab9f5 100644
--- a/src/com/android/settings/development/widevine/WidevineSettingsFragment.java
+++ b/src/com/android/settings/development/widevine/WidevineSettingsFragment.java
@@ -21,6 +21,7 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.search.SearchIndexable;
@@ -29,7 +30,8 @@
  * Fragment for native widevine settings in Developer options.
 */
 @SearchIndexable
-public class WidevineSettingsFragment extends DashboardFragment {
+public class WidevineSettingsFragment extends DashboardFragment implements
+        DeveloperOptionAwareMixin {
     private static final String TAG = "WidevineSettings";
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
index 6a65dc0..907c89f 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
@@ -17,10 +17,8 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.icu.text.NumberFormat;
 import android.os.BatteryManager;
 import android.os.PowerManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
@@ -145,8 +143,6 @@
     }
 
     private CharSequence formatBatteryPercentageText(int batteryLevel) {
-        return TextUtils.expandTemplate(
-                mContext.getText(R.string.battery_header_title_alternate),
-                NumberFormat.getIntegerInstance().format(batteryLevel));
+        return com.android.settings.Utils.formatPercentage(batteryLevel);
     }
 }
diff --git a/src/com/android/settings/fuelgauge/InactiveApps.java b/src/com/android/settings/fuelgauge/InactiveApps.java
index 465a9d8..44f41cd 100644
--- a/src/com/android/settings/fuelgauge/InactiveApps.java
+++ b/src/com/android/settings/fuelgauge/InactiveApps.java
@@ -40,12 +40,13 @@
 
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 
 import java.util.Arrays;
 import java.util.List;
 
 public class InactiveApps extends SettingsPreferenceFragment
-        implements Preference.OnPreferenceChangeListener {
+        implements Preference.OnPreferenceChangeListener, DeveloperOptionAwareMixin {
 
     private static final CharSequence[] FULL_SETTABLE_BUCKETS_NAMES = {
         "ACTIVE", "WORKING_SET", "FREQUENT", "RARE", "RESTRICTED"
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 1abcbdb..a287b84 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -18,6 +18,7 @@
 
 import android.net.Uri
 import android.os.Bundle
+import android.provider.Telephony
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
@@ -142,39 +143,39 @@
             SettingsOutlinedTextField(
                 value = apnData.name,
                 label = stringResource(R.string.apn_name),
-                enabled = apnData.nameEnabled,
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.NAME),
                 errorMessage = validateName(apnData.validEnabled, apnData.name, context)
             ) { apnData = apnData.copy(name = it) }
             SettingsOutlinedTextField(
                 value = apnData.apn,
                 label = stringResource(R.string.apn_apn),
-                enabled = apnData.apnEnabled,
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.APN),
                 errorMessage = validateAPN(apnData.validEnabled, apnData.apn, context)
             ) { apnData = apnData.copy(apn = it) }
             SettingsOutlinedTextField(
                 value = apnData.proxy,
                 label = stringResource(R.string.apn_http_proxy),
-                enabled = apnData.proxyEnabled
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.PROXY),
             ) { apnData = apnData.copy(proxy = it) }
             SettingsOutlinedTextField(
                 value = apnData.port,
                 label = stringResource(R.string.apn_http_port),
-                enabled = apnData.portEnabled
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.PORT),
             ) { apnData = apnData.copy(port = it) }
             SettingsOutlinedTextField(
                 value = apnData.userName,
                 label = stringResource(R.string.apn_user),
-                enabled = apnData.userNameEnabled
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.USER),
             ) { apnData = apnData.copy(userName = it) }
             SettingsTextFieldPassword(
                 value = apnData.passWord,
                 label = stringResource(R.string.apn_password),
-                enabled = apnData.passWordEnabled
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.PASSWORD),
             ) { apnData = apnData.copy(passWord = it) }
             SettingsOutlinedTextField(
                 value = apnData.server,
                 label = stringResource(R.string.apn_server),
-                enabled = apnData.serverEnabled
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.SERVER),
             ) { apnData = apnData.copy(server = it) }
             ApnTypeCheckBox(
                 apnData = apnData,
@@ -186,42 +187,45 @@
                     value = apnData.mmsc,
                     label = stringResource(R.string.apn_mmsc),
                     errorMessage = validateMMSC(apnData.validEnabled, apnData.mmsc, context),
-                    enabled = apnData.mmscEnabled
+                    enabled = apnData.isFieldEnabled(Telephony.Carriers.MMSC),
                 ) { apnData = apnData.copy(mmsc = it) }
                 SettingsOutlinedTextField(
                     value = apnData.mmsProxy,
                     label = stringResource(R.string.apn_mms_proxy),
-                    enabled = apnData.mmsProxyEnabled
+                    enabled = apnData.isFieldEnabled(Telephony.Carriers.MMSPROXY),
                 ) { apnData = apnData.copy(mmsProxy = it) }
                 SettingsOutlinedTextField(
                     value = apnData.mmsPort,
                     label = stringResource(R.string.apn_mms_port),
-                    enabled = apnData.mmsPortEnabled
+                    enabled = apnData.isFieldEnabled(Telephony.Carriers.MMSPORT),
                 ) { apnData = apnData.copy(mmsPort = it) }
             }
             SettingsDropdownBox(
                 label = stringResource(R.string.apn_auth_type),
                 options = authTypeOptions,
                 selectedOptionIndex = apnData.authType,
-                enabled = apnData.authTypeEnabled,
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.AUTH_TYPE),
             ) { apnData = apnData.copy(authType = it) }
             SettingsDropdownBox(
                 label = stringResource(R.string.apn_protocol),
                 options = apnProtocolOptions,
                 selectedOptionIndex = apnData.apnProtocol,
-                enabled = apnData.apnProtocolEnabled
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.PROTOCOL),
             ) { apnData = apnData.copy(apnProtocol = it) }
             SettingsDropdownBox(
                 label = stringResource(R.string.apn_roaming_protocol),
                 options = apnProtocolOptions,
                 selectedOptionIndex = apnData.apnRoaming,
-                enabled = apnData.apnRoamingEnabled
+                enabled = apnData.isFieldEnabled(Telephony.Carriers.ROAMING_PROTOCOL),
             ) { apnData = apnData.copy(apnRoaming = it) }
             ApnNetworkTypeCheckBox(apnData) { apnData = apnData.copy(networkType = it) }
             SwitchPreference(
                 object : SwitchPreferenceModel {
-                    override val title = context.resources.getString(R.string.carrier_enabled)
-                    override val changeable = { apnData.apnEnableEnabled }
+                    override val title = stringResource(R.string.carrier_enabled)
+                    override val changeable = {
+                        apnData.apnEnableEnabled &&
+                            apnData.isFieldEnabled(Telephony.Carriers.CARRIER_ENABLED)
+                    }
                     override val checked = { apnData.apnEnable }
                     override val onCheckedChange = { newChecked: Boolean ->
                         apnData = apnData.copy(apnEnable = newChecked)
diff --git a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
index a42031b..305f7b6 100644
--- a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
+++ b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
@@ -16,6 +16,7 @@
 
 package com.android.settings.network.apn
 
+import android.provider.Telephony
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.res.stringResource
@@ -29,7 +30,11 @@
         label = stringResource(R.string.network_type),
         options = options,
         emptyText = stringResource(R.string.network_type_unspecified),
-        enabled = apnData.networkTypeEnabled,
+        enabled = apnData.isFieldEnabled(
+            Telephony.Carriers.BEARER,
+            Telephony.Carriers.BEARER_BITMASK,
+            Telephony.Carriers.NETWORK_TYPE_BITMASK
+        ),
     ) {
         onNetworkTypeChanged(ApnNetworkTypes.optionsToNetworkType(options))
     }
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index ab16f1c..aa816fc 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -48,22 +48,7 @@
     val networkType: Long = 0,
     val edited: Int = Telephony.Carriers.USER_EDITED,
     val userEditable: Int = 1,
-    val nameEnabled: Boolean = true,
-    val apnEnabled: Boolean = true,
-    val proxyEnabled: Boolean = true,
-    val portEnabled: Boolean = true,
-    val userNameEnabled: Boolean = true,
-    val passWordEnabled: Boolean = true,
-    val serverEnabled: Boolean = true,
-    val mmscEnabled: Boolean = true,
-    val mmsProxyEnabled: Boolean = true,
-    val mmsPortEnabled: Boolean = true,
-    val authTypeEnabled: Boolean = true,
-    val apnTypeEnabled: Boolean = true,
-    val apnProtocolEnabled: Boolean = true,
-    val apnRoamingEnabled: Boolean = true,
     val apnEnableEnabled: Boolean = true,
-    val networkTypeEnabled: Boolean = true,
     val newApn: Boolean = false,
     val subId: Int = -1,
     val validEnabled: Boolean = false,
@@ -93,6 +78,10 @@
         if (newApn) context.getApnIdMap(subId).forEach(::putObject)
         getContentValueMap(context).forEach(::putObject)
     }
+
+    fun isFieldEnabled(vararg fieldName: String): Boolean =
+        !customizedConfig.readOnlyApn &&
+            fieldName.all { it !in customizedConfig.readOnlyApnFields }
 }
 
 data class CustomizedConfig(
@@ -271,83 +260,17 @@
 fun disableInit(apnDataInit: ApnData): ApnData {
     if (apnDataInit.isReadOnly()) {
         Log.d(TAG, "disableInit: read-only APN")
-        val apnData = apnDataInit.copy(
+        return apnDataInit.copy(
             customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true)
         )
-        return disableAllFields(apnData)
     }
     val readOnlyApnFields = apnDataInit.customizedConfig.readOnlyApnFields
     if (readOnlyApnFields.isNotEmpty()) {
         Log.d(TAG, "disableInit: readOnlyApnFields $readOnlyApnFields)")
-        return disableFields(readOnlyApnFields, apnDataInit)
     }
     return apnDataInit
 }
 
-/**
- * Disables all fields so that user cannot modify the APN
- */
-private fun disableAllFields(apnDataInit: ApnData): ApnData {
-    var apnData = apnDataInit
-    apnData = apnData.copy(nameEnabled = false)
-    apnData = apnData.copy(apnEnabled = false)
-    apnData = apnData.copy(proxyEnabled = false)
-    apnData = apnData.copy(portEnabled = false)
-    apnData = apnData.copy(userNameEnabled = false)
-    apnData = apnData.copy(passWordEnabled = false)
-    apnData = apnData.copy(serverEnabled = false)
-    apnData = apnData.copy(mmscEnabled = false)
-    apnData = apnData.copy(mmsProxyEnabled = false)
-    apnData = apnData.copy(mmsPortEnabled = false)
-    apnData = apnData.copy(authTypeEnabled = false)
-    apnData = apnData.copy(apnTypeEnabled = false)
-    apnData = apnData.copy(apnProtocolEnabled = false)
-    apnData = apnData.copy(apnRoamingEnabled = false)
-    apnData = apnData.copy(apnEnableEnabled = false)
-    apnData = apnData.copy(networkTypeEnabled = false)
-    return apnData
-}
-
-/**
- * Disables given fields so that user cannot modify them
- *
- * @param apnFields fields to be disabled
- */
-private fun disableFields(apnFields: List<String>, apnDataInit: ApnData): ApnData {
-    var apnData = apnDataInit
-    for (apnField in apnFields) {
-        apnData = disableByFieldName(apnField, apnDataInit)
-    }
-    return apnData
-}
-
-private fun disableByFieldName(apnField: String, apnDataInit: ApnData): ApnData {
-    var apnData = apnDataInit
-    when (apnField) {
-        Telephony.Carriers.NAME -> apnData = apnData.copy(nameEnabled = false)
-        Telephony.Carriers.APN -> apnData = apnData.copy(apnEnabled = false)
-        Telephony.Carriers.PROXY -> apnData = apnData.copy(proxyEnabled = false)
-        Telephony.Carriers.PORT -> apnData = apnData.copy(portEnabled = false)
-        Telephony.Carriers.USER -> apnData = apnData.copy(userNameEnabled = false)
-        Telephony.Carriers.SERVER -> apnData = apnData.copy(serverEnabled = false)
-        Telephony.Carriers.PASSWORD -> apnData = apnData.copy(passWordEnabled = false)
-        Telephony.Carriers.MMSPROXY -> apnData = apnData.copy(mmsProxyEnabled = false)
-        Telephony.Carriers.MMSPORT -> apnData = apnData.copy(mmsPortEnabled = false)
-        Telephony.Carriers.MMSC -> apnData = apnData.copy(mmscEnabled = false)
-        Telephony.Carriers.TYPE -> apnData = apnData.copy(apnTypeEnabled = false)
-        Telephony.Carriers.AUTH_TYPE -> apnData = apnData.copy(authTypeEnabled = false)
-        Telephony.Carriers.PROTOCOL -> apnData = apnData.copy(apnProtocolEnabled = false)
-        Telephony.Carriers.ROAMING_PROTOCOL -> apnData = apnData.copy(apnRoamingEnabled = false)
-        Telephony.Carriers.CARRIER_ENABLED -> apnData = apnData.copy(apnEnableEnabled = false)
-        Telephony.Carriers.BEARER, Telephony.Carriers.BEARER_BITMASK,
-        Telephony.Carriers.NETWORK_TYPE_BITMASK -> apnData = apnData.copy(
-            networkTypeEnabled =
-            false
-        )
-    }
-    return apnData
-}
-
 fun deleteApn(uri: Uri, context: Context) {
     val contentResolver = context.contentResolver
     contentResolver.delete(uri, null, null)
diff --git a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
index 4d0659c..aa757cc 100644
--- a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
+++ b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
@@ -16,6 +16,7 @@
 
 package com.android.settings.network.apn
 
+import android.provider.Telephony
 import android.telephony.data.ApnSetting
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -45,7 +46,7 @@
     SettingsDropdownCheckBox(
         label = stringResource(R.string.apn_type),
         options = apnTypeOptions,
-        enabled = apnData.apnTypeEnabled,
+        enabled = apnData.isFieldEnabled(Telephony.Carriers.TYPE),
     ) {
         onTypeChanged(apnTypeOptions.toApnType())
         updateMmsSelected()
diff --git a/src/com/android/settings/webview/WebViewAppPicker.java b/src/com/android/settings/webview/WebViewAppPicker.java
index b1dfd14..0060fa0 100644
--- a/src/com/android/settings/webview/WebViewAppPicker.java
+++ b/src/com/android/settings/webview/WebViewAppPicker.java
@@ -33,12 +33,14 @@
 
 import com.android.settings.R;
 import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
+import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settingslib.applications.DefaultAppInfo;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class WebViewAppPicker extends DefaultAppPickerFragment {
+public class WebViewAppPicker extends DefaultAppPickerFragment implements
+        DeveloperOptionAwareMixin {
     private WebViewUpdateServiceWrapper mWebViewUpdateServiceWrapper;
 
     private WebViewUpdateServiceWrapper getWebViewUpdateServiceWrapper() {
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java
index d82dcc4..3980711 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java
@@ -16,82 +16,62 @@
 
 package com.android.settings.bluetooth;
 
-import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.DISABLED;
-import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.ENABLED;
 import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.PREF_KEY;
-import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
-import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
 import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
-import static com.android.settingslib.flags.Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
+import android.bluetooth.BluetoothAdapter;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
 
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 
 @RunWith(RobolectricTestRunner.class)
 public class BluetoothAutoOnPreferenceControllerTest {
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private Context mContext;
     private ContentResolver mContentResolver;
     private BluetoothAutoOnPreferenceController mController;
+    private BluetoothAdapter mBluetoothAdapter;
 
     @Before
     public void setUp() {
-        mSetFlagsRule.enableFlags(FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE);
         mContext = spy(ApplicationProvider.getApplicationContext());
         mContentResolver = mContext.getContentResolver();
         mController = new BluetoothAutoOnPreferenceController(mContext, PREF_KEY);
+        mBluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter());
+        mController.mBluetoothAdapter = mBluetoothAdapter;
     }
 
     @Test
     public void getAvailability_valueUnset_returnUnsupported() {
-        Settings.Secure.putInt(mContentResolver, SETTING_NAME, UNSET);
+        doReturn(false).when(mBluetoothAdapter).isAutoOnSupported();
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
     public void getAvailability_valueSet_returnAvailable() {
-        Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
+        doReturn(true).when(mBluetoothAdapter).isAutoOnSupported();
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
     }
 
     @Test
     public void isChecked_valueEnabled_returnTrue() {
-        Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
+        doReturn(true).when(mBluetoothAdapter).isAutoOnSupported();
+        doReturn(true).when(mBluetoothAdapter).isAutoOnEnabled();
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
         assertThat(mController.isChecked()).isEqualTo(true);
     }
-
-    @Test
-    public void setChecked_returnTrue() {
-        Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
-
-        mController.setChecked(true);
-        assertThat(mController.isChecked()).isEqualTo(true);
-    }
-
-    @Test
-    public void setChecked_returnFalse() {
-        Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
-
-        mController.setChecked(false);
-        assertThat(mController.isChecked()).isEqualTo(false);
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
index 1df8a40..9457f99 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
@@ -28,10 +28,8 @@
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
-import android.icu.text.NumberFormat;
 import android.os.BatteryManager;
 import android.os.PowerManager;
-import android.text.TextUtils;
 
 import androidx.preference.PreferenceScreen;
 
@@ -320,9 +318,7 @@
     }
 
     private CharSequence formatBatteryPercentageText() {
-        return TextUtils.expandTemplate(
-                mContext.getText(R.string.battery_header_title_alternate),
-                NumberFormat.getIntegerInstance().format(BATTERY_LEVEL));
+        return com.android.settings.Utils.formatPercentage(BATTERY_LEVEL);
     }
 
     private void setChargingState(boolean isDischarging, boolean updatedByStatusFeature) {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageTimeControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageTimeControllerTest.java
index 22d4ba3..e8eb126 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageTimeControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageTimeControllerTest.java
@@ -174,7 +174,7 @@
 
         verifyAllPreferencesVisible(true);
         verify(mScreenTimePreference).setTimeSummary("1 min");
-        verify(mBackgroundTimePreference).setTimeSummary("Less than a min");
+        verify(mBackgroundTimePreference).setTimeSummary("Less than a minute");
     }
 
     @Test
@@ -190,7 +190,7 @@
                 /* anomalyHintText= */ null);
 
         verifyAllPreferencesVisible(true);
-        verify(mScreenTimePreference).setTimeSummary("Less than a min");
+        verify(mScreenTimePreference).setTimeSummary("Less than a minute");
         verify(mBackgroundTimePreference).setTimeSummary("2 min");
     }
 
@@ -207,8 +207,8 @@
                 /* anomalyHintText= */ null);
 
         verifyAllPreferencesVisible(true);
-        verify(mScreenTimePreference).setTimeSummary("Less than a min");
-        verify(mBackgroundTimePreference).setTimeSummary("Less than a min");
+        verify(mScreenTimePreference).setTimeSummary("Less than a minute");
+        verify(mBackgroundTimePreference).setTimeSummary("Less than a minute");
     }
 
     @Test
@@ -258,7 +258,7 @@
                 TEST_ANOMALY_HINT_TEXT);
 
         verifyAllPreferencesVisible(true);
-        verify(mScreenTimePreference).setTimeSummary("Less than a min");
+        verify(mScreenTimePreference).setTimeSummary("Less than a minute");
         verify(mScreenTimePreference).setAnomalyHint(TEST_ANOMALY_HINT_TEXT);
         verify(mBackgroundTimePreference, never()).setAnomalyHint(anyString());
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
index 5704be9..9dac9d5 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
@@ -335,7 +335,7 @@
                         /* foregroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS - 1,
                         /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0));
-        assertThat(pref.getSummary().toString()).isEqualTo("Total: less than a min");
+        assertThat(pref.getSummary().toString()).isEqualTo("Total: less than a minute");
     }
 
     @Test
@@ -432,7 +432,7 @@
                         /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS - 1));
         assertThat(pref.getSummary().toString())
-                .isEqualTo("Screen time: less than a min\nBackground: less than a min");
+                .isEqualTo("Screen time: less than a minute\nBackground: less than a minute");
     }
 
     private BatteryDiffEntry createBatteryDiffEntry(
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt
index 539783c..6b61eba 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt
@@ -17,8 +17,10 @@
 package com.android.settings.network.apn
 
 import android.os.PersistableBundle
+import android.provider.Telephony
 import android.telephony.CarrierConfigManager
 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.kotlin.doReturn
@@ -26,11 +28,8 @@
 
 @RunWith(AndroidJUnit4::class)
 class ApnStatusTest {
-    private val apnData = mock<ApnData> {
-        on {
-            it.subId
-        } doReturn 1
-    }
+    private val apnData = ApnData(subId = 1)
+
     private val configManager = mock<CarrierConfigManager> {
         val p = PersistableBundle()
         p.putBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL, true)
@@ -51,4 +50,35 @@
     fun getCarrierCustomizedConfig_test() {
         assert(getCarrierCustomizedConfig(apnData, configManager).isAddApnAllowed)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun isFieldEnabled_default() {
+        val apnData = ApnData()
+
+        val enabled = apnData.isFieldEnabled(Telephony.Carriers.NAME)
+
+        assertThat(enabled).isTrue()
+    }
+
+    @Test
+    fun isFieldEnabled_readOnlyApn() {
+        val apnData = ApnData(customizedConfig = CustomizedConfig(readOnlyApn = true))
+
+        val enabled = apnData.isFieldEnabled(Telephony.Carriers.NAME)
+
+        assertThat(enabled).isFalse()
+    }
+
+    @Test
+    fun isFieldEnabled_readOnlyApnFields() {
+        val apnData = ApnData(
+            customizedConfig = CustomizedConfig(
+                readOnlyApnFields = listOf(Telephony.Carriers.NAME, Telephony.Carriers.PROXY),
+            ),
+        )
+
+        assertThat(apnData.isFieldEnabled(Telephony.Carriers.NAME)).isFalse()
+        assertThat(apnData.isFieldEnabled(Telephony.Carriers.PROXY)).isFalse()
+        assertThat(apnData.isFieldEnabled(Telephony.Carriers.APN)).isTrue()
+    }
+}