Merge "Mock method to avoid exception." into main
diff --git a/res/layout/le_audio_bt_entity_header.xml b/res/layout/le_audio_bt_entity_header.xml
index 81911e9..421ee39 100644
--- a/res/layout/le_audio_bt_entity_header.xml
+++ b/res/layout/le_audio_bt_entity_header.xml
@@ -176,5 +176,23 @@
                 android:padding="@dimen/le_bluetooth_summary_padding"
                 android:drawablePadding="@dimen/le_bluetooth_summary_drawable_padding"/>
         </LinearLayout>
+        <LinearLayout
+            android:id="@+id/bt_battery_mono"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/le_bluetooth_battery_start_margin"
+            android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+            android:gravity="center_horizontal"
+            android:orientation="horizontal"
+            android:visibility="gone"
+            android:focusable="true">
+            <TextView
+                android:id="@+id/bt_battery_mono_summary"
+                style="@style/TextAppearance.EntityHeaderSummary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:padding="@dimen/le_bluetooth_summary_padding"
+                android:drawablePadding="@dimen/le_bluetooth_summary_drawable_padding"/>
+        </LinearLayout>
     </LinearLayout>
 </LinearLayout>
diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml
index e815d44..51cff33 100644
--- a/res/xml/bluetooth_screen.xml
+++ b/res/xml/bluetooth_screen.xml
@@ -16,6 +16,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:key="bluetooth_switchbar_screen"
     android:title="@string/bluetooth_settings_title">
 
     <SwitchPreferenceCompat
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 363d601..f272c1d 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -17,7 +17,6 @@
 package com.android.settings;
 
 import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
-import static com.android.settingslib.media.PhoneMediaDevice.isDesktop;
 
 import android.app.Activity;
 import android.app.Dialog;
@@ -187,13 +186,6 @@
 
     /** Returns if catalyst is enabled on current screen. */
     public final boolean isCatalystEnabled() {
-        // TODO(b/379130874): make Catalyst compatible with desktop device, such as user restriction
-        // check.
-        Context context = getContext();
-        if (context != null && isDesktop(context)) {
-            return false;
-        }
-
         return getPreferenceScreenCreator() != null;
     }
 
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index 2546d44..52560c9 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -162,18 +162,13 @@
         }
 
         final int side = device.getDeviceSide();
-        if (side == HearingAidInfo.DeviceSide.SIDE_LEFT_AND_RIGHT) {
-            return mContext.getString(
-                    R.string.accessibility_hearingaid_left_and_right_side_device_summary, name);
-        } else if (side == HearingAidInfo.DeviceSide.SIDE_LEFT) {
+        if (side == HearingAidInfo.DeviceSide.SIDE_LEFT) {
             return mContext.getString(
                     R.string.accessibility_hearingaid_left_side_device_summary, name);
         } else if (side == HearingAidInfo.DeviceSide.SIDE_RIGHT) {
             return mContext.getString(
                     R.string.accessibility_hearingaid_right_side_device_summary, name);
         }
-
-        // Invalid side
         return mContext.getString(
                 R.string.accessibility_hearingaid_active_device_summary, name);
     }
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
index 32d2a11..d520cd6 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@@ -144,7 +144,7 @@
 
     @Override
     protected void startEnrollmentInternal() {
-        super.startEnrollment();
+        super.startEnrollmentInternal();
         mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager()
                 .findFragmentByTag(TAG_FACE_PREVIEW);
         if (mPreviewFragment == null) {
diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
index 2524894..4c7c98e 100644
--- a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
@@ -225,6 +225,8 @@
             return R.id.bt_battery_left_summary;
         } else if (containerId == R.id.bt_battery_right) {
             return R.id.bt_battery_right_summary;
+        } else if (containerId == R.id.bt_battery_mono) {
+            return R.id.bt_battery_mono_summary;
         }
         Log.d(TAG, "No summary resource id. The containerId is " + containerId);
         return INVALID_RESOURCE_ID;
@@ -237,6 +239,8 @@
         updateBatteryLayout(R.id.bt_battery_left, BluetoothUtils.META_INT_ERROR);
         // hide the right
         updateBatteryLayout(R.id.bt_battery_right, BluetoothUtils.META_INT_ERROR);
+        // hide the mono
+        updateBatteryLayout(R.id.bt_battery_mono, BluetoothUtils.META_INT_ERROR);
     }
 
     private void updateBatteryLayout() {
@@ -261,11 +265,6 @@
             int deviceId = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
             Log.d(TAG, "LeAudioDevices:" + cachedDevice.getDevice().getAnonymizedAddress()
                     + ", deviceId:" + deviceId);
-
-            if (deviceId == BluetoothLeAudio.AUDIO_LOCATION_INVALID) {
-                Log.d(TAG, "The device does not support the AUDIO_LOCATION.");
-                return;
-            }
             boolean isLeft = (deviceId & LEFT_DEVICE_ID) != 0;
             boolean isRight = (deviceId & RIGHT_DEVICE_ID) != 0;
             boolean isLeftRight = isLeft && isRight;
@@ -280,6 +279,8 @@
                 updateBatteryLayout(R.id.bt_battery_left, cachedDevice.getBatteryLevel());
             } else if (isRight) {
                 updateBatteryLayout(R.id.bt_battery_right, cachedDevice.getBatteryLevel());
+            } else if (deviceId == BluetoothLeAudio.AUDIO_LOCATION_MONO) {
+                updateBatteryLayout(R.id.bt_battery_mono, cachedDevice.getBatteryLevel());
             } else {
                 Log.d(TAG, "The device id is other Audio Location. Do nothing.");
             }
diff --git a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
index 90ef5b9..d3434be 100644
--- a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
@@ -15,11 +15,16 @@
  */
 package com.android.settings.connecteddevice;
 
+import android.app.Activity;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -28,6 +33,7 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
+import com.android.settings.bluetooth.AlwaysDiscoverable;
 import com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController;
 import com.android.settings.bluetooth.BluetoothSwitchPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
@@ -47,7 +53,6 @@
 public class BluetoothDashboardFragment extends DashboardFragment {
 
     private static final String TAG = "BluetoothDashboardFrag";
-    private static final String KEY_BLUETOOTH_SCREEN_FOOTER = "bluetooth_screen_footer";
     private static final String SLICE_ACTION = "com.android.settings.SEARCH_RESULT_TRAMPOLINE";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -55,6 +60,8 @@
     private SettingsMainSwitchBar mSwitchBar;
     private BluetoothSwitchPreferenceController mController;
 
+    private @Nullable AlwaysDiscoverable mAlwaysDiscoverable;
+
     @Override
     public int getMetricsCategory() {
         return SettingsEnums.BLUETOOTH_FRAGMENT;
@@ -78,7 +85,9 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        mFooterPreference = findPreference(KEY_BLUETOOTH_SCREEN_FOOTER);
+        if (!isCatalystEnabled()) {
+            mFooterPreference = findPreference(BluetoothFooterPreference.KEY);
+        }
     }
 
     @Override
@@ -90,6 +99,9 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
+        if (isCatalystEnabled()) {
+            return;
+        }
         String callingAppPackageName = PasswordUtils.getCallingAppPackageName(
                 getActivity().getActivityToken());
         String action = getIntent() != null ? getIntent().getAction() : "";
@@ -101,6 +113,7 @@
         SettingsActivity activity = (SettingsActivity) getActivity();
         mSwitchBar = activity.getSwitchBar();
         mSwitchBar.setTitle(getContext().getString(R.string.bluetooth_main_switch_title));
+        mSwitchBar.getRootView().setAccessibilityDelegate(new MainSwitchAccessibilityDelegate());
         mController = new BluetoothSwitchPreferenceController(activity,
                 new MainSwitchBarController(mSwitchBar), mFooterPreference);
         mController.setAlwaysDiscoverable(isAlwaysDiscoverable(callingAppPackageName, action));
@@ -110,8 +123,37 @@
         }
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (isCatalystEnabled()) {
+            Activity activity = requireActivity();
+            String callingAppPackageName = PasswordUtils.getCallingAppPackageName(
+                    activity.getActivityToken());
+            Intent intent = activity.getIntent();
+            String action = intent != null ? intent.getAction() : "";
+            if (DEBUG) {
+                Log.d(TAG, "onActivityCreated() calling package name is : " + callingAppPackageName
+                        + ", action : " + action);
+            }
+            if (isAlwaysDiscoverable(callingAppPackageName, action)) {
+                mAlwaysDiscoverable = new AlwaysDiscoverable(activity);
+                mAlwaysDiscoverable.start();
+            }
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mAlwaysDiscoverable != null) {
+            mAlwaysDiscoverable.stop();
+            mAlwaysDiscoverable = null;
+        }
+    }
+
     @VisibleForTesting
-    boolean isAlwaysDiscoverable(String callingAppPackageName, String action) {
+    boolean isAlwaysDiscoverable(@Nullable String callingAppPackageName, @Nullable String action) {
         return TextUtils.equals(SLICE_ACTION, action) ? false
             : TextUtils.equals(Utils.SETTINGS_PACKAGE_NAME, callingAppPackageName)
                 || TextUtils.equals(Utils.SYSTEMUI_PACKAGE_NAME, callingAppPackageName);
@@ -127,4 +169,19 @@
     public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
         return BluetoothDashboardScreen.KEY;
     }
+
+    private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate {
+        @Override
+        public boolean onRequestSendAccessibilityEvent(
+                @NonNull ViewGroup host, @NonNull View view, @NonNull AccessibilityEvent event) {
+            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+                    && (event.getContentChangeTypes()
+                    & AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED)
+                    != 0) {
+                Log.d(TAG, "Skip accessibility event for CONTENT_CHANGE_TYPE_ENABLED");
+                return false;
+            }
+            return super.onRequestSendAccessibilityEvent(host, view, event);
+        }
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt b/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt
index 66fd8b1..e6985ca 100644
--- a/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt
+++ b/src/com/android/settings/connecteddevice/BluetoothDashboardScreen.kt
@@ -17,7 +17,10 @@
 
 import android.content.Context
 import com.android.settings.R
+import com.android.settings.Settings.BluetoothDashboardActivity
 import com.android.settings.flags.Flags
+import com.android.settings.utils.makeLaunchIntent
+import com.android.settingslib.metadata.PreferenceMetadata
 import com.android.settingslib.metadata.ProvidePreferenceScreen
 import com.android.settingslib.metadata.preferenceHierarchy
 import com.android.settingslib.preference.PreferenceScreenCreator
@@ -39,7 +42,15 @@
 
     override fun fragmentClass() = BluetoothDashboardFragment::class.java
 
-    override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
+    override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
+        makeLaunchIntent(context, BluetoothDashboardActivity::class.java, metadata?.key)
+
+    override fun getPreferenceHierarchy(context: Context) =
+        preferenceHierarchy(this) {
+            val bluetoothDataStore = BluetoothPreference.createDataStore(context)
+            +BluetoothPreference(bluetoothDataStore)
+            +BluetoothFooterPreference(bluetoothDataStore)
+        }
 
     companion object {
         const val KEY = "bluetooth_switchbar_screen"
diff --git a/src/com/android/settings/connecteddevice/BluetoothFooterPreference.kt b/src/com/android/settings/connecteddevice/BluetoothFooterPreference.kt
new file mode 100644
index 0000000..72e51a3
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/BluetoothFooterPreference.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.connecteddevice
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.bluetooth.Utils
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.location.BluetoothScanningFragment
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceSummaryProvider
+import com.android.settingslib.preference.PreferenceBinding
+import com.android.settingslib.widget.FooterPreference
+
+class BluetoothFooterPreference(private val bluetoothDataStore: BluetoothDataStore) :
+    PreferenceMetadata, PreferenceBinding, PreferenceSummaryProvider {
+
+    override val key: String
+        get() = KEY
+
+    override fun isIndexable(context: Context) = false
+
+    override fun dependencies(context: Context) = arrayOf(BluetoothPreference.KEY)
+
+    override fun intent(context: Context): Intent? = subSettingLauncher(context).toIntent()
+
+    override fun createWidget(context: Context) = FooterPreference(context)
+
+    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+        super.bind(preference, metadata)
+        preference.isSelectable = false
+        val bluetoothDisabled = bluetoothDataStore.getBoolean(BluetoothPreference.KEY) != true
+        val footerPreference = preference as FooterPreference
+        val context = preference.context
+        if (bluetoothDisabled && Utils.isBluetoothScanningEnabled(context)) {
+            footerPreference.setLearnMoreText(context.getString(R.string.bluetooth_scan_change))
+            footerPreference.setLearnMoreAction { subSettingLauncher(context).launch() }
+        } else {
+            footerPreference.setLearnMoreText("")
+            footerPreference.setLearnMoreAction(null)
+        }
+    }
+
+    private fun subSettingLauncher(context: Context) =
+        SubSettingLauncher(context)
+            .setDestination(BluetoothScanningFragment::class.java.name)
+            .setSourceMetricsCategory(SettingsEnums.BLUETOOTH_FRAGMENT)
+
+    override fun getSummary(context: Context): CharSequence? {
+        val bluetoothDisabled = bluetoothDataStore.getBoolean(BluetoothPreference.KEY) != true
+        val resId =
+            if (bluetoothDisabled && Utils.isBluetoothScanningEnabled(context)) {
+                when (isAutoOnFeatureAvailable()) {
+                    true -> R.string.bluetooth_scanning_on_info_message_auto_on_available
+                    else -> R.string.bluetooth_scanning_on_info_message
+                }
+            } else {
+                when (isAutoOnFeatureAvailable()) {
+                    true -> R.string.bluetooth_empty_list_bluetooth_off_auto_on_available
+                    else -> R.string.bluetooth_empty_list_bluetooth_off
+                }
+            }
+        return context.getString(resId)
+    }
+
+    private fun isAutoOnFeatureAvailable() =
+        try {
+            bluetoothDataStore.bluetoothAdapter?.isAutoOnSupported == true
+        } catch (e: Exception) {
+            Log.e(TAG, "isAutoOnSupported failed", e)
+            false
+        }
+
+    companion object {
+        const val KEY = "bluetooth_screen_footer"
+        const val TAG = "BluetoothFooterPreference"
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreference.kt b/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreference.kt
deleted file mode 100644
index bf80653..0000000
--- a/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreference.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.connecteddevice
-
-import android.bluetooth.BluetoothAdapter
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import com.android.settings.R
-import com.android.settings.widget.MainSwitchBarMetadata
-import com.android.settingslib.datastore.KeyValueStore
-import com.android.settingslib.datastore.NoOpKeyedObservable
-import com.android.settingslib.metadata.PreferenceLifecycleContext
-import com.android.settingslib.metadata.PreferenceLifecycleProvider
-import com.android.settingslib.metadata.ReadWritePermit
-
-class BluetoothMainSwitchPreference(private val bluetoothAdapter: BluetoothAdapter?) :
-    MainSwitchBarMetadata, PreferenceLifecycleProvider {
-
-    private lateinit var broadcastReceiver: BroadcastReceiver
-
-    override val key
-        get() = "use_bluetooth"
-
-    override val title
-        get() = R.string.bluetooth_main_switch_title
-
-    override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
-        ReadWritePermit.ALLOW
-
-    override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
-        ReadWritePermit.ALLOW
-
-    override fun storage(context: Context) = BluetoothStateStore(bluetoothAdapter)
-
-    override fun onStart(context: PreferenceLifecycleContext) {
-        broadcastReceiver =
-            object : BroadcastReceiver() {
-                override fun onReceive(receiverContext: Context, intent: Intent) {
-                    context.notifyPreferenceChange(key)
-                }
-            }
-        context.registerReceiver(
-            broadcastReceiver,
-            IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED),
-            Context.RECEIVER_EXPORTED_UNAUDITED
-        )
-    }
-
-    override fun onStop(context: PreferenceLifecycleContext) {
-        if (::broadcastReceiver.isInitialized) {
-            context.unregisterReceiver(broadcastReceiver)
-        }
-    }
-
-    override fun isEnabled(context: Context): Boolean {
-        return bluetoothAdapter?.state.let {
-            it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_OFF
-        }
-    }
-
-    @Suppress("UNCHECKED_CAST")
-    class BluetoothStateStore(private val bluetoothAdapter: BluetoothAdapter?) :
-        NoOpKeyedObservable<String>(), KeyValueStore {
-
-        override fun contains(key: String) = true
-
-        override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
-            return (bluetoothAdapter?.state.let {
-                it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_TURNING_ON
-            }) as T
-        }
-
-        override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
-            if (value is Boolean) {
-                if (value) {
-                    bluetoothAdapter?.enable()
-                } else {
-                    bluetoothAdapter?.disable()
-                }
-            }
-        }
-    }
-}
diff --git a/src/com/android/settings/connecteddevice/BluetoothPreference.kt b/src/com/android/settings/connecteddevice/BluetoothPreference.kt
new file mode 100644
index 0000000..c9b3953
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/BluetoothPreference.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.connecteddevice
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothAdapter
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserManager
+import android.provider.Settings
+import android.widget.Toast
+import androidx.preference.Preference
+import com.android.settings.PreferenceRestrictionMixin
+import com.android.settings.R
+import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn
+import com.android.settings.network.SatelliteWarningDialogActivity
+import com.android.settings.widget.MainSwitchBarMetadata
+import com.android.settingslib.WirelessUtils
+import com.android.settingslib.datastore.AbstractKeyedDataObservable
+import com.android.settingslib.datastore.DataChangeReason
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.ReadWritePermit
+import com.android.settingslib.metadata.SensitivityLevel
+
+@SuppressLint("MissingPermission")
+class BluetoothPreference(private val bluetoothDataStore: BluetoothDataStore) :
+    MainSwitchBarMetadata, PreferenceRestrictionMixin, Preference.OnPreferenceChangeListener {
+
+    override val key
+        get() = KEY
+
+    override val title
+        get() = R.string.bluetooth_main_switch_title
+
+    override val restrictionKeys: Array<String>
+        get() = arrayOf(UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_CONFIG_BLUETOOTH)
+
+    override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+        ReadWritePermit.ALLOW
+
+    override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
+        when {
+            isSatelliteOn(context, 3000) ||
+                (value == true &&
+                    !WirelessUtils.isRadioAllowed(context, Settings.Global.RADIO_BLUETOOTH)) ->
+                ReadWritePermit.DISALLOW
+            else -> ReadWritePermit.ALLOW
+        }
+
+    override val sensitivityLevel
+        get() = SensitivityLevel.LOW_SENSITIVITY
+
+    override fun storage(context: Context) = bluetoothDataStore
+
+    override fun isEnabled(context: Context): Boolean {
+        return super<PreferenceRestrictionMixin>.isEnabled(context) &&
+            bluetoothDataStore.bluetoothAdapter?.state.let {
+                it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_OFF
+            }
+    }
+
+    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+        super.bind(preference, metadata)
+        preference.onPreferenceChangeListener = this
+    }
+
+    override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
+        val context = preference.context
+
+        if (isSatelliteOn(context, 3000)) {
+            context.startActivity(
+                Intent(context, SatelliteWarningDialogActivity::class.java)
+                    .putExtra(
+                        SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG,
+                        SatelliteWarningDialogActivity.TYPE_IS_BLUETOOTH,
+                    )
+            )
+            return false
+        }
+
+        // Show toast message if Bluetooth is not allowed in airplane mode
+        if (
+            newValue == true &&
+                !WirelessUtils.isRadioAllowed(context, Settings.Global.RADIO_BLUETOOTH)
+        ) {
+            Toast.makeText(context, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show()
+            return false
+        }
+
+        return true
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    private class BluetoothStorage(
+        private val context: Context,
+        override val bluetoothAdapter: BluetoothAdapter?,
+    ) : AbstractKeyedDataObservable<String>(), BluetoothDataStore {
+
+        private var broadcastReceiver: BroadcastReceiver? = null
+
+        override fun contains(key: String) = key == KEY && bluetoothAdapter != null
+
+        override fun <T : Any> getValue(key: String, valueType: Class<T>): T {
+            return (bluetoothAdapter?.state.let {
+                it == BluetoothAdapter.STATE_ON || it == BluetoothAdapter.STATE_TURNING_ON
+            })
+                as T
+        }
+
+        @Suppress("DEPRECATION")
+        override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+            if (value is Boolean) {
+                if (value) {
+                    bluetoothAdapter?.enable()
+                } else {
+                    bluetoothAdapter?.disable()
+                }
+            }
+        }
+
+        @SuppressLint("WrongConstant")
+        override fun onFirstObserverAdded() {
+            broadcastReceiver =
+                object : BroadcastReceiver() {
+                    override fun onReceive(context: Context, intent: Intent) {
+                        notifyChange(KEY, DataChangeReason.UPDATE)
+                    }
+                }
+            context.registerReceiver(
+                broadcastReceiver,
+                IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED),
+                Context.RECEIVER_EXPORTED_UNAUDITED,
+            )
+        }
+
+        override fun onLastObserverRemoved() {
+            context.unregisterReceiver(broadcastReceiver)
+        }
+    }
+
+    companion object {
+        const val KEY = "use_bluetooth"
+
+        @Suppress("DEPRECATION")
+        fun createDataStore(context: Context) =
+            createDataStore(context, BluetoothAdapter.getDefaultAdapter())
+
+        fun createDataStore(
+            context: Context,
+            bluetoothAdapter: BluetoothAdapter?,
+        ): BluetoothDataStore = BluetoothStorage(context, bluetoothAdapter)
+    }
+}
+
+/** Datastore of the bluetooth preference. */
+interface BluetoothDataStore : KeyValueStore {
+    val bluetoothAdapter: BluetoothAdapter?
+}
diff --git a/src/com/android/settings/network/AirplaneModePreference.kt b/src/com/android/settings/network/AirplaneModePreference.kt
index 758bcfb..e3b7f55 100644
--- a/src/com/android/settings/network/AirplaneModePreference.kt
+++ b/src/com/android/settings/network/AirplaneModePreference.kt
@@ -26,13 +26,13 @@
 import android.provider.Settings
 import android.telephony.PhoneStateListener
 import android.telephony.TelephonyManager
-import android.util.Log
 import androidx.annotation.DrawableRes
 import androidx.preference.Preference
 import com.android.settings.AirplaneModeEnabler
 import com.android.settings.PreferenceRestrictionMixin
 import com.android.settings.R
 import com.android.settings.Utils
+import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn
 import com.android.settingslib.RestrictedSwitchPreference
 import com.android.settingslib.datastore.AbstractKeyedDataObservable
 import com.android.settingslib.datastore.DataChangeReason
@@ -45,8 +45,6 @@
 import com.android.settingslib.metadata.ReadWritePermit
 import com.android.settingslib.metadata.SensitivityLevel
 import com.android.settingslib.metadata.SwitchPreference
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
 
 // LINT.IfChange
 class AirplaneModePreference :
@@ -113,9 +111,7 @@
             context.getSystemService(TelephonyManager::class.java)?.let {
                 phoneStateListener =
                     object : PhoneStateListener(Looper.getMainLooper()) {
-                        @Deprecated("Deprecated in Java")
                         override fun onRadioPowerStateChanged(state: Int) {
-                            Log.d(TAG, "onRadioPowerStateChanged(), state=$state")
                             notifyChange(KEY, DataChangeReason.UPDATE)
                         }
                     }
@@ -163,17 +159,6 @@
             context.getSystemService(TelephonyManager::class.java),
         )
 
-    private fun isSatelliteOn(context: Context): Boolean {
-        try {
-            return SatelliteRepository(context)
-                .requestIsSessionStarted(Executors.newSingleThreadExecutor())
-                .get(2000, TimeUnit.MILLISECONDS)
-        } catch (e: Exception) {
-            Log.e(TAG, "Error to get satellite status : $e")
-        }
-        return false
-    }
-
     private fun showEcmDialog(context: PreferenceLifecycleContext) {
         val intent =
             Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null)
@@ -192,7 +177,6 @@
     }
 
     companion object {
-        const val TAG = "AirplaneModePreference"
         const val KEY = Settings.Global.AIRPLANE_MODE_ON
         const val DEFAULT_VALUE = false
         const val REQUEST_CODE_EXIT_ECM = 1
diff --git a/src/com/android/settings/network/NetworkDashboardScreen.kt b/src/com/android/settings/network/NetworkDashboardScreen.kt
index 15bf590..1ed88c0 100644
--- a/src/com/android/settings/network/NetworkDashboardScreen.kt
+++ b/src/com/android/settings/network/NetworkDashboardScreen.kt
@@ -17,9 +17,12 @@
 
 import android.content.Context
 import com.android.settings.R
+import com.android.settings.Settings.NetworkDashboardActivity
 import com.android.settings.datausage.DataSaverScreen
 import com.android.settings.flags.Flags
+import com.android.settings.utils.makeLaunchIntent
 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
@@ -44,6 +47,9 @@
 
     override fun fragmentClass() = NetworkDashboardFragment::class.java
 
+    override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
+        makeLaunchIntent(context, NetworkDashboardActivity::class.java, metadata?.key)
+
     override fun getPreferenceHierarchy(context: Context) =
         preferenceHierarchy(this) {
             +MobileNetworkListScreen.KEY order -15
diff --git a/src/com/android/settings/network/SatelliteRepository.kt b/src/com/android/settings/network/SatelliteRepository.kt
index b7c25f4..c70484a 100644
--- a/src/com/android/settings/network/SatelliteRepository.kt
+++ b/src/com/android/settings/network/SatelliteRepository.kt
@@ -26,6 +26,8 @@
 import com.google.common.util.concurrent.Futures.immediateFuture
 import com.google.common.util.concurrent.ListenableFuture
 import java.util.concurrent.Executor
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
@@ -203,5 +205,15 @@
         fun setIsSessionStartedForTesting(isEnabled: Boolean) {
             this.isSessionStarted = isEnabled
         }
+
+        fun isSatelliteOn(context: Context, timeoutMs: Long = 2000): Boolean =
+            try {
+                SatelliteRepository(context)
+                    .requestIsSessionStarted(Executors.newSingleThreadExecutor())
+                    .get(timeoutMs, TimeUnit.MILLISECONDS)
+            } catch (e: Exception) {
+                Log.e(TAG, "Error to get satellite status : $e")
+                false
+            }
     }
 }
diff --git a/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java b/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java
index ef53e2d..53b1377 100644
--- a/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java
+++ b/src/com/android/settings/notification/LockScreenNotificationsPreferencePageFragment.java
@@ -33,8 +33,7 @@
 
     @Override
     public int getMetricsCategory() {
-        //TODO(b/367455695): create a new metrics category
-        return SettingsEnums.SETTINGS_LOCK_SCREEN_PREFERENCES;
+        return SettingsEnums.SETTINGS_NOTIFICATIONS_ON_LOCK_SCREEN;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
index 4dfb242..254bbfd 100644
--- a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
@@ -148,7 +148,8 @@
             makeSelectorPreference(KEY_ANY,
                     R.string.zen_mode_from_anyone, null, mIsMessages, true);
             makeSelectorPreference(KEY_NONE,
-                    R.string.zen_mode_none_messages, null, mIsMessages, true);
+                    mIsMessages ? R.string.zen_mode_none_messages : R.string.zen_mode_none_calls,
+                    null, mIsMessages, true);
         }
         super.displayPreference(screen);
     }
diff --git a/src/com/android/settings/password/PasswordUtils.java b/src/com/android/settings/password/PasswordUtils.java
index 8c8afc2..da2ad63 100644
--- a/src/com/android/settings/password/PasswordUtils.java
+++ b/src/com/android/settings/password/PasswordUtils.java
@@ -109,7 +109,8 @@
     public static void setupScreenLockOptionsButton(Context context, View view, Button optButton) {
         final LinearLayout headerLayout = view.findViewById(
                 com.google.android.setupdesign.R.id.sud_layout_header);
-        final TextView sucTitleView = headerLayout.findViewById(R.id.suc_layout_title);
+        final TextView sucTitleView = headerLayout.findViewById(
+                com.google.android.setupdesign.R.id.suc_layout_title);
         if (headerLayout != null && sucTitleView != null) {
             final ViewGroup.MarginLayoutParams layoutTitleParams =
                     (ViewGroup.MarginLayoutParams) sucTitleView.getLayoutParams();
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index dd6a4bb..e474cd3 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -62,6 +62,7 @@
     private final ActivityManager mActivityManager;
     private  int mErrorCode;
     @GuardedBy("this")
+    @Nullable
     private UserHandle mUserHandle;
     private final KeyguardManager mKeyguardManager;
     /** This variable should be accessed via {@link #getProfileBroadcastReceiver()} only. */
@@ -419,12 +420,16 @@
             mContext.unregisterReceiver(/* receiver= */ this);
         }
 
+        @GuardedBy("PrivateSpaceMaintainer.this")
         @Override
         public void onReceive(@NonNull Context context, @NonNull Intent intent) {
             UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
             if (intent.getAction().equals(Intent.ACTION_PROFILE_REMOVED)) {
                 // This applies to all profiles getting removed, since there is no way to tell if
                 // it is a private profile that got removed.
+                if (userHandle.equals(getPrivateProfileHandle())) {
+                    mUserHandle = null;
+                }
                 removeSettingsAllTasks();
                 unregisterBroadcastReceiver();
                 return;
diff --git a/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt b/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt
index 16104de..a1329c2 100644
--- a/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt
+++ b/src/com/android/settings/restriction/UserRestrictionBindingHelper.kt
@@ -25,9 +25,9 @@
 
 /** Helper to rebind preference immediately when user restriction is changed. */
 class UserRestrictionBindingHelper(
-    context: Context,
+    private val context: Context,
     private val screenBindingHelper: PreferenceScreenBindingHelper,
-) : AutoCloseable {
+) : KeyedObserver<String>, AutoCloseable {
     private val restrictionKeysToPreferenceKeys: Map<String, MutableSet<String>> =
         mutableMapOf<String, MutableSet<String>>()
             .apply {
@@ -42,27 +42,29 @@
             }
             .toMap()
 
-    private val userRestrictionObserver: KeyedObserver<String?>?
-
     init {
-        if (restrictionKeysToPreferenceKeys.isEmpty()) {
-            userRestrictionObserver = null
-        } else {
-            val observer =
-                KeyedObserver<String?> { restrictionKey, _ ->
-                    restrictionKey?.let { notifyRestrictionChanged(it) }
-                }
-            UserRestrictions.addObserver(context, observer, HandlerExecutor.main)
-            userRestrictionObserver = observer
+        val restrictionKeys = restrictionKeysToPreferenceKeys.keys
+        if (restrictionKeys.isNotEmpty()) {
+            val userRestrictions = UserRestrictions.get(context)
+            val executor = HandlerExecutor.main
+            for (restrictionKey in restrictionKeys) {
+                userRestrictions.addObserver(restrictionKey, this, executor)
+            }
         }
     }
 
-    private fun notifyRestrictionChanged(restrictionKey: String) {
+    override fun onKeyChanged(restrictionKey: String, reason: Int) {
         val keys = restrictionKeysToPreferenceKeys[restrictionKey] ?: return
         for (key in keys) screenBindingHelper.notifyChange(key, CHANGE_REASON_STATE)
     }
 
     override fun close() {
-        userRestrictionObserver?.let { UserRestrictions.removeObserver(it) }
+        val restrictionKeys = restrictionKeysToPreferenceKeys.keys
+        if (restrictionKeys.isNotEmpty()) {
+            val userRestrictions = UserRestrictions.get(context)
+            for (restrictionKey in restrictionKeys) {
+                userRestrictions.removeObserver(restrictionKey, this)
+            }
+        }
     }
 }
diff --git a/src/com/android/settings/restriction/UserRestrictions.kt b/src/com/android/settings/restriction/UserRestrictions.kt
index 1fa6830..880aa5d 100644
--- a/src/com/android/settings/restriction/UserRestrictions.kt
+++ b/src/com/android/settings/restriction/UserRestrictions.kt
@@ -16,68 +16,58 @@
 
 package com.android.settings.restriction
 
+import android.content.BroadcastReceiver
 import android.content.Context
-import android.os.Bundle
-import android.os.IUserRestrictionsListener
+import android.content.Intent
+import android.content.IntentFilter
 import android.os.UserManager
-import com.android.settingslib.datastore.KeyedDataObservable
+import com.android.settingslib.datastore.AbstractKeyedDataObservable
+import com.android.settingslib.datastore.DataChangeReason
 import com.android.settingslib.datastore.KeyedObserver
 import java.util.concurrent.Executor
-import java.util.concurrent.atomic.AtomicBoolean
 
 /** Helper class to monitor user restriction changes. */
-object UserRestrictions {
-    private val observable = KeyedDataObservable<String>()
+class UserRestrictions private constructor(private val applicationContext: Context) {
 
-    private val userRestrictionsListener =
-        object : IUserRestrictionsListener.Stub() {
-            override fun onUserRestrictionsChanged(
-                userId: Int,
-                newRestrictions: Bundle,
-                prevRestrictions: Bundle,
-            ) {
-                // there is no API to remove listener, do a quick check to avoid unnecessary work
-                if (!observable.hasAnyObserver()) return
+    private val observable =
+        object : AbstractKeyedDataObservable<String>() {
+            override fun onFirstObserverAdded() {
+                val intentFilter = IntentFilter()
+                intentFilter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)
+                applicationContext.registerReceiver(broadcastReceiver, intentFilter)
+            }
 
-                val changedKeys = mutableSetOf<String>()
-                val keys = newRestrictions.keySet() + prevRestrictions.keySet()
-                for (key in keys) {
-                    if (newRestrictions.getBoolean(key) != prevRestrictions.getBoolean(key)) {
-                        changedKeys.add(key)
-                    }
-                }
-
-                for (key in changedKeys) observable.notifyChange(key, 0)
+            override fun onLastObserverRemoved() {
+                applicationContext.unregisterReceiver(broadcastReceiver)
             }
         }
 
-    private val listenerAdded = AtomicBoolean()
+    private val broadcastReceiver: BroadcastReceiver =
+        object : BroadcastReceiver() {
+            override fun onReceive(context: Context, intent: Intent) {
+                // there is no way to get the changed keys, just notify all observers
+                observable.notifyChange(DataChangeReason.UPDATE)
+            }
+        }
 
-    fun addObserver(context: Context, observer: KeyedObserver<String?>, executor: Executor) {
-        context.addUserRestrictionsListener()
+    fun addObserver(observer: KeyedObserver<String?>, executor: Executor) =
         observable.addObserver(observer, executor)
-    }
 
-    fun addObserver(
-        context: Context,
-        key: String,
-        observer: KeyedObserver<String>,
-        executor: Executor,
-    ) {
-        context.addUserRestrictionsListener()
+    fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) =
         observable.addObserver(key, observer, executor)
-    }
-
-    private fun Context.addUserRestrictionsListener() {
-        if (listenerAdded.getAndSet(true)) return
-        // surprisingly, there is no way to remove the listener
-        applicationContext
-            .getSystemService(UserManager::class.java)
-            .addUserRestrictionsListener(userRestrictionsListener)
-    }
 
     fun removeObserver(observer: KeyedObserver<String?>) = observable.removeObserver(observer)
 
     fun removeObserver(key: String, observer: KeyedObserver<String>) =
         observable.removeObserver(key, observer)
+
+    companion object {
+        @Volatile private var instance: UserRestrictions? = null
+
+        fun get(context: Context) =
+            instance
+                ?: synchronized(this) {
+                    instance ?: UserRestrictions(context.applicationContext).also { instance = it }
+                }
+    }
 }
diff --git a/src/com/android/settings/widget/MainSwitchBarPreference.kt b/src/com/android/settings/widget/MainSwitchBarPreference.kt
index 6ed8877..b3b341c 100644
--- a/src/com/android/settings/widget/MainSwitchBarPreference.kt
+++ b/src/com/android/settings/widget/MainSwitchBarPreference.kt
@@ -17,6 +17,8 @@
 package com.android.settings.widget
 
 import android.content.Context
+import android.os.Parcel
+import android.os.Parcelable
 import android.widget.CompoundButton
 import android.widget.CompoundButton.OnCheckedChangeListener
 import androidx.preference.TwoStatePreference
@@ -26,26 +28,32 @@
 
 /** Preference abstraction of the [MainSwitchBar] in settings activity. */
 class MainSwitchBarPreference(context: Context, private val metadata: MainSwitchBarMetadata) :
-    TwoStatePreference(context), OnCheckedChangeListener {
+    TwoStatePreference(context), OnCheckedChangeListener, MainSwitchBar.PreChangeListener {
 
-    private val mainSwitchBar: MainSwitchBar = (context as SettingsActivity).switchBar
+    // main switch bar might be null when configuration is just changed
+    private val mainSwitchBar: MainSwitchBar?
+        get() = (context as SettingsActivity).switchBar
 
     override fun setTitle(title: CharSequence?) {
-        mainSwitchBar.setTitle(title)
+        mainSwitchBar?.setTitle(title)
+        super.setTitle(title)
     }
 
     override fun setSummary(summary: CharSequence?) {
-        mainSwitchBar.setSummary(summary)
+        mainSwitchBar?.setSummary(summary)
+        super.setSummary(summary)
     }
 
     override fun setEnabled(enabled: Boolean) {
-        mainSwitchBar.isEnabled = enabled
+        mainSwitchBar?.isEnabled = enabled
+        super.setEnabled(enabled)
     }
 
     // Preference.setVisible is final, we cannot override it
     fun updateVisibility() {
         // always make preference invisible, the UI visibility is reflected on MainSwitchBar
         isVisible = false
+        val mainSwitchBar = mainSwitchBar ?: return
         if ((metadata as? PreferenceAvailabilityProvider)?.isAvailable(context) != false) {
             mainSwitchBar.show()
         } else {
@@ -54,6 +62,7 @@
     }
 
     override fun setChecked(checked: Boolean) {
+        val mainSwitchBar = mainSwitchBar ?: return
         // remove listener to update UI only
         mainSwitchBar.removeOnSwitchChangeListener(this)
         mainSwitchBar.isChecked = checked
@@ -62,9 +71,13 @@
 
     override fun onAttached() {
         super.onAttached()
+        val mainSwitchBar = mainSwitchBar!!
+        mainSwitchBar.setPreChangeListener(this)
         mainSwitchBar.addOnSwitchChangeListener(this)
     }
 
+    override fun preChange(isCheck: Boolean) = callChangeListener(isCheck)
+
     override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
         // prevent user from toggling the switch before data store operation is done
         isEnabled = false
@@ -73,7 +86,65 @@
     }
 
     override fun onDetached() {
+        val mainSwitchBar = mainSwitchBar!!
         mainSwitchBar.removeOnSwitchChangeListener(this)
+        mainSwitchBar.setPreChangeListener(null)
         super.onDetached()
     }
+
+    override fun onSaveInstanceState(): Parcelable =
+        SavedState(super.onSaveInstanceState()!!).also {
+            it.isEnabled = isEnabled
+            it.title = title
+            it.summary = summary
+            it.mainSwitchBarState = mainSwitchBar?.onSaveInstanceState()
+        }
+
+    override fun onRestoreInstanceState(state: Parcelable?) {
+        val savedState = state as SavedState
+        super.onRestoreInstanceState(savedState.superState)
+        isEnabled = savedState.isEnabled
+        title = savedState.title
+        summary = savedState.summary
+        mainSwitchBar?.onRestoreInstanceState(savedState.mainSwitchBarState!!)
+    }
+
+    private class SavedState : BaseSavedState {
+        var isEnabled: Boolean = false
+        var title: CharSequence? = null
+        var summary: CharSequence? = null
+        var mainSwitchBarState: Parcelable? = null
+
+        constructor(source: Parcel) : super(source) {
+            isEnabled = source.readBoolean()
+            title = source.readCharSequence()
+            summary = source.readCharSequence()
+            val stateClass = MainSwitchBar.SavedState::class.java
+            mainSwitchBarState = source.readParcelable(stateClass.classLoader, stateClass)
+        }
+
+        constructor(superState: Parcelable) : super(superState)
+
+        override fun writeToParcel(dest: Parcel, flags: Int) {
+            super.writeToParcel(dest, flags)
+            dest.writeBoolean(isEnabled)
+            dest.writeCharSequence(title)
+            dest.writeCharSequence(summary)
+            dest.writeParcelable(mainSwitchBarState, flags)
+        }
+
+        companion object {
+            @JvmField
+            val CREATOR: Parcelable.Creator<SavedState> =
+                object : Parcelable.Creator<SavedState> {
+                    override fun createFromParcel(parcel: Parcel): SavedState {
+                        return SavedState(parcel)
+                    }
+
+                    override fun newArray(size: Int): Array<SavedState?> {
+                        return arrayOfNulls(size)
+                    }
+                }
+        }
+    }
 }
diff --git a/src/com/android/settings/widget/SettingsMainSwitchBar.java b/src/com/android/settings/widget/SettingsMainSwitchBar.java
index e8c6fc9..6bccbd7 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchBar.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchBar.java
@@ -109,7 +109,7 @@
             return true;
         }
 
-        return mSwitch.performClick();
+        return callPreChangeListener() && mSwitch.performClick();
     }
 
     @Override
diff --git a/src/com/android/settings/wifi/WifiSwitchPreference.kt b/src/com/android/settings/wifi/WifiSwitchPreference.kt
index 2a18d3f..ba6fb02 100644
--- a/src/com/android/settings/wifi/WifiSwitchPreference.kt
+++ b/src/com/android/settings/wifi/WifiSwitchPreference.kt
@@ -23,13 +23,12 @@
 import android.net.wifi.WifiManager
 import android.os.UserManager
 import android.provider.Settings
-import android.util.Log
 import android.widget.Toast
 import androidx.preference.Preference
 import androidx.preference.Preference.OnPreferenceChangeListener
 import com.android.settings.PreferenceRestrictionMixin
 import com.android.settings.R
-import com.android.settings.network.SatelliteRepository
+import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn
 import com.android.settings.network.SatelliteWarningDialogActivity
 import com.android.settingslib.RestrictedSwitchPreference
 import com.android.settingslib.WirelessUtils
@@ -42,8 +41,6 @@
 import com.android.settingslib.metadata.SensitivityLevel
 import com.android.settingslib.metadata.SwitchPreference
 import com.android.settingslib.preference.SwitchPreferenceBinding
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
 
 // LINT.IfChange
 class WifiSwitchPreference :
@@ -75,7 +72,7 @@
         val context = preference.context
 
         // Show dialog and do nothing under satellite mode.
-        if (context.isSatelliteOn()) {
+        if (isSatelliteOn(context)) {
             context.startActivity(
                 Intent(context, SatelliteWarningDialogActivity::class.java)
                     .putExtra(
@@ -100,7 +97,7 @@
 
     override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
         when {
-            (value == true && !context.isRadioAllowed()) || context.isSatelliteOn() ->
+            (value == true && !context.isRadioAllowed()) || isSatelliteOn(context) ->
                 ReadWritePermit.DISALLOW
             else -> ReadWritePermit.ALLOW
         }
@@ -155,22 +152,11 @@
     }
 
     companion object {
-        const val TAG = "WifiSwitchPreference"
         const val KEY = "main_toggle_wifi"
 
         private fun Context.isRadioAllowed() =
             WirelessUtils.isRadioAllowed(this, Settings.Global.RADIO_WIFI)
 
-        private fun Context.isSatelliteOn() =
-            try {
-                SatelliteRepository(this)
-                    .requestIsSessionStarted(Executors.newSingleThreadExecutor())
-                    .get(2000, TimeUnit.MILLISECONDS)
-            } catch (e: Exception) {
-                Log.e(TAG, "Error to get satellite status : $e")
-                false
-            }
-
         private val Intent.wifiState
             get() = getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)
     }
diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
index c185e02..931583a 100644
--- a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
+++ b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
@@ -100,7 +100,7 @@
             .toIntent()
 
     override fun isEnabled(context: Context) =
-        wifiHotspotStore.dataSaverStore.getBoolean(DATA_SAVER_KEY) == true &&
+        wifiHotspotStore.dataSaverStore.getBoolean(DATA_SAVER_KEY) != true &&
             super<PreferenceRestrictionMixin>.isEnabled(context)
 
     override val restrictionKeys
@@ -193,6 +193,7 @@
         super.bind(preference, metadata)
         (preference as PrimarySwitchPreference).apply {
             isChecked = preferenceDataStore!!.getBoolean(key, false)
+            isSwitchEnabled = isEnabled
         }
     }
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
index 97faa63..b8b0699 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
@@ -208,7 +208,24 @@
         ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
-                "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right")).isTrue();
+                "TEST_HEARING_AID_BT_DEVICE_NAME active")).isTrue();
+    }
+
+    @Test
+    public void getSummary_connectedLeAudioHearingAidMonoSide_connectedSummary() {
+        when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
+                HearingAidInfo.DeviceSide.SIDE_MONO);
+        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(new HashSet<>());
+        when(mHapClientProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
+
+        mPreferenceController.onStart();
+        Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
+        intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
+        sendIntent(intent);
+        ShadowLooper.idleMainLooper();
+
+        assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
+                "TEST_HEARING_AID_BT_DEVICE_NAME active")).isTrue();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreferenceTest.kt b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothPreferenceTest.kt
similarity index 75%
rename from tests/robotests/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreferenceTest.kt
rename to tests/robotests/src/com/android/settings/connecteddevice/BluetoothPreferenceTest.kt
index 15db130..8b739db 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothMainSwitchPreferenceTest.kt
+++ b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothPreferenceTest.kt
@@ -31,45 +31,42 @@
 import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
-class BluetoothMainSwitchPreferenceTest {
+class BluetoothPreferenceTest {
     @get:Rule val setFlagsRule = SetFlagsRule()
     private val context: Context = ApplicationProvider.getApplicationContext()
     private lateinit var bluetoothAdapter: BluetoothAdapter
-    private lateinit var bluetoothMainSwitchPreference: BluetoothMainSwitchPreference
+    private lateinit var bluetoothPreference: BluetoothPreference
 
     @Before
     fun setUp() {
         bluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter())
         whenever(bluetoothAdapter.state).thenReturn(BluetoothAdapter.STATE_ON)
-        bluetoothMainSwitchPreference = BluetoothMainSwitchPreference(bluetoothAdapter)
+        bluetoothPreference =
+            BluetoothPreference(BluetoothPreference.createDataStore(context, bluetoothAdapter))
     }
 
     @Test
     fun isEnabled_bluetoothOn_returnTrue() {
-        assertThat(bluetoothMainSwitchPreference.isEnabled(context)).isTrue()
+        assertThat(bluetoothPreference.isEnabled(context)).isTrue()
     }
 
     @Test
     fun isEnabled_bluetoothTurningOn_returnFalse() {
         whenever(bluetoothAdapter.state).thenReturn(BluetoothAdapter.STATE_TURNING_ON)
 
-        assertThat(bluetoothMainSwitchPreference.isEnabled(context)).isFalse()
+        assertThat(bluetoothPreference.isEnabled(context)).isFalse()
     }
 
     @Test
     fun storageSetOff_turnOff() {
-        bluetoothMainSwitchPreference
-            .storage(context)
-            .setBoolean(bluetoothMainSwitchPreference.key, false)
+        bluetoothPreference.storage(context).setBoolean(bluetoothPreference.key, false)
 
         verify(bluetoothAdapter).disable()
     }
 
     @Test
     fun storageSetOn_turnOn() {
-        bluetoothMainSwitchPreference
-            .storage(context)
-            .setBoolean(bluetoothMainSwitchPreference.key, true)
+        bluetoothPreference.storage(context).setBoolean(bluetoothPreference.key, true)
 
         verify(bluetoothAdapter).enable()
     }
diff --git a/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java
index 477a63f..6e650fd 100644
--- a/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/SatelliteSettingsPreferenceControllerTest.java
@@ -56,6 +56,7 @@
 import org.mockito.junit.MockitoRule;
 
 @RunWith(AndroidJUnit4.class)
+@Ignore("b/382664790")
 public class SatelliteSettingsPreferenceControllerTest {
     private static final String KEY = "key";
     private static final int TEST_SUB_ID = 0;
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
index bfe4be2..191935f 100644
--- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -36,9 +36,11 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Flags;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
@@ -46,6 +48,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 import com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace;
 
 import org.junit.After;
@@ -57,6 +60,9 @@
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidJUnit4.class)
 public class PrivateSpaceMaintainerTest {
     private static final String TAG = "PSMaintainerTest";
@@ -506,4 +512,25 @@
                 /* enabled */ 1,
                 privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier());
     }
+
+    @Test
+    public void profileRemovedFromUserManager_privateSpaceNoLongerExists() {
+        PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(
+                mContext);
+        privateSpaceMaintainer.createPrivateSpace();
+        UserHandle privateSpaceUserHandle = privateSpaceMaintainer.getPrivateProfileHandle();
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+        Intent removedIntent = new Intent(Intent.ACTION_PROFILE_REMOVED);
+        assertThat(privateSpaceUserHandle).isNotNull();
+        final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+                removedIntent.getAction());
+        receiver.register();
+
+        Objects.requireNonNull(mContext.getSystemService(UserManager.class)).removeUser(
+                privateSpaceUserHandle);
+
+        receiver.awaitForBroadcast(TimeUnit.SECONDS.toMillis(10));
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isFalse();
+    }
+
 }