Merge "Enable use_resource_processor for all sysui deps" into main
diff --git a/res/drawable/ic_battery_full.xml b/res/drawable/ic_battery_full.xml
new file mode 100644
index 0000000..8b1321f
--- /dev/null
+++ b/res/drawable/ic_battery_full.xml
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="960"
+        android:viewportHeight="960"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M320,880Q303,880 291.5,868.5Q280,857 280,840L280,200Q280,183 291.5,171.5Q303,160 320,160L400,160L400,80L560,80L560,160L640,160Q657,160 668.5,171.5Q680,183 680,200L680,840Q680,857 668.5,868.5Q657,880 640,880L320,880Z"/>
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f8eaaff..646a3ee 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2119,6 +2119,8 @@
     <string name="internet_source_mobile_data">Mobile data</string>
     <!-- Ethernet summary in Internet source preference [CHAR LIMIT=NONE]-->
     <string name="internet_source_ethernet">Ethernet</string>
+    <!-- Hotspot device details battery charging summary [CHAR LIMIT=NONE]-->
+    <string name="hotspot_battery_charging_summary"><xliff:g id="battery_percentage" example="80%">%1$s</xliff:g> \u2011 Charging</string>
     <!-- Hotspot device details preference category title in Network details [CHAR LIMIT=NONE]-->
     <string name="hotspot_connection_category">Hotspot connection</string>
     <!-- Connection strength preference in Hotspot connection preference category [CHAR LIMIT=NONE]-->
@@ -6908,14 +6910,6 @@
          For example, they may have alerts just once or every 2 or 15 minutes.  [CHAR LIMIT=30] -->
     <string name="repeat_title">Repeat</string>
 
-    <!-- Call Manager enable settings title.  [CHAR LIMIT=50] -->
-    <string name="call_manager_enable_title">Enable Call Manager</string>
-    <!-- Call Manager enable settings summary.  [CHAR LIMIT=80] -->
-    <string name="call_manager_enable_summary">Allow this service to manage how your calls are made.</string>
-    <!-- Call Manager settings title.  [CHAR LIMIT=50] -->
-    <string name="call_manager_title">Call Manager</string>
-    <!-- Call Manager settings summary.  [CHAR LIMIT=50] -->
-    <string name="call_manager_summary"><xliff:g id="app">%1$s</xliff:g></string>
     <!-- Cell Broadcast settings title.  [CHAR LIMIT=50] -->
     <string name="cell_broadcast_settings">Wireless emergency alerts</string>
     <!-- Network operators settings title.  [CHAR LIMIT=50] -->
diff --git a/res/xml/wifi_network_details_fragment2.xml b/res/xml/wifi_network_details_fragment2.xml
index e3464c2..0062474 100644
--- a/res/xml/wifi_network_details_fragment2.xml
+++ b/res/xml/wifi_network_details_fragment2.xml
@@ -52,6 +52,7 @@
             settings:enableCopying="true"/>
         <Preference
             android:key="hotspot_device_details_battery"
+            android:icon="@drawable/ic_battery_full"
             android:title="@string/power_usage_summary_title"
             android:selectable="false"
             settings:enableCopying="true"/>
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
index d65e057..155ced5 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
@@ -30,10 +30,10 @@
 import android.os.Bundle;
 
 import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentActivity;
 import androidx.fragment.app.FragmentManager;
 
 import com.android.settings.R;
-import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 
 /** Fingerprint error dialog, will be shown when an error occurs during fingerprint enrollment. */
@@ -95,7 +95,7 @@
         return dialog;
     }
 
-    public static void showErrorDialog(BiometricEnrollBase host, int errMsgId, boolean isSetup) {
+    public static void showErrorDialog(FragmentActivity host, int errMsgId, boolean isSetup) {
         if (host.isFinishing()) {
             return;
         }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index d43aeba..31afcb7 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -20,11 +20,13 @@
 import android.app.Activity
 import android.content.Intent
 import android.content.res.ColorStateList
+import android.content.res.Configuration
 import android.graphics.Color
 import android.hardware.fingerprint.FingerprintManager
 import android.os.Bundle
 import android.provider.Settings
 import android.util.Log
+import android.view.accessibility.AccessibilityManager
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
@@ -44,17 +46,21 @@
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.AccessibilityViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Confirmation
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Education
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Enrollment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Finish
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FoldStateViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Intro
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.LaunchConfirmDeviceCredential
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
 import com.android.settings.password.ChooseLockGeneric
 import com.android.settings.password.ChooseLockSettingsHelper
 import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
@@ -72,6 +78,10 @@
 class FingerprintEnrollmentV2Activity : FragmentActivity() {
   private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
   private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
+  private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
+  private lateinit var accessibilityViewModel: AccessibilityViewModel
+  private lateinit var foldStateViewModel: FoldStateViewModel
+  private lateinit var orientationStateViewModel: OrientationStateViewModel
   private val coroutineDispatcher = Dispatchers.Default
 
   /** Result listener for ChooseLock activity flow. */
@@ -94,6 +104,11 @@
     super.onAttachedToWindow()
   }
 
+  override fun onConfigurationChanged(newConfig: Configuration) {
+    super.onConfigurationChanged(newConfig)
+    foldStateViewModel.onConfigurationChange(newConfig)
+  }
+
   @ColorInt
   private fun getBackgroundColor(): Int {
     val stateList: ColorStateList? =
@@ -178,16 +193,53 @@
         )
       )[FingerprintEnrollNavigationViewModel::class.java]
 
+    // Initialize FoldStateViewModel
+    foldStateViewModel =
+      ViewModelProvider(this, FoldStateViewModel.FoldStateViewModelFactory(context))[
+        FoldStateViewModel::class.java]
+    foldStateViewModel.onConfigurationChange(resources.configuration)
+
     // Initialize FingerprintViewModel
-    ViewModelProvider(
-      this,
-      FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(interactor, backgroundDispatcher)
-    )[FingerprintEnrollViewModel::class.java]
+    fingerprintEnrollViewModel =
+      ViewModelProvider(
+        this,
+        FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
+          interactor,
+          backgroundDispatcher
+        )
+      )[FingerprintEnrollViewModel::class.java]
 
     // Initialize scroll view model
     ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
       FingerprintScrollViewModel::class.java]
 
+    // Initialize AccessibilityViewModel
+    accessibilityViewModel =
+      ViewModelProvider(
+        this,
+        AccessibilityViewModel.AccessibilityViewModelFactory(
+          getSystemService(AccessibilityManager::class.java)!!
+        )
+      )[AccessibilityViewModel::class.java]
+
+    // Initialize OrientationViewModel
+    orientationStateViewModel =
+      ViewModelProvider(this, OrientationStateViewModel.OrientationViewModelFactory(context))[
+        OrientationStateViewModel::class.java]
+
+    // Initialize FingerprintEnrollFindSensorViewModel
+    ViewModelProvider(
+      this,
+      FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
+        navigationViewModel,
+        fingerprintEnrollViewModel,
+        gatekeeperViewModel,
+        accessibilityViewModel,
+        foldStateViewModel,
+        orientationStateViewModel
+      )
+    )[FingerprintEnrollFindSensorViewModel::class.java]
+
     lifecycleScope.launch {
       navigationViewModel.navigationViewModel.filterNotNull().collect {
         Log.d(TAG, "navigationStep $it")
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt
index 3f615ce..0140d57 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt
@@ -19,10 +19,11 @@
 import android.os.Bundle
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
+import com.android.settings.R
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
 
 /** A fragment that is responsible for enrolling a users fingerprint. */
-class FingerprintEnrollEnrollingV2Fragment : Fragment() {
+class FingerprintEnrollEnrollingV2Fragment : Fragment(R.layout.fingerprint_enroll_enrolling) {
 
   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
index beb84e9..dcdcccf 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
@@ -17,26 +17,197 @@
 package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment
 
 import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
+import com.airbnb.lottie.LottieAnimationView
 import com.android.settings.R
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
+import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
+import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.google.android.setupcompat.template.FooterBarMixin
+import com.google.android.setupcompat.template.FooterButton
+import com.google.android.setupdesign.GlifLayout
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
 
 /**
  * A fragment that is used to educate the user about the fingerprint sensor on this device.
  *
+ * If the sensor is not a udfps sensor, this fragment listens to fingerprint enrollment for
+ * proceeding to the enroll enrolling.
+ *
  * The main goals of this page are
  * 1. Inform the user where the fingerprint sensor is on their device
  * 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
  *    will work.
  */
-class FingerprintEnrollFindSensorV2Fragment : Fragment(R.layout.fingerprint_v2_enroll_find_sensor) {
+class FingerprintEnrollFindSensorV2Fragment : Fragment() {
+  // This is only for non-udfps or non-sfps sensor. For udfps and sfps, we show lottie.
+  private var animation: FingerprintFindSensorAnimation? = null
+
+  private var contentLayoutId: Int = -1
+  private lateinit var viewModel: FingerprintEnrollFindSensorViewModel
 
   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
-    if (savedInstanceState == null) {
-      val navigationViewModel =
-        ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
+    viewModel =
+      ViewModelProvider(requireActivity())[FingerprintEnrollFindSensorViewModel::class.java]
+    lifecycleScope.launch {
+      viewModel.sensorType.collect {
+        contentLayoutId =
+          when (it) {
+            FingerprintSensorType.UDFPS_OPTICAL,
+            FingerprintSensorType.UDFPS_ULTRASONIC -> R.layout.udfps_enroll_find_sensor_layout
+            FingerprintSensorType.POWER_BUTTON -> R.layout.sfps_enroll_find_sensor_layout
+            else -> R.layout.fingerprint_v2_enroll_find_sensor
+          }
+      }
     }
   }
+
+  override fun onCreateView(
+    inflater: LayoutInflater,
+    container: ViewGroup?,
+    savedInstanceState: Bundle?
+  ): View? {
+    return inflater.inflate(contentLayoutId, container, false).also { it ->
+      val view = it!! as GlifLayout
+
+      // Set up header and description
+      lifecycleScope.launch { viewModel.sensorType.collect { setTexts(it, view) } }
+
+      // Set up footer bar
+      val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
+      setupSecondaryButton(footerBarMixin)
+      lifecycleScope.launch {
+        viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
+      }
+
+      // Set up lottie or animation
+      lifecycleScope.launch {
+        viewModel.showSfpsLottie.collect { (isFolded, rotation) ->
+          setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
+        }
+      }
+      lifecycleScope.launch {
+        viewModel.showUdfpsLottie.collect { isAccessibilityEnabled ->
+          val lottieAnimation =
+            if (isAccessibilityEnabled) R.raw.udfps_edu_a11y_lottie else R.raw.udfps_edu_lottie
+          setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
+        }
+      }
+      lifecycleScope.launch {
+        viewModel.showRfpsAnimation.collect {
+          animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
+          animation!!.startAnimation()
+        }
+      }
+
+      lifecycleScope.launch {
+        viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
+          // TODO: Covert error dialog kotlin as well
+          FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup)
+        }
+      }
+    }
+  }
+
+  override fun onDestroy() {
+    animation?.stopAnimation()
+    super.onDestroy()
+  }
+
+  private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
+    footerBarMixin.secondaryButton =
+      FooterButton.Builder(requireActivity())
+        .setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
+        .setListener {
+          run {
+            // TODO: Show the dialog for suw
+            Log.d(TAG, "onSkipClicked")
+            // TODO: Finish activity in the root activity instead.
+            requireActivity().finish()
+          }
+        }
+        .setButtonType(FooterButton.ButtonType.SKIP)
+        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
+        .build()
+  }
+
+  private fun setupPrimaryButton(footerBarMixin: FooterBarMixin) {
+    footerBarMixin.primaryButton =
+      FooterButton.Builder(requireActivity())
+        .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
+        .setListener {
+          run {
+            Log.d(TAG, "onStartButtonClick")
+            viewModel.proceedToEnrolling()
+          }
+        }
+        .setButtonType(FooterButton.ButtonType.NEXT)
+        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+        .build()
+  }
+
+  private fun setupLottie(
+    view: View,
+    lottieAnimation: Int,
+    lottieClickListener: View.OnClickListener? = null
+  ) {
+    val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
+    illustrationLottie?.setAnimation(lottieAnimation)
+    illustrationLottie?.playAnimation()
+    illustrationLottie?.setOnClickListener(lottieClickListener)
+    illustrationLottie?.visibility = View.VISIBLE
+  }
+
+  private fun setTexts(sensorType: FingerprintSensorType, view: GlifLayout) {
+    when (sensorType) {
+      FingerprintSensorType.UDFPS_OPTICAL,
+      FingerprintSensorType.UDFPS_ULTRASONIC -> {
+        view.setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title)
+        view.setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message)
+      }
+      FingerprintSensorType.POWER_BUTTON -> {
+        view.setHeaderText(R.string.security_settings_sfps_enroll_find_sensor_title)
+        view.setDescriptionText(R.string.security_settings_sfps_enroll_find_sensor_message)
+      }
+      else -> {
+        view.setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title)
+        view.setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message)
+      }
+    }
+  }
+
+  private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
+    val animation: Int
+    when (rotation) {
+      Surface.ROTATION_90 ->
+        animation =
+          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_left
+          else R.raw.fingerprint_edu_lottie_portrait_top_left)
+      Surface.ROTATION_180 ->
+        animation =
+          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_left
+          else R.raw.fingerprint_edu_lottie_landscape_bottom_left)
+      Surface.ROTATION_270 ->
+        animation =
+          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_right
+          else R.raw.fingerprint_edu_lottie_portrait_bottom_right)
+      else ->
+        animation =
+          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_right
+          else R.raw.fingerprint_edu_lottie_landscape_top_right)
+    }
+    return animation
+  }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
index 03c7a5f..dbf6d12 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
@@ -180,7 +180,10 @@
     scrollView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
     // Next button responsible for starting the next fragment.
     val onNextButtonClick: View.OnClickListener =
-      View.OnClickListener { Log.d(TAG, "OnNextClicked") }
+      View.OnClickListener {
+        Log.d(TAG, "OnNextClicked")
+        navigationViewModel.nextStep()
+      }
 
     val layout: GlifLayout = requireActivity().requireViewById(R.id.setup_wizard_layout)
     footerBarMixin = layout.getMixin(FooterBarMixin::class.java)
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/AccessibilityViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/AccessibilityViewModel.kt
new file mode 100644
index 0000000..a86ad5d
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/AccessibilityViewModel.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.biometrics.fingerprint2.ui.enrollment.viewmodel
+
+import android.view.accessibility.AccessibilityManager
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Represents all of the information on accessibility state. */
+class AccessibilityViewModel(accessibilityManager: AccessibilityManager) : ViewModel() {
+  /** A flow that contains whether or not accessibility is enabled */
+  val isAccessibilityEnabled: Flow<Boolean> =
+    callbackFlow {
+        val listener =
+          AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) }
+        accessibilityManager.addAccessibilityStateChangeListener(listener)
+
+        // This clause will be called when no one is listening to the flow
+        awaitClose { accessibilityManager.removeAccessibilityStateChangeListener(listener) }
+      }
+      .stateIn(
+        viewModelScope, // This is going to tied to the view model scope
+        SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
+        false
+      )
+
+  class AccessibilityViewModelFactory(private val accessibilityManager: AccessibilityManager) :
+    ViewModelProvider.Factory {
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : ViewModel> create(
+      modelClass: Class<T>,
+    ): T {
+      return AccessibilityViewModel(accessibilityManager) as T
+    }
+  }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
new file mode 100644
index 0000000..dbf6b33
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.biometrics.fingerprint2.ui.enrollment.viewmodel
+
+import android.hardware.fingerprint.FingerprintManager
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.combineTransform
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.transform
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+/** Models the UI state for [FingerprintEnrollFindSensorV2Fragment]. */
+class FingerprintEnrollFindSensorViewModel(
+  private val navigationViewModel: FingerprintEnrollNavigationViewModel,
+  private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
+  private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
+  accessibilityViewModel: AccessibilityViewModel,
+  foldStateViewModel: FoldStateViewModel,
+  orientationStateViewModel: OrientationStateViewModel
+) : ViewModel() {
+  /** Represents the stream of sensor type. */
+  val sensorType: Flow<FingerprintSensorType> =
+    fingerprintEnrollViewModel.sensorType.filterWhenEducationIsShown()
+  private val _isUdfps: Flow<Boolean> =
+    sensorType.map {
+      it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC
+    }
+  private val _isSfps: Flow<Boolean> = sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
+  private val _isRearSfps: Flow<Boolean> =
+    combineTransform(_isSfps, _isUdfps) { v1, v2 -> !v1 && !v2 }
+
+  /** Represents the stream of showing primary button. */
+  val showPrimaryButton: Flow<Boolean> = _isUdfps.transform { if (it) emit(true) }
+
+  /** Represents the stream of showing sfps lottie, Pair(isFolded, rotation). */
+  val showSfpsLottie: Flow<Pair<Boolean, Int>> =
+    combineTransform(
+      _isSfps,
+      foldStateViewModel.isFolded,
+      orientationStateViewModel.rotation,
+    ) { isSfps, isFolded, rotation ->
+      if (isSfps) emit(Pair(isFolded, rotation))
+    }
+
+  /** Represents the stream of showing udfps lottie. */
+  val showUdfpsLottie: Flow<Boolean> =
+    combineTransform(
+      _isUdfps,
+      accessibilityViewModel.isAccessibilityEnabled,
+    ) { isUdfps, isAccessibilityEnabled ->
+      if (isUdfps) emit(isAccessibilityEnabled)
+    }
+
+  /** Represents the stream of showing rfps animation. */
+  val showRfpsAnimation: Flow<Boolean> = _isRearSfps.transform { if (it) emit(true) }
+
+  private val _showErrorDialog: MutableStateFlow<Pair<Int, Boolean>?> = MutableStateFlow(null)
+  /** Represents the stream of showing error dialog. */
+  val showErrorDialog = _showErrorDialog.filterNotNull()
+
+  init {
+    // Start or end enroll flow
+    viewModelScope.launch {
+      combine(
+          fingerprintEnrollViewModel.sensorType,
+          gatekeeperViewModel.hasValidGatekeeperInfo,
+          gatekeeperViewModel.gatekeeperInfo,
+          navigationViewModel.navigationViewModel
+        ) { sensorType, hasValidGatekeeperInfo, gatekeeperInfo, navigationViewModel ->
+          val shouldStartEnroll =
+            navigationViewModel.currStep == Education &&
+              sensorType != FingerprintSensorType.UDFPS_OPTICAL &&
+              sensorType != FingerprintSensorType.UDFPS_ULTRASONIC &&
+              hasValidGatekeeperInfo
+          if (shouldStartEnroll) (gatekeeperInfo as GatekeeperInfo.GatekeeperPasswordInfo).token
+          else null
+        }
+        .collect { token ->
+          if (token != null) {
+            fingerprintEnrollViewModel.startEnroll(token, EnrollReason.FindSensor)
+          } else {
+            fingerprintEnrollViewModel.stopEnroll()
+          }
+        }
+    }
+
+    // Enroll progress flow
+    viewModelScope.launch {
+      combine(
+          navigationViewModel.enrollType,
+          fingerprintEnrollViewModel.enrollFlow.filterNotNull()
+        ) { enrollType, enrollFlow ->
+          Pair(enrollType, enrollFlow)
+        }
+        .collect { (enrollType, enrollFlow) ->
+          when (enrollFlow) {
+            // TODO: Cancel the enroll() when EnrollProgress is received instead of proceeding to
+            // Enrolling page. Otherwise Enrolling page will receive the EnrollError.
+            is FingerEnrollStateViewModel.EnrollProgress -> proceedToEnrolling()
+            is FingerEnrollStateViewModel.EnrollError -> {
+              val errMsgId = enrollFlow.errMsgId
+              if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+                proceedToEnrolling()
+              } else {
+                _showErrorDialog.update { Pair(errMsgId, enrollType == SetupWizard) }
+              }
+            }
+            is FingerEnrollStateViewModel.EnrollHelp -> {}
+          }
+        }
+    }
+  }
+
+  /** Proceed to EnrollEnrolling page. */
+  fun proceedToEnrolling() {
+    navigationViewModel.nextStep()
+  }
+
+  // TODO: If we decide to remove previous fragment from activity, then we don't need to check
+  // whether education is shown for the flows that are subscribed by
+  // [FingerprintEnrollFindSensorV2Fragment].
+  private fun <T> Flow<T>.filterWhenEducationIsShown() =
+    combineTransform(navigationViewModel.navigationViewModel) { value, navigationViewModel ->
+      if (navigationViewModel.currStep == Education) {
+        emit(value)
+      }
+    }
+
+  class FingerprintEnrollFindSensorViewModelFactory(
+    private val navigationViewModel: FingerprintEnrollNavigationViewModel,
+    private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
+    private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
+    private val accessibilityViewModel: AccessibilityViewModel,
+    private val foldStateViewModel: FoldStateViewModel,
+    private val orientationStateViewModel: OrientationStateViewModel
+  ) : ViewModelProvider.Factory {
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : ViewModel> create(modelClass: Class<T>): T {
+      return FingerprintEnrollFindSensorViewModel(
+        navigationViewModel,
+        fingerprintEnrollViewModel,
+        gatekeeperViewModel,
+        accessibilityViewModel,
+        foldStateViewModel,
+        orientationStateViewModel
+      )
+        as T
+    }
+  }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FoldStateViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FoldStateViewModel.kt
new file mode 100644
index 0000000..a4c7ff2
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FoldStateViewModel.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.biometrics.fingerprint2.ui.enrollment.viewmodel
+
+import android.content.Context
+import android.content.res.Configuration
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.unfold.updates.FoldProvider
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+
+/** Represents all of the information on fold state. */
+class FoldStateViewModel(context: Context) : ViewModel() {
+
+  private val screenSizeFoldProvider = ScreenSizeFoldProvider(context)
+
+  /** A flow that contains the fold state info */
+  val isFolded: Flow<Boolean> = callbackFlow {
+    val foldStateListener =
+      object : FoldProvider.FoldCallback {
+        override fun onFoldUpdated(isFolded: Boolean) {
+          trySend(isFolded)
+        }
+      }
+    screenSizeFoldProvider.registerCallback(foldStateListener, context.mainExecutor)
+    awaitClose { screenSizeFoldProvider.unregisterCallback(foldStateListener) }
+  }
+
+  fun onConfigurationChange(newConfig: Configuration) {
+    screenSizeFoldProvider.onConfigurationChange(newConfig)
+  }
+
+  class FoldStateViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : ViewModel> create(
+      modelClass: Class<T>,
+    ): T {
+      return FoldStateViewModel(context) as T
+    }
+  }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/OrientationStateViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/OrientationStateViewModel.kt
new file mode 100644
index 0000000..2e5f734
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/OrientationStateViewModel.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.biometrics.fingerprint2.ui.enrollment.viewmodel
+
+import android.content.Context
+import android.view.OrientationEventListener
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
+import com.android.internal.R
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Represents all of the information on orientation state and rotation state. */
+class OrientationStateViewModel(private val context: Context) : ViewModel() {
+
+  /** A flow that contains the orientation info */
+  val orientation: Flow<Int> = callbackFlow {
+    val orientationEventListener =
+      object : OrientationEventListener(context) {
+        override fun onOrientationChanged(orientation: Int) {
+          trySend(orientation)
+        }
+      }
+    orientationEventListener.enable()
+    awaitClose { orientationEventListener.disable() }
+  }
+
+  /** A flow that contains the rotation info */
+  val rotation: Flow<Int> =
+    callbackFlow {
+        val orientationEventListener =
+          object : OrientationEventListener(context) {
+            override fun onOrientationChanged(orientation: Int) {
+              trySend(getRotationFromDefault(context.display!!.rotation))
+            }
+          }
+        orientationEventListener.enable()
+        awaitClose { orientationEventListener.disable() }
+      }
+      .stateIn(
+        viewModelScope, // This is going to tied to the view model scope
+        SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
+        context.display!!.rotation
+      )
+
+  fun getRotationFromDefault(rotation: Int): Int {
+    val isReverseDefaultRotation =
+      context.resources.getBoolean(R.bool.config_reverseDefaultRotation)
+    return if (isReverseDefaultRotation) {
+      (rotation + 1) % 4
+    } else {
+      rotation
+    }
+  }
+
+  class OrientationViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : ViewModel> create(
+      modelClass: Class<T>,
+    ): T {
+      return OrientationStateViewModel(context) as T
+    }
+  }
+}
diff --git a/src/com/android/settings/datausage/BillingCyclePreference.kt b/src/com/android/settings/datausage/BillingCyclePreference.kt
index 05066be..619f7e9 100644
--- a/src/com/android/settings/datausage/BillingCyclePreference.kt
+++ b/src/com/android/settings/datausage/BillingCyclePreference.kt
@@ -49,6 +49,7 @@
         this.subId = subId
         summary = null
         updateEnabled()
+        intent = intent
     }
 
     override fun onAttached() {
diff --git a/src/com/android/settings/datausage/DataUsageBaseFragment.java b/src/com/android/settings/datausage/DataUsageBaseFragment.java
index eee3228..5ddbab8 100644
--- a/src/com/android/settings/datausage/DataUsageBaseFragment.java
+++ b/src/com/android/settings/datausage/DataUsageBaseFragment.java
@@ -15,23 +15,13 @@
 package com.android.settings.datausage;
 
 import android.content.Context;
-import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.os.Bundle;
-import android.os.INetworkManagementService;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
 
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.NetworkPolicyEditor;
 
 public abstract class DataUsageBaseFragment extends DashboardFragment {
-    private static final String TAG = "DataUsageBase";
-    private static final String ETHERNET = "ethernet";
 
     protected final TemplatePreference.NetworkServices services =
             new TemplatePreference.NetworkServices();
@@ -41,16 +31,10 @@
         super.onCreate(icicle);
         Context context = getContext();
 
-        services.mNetworkService = INetworkManagementService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
         services.mPolicyManager = (NetworkPolicyManager) context
                 .getSystemService(Context.NETWORK_POLICY_SERVICE);
 
         services.mPolicyEditor = new NetworkPolicyEditor(services.mPolicyManager);
-
-        services.mTelephonyManager = context.getSystemService(TelephonyManager.class);
-        services.mSubscriptionManager = SubscriptionManager.from(context);
-        services.mUserManager = UserManager.get(context);
     }
 
     @Override
@@ -58,33 +42,4 @@
         super.onResume();
         services.mPolicyEditor.read();
     }
-
-    protected boolean isAdmin() {
-        return services.mUserManager.isAdminUser();
-    }
-
-    protected boolean isMobileDataAvailable(int subId) {
-        return services.mSubscriptionManager.getActiveSubscriptionInfo(subId) != null;
-    }
-
-    protected boolean isNetworkPolicyModifiable(NetworkPolicy policy, int subId) {
-        return policy != null && isBandwidthControlEnabled() && services.mUserManager.isAdminUser()
-                && isDataEnabled(subId);
-    }
-
-    private boolean isDataEnabled(int subId) {
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            return true;
-        }
-        return services.mTelephonyManager.getDataEnabled(subId);
-    }
-
-    protected boolean isBandwidthControlEnabled() {
-        try {
-            return services.mNetworkService.isBandwidthControlEnabled();
-        } catch (RemoteException e) {
-            Log.w(TAG, "problem talking with INetworkManagementService: ", e);
-            return false;
-        }
-    }
 }
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
index 39287c19..15a5603 100644
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -32,7 +32,6 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ImageView;
 import android.widget.Spinner;
 
 import androidx.annotation.NonNull;
@@ -46,6 +45,7 @@
 import com.android.settings.R;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
+import com.android.settings.datausage.lib.BillingCycleRepository;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.network.MobileNetworkRepository;
 import com.android.settings.widget.LoadingViewController;
@@ -108,6 +108,7 @@
     private MobileNetworkRepository mMobileNetworkRepository;
     private SubscriptionInfoEntity mSubscriptionInfoEntity;
     private DataUsageListAppsController mDataUsageListAppsController;
+    private BillingCycleRepository mBillingCycleRepository;
 
     @Override
     public int getMetricsCategory() {
@@ -125,7 +126,8 @@
         }
 
         final Activity activity = getActivity();
-        if (!isBandwidthControlEnabled()) {
+        mBillingCycleRepository = createBillingCycleRepository();
+        if (!mBillingCycleRepository.isBandwidthControlEnabled()) {
             Log.w(TAG, "No bandwidth control; leaving");
             activity.finish();
             return;
@@ -146,6 +148,12 @@
         mDataUsageListAppsController.init(mTemplate);
     }
 
+    @VisibleForTesting
+    @NonNull
+    BillingCycleRepository createBillingCycleRepository() {
+        return new BillingCycleRepository(requireContext());
+    }
+
     @Override
     public void onViewCreated(@NonNull View v, Bundle savedInstanceState) {
         super.onViewCreated(v, savedInstanceState);
@@ -286,10 +294,9 @@
         final NetworkPolicy policy = services.mPolicyEditor.getPolicy(mTemplate);
         final View configureButton = mHeader.findViewById(R.id.filter_settings);
         //SUB SELECT
-        if (isNetworkPolicyModifiable(policy, mSubId) && isMobileDataAvailable(mSubId)) {
+        if (policy != null && isMobileDataAvailable()) {
             mChart.setNetworkPolicy(policy);
             configureButton.setVisibility(View.VISIBLE);
-            ((ImageView) configureButton).setColorFilter(android.R.color.white);
         } else {
             // controls are disabled; don't bind warning/limit sweeps
             mChart.setNetworkPolicy(null);
@@ -304,6 +311,12 @@
         updateSelectedCycle();
     }
 
+    private boolean isMobileDataAvailable() {
+        return mBillingCycleRepository.isModifiable(mSubId)
+                && SubscriptionManager.from(requireContext())
+                .getActiveSubscriptionInfo(mSubId) != null;
+    }
+
     /**
      * Updates the chart and detail data when initial loaded or selected cycle changed.
      */
diff --git a/src/com/android/settings/datausage/DataUsageListAppsController.kt b/src/com/android/settings/datausage/DataUsageListAppsController.kt
index cc55e1a..c324407 100644
--- a/src/com/android/settings/datausage/DataUsageListAppsController.kt
+++ b/src/com/android/settings/datausage/DataUsageListAppsController.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.net.NetworkTemplate
 import android.os.Bundle
+import androidx.annotation.OpenForTesting
 import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.LifecycleCoroutineScope
 import androidx.lifecycle.LifecycleOwner
@@ -37,7 +38,8 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
-class DataUsageListAppsController(context: Context, preferenceKey: String) :
+@OpenForTesting
+open class DataUsageListAppsController(context: Context, preferenceKey: String) :
     BasePreferenceController(context, preferenceKey) {
 
     private val uidDetailProvider = UidDetailProvider(context)
@@ -48,7 +50,7 @@
 
     private var cycleData: List<NetworkCycleChartData>? = null
 
-    fun init(template: NetworkTemplate) {
+    open fun init(template: NetworkTemplate) {
         this.template = template
         repository = AppDataUsageRepository(
             context = mContext,
diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java
index 6966611..4f876ab 100644
--- a/src/com/android/settings/datausage/DataUsageSummary.java
+++ b/src/com/android/settings/datausage/DataUsageSummary.java
@@ -103,7 +103,7 @@
         mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
         mSummaryPreference = findPreference(KEY_STATUS_HEADER);
 
-        if (!hasMobileData || !isAdmin()) {
+        if (!hasMobileData || !UserManager.get(context).isAdminUser()) {
             removePreference(KEY_RESTRICT_BACKGROUND);
         }
         boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context);
diff --git a/src/com/android/settings/datausage/TemplatePreference.java b/src/com/android/settings/datausage/TemplatePreference.java
index 6182229..8e780db 100644
--- a/src/com/android/settings/datausage/TemplatePreference.java
+++ b/src/com/android/settings/datausage/TemplatePreference.java
@@ -16,10 +16,6 @@
 
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
-import android.os.INetworkManagementService;
-import android.os.UserManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
 
 import com.android.settingslib.NetworkPolicyEditor;
 
@@ -29,11 +25,7 @@
     void setTemplate(NetworkTemplate template, int subId);
 
     class NetworkServices {
-        INetworkManagementService mNetworkService;
         NetworkPolicyManager mPolicyManager;
-        TelephonyManager mTelephonyManager;
-        SubscriptionManager mSubscriptionManager;
-        UserManager mUserManager;
         NetworkPolicyEditor mPolicyEditor;
     }
 
diff --git a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
index 779ae4a..bd6aa27 100644
--- a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
+++ b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
@@ -21,9 +21,11 @@
 import android.os.ServiceManager
 import android.telephony.TelephonyManager
 import android.util.Log
+import androidx.annotation.OpenForTesting
 import com.android.settingslib.spaprivileged.framework.common.userManager
 
-class BillingCycleRepository(
+@OpenForTesting
+open class BillingCycleRepository @JvmOverloads constructor(
     context: Context,
     private val networkService: INetworkManagementService =
         INetworkManagementService.Stub.asInterface(
@@ -36,7 +38,7 @@
     fun isModifiable(subId: Int): Boolean =
         isBandwidthControlEnabled() && userManager.isAdminUser && isDataEnabled(subId)
 
-    fun isBandwidthControlEnabled(): Boolean = try {
+    open fun isBandwidthControlEnabled(): Boolean = try {
         networkService.isBandwidthControlEnabled
     } catch (e: Exception) {
         Log.w(TAG, "problem talking with INetworkManagementService: ", e)
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
index fd0f866..4a336eb 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -120,7 +120,7 @@
                     DatabaseUtils.sendBatteryUsageSlotData(context,
                             ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap));
                     if (batteryDiffDataMap.values().stream().anyMatch(data ->
-                            (!data.getAppDiffEntryList().isEmpty()
+                            data != null && (!data.getAppDiffEntryList().isEmpty()
                                     || !data.getSystemDiffEntryList().isEmpty()))) {
                         FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider()
                                 .detectSettingsAnomaly(context, /* displayDrain= */ 0);
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 90d96c6..f2c3325 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -34,6 +34,7 @@
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
+import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
 import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -86,8 +87,13 @@
     val context = LocalContext.current
     val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
     val apnProtocolOptions = stringArrayResource(R.array.apn_protocol_entries).toList()
+    val bearerOptionsAll = stringArrayResource(R.array.bearer_entries)
+    val bearerOptions = bearerOptionsAll.drop(1).toList()
+    val bearerEmptyVal = bearerOptionsAll[0]
     val mvnoTypeOptions = stringArrayResource(R.array.mvno_type_entries).toList()
-
+    val bearerSelectedOptionsState = remember {
+        getBearerSelectedOptionsState(apnData.bearer, apnData.bearerBitmask, context)
+    }
     RegularScaffold(
         title = stringResource(id = R.string.apn_edit),
     ) {
@@ -184,6 +190,13 @@
                     }
                 }
             )
+            SettingsExposedDropdownMenuCheckBox(
+                stringResource(R.string.bearer),
+                bearerOptions,
+                bearerSelectedOptionsState,
+                bearerEmptyVal,
+                apnData.bearerEnabled
+            ) {}
             SettingsExposedDropdownMenuBox(
                 stringResource(R.string.mvno_type),
                 mvnoTypeOptions,
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index 7f4c297..06d8cfb 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -16,8 +16,12 @@
 
 package com.android.settings.network.apn
 
+import android.content.Context
 import android.provider.Telephony
 import android.telephony.TelephonyManager
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.snapshots.SnapshotStateList
+import com.android.settings.R
 
 data class ApnData(
     val name: String = "",
@@ -65,4 +69,29 @@
     var bearerEnabled = true
     var mvnoTypeEnabled = true
     var mvnoValueEnabled = false
-}
\ No newline at end of file
+}
+
+fun getBearerSelectedOptionsState(
+    bearer: Int,
+    bearerBitmask: Int,
+    context: Context
+): SnapshotStateList<Int> {
+    val bearerValues = context.resources.getStringArray(R.array.bearer_values)
+    val bearerSelectedOptionsState = mutableStateListOf<Int>()
+    if (bearerBitmask != 0) {
+        var i = 1
+        var _bearerBitmask = bearerBitmask
+        while (_bearerBitmask != 0) {
+            if (_bearerBitmask and 1 == 1 && !bearerSelectedOptionsState.contains(i)) {
+                bearerSelectedOptionsState.add(bearerValues.indexOf("$i") - 1)
+            }
+            _bearerBitmask = _bearerBitmask shr 1
+            i++
+        }
+    }
+    if (bearer != 0 && !bearerSelectedOptionsState.contains(bearer)) {
+        // add mBearerInitialVal to bearers
+        bearerSelectedOptionsState.add(bearerValues.indexOf("$bearer") - 1)
+    }
+    return bearerSelectedOptionsState
+}
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 737d1df..800adb0 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -1034,8 +1034,6 @@
                     getActivity().getWindow().getDecorView());
 
             mPasswordEntryInputDisabler.setInputEnabled(false);
-            setNextEnabled(false);
-
             mSaveAndFinishWorker = new SaveAndFinishWorker();
             mSaveAndFinishWorker
                     .setListener(this)
diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
index 65f2705..0384f0d 100644
--- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
+++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
@@ -23,7 +23,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
-import android.graphics.ColorFilter;
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
@@ -67,7 +66,6 @@
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.graph.ThemedBatteryDrawable;
 import com.android.wifitrackerlib.NetworkDetailsTracker;
 import com.android.wifitrackerlib.WifiEntry;
 
@@ -441,23 +439,8 @@
     @VisibleForTesting
     void updateBattery(boolean isChanging, int percentage) {
         Preference battery = getPreferenceScreen().findPreference(KEY_HOTSPOT_DEVICE_BATTERY);
-        battery.setSummary(formatPercentage(percentage));
-        ThemedBatteryDrawable drawable = getBatteryDrawable();
-        if (drawable != null) {
-            drawable.setCharging(isChanging);
-            drawable.setBatteryLevel(percentage);
-        }
-        battery.setIcon(drawable);
-    }
-
-    @VisibleForTesting
-    ThemedBatteryDrawable getBatteryDrawable() {
-        int frameColor = getContext()
-                .getColor(com.android.settingslib.R.color.meter_background_color);
-        ThemedBatteryDrawable drawable = new ThemedBatteryDrawable(getContext(), frameColor);
-        ColorFilter colorFilter = Utils.getAlphaInvariantColorFilterForColor(
-                Utils.getColorAttrDefaultColor(getContext(), android.R.attr.colorControlNormal));
-        drawable.setColorFilter(colorFilter);
-        return drawable;
+        battery.setSummary((isChanging)
+                ? getString(R.string.hotspot_battery_charging_summary, formatPercentage(percentage))
+                : formatPercentage(percentage));
     }
 }
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
index b16d336..5eee615 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.datausage;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -38,22 +39,28 @@
 import android.widget.FrameLayout;
 import android.widget.Spinner;
 
+import androidx.annotation.NonNull;
 import androidx.fragment.app.FragmentActivity;
 import androidx.loader.app.LoaderManager;
 import androidx.preference.PreferenceManager;
 
 import com.android.settings.R;
+import com.android.settings.datausage.lib.BillingCycleRepository;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.widget.LoadingViewController;
 import com.android.settingslib.NetworkPolicyEditor;
+import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.android.controller.ActivityController;
@@ -64,6 +71,8 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class DataUsageListTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock
     private MobileDataEnabledListener mMobileDataEnabledListener;
@@ -73,20 +82,23 @@
     private LoaderManager mLoaderManager;
     @Mock
     private UserManager mUserManager;
+    @Mock
+    private BillingCycleRepository mBillingCycleRepository;
 
     private Activity mActivity;
-    private DataUsageList mDataUsageList;
+
+    @Spy
+    private TestDataUsageList mDataUsageList;
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
         FakeFeatureFactory.setupForTest();
         final ActivityController<Activity> mActivityController =
                 Robolectric.buildActivity(Activity.class);
         mActivity = spy(mActivityController.get());
         mNetworkServices.mPolicyEditor = mock(NetworkPolicyEditor.class);
-        mDataUsageList = spy(DataUsageList.class);
         mDataUsageList.mDataStateListener = mMobileDataEnabledListener;
+        mDataUsageList.mTemplate = mock(NetworkTemplate.class);
 
         doReturn(mActivity).when(mDataUsageList).getContext();
         doReturn(mUserManager).when(mActivity).getSystemService(UserManager.class);
@@ -97,6 +109,7 @@
         doReturn(mLoaderManager).when(mDataUsageList).getLoaderManager();
         mDataUsageList.mLoadingViewController = mock(LoadingViewController.class);
         doNothing().when(mDataUsageList).updateSubscriptionInfoEntity();
+        when(mBillingCycleRepository.isBandwidthControlEnabled()).thenReturn(true);
     }
 
     @Test
@@ -225,15 +238,13 @@
         final View rootView = LayoutInflater.from(mActivity)
                 .inflate(R.layout.preference_list_fragment, null, false);
         final FrameLayout pinnedHeader = rootView.findViewById(R.id.pinned_header);
-        final View header = mActivity.getLayoutInflater()
-                .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
 
-        return header;
+        return mActivity.getLayoutInflater()
+                .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
     }
 
     private Spinner getSpinner(View header) {
-        final Spinner spinner = header.findViewById(R.id.filter_spinner);
-        return spinner;
+        return header.findViewById(R.id.filter_spinner);
     }
 
     @Implements(DataUsageBaseFragment.class)
@@ -242,10 +253,18 @@
         public void onCreate(Bundle icicle) {
             // do nothing
         }
+    }
 
-        @Implementation
-        protected boolean isBandwidthControlEnabled() {
-            return true;
+    public class TestDataUsageList extends DataUsageList {
+        @Override
+        protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
+            return mock(clazz);
+        }
+
+        @NonNull
+        @Override
+        BillingCycleRepository createBillingCycleRepository() {
+            return mBillingCycleRepository;
         }
     }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java
index ad4aebf..8f96e27 100644
--- a/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java
@@ -58,7 +58,6 @@
 import com.android.settings.wifi.WifiUtils;
 import com.android.settings.wifi.details2.WifiDetailPreferenceController2;
 import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.graph.ThemedBatteryDrawable;
 import com.android.wifitrackerlib.NetworkDetailsTracker;
 import com.android.wifitrackerlib.WifiEntry;
 
@@ -67,7 +66,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnit;
@@ -112,8 +110,6 @@
 
     FakeFragment mFragment;
     PreferenceScreen mScreen;
-    ArgumentCaptor<ThemedBatteryDrawable> mThemedBatteryDrawableCaptor =
-            ArgumentCaptor.forClass(ThemedBatteryDrawable.class);
 
     @Before
     public void setUp() {
@@ -290,25 +286,20 @@
     }
 
     @Test
-    public void updateBattery_hiPercentageNoCharging_setResourceCorrect() {
+    public void updateBattery_hiPercentageNoCharging_setSummaryCorrect() {
         mFragment.updateBattery(false /* isChanging */, BATTERY_PERCENTAGE_MAX);
 
         verify(mBattery).setSummary(formatPercentage(BATTERY_PERCENTAGE_MAX));
-        verify(mBattery).setIcon(mThemedBatteryDrawableCaptor.capture());
-        ThemedBatteryDrawable drawable = mThemedBatteryDrawableCaptor.getValue();
-        assertThat(drawable.getCharging()).isFalse();
-        assertThat(drawable.getBatteryLevel()).isEqualTo(BATTERY_PERCENTAGE_MAX);
     }
 
     @Test
-    public void updateBattery_lowPercentageWithCharging_setResourceCorrect() {
+    public void updateBattery_lowPercentageWithCharging_setSummaryCorrect() {
+        String summary = mContext.getString(R.string.hotspot_battery_charging_summary,
+                formatPercentage(0));
+
         mFragment.updateBattery(true /* isChanging */, 0 /* percentage */);
 
-        verify(mBattery).setSummary(formatPercentage(0));
-        verify(mBattery).setIcon(mThemedBatteryDrawableCaptor.capture());
-        ThemedBatteryDrawable drawable = mThemedBatteryDrawableCaptor.getValue();
-        assertThat(drawable.getCharging()).isTrue();
-        assertThat(drawable.getBatteryLevel()).isEqualTo(0);
+        verify(mBattery).setSummary(summary);
     }
 
     // Fake WifiNetworkDetailsFragment to override the protected method as public.
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java
similarity index 92%
rename from tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java
rename to tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java
index c009442..4dca749 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java
@@ -25,6 +25,7 @@
 import org.robolectric.annotation.Implements;
 import org.robolectric.annotation.Resetter;
 
+/** Robolectric shadow for the bluetooth utils. */
 @Implements(Utils.class)
 public class ShadowBluetoothUtils {
 
@@ -35,6 +36,7 @@
         return sLocalBluetoothManager;
     }
 
+    /** Resets the local bluetooth manager to null. */
     @Resetter
     public static void reset() {
         sLocalBluetoothManager = null;
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
index 590fe9e..0d2dcef 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -17,16 +17,22 @@
 package com.android.settings.network.apn
 
 import android.content.Context
+import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOff
 import androidx.compose.ui.test.assertIsOn
 import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isFocused
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onAllNodesWithText
 import androidx.compose.ui.test.onChild
 import androidx.compose.ui.test.onChildAt
+import androidx.compose.ui.test.onLast
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performScrollToNode
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -49,15 +55,20 @@
     private val apnType = "apn_type"
     private val apnRoaming = "IPv4"
     private val apnEnable = context.resources.getString(R.string.carrier_enabled)
-    private val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
-    private val apnData = ApnData(
-        name = apnName,
-        mmsc = mmsc,
-        mmsProxy = mmsProxy,
-        mnc = mnc,
-        apnType = apnType,
-        apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
-        apnEnable = true
+    private val apnProtocolOptions =
+        context.resources.getStringArray(R.array.apn_protocol_entries).toList()
+    private val bearer = context.resources.getString(R.string.bearer)
+    private val bearerOptions = context.resources.getStringArray(R.array.bearer_entries).toList()
+    private val apnData = mutableStateOf(
+        ApnData(
+            name = apnName,
+            mmsc = mmsc,
+            mmsProxy = mmsProxy,
+            mnc = mnc,
+            apnType = apnType,
+            apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
+            apnEnable = true
+        )
     )
 
     @Test
@@ -69,7 +80,7 @@
     fun title_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onNodeWithText(context.getString(R.string.apn_edit)).assertIsDisplayed()
@@ -79,7 +90,7 @@
     fun name_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onNodeWithText(apnName, true).assertIsDisplayed()
@@ -89,7 +100,7 @@
     fun mmsc_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
@@ -101,7 +112,7 @@
     fun mms_proxy_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
@@ -113,7 +124,7 @@
     fun mnc_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
@@ -125,7 +136,7 @@
     fun apn_type_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
@@ -137,7 +148,7 @@
     fun apn_roaming_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
@@ -149,7 +160,7 @@
     fun carrier_enabled_displayed() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
@@ -161,11 +172,73 @@
     fun carrier_enabled_isChecked() {
         composeTestRule.setContent {
             ApnPage(remember {
-                mutableStateOf(apnData)
+                apnData
             })
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(apnEnable, true))
         composeTestRule.onNodeWithText(apnEnable, true).assertIsOn()
     }
+
+    @Test
+    fun carrier_enabled_checkChanged() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                apnData
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnEnable, true))
+        composeTestRule.onNodeWithText(apnEnable, true).performClick()
+        composeTestRule.onNodeWithText(apnEnable, true).assertIsOff()
+    }
+
+    @Test
+    fun bearer_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                apnData
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(bearer, true))
+        composeTestRule.onNodeWithText(bearer, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun bearer_changed() {
+        var apnDataa: MutableState<ApnData> = apnData
+        composeTestRule.setContent {
+            apnDataa = remember {
+                apnData
+            }
+            ApnPage(apnDataa)
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(bearer, true))
+        composeTestRule.onNodeWithText(bearer, true).performClick()
+        composeTestRule.onNodeWithText(bearerOptions[1], true).performClick()
+        composeTestRule.onNode(hasText(bearerOptions[0]) and isFocused(), true).assertDoesNotExist()
+        composeTestRule.onNode(hasText(bearerOptions[1]) and isFocused(), true).assertIsDisplayed()
+    }
+
+    @Test
+    fun bearer_changed_back2Default() {
+        var apnDataa: MutableState<ApnData> = apnData
+        composeTestRule.setContent {
+            apnDataa = remember {
+                apnData
+            }
+            ApnPage(apnDataa)
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(bearer, true))
+        composeTestRule.onNodeWithText(bearer, true).performClick()
+        composeTestRule.onNodeWithText(bearerOptions[1], true).performClick()
+        composeTestRule.onNode(hasText(bearerOptions[0]) and isFocused(), true).assertDoesNotExist()
+        composeTestRule.onNode(hasText(bearerOptions[1]) and isFocused(), true).assertIsDisplayed()
+        composeTestRule.onAllNodesWithText(bearerOptions[1], true).onLast().performClick()
+        composeTestRule.onNode(hasText(bearerOptions[0]) and isFocused(), true).assertIsDisplayed()
+        composeTestRule.onNode(hasText(bearerOptions[1]) and isFocused(), true).assertDoesNotExist()
+    }
 }
\ No newline at end of file