Merge "Guard slices from being requested by guest user" into udc-qpr-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 185d21e..9c5d6c6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2398,6 +2398,7 @@
                 <action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL" />
                 <action android:name="android.app.action.CONFIRM_FRP_CREDENTIAL" />
                 <action android:name="android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL" />
+                <action android:name="android.app.action.CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/res/layout/udfps_enroll_view.xml b/res/layout/udfps_enroll_view.xml
index 6bf339b..bd62609 100644
--- a/res/layout/udfps_enroll_view.xml
+++ b/res/layout/udfps_enroll_view.xml
@@ -18,7 +18,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/udfps_animation_view"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:visibility="gone">
 
     <ImageView
         android:id="@+id/udfps_enroll_animation_fp_progress_view"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bd4e018..a78e74a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3495,6 +3495,18 @@
     <!-- Checkbox label to set password as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
     <string name="lockpassword_remote_validation_set_password_as_screenlock">Also use password to unlock this device</string>
 
+    <!-- Header shown when pattern needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pattern_header">Verify pattern</string>
+    <!-- Header shown when the pin needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pin_header">Verify PIN</string>
+    <!-- Header shown when the password needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_password_header">Verify password</string>
+    <!-- An explanation text that the pattern needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pattern_details" translatable="false">Enter your device pattern enrolled in normal mode to continue</string>
+    <!-- An explanation text that the PIN needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pin_details" translatable="false">Enter your device PIN enrolled in normal mode to continue</string>
+    <!-- An explanation text that the password needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_password_details" translatable="false">Enter your device password enrolled in normal mode to continue</string>
 
     <!-- Security & location settings screen, change security method screen instruction if user
          enters incorrect PIN [CHAR LIMIT=30] -->
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 68b1a48..a2195df 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -708,9 +708,13 @@
         final int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
         if (userId == LockPatternUtils.USER_FRP) {
             return allowAnyUser ? userId : checkUserOwnsFrpCredential(context, userId);
-        } else {
-            return allowAnyUser ? userId : enforceSameOwner(context, userId);
         }
+        if (userId == LockPatternUtils.USER_REPAIR_MODE) {
+            enforceRepairModeActive(context);
+            // any users can exit repair mode
+            return userId;
+        }
+        return allowAnyUser ? userId : enforceSameOwner(context, userId);
     }
 
     /**
@@ -730,6 +734,16 @@
     }
 
     /**
+     * Throws {@link SecurityException} if repair mode is not active on the device.
+     */
+    private static void enforceRepairModeActive(Context context) {
+        if (LockPatternUtils.isRepairModeActive(context)) {
+            return;
+        }
+        throw new SecurityException("Repair mode is not active on the device.");
+    }
+
+    /**
      * Returns the given user id if it belongs to the current user.
      *
      * @throws SecurityException if the given userId does not belong to the current user group.
diff --git a/src/com/android/settings/applications/specialaccess/DataSaverController.kt b/src/com/android/settings/applications/specialaccess/DataSaverController.kt
index 3a2fdb0..baed0aa 100644
--- a/src/com/android/settings/applications/specialaccess/DataSaverController.kt
+++ b/src/com/android/settings/applications/specialaccess/DataSaverController.kt
@@ -51,7 +51,7 @@
         preference = screen.findPreference(preferenceKey)!!
     }
 
-    fun init(viewLifecycleOwner: LifecycleOwner) {
+    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
         viewLifecycleOwner.lifecycleScope.launch {
             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 preference.summary = getUnrestrictedSummary(mContext)
diff --git a/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java
index 9f4c895..2cbc304 100644
--- a/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java
+++ b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java
@@ -21,10 +21,6 @@
 
 import android.app.settings.SettingsEnums;
 import android.os.Bundle;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
@@ -51,12 +47,6 @@
     }
 
     @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        use(DataSaverController.class).init(getViewLifecycleOwner());
-    }
-
-    @Override
     protected int getPreferenceScreenResId() {
         return R.xml.special_access;
     }
diff --git a/src/com/android/settings/biometrics/BiometricEnrollSidecar.java b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
index 97d46a4..369fa4b 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
@@ -48,11 +48,16 @@
         /**
          * Called when a pointer down event has occurred.
          */
-        default void onPointerDown(int sensorId) { }
+        default void onUdfpsPointerDown(int sensorId) { }
         /**
          * Called when a pointer up event has occurred.
          */
-        default void onPointerUp(int sensorId) { }
+        default void onUdfpsPointerUp(int sensorId) { }
+
+        /**
+         * Called when udfps overlay is shown.
+         */
+        default void onUdfpsOverlayShown() { }
     }
 
     private int mEnrollmentSteps = -1;
@@ -126,29 +131,36 @@
         }
     }
 
-    private class QueuedPointerDown extends QueuedEvent {
+    private class QueuedUdfpsPointerDown extends QueuedEvent {
         private final int sensorId;
 
-        public QueuedPointerDown(int sensorId) {
+        QueuedUdfpsPointerDown(int sensorId) {
             this.sensorId = sensorId;
         }
 
         @Override
         public void send(Listener listener) {
-            listener.onPointerDown(sensorId);
+            listener.onUdfpsPointerDown(sensorId);
         }
     }
 
-    private class QueuedPointerUp extends QueuedEvent {
+    private class QueuedUdfpsPointerUp extends QueuedEvent {
         private final int sensorId;
 
-        public QueuedPointerUp(int sensorId) {
+        QueuedUdfpsPointerUp(int sensorId) {
             this.sensorId = sensorId;
         }
 
         @Override
         public void send(Listener listener) {
-            listener.onPointerUp(sensorId);
+            listener.onUdfpsPointerUp(sensorId);
+        }
+    }
+
+    private class QueuedUdfpsOverlayShown extends QueuedEvent {
+        @Override
+        public void send(Listener listener) {
+            listener.onUdfpsOverlayShown();
         }
     }
 
@@ -249,19 +261,27 @@
         }
     }
 
-    protected void onPointerDown(int sensorId) {
+    protected void onUdfpsPointerDown(int sensorId) {
         if (mListener != null) {
-            mListener.onPointerDown(sensorId);
+            mListener.onUdfpsPointerDown(sensorId);
         } else {
-            mQueuedEvents.add(new QueuedPointerDown(sensorId));
+            mQueuedEvents.add(new QueuedUdfpsPointerDown(sensorId));
         }
     }
 
-    protected void onPointerUp(int sensorId) {
+    protected void onUdfpsPointerUp(int sensorId) {
         if (mListener != null) {
-            mListener.onPointerUp(sensorId);
+            mListener.onUdfpsPointerUp(sensorId);
         } else {
-            mQueuedEvents.add(new QueuedPointerUp(sensorId));
+            mQueuedEvents.add(new QueuedUdfpsPointerUp(sensorId));
+        }
+    }
+
+    protected void onUdfpsOverlayShown() {
+        if (mListener != null) {
+            mListener.onUdfpsOverlayShown();
+        } else {
+            mQueuedEvents.add(new QueuedUdfpsOverlayShown());
         }
     }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 400a92e..dbdb024 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -828,19 +828,26 @@
     }
 
     @Override
-    public void onPointerDown(int sensorId) {
+    public void onUdfpsPointerDown(int sensorId) {
         if (mUdfpsEnrollHelper != null) {
             mUdfpsEnrollHelper.onPointerDown(sensorId);
         }
     }
 
     @Override
-    public void onPointerUp(int sensorId) {
+    public void onUdfpsPointerUp(int sensorId) {
         if (mUdfpsEnrollHelper != null) {
             mUdfpsEnrollHelper.onPointerUp(sensorId);
         }
     }
 
+    @Override
+    public void onUdfpsOverlayShown() {
+        if (mCanAssumeUdfps) {
+            findViewById(R.id.udfps_animation_view).setVisibility(View.VISIBLE);
+        }
+    }
+
     private void updateProgress(boolean animate) {
         if (mSidecar == null || !mSidecar.isEnrolling()) {
             Log.d(TAG, "Enrollment not started yet");
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
index 5d04cd6..493302b 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
@@ -124,13 +124,18 @@
         }
 
         @Override
-        public void onPointerDown(int sensorId) {
-            FingerprintEnrollSidecar.super.onPointerDown(sensorId);
+        public void onUdfpsPointerDown(int sensorId) {
+            FingerprintEnrollSidecar.super.onUdfpsPointerDown(sensorId);
         }
 
         @Override
-        public void onPointerUp(int sensorId) {
-            FingerprintEnrollSidecar.super.onPointerUp(sensorId);
+        public void onUdfpsPointerUp(int sensorId) {
+            FingerprintEnrollSidecar.super.onUdfpsPointerUp(sensorId);
+        }
+
+        @Override
+        public void onUdfpsOverlayShown() {
+            FingerprintEnrollSidecar.super.onUdfpsOverlayShown();
         }
     };
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index be090e3..fb3319c 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -623,9 +623,9 @@
                 return; // Activity went away
             }
 
-            final Preference addPreference = findPreference(KEY_FINGERPRINT_ADD);
+            mAddFingerprintPreference = findPreference(KEY_FINGERPRINT_ADD);
 
-            if (addPreference == null) {
+            if (mAddFingerprintPreference == null) {
                 return; // b/275519315 Skip if updateAddPreference() invoke before addPreference()
             }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
index 36325a7..306b1a3 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
@@ -98,13 +98,18 @@
         }
 
         @Override
-        public void onPointerDown(int sensorId) {
-            mCallback.onPointerDown(sensorId);
+        public void onUdfpsPointerDown(int sensorId) {
+            mCallback.onUdfpsPointerDown(sensorId);
         }
 
         @Override
-        public void onPointerUp(int sensorId) {
-            mCallback.onPointerUp(sensorId);
+        public void onUdfpsPointerUp(int sensorId) {
+            mCallback.onUdfpsPointerUp(sensorId);
+        }
+
+        @Override
+        public void onUdfpsOverlayShown() {
+            mCallback.onUdfpsOverlayShown();
         }
     }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
index 9225c64..df2f2f7 100644
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
@@ -86,7 +86,7 @@
             UdfpsEnrollHelper udfpsEnrollHelper,
             AccessibilityManager accessibilityManager) {
         mAccessibilityManager = accessibilityManager;
-        initUdfpsEnrollView(mUdfpsEnrollView, udfpsProps, udfpsEnrollHelper);
+        initUdfpsEnrollView(udfpsProps, udfpsEnrollHelper);
 
         if (!mIsLandscape) {
             adjustPortraitPaddings();
@@ -117,8 +117,7 @@
         });
     }
 
-    private void initUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
-                                     FingerprintSensorPropertiesInternal udfpsProps,
+    private void initUdfpsEnrollView(FingerprintSensorPropertiesInternal udfpsProps,
                                      UdfpsEnrollHelper udfpsEnrollHelper) {
         DisplayInfo displayInfo = new DisplayInfo();
         mContext.getDisplay().getDisplayInfo(displayInfo);
@@ -141,8 +140,8 @@
                 scaleFactor,
                 displayInfo.rotation);
 
-        udfpsEnrollView.setOverlayParams(params);
-        udfpsEnrollView.setEnrollHelper(udfpsEnrollHelper);
+        mUdfpsEnrollView.setOverlayParams(params);
+        mUdfpsEnrollView.setEnrollHelper(udfpsEnrollHelper);
     }
 
     private void adjustPortraitPaddings() {
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
index d77d9d3..7074288 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
@@ -103,12 +103,12 @@
         }
 
         @Override
-        public void onPointerDown(int sensorId) {
+        public void onUdfpsPointerDown(int sensorId) {
             mPointerDownLiveData.postValue(sensorId);
         }
 
         @Override
-        public void onPointerUp(int sensorId) {
+        public void onUdfpsPointerUp(int sensorId) {
             mPointerUpLiveData.postValue(sensorId);
         }
     };
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
index 7ee61ee..f2bc6fc 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
@@ -128,7 +128,7 @@
             if (device != null && mSelectedList.contains(device)) {
                 setResult(RESULT_OK);
                 finish();
-            } else if (mDevicePreferenceMap.containsKey(cachedDevice)) {
+            } else {
                 onDeviceDeleted(cachedDevice);
             }
         }
@@ -175,8 +175,6 @@
     public void updateContent(int bluetoothState) {
         switch (bluetoothState) {
             case BluetoothAdapter.STATE_ON:
-                mDevicePreferenceMap.clear();
-                clearPreferenceGroupCache();
                 mBluetoothAdapter.enable();
                 enableScanning();
                 break;
@@ -187,14 +185,6 @@
         }
     }
 
-    /**
-     * Clears all cached preferences in {@code preferenceGroup}.
-     */
-    private void clearPreferenceGroupCache() {
-        cacheRemoveAllPrefs(mAvailableDevicesCategory);
-        removeCachedPrefs(mAvailableDevicesCategory);
-    }
-
     @VisibleForTesting
     void showBluetoothTurnedOnToast() {
         Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 5256f3d..039080b 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,6 +35,8 @@
 import android.widget.ImageView;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.Preference;
@@ -52,6 +54,7 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * BluetoothDevicePreference is the preference type used to display each remote
@@ -79,7 +82,9 @@
     @VisibleForTesting
     BluetoothAdapter mBluetoothAdapter;
     private final boolean mShowDevicesWithoutNames;
-    private final long mCurrentTime;
+    @NonNull
+    private static final AtomicInteger sNextId = new AtomicInteger();
+    private final int mId;
     private final int mType;
 
     private AlertDialog mDisconnectDialog;
@@ -127,8 +132,9 @@
 
         mCachedDevice = cachedDevice;
         mCallback = new BluetoothDevicePreferenceCallback();
-        mCurrentTime = System.currentTimeMillis();
+        mId = sNextId.getAndIncrement();
         mType = type;
+        setVisible(false);
 
         onPreferenceAttributesChanged();
     }
@@ -229,35 +235,41 @@
 
     @SuppressWarnings("FutureReturnValueIgnored")
     void onPreferenceAttributesChanged() {
-        Pair<Drawable, String> pair = mCachedDevice.getDrawableWithDescription();
-        setIcon(pair.first);
-        contentDescription = pair.second;
-
-        /*
-         * The preference framework takes care of making sure the value has
-         * changed before proceeding. It will also call notifyChanged() if
-         * any preference info has changed from the previous value.
-         */
-        setTitle(mCachedDevice.getName());
         try {
             ThreadUtils.postOnBackgroundThread(() -> {
+                @Nullable String name = mCachedDevice.getName();
                 // Null check is done at the framework
-                ThreadUtils.postOnMainThread(() -> setSummary(getConnectionSummary()));
+                @Nullable String connectionSummary = getConnectionSummary();
+                @NonNull Pair<Drawable, String> pair = mCachedDevice.getDrawableWithDescription();
+                boolean isBusy = mCachedDevice.isBusy();
+                // Device is only visible in the UI if it has a valid name besides MAC address or
+                // when user allows showing devices without user-friendly name in developer settings
+                boolean isVisible =
+                        mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName();
+
+                ThreadUtils.postOnMainThread(() -> {
+                    /*
+                     * The preference framework takes care of making sure the value has
+                     * changed before proceeding. It will also call notifyChanged() if
+                     * any preference info has changed from the previous value.
+                     */
+                    setTitle(name);
+                    setSummary(connectionSummary);
+                    setIcon(pair.first);
+                    contentDescription = pair.second;
+                    // Used to gray out the item
+                    setEnabled(!isBusy);
+                    setVisible(isVisible);
+
+                    // This could affect ordering, so notify that
+                    if (mNeedNotifyHierarchyChanged) {
+                        notifyHierarchyChanged();
+                    }
+                });
             });
         } catch (RejectedExecutionException e) {
             Log.w(TAG, "Handler thread unavailable, skipping getConnectionSummary!");
         }
-        // Used to gray out the item
-        setEnabled(!mCachedDevice.isBusy());
-
-        // Device is only visible in the UI if it has a valid name besides MAC address or when user
-        // allows showing devices without user-friendly name in developer settings
-        setVisible(mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName());
-
-        // This could affect ordering, so notify that
-        if (mNeedNotifyHierarchyChanged) {
-            notifyHierarchyChanged();
-        }
     }
 
     @Override
@@ -311,7 +323,7 @@
                 return mCachedDevice
                         .compareTo(((BluetoothDevicePreference) another).mCachedDevice);
             case SortType.TYPE_FIFO:
-                return mCurrentTime > ((BluetoothDevicePreference) another).mCurrentTime ? 1 : -1;
+                return mId > ((BluetoothDevicePreference) another).mId ? 1 : -1;
             default:
                 return super.compareTo(another);
         }
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
deleted file mode 100644
index a4a9891..0000000
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2011 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.bluetooth;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.le.BluetoothLeScanner;
-import android.bluetooth.le.ScanCallback;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanResult;
-import android.bluetooth.le.ScanSettings;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.text.BidiFormatter;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.RestrictedDashboardFragment;
-import com.android.settingslib.bluetooth.BluetoothCallback;
-import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Parent class for settings fragments that contain a list of Bluetooth
- * devices.
- *
- * @see DevicePickerFragment
- */
-// TODO: Refactor this fragment
-public abstract class DeviceListPreferenceFragment extends
-        RestrictedDashboardFragment implements BluetoothCallback {
-
-    private static final String TAG = "DeviceListPreferenceFragment";
-
-    private static final String KEY_BT_SCAN = "bt_scan";
-
-    // Copied from BluetoothDeviceNoNamePreferenceController.java
-    private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
-            "persist.bluetooth.showdeviceswithoutnames";
-
-    private BluetoothDeviceFilter.Filter mFilter;
-    private List<ScanFilter> mLeScanFilters;
-    private ScanCallback mScanCallback;
-
-    @VisibleForTesting
-    protected boolean mScanEnabled;
-
-    protected BluetoothDevice mSelectedDevice;
-
-    protected BluetoothAdapter mBluetoothAdapter;
-    protected LocalBluetoothManager mLocalManager;
-    protected CachedBluetoothDeviceManager mCachedDeviceManager;
-
-    @VisibleForTesting
-    protected PreferenceGroup mDeviceListGroup;
-
-    protected final HashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
-            new HashMap<>();
-    protected final List<BluetoothDevice> mSelectedList = new ArrayList<>();
-
-    protected boolean mShowDevicesWithoutNames;
-
-    public DeviceListPreferenceFragment(String restrictedKey) {
-        super(restrictedKey);
-        mFilter = BluetoothDeviceFilter.ALL_FILTER;
-    }
-
-    protected final void setFilter(BluetoothDeviceFilter.Filter filter) {
-        mFilter = filter;
-    }
-
-    protected final void setFilter(int filterType) {
-        mFilter = BluetoothDeviceFilter.getFilter(filterType);
-    }
-
-    /**
-     * Sets the bluetooth device scanning filter with {@link ScanFilter}s. It will change to start
-     * {@link BluetoothLeScanner} which will scan BLE device only.
-     *
-     * @param leScanFilters list of settings to filter scan result
-     */
-    protected void setFilter(List<ScanFilter> leScanFilters) {
-        mFilter = null;
-        mLeScanFilters = leScanFilters;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mLocalManager = Utils.getLocalBtManager(getActivity());
-        if (mLocalManager == null) {
-            Log.e(TAG, "Bluetooth is not supported on this device");
-            return;
-        }
-        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-        mCachedDeviceManager = mLocalManager.getCachedDeviceManager();
-        mShowDevicesWithoutNames = SystemProperties.getBoolean(
-                BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
-
-        initPreferencesFromPreferenceScreen();
-
-        mDeviceListGroup = (PreferenceCategory) findPreference(getDeviceListKey());
-    }
-
-    /** find and update preference that already existed in preference screen */
-    protected abstract void initPreferencesFromPreferenceScreen();
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mLocalManager == null || isUiRestricted()) return;
-
-        mLocalManager.setForegroundActivity(getActivity());
-        mLocalManager.getEventManager().registerCallback(this);
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (mLocalManager == null || isUiRestricted()) {
-            return;
-        }
-
-        removeAllDevices();
-        mLocalManager.setForegroundActivity(null);
-        mLocalManager.getEventManager().unregisterCallback(this);
-    }
-
-    void removeAllDevices() {
-        mDevicePreferenceMap.clear();
-        mDeviceListGroup.removeAll();
-    }
-
-    void addCachedDevices() {
-        Collection<CachedBluetoothDevice> cachedDevices =
-                mCachedDeviceManager.getCachedDevicesCopy();
-        for (CachedBluetoothDevice cachedDevice : cachedDevices) {
-            onDeviceAdded(cachedDevice);
-        }
-    }
-
-    @Override
-    public boolean onPreferenceTreeClick(Preference preference) {
-        if (KEY_BT_SCAN.equals(preference.getKey())) {
-            startScanning();
-            return true;
-        }
-
-        if (preference instanceof BluetoothDevicePreference) {
-            BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
-            CachedBluetoothDevice device = btPreference.getCachedDevice();
-            mSelectedDevice = device.getDevice();
-            mSelectedList.add(mSelectedDevice);
-            onDevicePreferenceClick(btPreference);
-            return true;
-        }
-
-        return super.onPreferenceTreeClick(preference);
-    }
-
-    protected void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
-        btPreference.onClicked();
-    }
-
-    @Override
-    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
-        if (mDevicePreferenceMap.get(cachedDevice) != null) {
-            return;
-        }
-
-        // Prevent updates while the list shows one of the state messages
-        if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) {
-            return;
-        }
-
-        if (mFilter != null && mFilter.matches(cachedDevice.getDevice())) {
-            createDevicePreference(cachedDevice);
-        }
-    }
-
-    void createDevicePreference(CachedBluetoothDevice cachedDevice) {
-        if (mDeviceListGroup == null) {
-            Log.w(TAG, "Trying to create a device preference before the list group/category "
-                    + "exists!");
-            return;
-        }
-
-        String key = cachedDevice.getDevice().getAddress();
-        BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
-
-        if (preference == null) {
-            preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice,
-                    mShowDevicesWithoutNames, BluetoothDevicePreference.SortType.TYPE_FIFO);
-            preference.setKey(key);
-            //Set hideSecondTarget is true if it's bonded device.
-            preference.hideSecondTarget(true);
-            mDeviceListGroup.addPreference(preference);
-        }
-
-        initDevicePreference(preference);
-        mDevicePreferenceMap.put(cachedDevice, preference);
-    }
-
-    protected void initDevicePreference(BluetoothDevicePreference preference) {
-        // Does nothing by default
-    }
-
-    @VisibleForTesting
-    void updateFooterPreference(Preference myDevicePreference) {
-        final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
-
-        myDevicePreference.setTitle(getString(
-                R.string.bluetooth_footer_mac_message,
-                bidiFormatter.unicodeWrap(mBluetoothAdapter.getAddress())));
-    }
-
-    @Override
-    public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
-        BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
-        if (preference != null) {
-            mDeviceListGroup.removePreference(preference);
-        }
-    }
-
-    @VisibleForTesting
-    protected void enableScanning() {
-        // BluetoothAdapter already handles repeated scan requests
-        if (!mScanEnabled) {
-            startScanning();
-            mScanEnabled = true;
-        }
-    }
-
-    @VisibleForTesting
-    protected void disableScanning() {
-        if (mScanEnabled) {
-            stopScanning();
-            mScanEnabled = false;
-        }
-    }
-
-    @Override
-    public void onScanningStateChanged(boolean started) {
-        if (!started && mScanEnabled) {
-            startScanning();
-        }
-    }
-
-    /**
-     * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
-     */
-    public abstract String getDeviceListKey();
-
-    public boolean shouldShowDevicesWithoutNames() {
-        return mShowDevicesWithoutNames;
-    }
-
-    @VisibleForTesting
-    void startScanning() {
-        if (mFilter != null) {
-            startClassicScanning();
-        } else if (mLeScanFilters != null) {
-            startLeScanning();
-        }
-
-    }
-
-    @VisibleForTesting
-    void stopScanning() {
-        if (mFilter != null) {
-            stopClassicScanning();
-        } else if (mLeScanFilters != null) {
-            stopLeScanning();
-        }
-    }
-
-    private void startClassicScanning() {
-        if (!mBluetoothAdapter.isDiscovering()) {
-            mBluetoothAdapter.startDiscovery();
-        }
-    }
-
-    private void stopClassicScanning() {
-        if (mBluetoothAdapter.isDiscovering()) {
-            mBluetoothAdapter.cancelDiscovery();
-        }
-    }
-
-    private void startLeScanning() {
-        final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
-        final ScanSettings settings = new ScanSettings.Builder()
-                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
-                .build();
-        mScanCallback = new ScanCallback() {
-            @Override
-            public void onScanResult(int callbackType, ScanResult result) {
-                final BluetoothDevice device = result.getDevice();
-                CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
-                if (cachedDevice == null) {
-                    cachedDevice = mCachedDeviceManager.addDevice(device);
-                }
-                // Only add device preference when it's not found in the map and there's no other
-                // state message showing in the list
-                if (mDevicePreferenceMap.get(cachedDevice) == null
-                        && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
-                    createDevicePreference(cachedDevice);
-                }
-            }
-
-            @Override
-            public void onScanFailed(int errorCode) {
-                Log.w(TAG, "BLE Scan failed with error code " + errorCode);
-            }
-        };
-        scanner.startScan(mLeScanFilters, settings, mScanCallback);
-    }
-
-    private void stopLeScanning() {
-        final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
-        if (scanner != null) {
-            scanner.stopScan(mScanCallback);
-        }
-    }
-}
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt
new file mode 100644
index 0000000..9c86e43
--- /dev/null
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.le.BluetoothLeScanner
+import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanFilter
+import android.bluetooth.le.ScanResult
+import android.bluetooth.le.ScanSettings
+import android.os.Bundle
+import android.os.SystemProperties
+import android.text.BidiFormatter
+import android.util.Log
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceGroup
+import com.android.settings.R
+import com.android.settings.dashboard.RestrictedDashboardFragment
+import com.android.settingslib.bluetooth.BluetoothCallback
+import com.android.settingslib.bluetooth.BluetoothDeviceFilter
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import java.util.concurrent.ConcurrentHashMap
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Parent class for settings fragments that contain a list of Bluetooth devices.
+ *
+ * @see DevicePickerFragment
+ *
+ * TODO: Refactor this fragment
+ */
+abstract class DeviceListPreferenceFragment(restrictedKey: String?) :
+    RestrictedDashboardFragment(restrictedKey), BluetoothCallback {
+
+    private var filter: BluetoothDeviceFilter.Filter? = BluetoothDeviceFilter.ALL_FILTER
+    private var leScanFilters: List<ScanFilter>? = null
+
+    @JvmField
+    @VisibleForTesting
+    var mScanEnabled = false
+
+    @JvmField
+    var mSelectedDevice: BluetoothDevice? = null
+
+    @JvmField
+    var mBluetoothAdapter: BluetoothAdapter? = null
+
+    @JvmField
+    var mLocalManager: LocalBluetoothManager? = null
+
+    @JvmField
+    var mCachedDeviceManager: CachedBluetoothDeviceManager? = null
+
+    @JvmField
+    @VisibleForTesting
+    var mDeviceListGroup: PreferenceGroup? = null
+
+    @VisibleForTesting
+    val devicePreferenceMap =
+        ConcurrentHashMap<CachedBluetoothDevice, BluetoothDevicePreference>()
+
+    @JvmField
+    val mSelectedList: MutableList<BluetoothDevice> = ArrayList()
+
+    private var showDevicesWithoutNames = false
+
+    protected fun setFilter(filter: BluetoothDeviceFilter.Filter?) {
+        this.filter = filter
+    }
+
+    protected fun setFilter(filterType: Int) {
+        filter = BluetoothDeviceFilter.getFilter(filterType)
+    }
+
+    /**
+     * Sets the bluetooth device scanning filter with [ScanFilter]s. It will change to start
+     * [BluetoothLeScanner] which will scan BLE device only.
+     *
+     * @param leScanFilters list of settings to filter scan result
+     */
+    fun setFilter(leScanFilters: List<ScanFilter>?) {
+        filter = null
+        this.leScanFilters = leScanFilters
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mLocalManager = Utils.getLocalBtManager(activity)
+        if (mLocalManager == null) {
+            Log.e(TAG, "Bluetooth is not supported on this device")
+            return
+        }
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
+        mCachedDeviceManager = mLocalManager!!.cachedDeviceManager
+        showDevicesWithoutNames = SystemProperties.getBoolean(
+            BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false
+        )
+        initPreferencesFromPreferenceScreen()
+        mDeviceListGroup = findPreference<Preference>(deviceListKey) as PreferenceCategory
+    }
+
+    /** find and update preference that already existed in preference screen  */
+    protected abstract fun initPreferencesFromPreferenceScreen()
+
+    private var lifecycleScope: LifecycleCoroutineScope? = null
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        lifecycleScope = viewLifecycleOwner.lifecycleScope
+    }
+
+    override fun onStart() {
+        super.onStart()
+        if (mLocalManager == null || isUiRestricted) return
+        mLocalManager!!.foregroundActivity = activity
+        mLocalManager!!.eventManager.registerCallback(this)
+    }
+
+    override fun onStop() {
+        super.onStop()
+        if (mLocalManager == null || isUiRestricted) {
+            return
+        }
+        removeAllDevices()
+        mLocalManager!!.foregroundActivity = null
+        mLocalManager!!.eventManager.unregisterCallback(this)
+    }
+
+    fun removeAllDevices() {
+        devicePreferenceMap.clear()
+        mDeviceListGroup!!.removeAll()
+    }
+
+    fun addCachedDevices() {
+        lifecycleScope?.launch {
+            withContext(Dispatchers.Default) {
+                val cachedDevices = mCachedDeviceManager!!.cachedDevicesCopy
+                for (cachedDevice in cachedDevices) {
+                    onDeviceAdded(cachedDevice)
+                }
+            }
+        }
+    }
+
+    override fun onPreferenceTreeClick(preference: Preference): Boolean {
+        if (KEY_BT_SCAN == preference.key) {
+            startScanning()
+            return true
+        }
+        if (preference is BluetoothDevicePreference) {
+            val device = preference.cachedDevice.device
+            mSelectedDevice = device
+            mSelectedList.add(device)
+            onDevicePreferenceClick(preference)
+            return true
+        }
+        return super.onPreferenceTreeClick(preference)
+    }
+
+    protected open fun onDevicePreferenceClick(btPreference: BluetoothDevicePreference) {
+        btPreference.onClicked()
+    }
+
+    override fun onDeviceAdded(cachedDevice: CachedBluetoothDevice) {
+        lifecycleScope?.launch {
+            addDevice(cachedDevice)
+        }
+    }
+
+    private suspend fun addDevice(cachedDevice: CachedBluetoothDevice) =
+        withContext(Dispatchers.Default) {
+            // Prevent updates while the list shows one of the state messages
+            if (mBluetoothAdapter!!.state == BluetoothAdapter.STATE_ON &&
+                filter?.matches(cachedDevice.device) == true
+            ) {
+                createDevicePreference(cachedDevice)
+            }
+        }
+
+    private suspend fun createDevicePreference(cachedDevice: CachedBluetoothDevice) {
+        if (mDeviceListGroup == null) {
+            Log.w(
+                TAG,
+                "Trying to create a device preference before the list group/category exists!",
+            )
+            return
+        }
+        // Only add device preference when it's not found in the map and there's no other state
+        // message showing in the list
+        val preference = devicePreferenceMap.computeIfAbsent(cachedDevice) {
+            BluetoothDevicePreference(
+                prefContext,
+                cachedDevice,
+                showDevicesWithoutNames,
+                BluetoothDevicePreference.SortType.TYPE_FIFO,
+            ).apply {
+                key = cachedDevice.device.address
+                //Set hideSecondTarget is true if it's bonded device.
+                hideSecondTarget(true)
+            }
+        }
+        withContext(Dispatchers.Main) {
+            mDeviceListGroup!!.addPreference(preference)
+            initDevicePreference(preference)
+        }
+    }
+
+    protected open fun initDevicePreference(preference: BluetoothDevicePreference?) {
+        // Does nothing by default
+    }
+
+    @VisibleForTesting
+    fun updateFooterPreference(myDevicePreference: Preference) {
+        val bidiFormatter = BidiFormatter.getInstance()
+        myDevicePreference.title = getString(
+            R.string.bluetooth_footer_mac_message,
+            bidiFormatter.unicodeWrap(mBluetoothAdapter!!.address)
+        )
+    }
+
+    override fun onDeviceDeleted(cachedDevice: CachedBluetoothDevice) {
+        devicePreferenceMap.remove(cachedDevice)?.let {
+            mDeviceListGroup!!.removePreference(it)
+        }
+    }
+
+    @VisibleForTesting
+    open fun enableScanning() {
+        // BluetoothAdapter already handles repeated scan requests
+        if (!mScanEnabled) {
+            startScanning()
+            mScanEnabled = true
+        }
+    }
+
+    @VisibleForTesting
+    fun disableScanning() {
+        if (mScanEnabled) {
+            stopScanning()
+            mScanEnabled = false
+        }
+    }
+
+    override fun onScanningStateChanged(started: Boolean) {
+        if (!started && mScanEnabled) {
+            startScanning()
+        }
+    }
+
+    /**
+     * Return the key of the [PreferenceGroup] that contains the bluetooth devices
+     */
+    abstract val deviceListKey: String
+
+    @VisibleForTesting
+    open fun startScanning() {
+        if (filter != null) {
+            startClassicScanning()
+        } else if (leScanFilters != null) {
+            startLeScanning()
+        }
+    }
+
+    @VisibleForTesting
+    open fun stopScanning() {
+        if (filter != null) {
+            stopClassicScanning()
+        } else if (leScanFilters != null) {
+            stopLeScanning()
+        }
+    }
+
+    private fun startClassicScanning() {
+        if (!mBluetoothAdapter!!.isDiscovering) {
+            mBluetoothAdapter!!.startDiscovery()
+        }
+    }
+
+    private fun stopClassicScanning() {
+        if (mBluetoothAdapter!!.isDiscovering) {
+            mBluetoothAdapter!!.cancelDiscovery()
+        }
+    }
+
+    private val scanCallback = object : ScanCallback() {
+        override fun onScanResult(callbackType: Int, result: ScanResult) {
+            lifecycleScope?.launch {
+                withContext(Dispatchers.Default) {
+                    if (mBluetoothAdapter!!.state == BluetoothAdapter.STATE_ON) {
+                        val device = result.device
+                        val cachedDevice = mCachedDeviceManager!!.findDevice(device)
+                            ?: mCachedDeviceManager!!.addDevice(device)
+                        createDevicePreference(cachedDevice)
+                    }
+                }
+            }
+        }
+
+        override fun onScanFailed(errorCode: Int) {
+            Log.w(TAG, "BLE Scan failed with error code $errorCode")
+        }
+    }
+
+    private fun startLeScanning() {
+        val scanner = mBluetoothAdapter!!.bluetoothLeScanner
+        val settings = ScanSettings.Builder()
+            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+            .build()
+        scanner.startScan(leScanFilters, settings, scanCallback)
+    }
+
+    private fun stopLeScanning() {
+        val scanner = mBluetoothAdapter!!.bluetoothLeScanner
+        scanner?.stopScan(scanCallback)
+    }
+
+    companion object {
+        private const val TAG = "DeviceListPreferenceFragment"
+        private const val KEY_BT_SCAN = "bt_scan"
+
+        // Copied from BluetoothDeviceNoNamePreferenceController.java
+        private const val BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
+            "persist.bluetooth.showdeviceswithoutnames"
+    }
+}
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index f8a5d76..d4acfa1 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -25,11 +25,14 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.View;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceGroup;
@@ -170,6 +173,15 @@
     }
 
     @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
+        for (AbstractPreferenceController controller : mControllers) {
+            controller.onViewCreated(viewLifecycleOwner);
+        }
+    }
+
+    @Override
     public void onCategoriesChanged(Set<String> categories) {
         final String categoryKey = getCategoryKey();
         final DashboardCategory dashboardCategory =
diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt
index 13fbbfa..0828d36 100644
--- a/src/com/android/settings/datausage/DataSaverSummary.kt
+++ b/src/com/android/settings/datausage/DataSaverSummary.kt
@@ -19,11 +19,9 @@
 import android.content.Context
 import android.os.Bundle
 import android.telephony.SubscriptionManager
-import android.view.View
 import android.widget.Switch
 import com.android.settings.R
 import com.android.settings.SettingsActivity
-import com.android.settings.applications.specialaccess.DataSaverController
 import com.android.settings.dashboard.DashboardFragment
 import com.android.settings.search.BaseSearchIndexProvider
 import com.android.settings.widget.SettingsMainSwitchBar
@@ -59,11 +57,6 @@
         }
     }
 
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-        use(DataSaverController::class.java).init(viewLifecycleOwner)
-    }
-
     override fun onResume() {
         super.onResume()
         dataSaverBackend.addListener(dataSaverBackendListener)
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 943a937..9533314 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -362,7 +362,8 @@
         }
 
         @NonNull public ChooseLockSettingsHelper build() {
-            if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP) {
+            if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP
+                    && mUserId != LockPatternUtils.USER_REPAIR_MODE) {
                 Utils.enforceSameOwner(mActivity, mUserId);
             }
 
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index d409c0f..314ce05 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -166,8 +166,12 @@
         mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION);
         String alternateButton = intent.getStringExtra(
                 KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
-        boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
-        boolean remoteValidation =
+        final boolean frp =
+                KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
+        final boolean repairMode =
+                KeyguardManager.ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL
+                        .equals(intent.getAction());
+        final boolean remoteValidation =
                 KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL.equals(intent.getAction());
         mTaskOverlay = isInternalActivity()
                 && intent.getBooleanExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, false);
@@ -222,6 +226,14 @@
                     .setExternal(true)
                     .setUserId(LockPatternUtils.USER_FRP)
                     .show();
+        } else if (repairMode) {
+            final ChooseLockSettingsHelper.Builder builder =
+                    new ChooseLockSettingsHelper.Builder(this);
+            launchedCDC = builder.setHeader(mTitle)
+                    .setDescription(mDetails)
+                    .setExternal(true)
+                    .setUserId(LockPatternUtils.USER_REPAIR_MODE)
+                    .show();
         } else if (remoteValidation) {
             RemoteLockscreenValidationSession remoteLockscreenValidationSession =
                     intent.getParcelableExtra(
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 5a123b8..43d8440 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -106,6 +106,7 @@
     protected boolean mFrp;
     protected boolean mRemoteValidation;
     protected boolean mRequestWriteRepairModePassword;
+    protected boolean mRepairMode;
     protected CharSequence mAlternateButtonText;
     protected BiometricManager mBiometricManager;
     @Nullable protected RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
@@ -181,6 +182,7 @@
         mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(),
                 isInternalActivity());
         mFrp = (mUserId == LockPatternUtils.USER_FRP);
+        mRepairMode = (mUserId == LockPatternUtils.USER_REPAIR_MODE);
         mUserManager = UserManager.get(getActivity());
         mEffectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
         mLockPatternUtils = new LockPatternUtils(getActivity());
@@ -269,7 +271,7 @@
     // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
     // fingerprint is disabled due to device restart.
     protected boolean isStrongAuthRequired() {
-        return mFrp
+        return mFrp || mRepairMode
                 || !mLockPatternUtils.isBiometricAllowedForUser(mEffectiveUserId)
                 || !mUserManager.isUserUnlocked(mUserId);
     }
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 1b53506..c6022b5 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -284,6 +284,11 @@
                 return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header_frp)
                         : getString(R.string.lockpassword_confirm_your_pin_header_frp);
             }
+            if (mRepairMode) {
+                return mIsAlpha
+                        ? getString(R.string.lockpassword_confirm_repair_mode_password_header)
+                        : getString(R.string.lockpassword_confirm_repair_mode_pin_header);
+            }
             if (mRemoteValidation) {
                 return getString(R.string.lockpassword_remote_validation_header);
             }
@@ -307,6 +312,11 @@
                 return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_details_frp)
                         : getString(R.string.lockpassword_confirm_your_pin_details_frp);
             }
+            if (mRepairMode) {
+                return mIsAlpha
+                        ? getString(R.string.lockpassword_confirm_repair_mode_password_details)
+                        : getString(R.string.lockpassword_confirm_repair_mode_pin_details);
+            }
             if (mRemoteValidation) {
                 return getContext().getString(mIsAlpha
                         ? R.string.lockpassword_remote_validation_password_details
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 3951bde..a2bcb5a 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -179,7 +179,7 @@
                 //              ability to disable the pattern in L. Remove this block after
                 //              ensuring it's safe to do so. (Note that ConfirmLockPassword
                 //              doesn't have this).
-                if (!mFrp && !mRemoteValidation
+                if (!mFrp && !mRemoteValidation && !mRepairMode
                         && !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
                     getActivity().setResult(Activity.RESULT_OK);
                     getActivity().finish();
@@ -308,6 +308,9 @@
             if (mFrp) {
                 return getString(R.string.lockpassword_confirm_your_pattern_details_frp);
             }
+            if (mRepairMode) {
+                return getString(R.string.lockpassword_confirm_repair_mode_pattern_details);
+            }
             if (mRemoteValidation) {
                 return getString(
                         R.string.lockpassword_remote_validation_pattern_details);
@@ -402,7 +405,12 @@
         }
 
         private String getDefaultHeader() {
-            if (mFrp) return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
+            if (mFrp) {
+                return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
+            }
+            if (mRepairMode) {
+                return getString(R.string.lockpassword_confirm_repair_mode_pattern_header);
+            }
             if (mRemoteValidation) {
                 return getString(R.string.lockpassword_remote_validation_header);
             }
diff --git a/src/com/android/settings/spa/development/UsageStats.kt b/src/com/android/settings/spa/development/UsageStats.kt
index b681d75..4d9c455 100644
--- a/src/com/android/settings/spa/development/UsageStats.kt
+++ b/src/com/android/settings/spa/development/UsageStats.kt
@@ -32,7 +32,6 @@
         AppListPage(
             title = stringResource(R.string.testing_usage_stats),
             listModel = rememberContext(::UsageStatsListModel),
-            primaryUserOnly = true,
         )
     }
 }
diff --git a/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java
index 5dc5758..0b6d533 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java
@@ -20,19 +20,29 @@
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiManager;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.SwitchPreference;
 
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.wifi.repository.WifiHotspotRepository;
 
 public class WifiTetherAutoOffPreferenceController extends BasePreferenceController implements
         Preference.OnPreferenceChangeListener {
 
     private final WifiManager mWifiManager;
     private boolean mSettingsOn;
+    @VisibleForTesting
+    boolean mNeedShutdownSecondarySap;
 
     public WifiTetherAutoOffPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
+        WifiHotspotRepository wifiHotspotRepository = FeatureFactory.getFactory(context)
+                .getWifiFeatureProvider().getWifiHotspotRepository();
+        if (wifiHotspotRepository.isSpeedFeatureAvailable() && wifiHotspotRepository.isDualBand()) {
+            mNeedShutdownSecondarySap = true;
+        }
         mWifiManager = context.getSystemService(WifiManager.class);
     }
 
@@ -51,14 +61,15 @@
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean settingsOn = (Boolean) newValue;
-        SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
-        SoftApConfiguration newSoftApConfiguration =
-                new SoftApConfiguration.Builder(softApConfiguration)
-                        .setAutoShutdownEnabled(settingsOn)
-                        .build();
+        boolean settingsOn = (Boolean) newValue;
+        SoftApConfiguration.Builder configBuilder =
+                new SoftApConfiguration.Builder(mWifiManager.getSoftApConfiguration());
+        configBuilder.setAutoShutdownEnabled(settingsOn);
+        if (mNeedShutdownSecondarySap) {
+            configBuilder.setBridgedModeOpportunisticShutdownEnabled(settingsOn);
+        }
         mSettingsOn = settingsOn;
-        return mWifiManager.setSoftApConfiguration(newSoftApConfiguration);
+        return mWifiManager.setSoftApConfiguration(configBuilder.build());
     }
 
     public boolean isEnabled() {
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
index e14e271..ea2852f 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
@@ -38,6 +38,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
@@ -93,6 +94,7 @@
         when(mAccessibilityManager.getInstalledAccessibilityServiceList()).thenReturn(
                 mAccessibilityServices);
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
index 1cd301f..4ee2a2d 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
@@ -22,6 +22,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
@@ -29,6 +30,7 @@
 import android.content.Context;
 
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
@@ -73,6 +75,7 @@
         final LayoutPreference resetPreference =
                 new LayoutPreference(mContext, R.layout.accessibility_text_reading_reset_button);
         doReturn(mContext).when(mFragment).getContext();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         doReturn(resetPreference).when(mFragment).findPreference(RESET_KEY);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
index 84783b21..aa622f5 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
@@ -75,6 +77,7 @@
         mFragment =
                 spy(new TestToggleScreenMagnificationPreferenceFragmentForSetupWizard(mContext));
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
index c604652..77e5b1f 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -28,6 +29,7 @@
 import android.content.Context;
 import android.os.Bundle;
 
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.test.core.app.ApplicationProvider;
@@ -72,6 +74,7 @@
     public void setUp() {
         mFragment = spy(new TestToggleScreenReaderPreferenceFragmentForSetupWizard(mContext));
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
index 7893831..8878064 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -28,6 +29,7 @@
 import android.content.Context;
 import android.os.Bundle;
 
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.test.core.app.ApplicationProvider;
@@ -72,6 +74,7 @@
     public void setUp() {
         mFragment = spy(new TestToggleSelectToSpeakPreferenceFragmentForSetupWizard(mContext));
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
index 8c84128..0f12d1e 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -353,6 +353,19 @@
     }
 
     @Test
+    public void fingerprintUdfpsOverlayEnrollment_udfpsAnimationViewVisibility() {
+        initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
+        when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
+        createActivity();
+
+        final UdfpsEnrollView enrollView = mActivity.findViewById(R.id.udfps_animation_view);
+        assertThat(enrollView.getVisibility()).isEqualTo(View.GONE);
+
+        mActivity.onUdfpsOverlayShown();
+        assertThat(enrollView.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
     public void forwardEnrollProgressEvents() {
         initializeActivityFor(TYPE_UDFPS_OPTICAL);
 
@@ -393,11 +406,11 @@
     }
 
     @Test
-    public void forwardEnrollPointerDownEvents() {
+    public void forwardUdfpsEnrollPointerDownEvents() {
         initializeActivityFor(TYPE_UDFPS_OPTICAL);
 
         EnrollListener listener = new EnrollListener(mActivity);
-        mActivity.onPointerDown(0);
+        mActivity.onUdfpsPointerDown(0);
         assertThat(listener.mProgress).isFalse();
         assertThat(listener.mHelp).isFalse();
         assertThat(listener.mAcquired).isFalse();
@@ -406,11 +419,11 @@
     }
 
     @Test
-    public void forwardEnrollPointerUpEvents() {
+    public void forwardUdfpsEnrollPointerUpEvents() {
         initializeActivityFor(TYPE_UDFPS_OPTICAL);
 
         EnrollListener listener = new EnrollListener(mActivity);
-        mActivity.onPointerUp(0);
+        mActivity.onUdfpsPointerUp(0);
         assertThat(listener.mProgress).isFalse();
         assertThat(listener.mHelp).isFalse();
         assertThat(listener.mAcquired).isFalse();
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
index 184f521..7c598e0 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
@@ -202,7 +202,7 @@
                 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
                         true, BluetoothDevicePreference.SortType.TYPE_FIFO);
         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
-        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+        mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
 
         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
@@ -210,7 +210,7 @@
         mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
                 BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
 
-        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
+        assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0);
     }
 
     @Test
@@ -221,7 +221,7 @@
                         true, BluetoothDevicePreference.SortType.TYPE_FIFO);
         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
         final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
-        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+        mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
 
         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
index 5fbfee8..ce67051 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
@@ -27,7 +27,12 @@
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.os.Bundle;
+import android.view.View;
 
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -53,6 +58,20 @@
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
+    private final Lifecycle mFakeLifecycle = new Lifecycle() {
+        @Override
+        public void addObserver(@NonNull LifecycleObserver observer) {}
+
+        @Override
+        public void removeObserver(@NonNull LifecycleObserver observer) {}
+
+        @NonNull
+        @Override
+        public State getCurrentState() {
+            return State.CREATED;
+        }
+    };
+
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private LocalBluetoothManager mLocalManager;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -74,6 +93,8 @@
                 .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
         doReturn(mFooterPreference).when(mFragment)
                 .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
+        doReturn(new View(mContext)).when(mFragment).getView();
+        doReturn((LifecycleOwner) () -> mFakeLifecycle).when(mFragment).getViewLifecycleOwner();
         doReturn(Collections.emptyList()).when(mDeviceManager).getCachedDevicesCopy();
 
         mFragment.mBluetoothAdapter = mBluetoothAdapter;
@@ -82,7 +103,7 @@
         mFragment.mDeviceListGroup = mAvailableDevicesCategory;
         mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
     }
-//
+
     @Test
     public void initPreferencesFromPreferenceScreen_findPreferences() {
         mFragment.initPreferencesFromPreferenceScreen();
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceControllerTest.java
index fbc4aaa..535e4ab 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceControllerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -28,6 +29,10 @@
 
 import androidx.preference.SwitchPreference;
 
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.wifi.factory.WifiFeatureProvider;
+import com.android.settings.wifi.repository.WifiHotspotRepository;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -54,6 +59,8 @@
 
         mContext = spy(RuntimeEnvironment.application);
 
+        WifiFeatureProvider provider = FakeFeatureFactory.setupForTest().getWifiFeatureProvider();
+        when(provider.getWifiHotspotRepository()).thenReturn(mock(WifiHotspotRepository.class));
         when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
         mSoftApConfiguration = new SoftApConfiguration.Builder().build();
         when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration);
@@ -101,6 +108,32 @@
         assertThat(mSwitchPreference.isChecked()).isTrue();
     }
 
+    @Test
+    public void onPreferenceChange_needShutdownSecondarySap_setSecondarySap() {
+        mController.mNeedShutdownSecondarySap = true;
+        setConfigShutdownSecondarySap(false);
+
+        mController.onPreferenceChange(mSwitchPreference, true);
+
+        ArgumentCaptor<SoftApConfiguration> config =
+                ArgumentCaptor.forClass(SoftApConfiguration.class);
+        verify(mWifiManager).setSoftApConfiguration(config.capture());
+        assertThat(config.getValue().isBridgedModeOpportunisticShutdownEnabled()).isTrue();
+    }
+
+    @Test
+    public void onPreferenceChange_noNeedShutdownSecondarySap_doNotSetSecondarySap() {
+        mController.mNeedShutdownSecondarySap = false;
+        setConfigShutdownSecondarySap(false);
+
+        mController.onPreferenceChange(mSwitchPreference, true);
+
+        ArgumentCaptor<SoftApConfiguration> config =
+                ArgumentCaptor.forClass(SoftApConfiguration.class);
+        verify(mWifiManager).setSoftApConfiguration(config.capture());
+        assertThat(config.getValue().isBridgedModeOpportunisticShutdownEnabled()).isFalse();
+    }
+
     private boolean getAutoOffSetting() {
         ArgumentCaptor<SoftApConfiguration> softApConfigCaptor =
                 ArgumentCaptor.forClass(SoftApConfiguration.class);
@@ -115,4 +148,12 @@
                         .build();
         when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration);
     }
+
+    private void setConfigShutdownSecondarySap(boolean enabled) {
+        mSoftApConfiguration =
+                new SoftApConfiguration.Builder(mSoftApConfiguration)
+                        .setBridgedModeOpportunisticShutdownEnabled(enabled)
+                        .build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration);
+    }
 }
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
index bdb45b0..2c830ad 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
@@ -379,7 +379,7 @@
 
         // Notify acquire message
         final int value = 33;
-        mCallbackWrapper.mValue.onPointerDown(value);
+        mCallbackWrapper.mValue.onUdfpsPointerDown(value);
         assertThat(liveData.getValue()).isEqualTo(value);
     }
 
@@ -397,7 +397,7 @@
 
         // Notify acquire message
         final int value = 44;
-        mCallbackWrapper.mValue.onPointerUp(value);
+        mCallbackWrapper.mValue.onUdfpsPointerUp(value);
         assertThat(liveData.getValue()).isEqualTo(value);
     }