Merge "The page id SettingsEnums.DIALOG_PREFERRED_SIM_PICKER is duplicated"
diff --git a/res/layout/battery_usage_graph.xml b/res/layout/battery_usage_graph.xml
deleted file mode 100644
index e79c7b9..0000000
--- a/res/layout/battery_usage_graph.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:settings="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingTop="16dp"
-    android:paddingStart="@dimen/preference_no_icon_padding_start"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:orientation="vertical">
-
-    <TextView
-        android:id="@+id/charge"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="16dp"
-        android:fontFamily="@*android:string/config_headlineFontFamily"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textSize="36sp"
-        android:textColor="?android:attr/colorAccent" />
-
-    <com.android.settings.widget.UsageView
-        android:id="@+id/battery_usage"
-        android:layout_width="match_parent"
-        android:layout_height="141dp"
-        android:layout_marginBottom="16dp"
-        settings:sideLabels="@array/battery_labels"
-        android:colorAccent="?android:attr/colorAccent"
-        android:gravity="end"
-        settings:textColor="?android:attr/textColorSecondary" />
-
-    <TextView
-        android:id="@+id/bottom_summary"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="16dp"
-        android:textAppearance="?android:attr/textAppearanceSmall" />
-
-</LinearLayout>
diff --git a/res/values/config.xml b/res/values/config.xml
index d7b2afa..e7efa6f 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -615,6 +615,9 @@
         <item>android.uid.system:1000</item>
     </string-array>
 
+    <!-- The default value for RedactionInterstitial in SUW -->
+    <bool name="default_allow_sensitive_lockscreen_content">true</bool>
+
     <!-- Whether to enable the app battery usage list page feature. -->
     <bool name="config_app_battery_usage_list_enabled">false</bool>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 91587a9..2cfec6e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4582,8 +4582,6 @@
     <string name="power_usage_summary_title">Battery</string>
     <!-- Activity title summary for App Fuel Gauge summary -->
     <string name="power_usage_summary">What has been using the battery</string>
-    <!-- Message to show when battery usage data is not available [CHAR LIMIT=30] -->
-    <string name="power_usage_not_available">Battery usage data isn\u2019t available.</string>
     <!-- Display the battery level and status [CHAR_LIMIT=60] -->
     <string name="power_usage_level_and_status"><xliff:g id="level">%1$s</xliff:g>
             - <xliff:g id="status">%2$s</xliff:g></string>
@@ -4631,8 +4629,6 @@
 
     <!-- Title for the screen usage in power use UI [CHAR_LIMIT=60] -->
     <string name="device_screen_usage">Screen usage since full charge</string>
-    <!-- Label for list of apps using battery in power use UI. Note: ^1 should be used in all translations[CHAR_LIMIT=120] -->
-    <string name="power_usage_list_summary">Battery usage since full charge</string>
     <!-- Temp string used to debug new battery estimates [DO NOT TRANSLATE] -->
     <string name="power_usage_enhanced_debug" translatable="false"><xliff:g id="time">%1$s</xliff:g> left (New ML est)</string>
     <!-- Temp string used to debug old battery estimates [DO NOT TRANSLATE] -->
@@ -4800,8 +4796,6 @@
     <!-- Label for power consumed by Calling -->
     <string name="power_phone">Voice calls</string>
 
-    <!-- Description for battery usage time for an app, i.e. Used for 30min. Note: ^1 should be used in all translations [CHAR LIMIT=60] -->
-    <string name="battery_used_for">Used for <xliff:g id="time">^1</xliff:g></string>
     <!-- Description for battery screen usage time for an app, i.e. Screen usage 30min. Note: ^1 should be used in all translations [CHAR LIMIT=60] -->
     <string name="battery_screen_usage">Screen usage <xliff:g id="time">^1</xliff:g></string>
     <!-- Description for battery usage info for an app, i.e. 60% used by facebook. [CHAR LIMIT=60] -->
@@ -4870,9 +4864,6 @@
     <!-- Description for other users aggregated battery usage data [CHAR LIMIT=120] -->
     <string name="battery_usage_other_users">Other users</string>
 
-    <!-- Graph subtext displayed to user when enhanced battery estimate is being used [CHAR LIMIT=120] -->
-    <string name="advanced_battery_graph_subtext">Battery left estimate is based on your device usage</string>
-
     <!-- Description for battery time left, i.e. 50min Estimated time left. [CHAR LIMIT=80]-->
     <string name="estimated_time_left">Estimated time left</string>
 
@@ -5141,6 +5132,8 @@
     <string name="adaptive_connectivity_title">Adaptive connectivity</string>
     <!-- Summary of Adaptive connectivity preference. [CHAR LIMIT=NONE] -->
     <string name="adaptive_connectivity_summary">Extends battery life and improves device performance by automatically managing your network connections</string>
+    <!-- Title for adaptive connectivity main switch preferences. [CHAR LIMIT=50] -->
+    <string name="adaptive_connectivity_main_switch_title">Use adaptive connectivity</string>
 
     <!-- Title of preference group for credential storage settings [CHAR LIMIT=30] -->
     <string name="credentials_title">Credential storage</string>
@@ -10486,8 +10479,8 @@
     <!-- Title for the button to reboot with MTE enabled. [CHAR LIMIT=NONE] -->
     <string name="reboot_with_mte_title">Reboot with MTE</string>
     <string name="reboot_with_mte_message">System will reboot and allow to experiment with Memory Tagging Extension (MTE). MTE may negatively impact system performance and stability. Will be reset on next subsequent reboot.</string>
-    <string name="reboot_with_mte_summary">Try MTE for a single boot for app development.</string>
-    <string name="reboot_with_mte_already_enabled">MTE is enabled through Advanced memory protection.</string>
+    <string name="reboot_with_mte_summary">Try MTE for a single boot for app development</string>
+    <string name="reboot_with_mte_already_enabled">MTE is enabled through Advanced memory protection</string>
     <!-- Toast that is shown when the user initiates capturing a heap dump for the system server. [CHAR LIMIT=NONE] -->
     <string name="capturing_system_heap_dump_message">Capturing system heap dump</string>
     <!-- Toast that is shown if there's an error capturing the user initiated heap dump. [CHAR LIMIT=NONE] -->
diff --git a/res/xml/adaptive_connectivity_settings.xml b/res/xml/adaptive_connectivity_settings.xml
index d5941ad..6306006 100644
--- a/res/xml/adaptive_connectivity_settings.xml
+++ b/res/xml/adaptive_connectivity_settings.xml
@@ -30,7 +30,7 @@
 
     <com.android.settingslib.widget.MainSwitchPreference
         android:key="adaptive_connectivity"
-        android:title="@string/adaptive_connectivity_title"
+        android:title="@string/adaptive_connectivity_main_switch_title"
         settings:controller="com.android.settings.network.AdaptiveConnectivityTogglePreferenceController"/>
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index 7635041..7fb818a 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -40,6 +40,9 @@
 import android.widget.CheckBox;
 import android.widget.Spinner;
 
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.core.InstrumentedFragment;
@@ -70,6 +73,7 @@
     // Arbitrary to avoid conficts
     private static final int KEYGUARD_REQUEST = 55;
 
+    private ActivityResultLauncher mActivityResultLauncher;
     private List<SubscriptionInfo> mSubscriptions;
 
     private View mContentView;
@@ -82,6 +86,10 @@
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         getActivity().setTitle(R.string.reset_network_title);
+
+        mActivityResultLauncher = registerForActivityResult(
+                new ActivityResultContracts.StartActivityForResult(),
+                result -> onActivityLauncherResult(result));
     }
 
     /**
@@ -96,20 +104,14 @@
                 new ChooseLockSettingsHelper.Builder(getActivity(), this);
         return builder.setRequestCode(request)
                 .setTitle(res.getText(R.string.reset_network_title))
+                .setActivityResultLauncher(mActivityResultLauncher)
                 .show();
     }
 
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-
-        if (requestCode != KEYGUARD_REQUEST) {
-            return;
-        }
-
+    public void onActivityLauncherResult(ActivityResult result) {
         // If the user entered a valid keyguard trace, present the final
         // confirmation prompt; otherwise, go back to the initial state.
-        if (resultCode == Activity.RESULT_OK) {
+        if (result.getResultCode() == Activity.RESULT_OK) {
             showFinalConfirmation();
         } else if (mContentView != null) {
             establishInitialState(getActiveSubscriptionInfoList());
@@ -119,14 +121,28 @@
     @VisibleForTesting
     void showFinalConfirmation() {
         Bundle args = new Bundle();
+
+        ResetNetworkRequest request = new ResetNetworkRequest(
+                ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER |
+                ResetNetworkRequest.RESET_VPN_MANAGER |
+                ResetNetworkRequest.RESET_WIFI_MANAGER |
+                ResetNetworkRequest.RESET_WIFI_P2P_MANAGER |
+                ResetNetworkRequest.RESET_BLUETOOTH_MANAGER
+        );
         if (mSubscriptions != null && mSubscriptions.size() > 0) {
             int selectedIndex = mSubscriptionSpinner.getSelectedItemPosition();
             SubscriptionInfo subscription = mSubscriptions.get(selectedIndex);
-            args.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
-                    subscription.getSubscriptionId());
+            int subId = subscription.getSubscriptionId();
+            request.setResetTelephonyAndNetworkPolicyManager(subId)
+                   .setResetApn(subId);
         }
-        args.putBoolean(MainClear.ERASE_ESIMS_EXTRA,
-                mEsimContainer.getVisibility() == View.VISIBLE && mEsimCheckbox.isChecked());
+        if (mEsimContainer.getVisibility() == View.VISIBLE && mEsimCheckbox.isChecked()) {
+            request.setResetEsim(getContext().getPackageName())
+                   .writeIntoBundle(args);
+        } else {
+            request.writeIntoBundle(args);
+        }
+
         new SubSettingLauncher(getContext())
                 .setDestination(ResetNetworkConfirm.class.getName())
                 .setArguments(args)
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index 52eb643..0cd94a5 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -56,10 +56,9 @@
     private static final String TAG = "ResetNetworkConfirm";
 
     @VisibleForTesting View mContentView;
-    @VisibleForTesting boolean mEraseEsim;
     @VisibleForTesting ResetNetworkTask mResetNetworkTask;
     @VisibleForTesting Activity mActivity;
-    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    @VisibleForTesting ResetNetworkRequest mResetNetworkRequest;
     private ProgressDialog mProgressDialog;
     private AlertDialog mAlertDialog;
     private OnSubscriptionsChangedListener mSubscriptionsChangedListener;
@@ -72,32 +71,25 @@
         private static final String TAG = "ResetNetworkTask";
 
         private final Context mContext;
-        private final String mPackageName;
 
         ResetNetworkTask(Context context) {
             mContext = context;
-            mPackageName = context.getPackageName();
         }
 
         @Override
         protected Boolean doInBackground(Void... params) {
             final AtomicBoolean resetEsimSuccess = new AtomicBoolean(true);
-            ResetNetworkOperationBuilder builder =
-                    (new ResetNetworkOperationBuilder(mContext))
-                    .resetConnectivityManager()
-                    .resetVpnManager()
-                    .resetWifiManager()
-                    .resetWifiP2pManager(Looper.getMainLooper());
-            if (mEraseEsim) {
-                builder = builder.resetEsim(mContext.getPackageName(),
+
+            String resetEsimPackageName = mResetNetworkRequest.getResetEsimPackageName();
+            ResetNetworkOperationBuilder builder = mResetNetworkRequest
+                    .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper());
+            if (resetEsimPackageName != null) {
+                // Override reset eSIM option for the result of reset operation
+                builder = builder.resetEsim(resetEsimPackageName,
                         success -> { resetEsimSuccess.set(success); }
                         );
             }
-            builder.resetTelephonyAndNetworkPolicyManager(mSubId)
-                    .resetBluetoothManager()
-                    .resetApn(mSubId)
-                    .build()
-                    .run();
+            builder.build().run();
 
             boolean isResetSucceed = resetEsimSuccess.get();
             Log.d(TAG, "network factoryReset complete. succeeded: "
@@ -138,12 +130,13 @@
             }
 
             // abandon execution if subscription no longer active
-            if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            int subId = mResetNetworkRequest.getResetApnSubId();
+            if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                 SubscriptionManager mgr = getSubscriptionManager();
                 // always remove listener
                 stopMonitorSubscriptionChange(mgr);
-                if (!isSubscriptionRemainActive(mgr, mSubId)) {
-                    Log.w(TAG, "subId " + mSubId + " disappear when confirm");
+                if (!isSubscriptionRemainActive(mgr, subId)) {
+                    Log.w(TAG, "subId " + subId + " disappear when confirm");
                     mActivity.finish();
                     return;
                 }
@@ -182,7 +175,7 @@
 
     @VisibleForTesting
     void setSubtitle() {
-        if (mEraseEsim) {
+        if (mResetNetworkRequest.getResetEsimPackageName() != null) {
             ((TextView) mContentView.findViewById(R.id.reset_network_confirm))
                     .setText(R.string.reset_network_final_desc_esim);
         }
@@ -193,6 +186,7 @@
             Bundle savedInstanceState) {
         View view = (new ResetNetworkRestrictionViewBuilder(mActivity)).build();
         if (view != null) {
+            stopMonitorSubscriptionChange(getSubscriptionManager());
             Log.w(TAG, "Access deny.");
             return view;
         }
@@ -207,15 +201,15 @@
         super.onCreate(savedInstanceState);
 
         Bundle args = getArguments();
-        if (args != null) {
-            mSubId = args.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
-                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-            mEraseEsim = args.getBoolean(MainClear.ERASE_ESIMS_EXTRA);
+        if (args == null) {
+            args = savedInstanceState;
         }
+        mResetNetworkRequest = new ResetNetworkRequest(args);
 
         mActivity = getActivity();
 
-        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (mResetNetworkRequest.getResetApnSubId()
+                == ResetNetworkRequest.INVALID_SUBSCRIPTION_ID) {
             return;
         }
         // close confirmation dialog when reset specific subscription
@@ -223,6 +217,12 @@
         startMonitorSubscriptionChange(getSubscriptionManager());
     }
 
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        mResetNetworkRequest.writeIntoBundle(outState);
+    }
+
     private SubscriptionManager getSubscriptionManager() {
         SubscriptionManager mgr = mActivity.getSystemService(SubscriptionManager.class);
         if (mgr == null) {
@@ -240,12 +240,13 @@
                 Looper.getMainLooper()) {
             @Override
             public void onSubscriptionsChanged() {
+                int subId = mResetNetworkRequest.getResetApnSubId();
                 SubscriptionManager mgr = getSubscriptionManager();
-                if (isSubscriptionRemainActive(mgr, mSubId)) {
+                if (isSubscriptionRemainActive(mgr, subId)) {
                     return;
                 }
                 // close UI if subscription no longer active
-                Log.w(TAG, "subId " + mSubId + " no longer active.");
+                Log.w(TAG, "subId " + subId + " no longer active.");
                 stopMonitorSubscriptionChange(mgr);
                 mActivity.finish();
             }
diff --git a/src/com/android/settings/ResetNetworkRequest.java b/src/com/android/settings/ResetNetworkRequest.java
new file mode 100644
index 0000000..40eebb0
--- /dev/null
+++ b/src/com/android/settings/ResetNetworkRequest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Looper;
+import android.telephony.SubscriptionManager;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.network.ResetNetworkOperationBuilder;
+
+/**
+ * A request which contains options required for resetting network.
+ */
+public class ResetNetworkRequest {
+
+    /* Reset option - nothing get reset */
+    public static final int RESET_NONE = 0x00;
+
+    /* Reset option - reset ConnectivityManager */
+    public static final int RESET_CONNECTIVITY_MANAGER = 0x01;
+
+    /* Reset option - reset VpnManager */
+    public static final int RESET_VPN_MANAGER = 0x02;
+
+    /* Reset option - reset WiFiManager */
+    public static final int RESET_WIFI_MANAGER = 0x04;
+
+    /* Reset option - reset WifiP2pManager */
+    public static final int RESET_WIFI_P2P_MANAGER = 0x08;
+
+    /* Reset option - reset BluetoothManager */
+    public static final int RESET_BLUETOOTH_MANAGER = 0x10;
+
+    /* Subscription ID for not performing reset TelephonyAndNetworkPolicy or reset APN */
+    public static final int INVALID_SUBSCRIPTION_ID = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    /* Subscription ID for performing reset TelephonyAndNetworkPolicy or reset APN
+        on all subscriptions */
+    public static final int ALL_SUBSCRIPTION_ID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+
+    /* Key within Bundle. To store some connectivity options for reset */
+    @VisibleForTesting
+    protected static final String KEY_RESET_OPTIONS = "resetNetworkOptions";
+
+    /* Key within Bundle. To store package name for resetting eSIM */
+    @VisibleForTesting
+    protected static final String KEY_ESIM_PACKAGE = "resetEsimPackage";
+
+    /**
+     * Key within Bundle. To store subscription ID for resetting
+     * telephony manager and network and network policy manager.
+     */
+    @VisibleForTesting
+    protected static final String KEY_TELEPHONY_NET_POLICY_MANAGER_SUBID =
+            "resetTelephonyNetPolicySubId";
+
+    /* Key within Bundle. To store subscription ID for resetting APN. */
+    @VisibleForTesting
+    protected static final String KEY_APN_SUBID = "resetApnSubId";
+
+    private int mResetOptions = RESET_NONE;
+    private String mResetEsimPackageName;
+    private int mResetTelephonyManager = INVALID_SUBSCRIPTION_ID;
+    private int mResetApn = INVALID_SUBSCRIPTION_ID;
+
+    /**
+     * Reconstruct based on keys stored within Bundle.
+     * @param optionsFromBundle is a Bundle which previously stored through #writeIntoBundle()
+     */
+    public ResetNetworkRequest(Bundle optionsFromBundle) {
+        if (optionsFromBundle == null) {
+            return;
+        }
+        mResetOptions = optionsFromBundle.getInt(KEY_RESET_OPTIONS, RESET_NONE);
+        mResetEsimPackageName = optionsFromBundle.getString(KEY_ESIM_PACKAGE);
+        mResetTelephonyManager = optionsFromBundle.getInt(
+                KEY_TELEPHONY_NET_POLICY_MANAGER_SUBID, INVALID_SUBSCRIPTION_ID);
+        mResetApn = optionsFromBundle.getInt(KEY_APN_SUBID, INVALID_SUBSCRIPTION_ID);
+    }
+
+    /**
+     * Construct of class
+     * @param resetOptions is a binary combination(OR logic operation) of constants
+     *         comes with RESET_ prefix. Which are the reset options comes within.
+     */
+    public ResetNetworkRequest(int resetOptions) {
+        mResetOptions = resetOptions;
+    }
+
+    /**
+     * Get the package name applied for resetting eSIM.
+     * @return package name. {@code null} means resetting eSIM is not part of the
+     *         option within this request.
+     */
+    public String getResetEsimPackageName() {
+        return mResetEsimPackageName;
+    }
+
+    /**
+     * Set the package name for resetting eSIM.
+     * @param packageName is the package name for resetting eSIM.
+     *        {@code null} will remove the resetting eSIM option out of this request.
+     * @return this request
+     */
+    public ResetNetworkRequest setResetEsim(String packageName) {
+        mResetEsimPackageName = packageName;
+        return this;
+    }
+
+    /**
+     * Get the subscription ID applied for resetting Telephony and NetworkPolicy.
+     * @return subscription ID.
+     *         {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions.
+     *         {@code INVALID_SUBSCRIPTION_ID} means
+     *         resetting Telephony and NetworkPolicy is not part of the option
+     *         within this request.
+     */
+    public int getResetTelephonyAndNetworkPolicyManager() {
+        return mResetTelephonyManager;
+    }
+
+    /**
+     * Set the subscription ID applied for resetting Telephony and NetworkPolicy.
+     * @param subscriptionId is the subscription ID referenced fron SubscriptionManager.
+     *         {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions.
+     *         {@code INVALID_SUBSCRIPTION_ID} means resetting Telephony and NetworkPolicy
+     *         will not take place.
+     * @return this request
+     */
+    public ResetNetworkRequest setResetTelephonyAndNetworkPolicyManager(int subscriptionId) {
+        mResetTelephonyManager = subscriptionId;
+        return this;
+    }
+
+    /**
+     * Get the subscription ID applied for resetting APN.
+     * @return subscription ID.
+     *         {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions.
+     *         {@code INVALID_SUBSCRIPTION_ID} means resetting APN
+     *         is not part of the option within this request.
+     */
+    public int getResetApnSubId() {
+        return mResetApn;
+    }
+
+    /**
+     * Set the subscription ID applied for resetting APN.
+     * @param subscriptionId is the subscription ID referenced fron SubscriptionManager.
+     *         {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions.
+     *         {@code INVALID_SUBSCRIPTION_ID} means resetting APN will not take place.
+     * @return this request
+     */
+    public ResetNetworkRequest setResetApn(int subscriptionId) {
+        mResetApn = subscriptionId;
+        return this;
+    }
+
+    /**
+     * Store a copy of this request into Bundle given.
+     * @param writeToBundle is a Bundle for storing configurations of this request.
+     * @return this request
+     */
+    public ResetNetworkRequest writeIntoBundle(Bundle writeToBundle) {
+        writeToBundle.putInt(KEY_RESET_OPTIONS, mResetOptions);
+        writeToBundle.putString(KEY_ESIM_PACKAGE, mResetEsimPackageName);
+        writeToBundle.putInt(KEY_TELEPHONY_NET_POLICY_MANAGER_SUBID, mResetTelephonyManager);
+        writeToBundle.putInt(KEY_APN_SUBID, mResetApn);
+        return this;
+    }
+
+    /**
+     * Build a ResetNetworkOperationBuilder based on configurations within this request.
+     * @param context required by ResetNetworkOperationBuilder
+     * @param looper required by ResetNetworkOperationBuilder for callback support
+     * @return a ResetNetworkOperationBuilder
+     */
+    public ResetNetworkOperationBuilder toResetNetworkOperationBuilder(Context context,
+            Looper looper) {
+        // Follow specific order based on previous design within file ResetNetworkConfirm.java
+        ResetNetworkOperationBuilder builder = new ResetNetworkOperationBuilder(context);
+        if ((mResetOptions & RESET_CONNECTIVITY_MANAGER) != 0) {
+            builder.resetConnectivityManager();
+        }
+        if ((mResetOptions & RESET_VPN_MANAGER) != 0) {
+            builder.resetVpnManager();
+        }
+        if ((mResetOptions & RESET_WIFI_MANAGER) != 0) {
+            builder.resetWifiManager();
+        }
+        if ((mResetOptions & RESET_WIFI_P2P_MANAGER) != 0) {
+            builder.resetWifiP2pManager(looper);
+        }
+        if (mResetEsimPackageName != null) {
+            builder.resetEsim(mResetEsimPackageName);
+        }
+        if (mResetTelephonyManager != INVALID_SUBSCRIPTION_ID) {
+            builder.resetTelephonyAndNetworkPolicyManager(mResetTelephonyManager);
+        }
+        if ((mResetOptions & RESET_BLUETOOTH_MANAGER) != 0) {
+            builder.resetBluetoothManager();
+        }
+        if (mResetApn != INVALID_SUBSCRIPTION_ID) {
+            builder.resetApn(mResetApn);
+        }
+        return builder;
+    }
+}
diff --git a/src/com/android/settings/TrustedCredentialsFragment.java b/src/com/android/settings/TrustedCredentialsFragment.java
index ca565a4..c90a44d 100644
--- a/src/com/android/settings/TrustedCredentialsFragment.java
+++ b/src/com/android/settings/TrustedCredentialsFragment.java
@@ -25,7 +25,6 @@
 import android.app.Activity;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
-import android.app.settings.SettingsEnums;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -68,7 +67,7 @@
 import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.TrustedCredentialsSettings.Tab;
-import com.android.settings.core.InstrumentedFragment;
+import com.android.settingslib.core.lifecycle.ObservableFragment;
 
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
@@ -81,7 +80,7 @@
 /**
  * Fragment to display trusted credentials settings for one tab.
  */
-public class TrustedCredentialsFragment extends InstrumentedFragment
+public class TrustedCredentialsFragment extends ObservableFragment
         implements TrustedCredentialsDialogBuilder.DelegateInterface {
 
     public static final String ARG_POSITION = "tab";
@@ -176,11 +175,6 @@
         return mFragmentView;
     }
 
-    @Override
-    public int getMetricsCategory() {
-        return SettingsEnums.TRUSTED_CREDENTIALS;
-    }
-
     private void createChildView(
             LayoutInflater inflater, ViewGroup parent, Bundle childState, int i) {
         boolean isWork = mGroupAdapter.getUserInfoByGroup(i).isManagedProfile();
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index 0bd9996..ab3716e 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -208,7 +208,7 @@
         final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule(
                 activityFilters,
                 intent,
-                true /* stickyPlaceholder */,
+                false /* stickyPlaceholder */,
                 SplitRule.FINISH_ADJACENT,
                 ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(mContext),
                 ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(mContext),
diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java
index 155583e..3261d6c 100644
--- a/src/com/android/settings/applications/AppInfoBase.java
+++ b/src/com/android/settings/applications/AppInfoBase.java
@@ -258,7 +258,7 @@
 
         @Override
         public int getMetricsCategory() {
-            return SettingsEnums.DIALOG_APP_INFO_ACTION;
+            return SettingsEnums.DIALOG_BASE_APP_INFO_ACTION;
         }
 
         @Override
diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragment.java b/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragment.java
index 6376d52..5a6d3df 100644
--- a/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragment.java
+++ b/src/com/android/settings/applications/appinfo/InstantAppButtonDialogFragment.java
@@ -49,7 +49,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.DIALOG_APP_INFO_ACTION;
+        return SettingsEnums.DIALOG_INSTANT_APP_INFO_ACTION;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/appinfo/WriteSettingsDetails.java b/src/com/android/settings/applications/appinfo/WriteSettingsDetails.java
index 4251346..d17f843 100644
--- a/src/com/android/settings/applications/appinfo/WriteSettingsDetails.java
+++ b/src/com/android/settings/applications/appinfo/WriteSettingsDetails.java
@@ -113,7 +113,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS;
+        return SettingsEnums.MODIFY_SYSTEM_SETTINGS;
     }
 
     public static CharSequence getSummary(Context context, AppEntry entry) {
diff --git a/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java b/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java
index c8f4c0b..6c59745 100644
--- a/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java
+++ b/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java
@@ -19,6 +19,7 @@
 import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE;
 
 import android.app.Dialog;
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.verify.domain.DomainOwner;
@@ -113,7 +114,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return 0;
+        return SettingsEnums.PROGRESS_DIALOG;
     }
 
     /**
diff --git a/src/com/android/settings/applications/intentpicker/SupportedLinksDialogFragment.java b/src/com/android/settings/applications/intentpicker/SupportedLinksDialogFragment.java
index dd5b746..e67fa04 100644
--- a/src/com/android/settings/applications/intentpicker/SupportedLinksDialogFragment.java
+++ b/src/com/android/settings/applications/intentpicker/SupportedLinksDialogFragment.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications.intentpicker;
 
 import android.app.Dialog;
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.verify.domain.DomainVerificationManager;
@@ -73,7 +74,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return 0;
+        return SettingsEnums.SUPPORTED_LINKS_DIALOG;
     }
 
     /** Display the dialog. */
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index fa5852e..4701d0d 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -527,7 +527,7 @@
             case LIST_TYPE_OVERLAY:
                 return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS;
             case LIST_TYPE_WRITE_SETTINGS:
-                return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS;
+                return SettingsEnums.MODIFY_SYSTEM_SETTINGS;
             case LIST_TYPE_MANAGE_SOURCES:
                 return SettingsEnums.MANAGE_EXTERNAL_SOURCES;
             case LIST_TYPE_WIFI_ACCESS:
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index 2f1fcf3..08fb5c0 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -331,7 +331,7 @@
 
     @Override
     public void onClick(LinkSpan span) {
-        if ("url".equals(span.getId())) {
+        if ("url".equals(span.getLink())) {
             String url = getString(R.string.help_url_fingerprint);
             Intent intent = HelpUtils.getHelpIntent(this, url, getClass().getName());
             if (intent == null) {
diff --git a/src/com/android/settings/biometrics2/factory/BiometricsFragmentFactory.java b/src/com/android/settings/biometrics2/factory/BiometricsFragmentFactory.java
deleted file mode 100644
index 9a0cab2..0000000
--- a/src/com/android/settings/biometrics2/factory/BiometricsFragmentFactory.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 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.biometrics2.factory;
-
-import android.app.Application;
-import android.app.admin.DevicePolicyManager;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentFactory;
-import androidx.lifecycle.ViewModelProvider;
-
-import com.android.settings.biometrics2.ui.view.FingerprintEnrollIntroFragment;
-
-/**
- * Fragment factory for biometrics
- */
-public class BiometricsFragmentFactory extends FragmentFactory {
-
-    private final Application mApplication;
-    private final ViewModelProvider mViewModelProvider;
-
-    public BiometricsFragmentFactory(Application application,
-            ViewModelProvider viewModelProvider) {
-        mApplication = application;
-        mViewModelProvider = viewModelProvider;
-    }
-
-    @NonNull
-    @Override
-    public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
-        final Class<? extends Fragment> clazz = loadFragmentClass(classLoader, className);
-        if (FingerprintEnrollIntroFragment.class.equals(clazz)) {
-            final DevicePolicyManager devicePolicyManager =
-                    mApplication.getSystemService(DevicePolicyManager.class);
-            if (devicePolicyManager != null) {
-                return new FingerprintEnrollIntroFragment(mViewModelProvider,
-                        devicePolicyManager.getResources());
-            }
-        }
-        return super.instantiate(classLoader, className);
-    }
-}
diff --git a/src/com/android/settings/biometrics2/ui/model/CredentialModel.java b/src/com/android/settings/biometrics2/ui/model/CredentialModel.java
index 06caf5e..b943608 100644
--- a/src/com/android/settings/biometrics2/ui/model/CredentialModel.java
+++ b/src/com/android/settings/biometrics2/ui/model/CredentialModel.java
@@ -22,6 +22,7 @@
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
 
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.UserHandle;
 
 import androidx.annotation.NonNull;
@@ -80,18 +81,31 @@
     @Nullable
     private Long mClearGkPwHandleMillis = null;
 
-    public CredentialModel(@NonNull Intent intent, @NonNull Clock clock) {
-        mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
-        mSensorId = intent.getIntExtra(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID);
-        mChallenge = intent.getLongExtra(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE);
-        mToken = intent.getByteArrayExtra(EXTRA_KEY_CHALLENGE_TOKEN);
-        mGkPwHandle = intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE,
-                INVALID_GK_PW_HANDLE);
+    public CredentialModel(@NonNull Bundle bundle, @NonNull Clock clock) {
+        mUserId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
+        mSensorId = bundle.getInt(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID);
+        mChallenge = bundle.getLong(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE);
+        mToken = bundle.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
+        mGkPwHandle = bundle.getLong(EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE);
         mClock = clock;
         mInitMillis = mClock.millis();
     }
 
     /**
+     * Get a bundle which can be used to recreate CredentialModel
+     */
+    @NonNull
+    public Bundle getBundle() {
+        final Bundle bundle = new Bundle();
+        bundle.putInt(Intent.EXTRA_USER_ID, mUserId);
+        bundle.putInt(EXTRA_KEY_SENSOR_ID, mSensorId);
+        bundle.putLong(EXTRA_KEY_CHALLENGE, mChallenge);
+        bundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, mToken);
+        bundle.putLong(EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle);
+        return bundle;
+    }
+
+    /**
      * Get userId for this credential
      */
     public int getUserId() {
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollIntroFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollIntroFragment.java
index 2308f2e..ecec36f 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollIntroFragment.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollIntroFragment.java
@@ -25,7 +25,7 @@
 import static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT;
 
 import android.app.Activity;
-import android.app.admin.DevicePolicyResourcesManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -44,6 +44,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.fragment.app.Fragment;
+import androidx.lifecycle.LiveData;
 import androidx.lifecycle.ViewModelProvider;
 
 import com.android.settings.R;
@@ -63,9 +64,6 @@
 
     private static final String TAG = "FingerprintEnrollIntroFragment";
 
-    @NonNull private final ViewModelProvider mViewModelProvider;
-    @Nullable private final DevicePolicyResourcesManager mDevicePolicyMgrRes;
-
     private FingerprintEnrollIntroViewModel mViewModel = null;
 
     private View mView = null;
@@ -75,12 +73,8 @@
     private TextView mFooterMessage6 = null;
     @Nullable private PorterDuffColorFilter mIconColorFilter;
 
-    public FingerprintEnrollIntroFragment(
-            @NonNull ViewModelProvider viewModelProvider,
-            @Nullable DevicePolicyResourcesManager devicePolicyMgrRes) {
+    public FingerprintEnrollIntroFragment() {
         super();
-        mViewModelProvider = viewModelProvider;
-        mDevicePolicyMgrRes = devicePolicyMgrRes;
     }
 
     @Nullable
@@ -127,15 +121,6 @@
         footerTitle2.setText(
                 R.string.security_settings_fingerprint_enroll_introduction_footer_title_2);
 
-        return mView;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-
-        final Context context = view.getContext();
-
         final TextView footerLink = mView.findViewById(R.id.footer_learn_more);
         footerLink.setMovementMethod(LinkMovementMethod.getInstance());
         final String footerLinkStr = getContext().getString(
@@ -146,18 +131,28 @@
         // footer buttons
         mPrimaryFooterButton = new FooterButton.Builder(context)
                 .setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
-                .setListener(mViewModel::onNextButtonClick)
                 .setButtonType(FooterButton.ButtonType.OPT_IN)
                 .setTheme(R.style.SudGlifButton_Primary)
                 .build();
         mSecondaryFooterButton = new FooterButton.Builder(context)
-                .setListener(mViewModel::onSkipOrCancelButtonClick)
                 .setButtonType(FooterButton.ButtonType.NEXT)
                 .setTheme(R.style.SudGlifButton_Primary)
                 .build();
         getFooterBarMixin().setPrimaryButton(mPrimaryFooterButton);
         getFooterBarMixin().setSecondaryButton(mSecondaryFooterButton, true /* usePrimaryStyle */);
 
+        return mView;
+    }
+
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        final Context context = view.getContext();
+
+        mPrimaryFooterButton.setOnClickListener(mViewModel::onNextButtonClick);
+        mSecondaryFooterButton.setOnClickListener(mViewModel::onSkipOrCancelButtonClick);
+
         if (mViewModel.canAssumeUdfps()) {
             mFooterMessage6.setVisibility(View.VISIBLE);
             mIconShield.setVisibility(View.VISIBLE);
@@ -165,7 +160,7 @@
             mFooterMessage6.setVisibility(View.GONE);
             mIconShield.setVisibility(View.GONE);
         }
-        mSecondaryFooterButton.setText(getContext(),
+        mSecondaryFooterButton.setText(context,
                 mViewModel.getEnrollmentRequest().isAfterSuwOrSuwSuggestedAction()
                 ? R.string.security_settings_fingerprint_enroll_introduction_cancel
                 : R.string.security_settings_fingerprint_enroll_introduction_no_thanks);
@@ -181,23 +176,37 @@
             setHeaderText(getActivity(),
                     R.string.security_settings_fingerprint_enroll_introduction_title);
         }
+        observePageStatusLiveDataIfNeed();
+    }
 
-        mViewModel.getPageStatusLiveData().observe(this, this::updateFooterButtons);
+    private void observePageStatusLiveDataIfNeed() {
+        final LiveData<FingerprintEnrollIntroStatus> statusLiveData =
+                mViewModel.getPageStatusLiveData();
+        final FingerprintEnrollIntroStatus status = statusLiveData.getValue();
+        if (status != null && status.hasScrollToBottom()) {
+            // Do not requireScrollWithButton() again when "I agree" or "Done" button is visible,
+            // because if we requireScrollWithButton() again, it will become "More" after scroll-up.
+            return;
+        }
 
         final RequireScrollMixin requireScrollMixin = getLayout()
                 .getMixin(RequireScrollMixin.class);
         requireScrollMixin.requireScrollWithButton(getActivity(), mPrimaryFooterButton,
                 getMoreButtonTextRes(), mViewModel::onNextButtonClick);
-        requireScrollMixin.setOnRequireScrollStateChangedListener(scrollNeeded -> {
-            if (!scrollNeeded) {
-                mViewModel.setHasScrolledToBottom();
-            }
-        });
+
+        // Always set true to setHasScrolledToBottom() before registering listener through
+        // setOnRequireScrollStateChangedListener(), because listener will not be called if first
+        // scrollNeeded is true
+        mViewModel.setHasScrolledToBottom(true);
+        requireScrollMixin.setOnRequireScrollStateChangedListener(
+                scrollNeeded -> mViewModel.setHasScrolledToBottom(!scrollNeeded));
+        statusLiveData.observe(this, this::updateFooterButtons);
     }
 
     @Override
     public void onAttach(@NonNull Context context) {
-        mViewModel = mViewModelProvider.get(FingerprintEnrollIntroViewModel.class);
+        mViewModel = new ViewModelProvider(getActivity())
+                .get(FingerprintEnrollIntroViewModel.class);
         getLifecycle().addObserver(mViewModel);
         super.onAttach(context);
     }
@@ -232,12 +241,16 @@
     private String getDescriptionDisabledByAdmin(@NonNull Context context) {
         final int defaultStrId =
                 R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled;
-        if (mDevicePolicyMgrRes == null) {
+
+        final DevicePolicyManager devicePolicyManager = getActivity()
+                .getSystemService(DevicePolicyManager.class);
+        if (devicePolicyManager != null) {
+            return devicePolicyManager.getResources().getString(FINGERPRINT_UNLOCK_DISABLED,
+                    () -> context.getString(defaultStrId));
+        } else {
             Log.w(TAG, "getDescriptionDisabledByAdmin, null device policy manager res");
             return "";
         }
-        return mDevicePolicyMgrRes.getString(FINGERPRINT_UNLOCK_DISABLED,
-                () -> context.getString(defaultStrId));
     }
 
     private void setHeaderText(@NonNull Activity activity, int resId) {
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
index e9cf6fd..1f67100 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
@@ -19,20 +19,19 @@
 import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;
 
 import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE;
 import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
 import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
+import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
+import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
 import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL;
 import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH;
 import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL;
 
-import android.app.Activity;
 import android.app.Application;
 import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.os.Bundle;
-import android.os.SystemClock;
 import android.util.Log;
 
 import androidx.activity.result.ActivityResult;
@@ -42,7 +41,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.lifecycle.viewmodel.CreationExtras;
 import androidx.lifecycle.viewmodel.MutableCreationExtras;
@@ -51,11 +49,9 @@
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
-import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollEnrolling;
+import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
 import com.android.settings.biometrics2.data.repository.FingerprintRepository;
-import com.android.settings.biometrics2.factory.BiometricsFragmentFactory;
 import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
-import com.android.settings.biometrics2.ui.model.CredentialModel;
 import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
 import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
 import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
@@ -99,15 +95,10 @@
         mViewModel = viewModelProvider.get(FingerprintEnrollmentViewModel.class);
         mViewModel.setRequest(new EnrollmentRequest(getIntent(), getApplicationContext()));
         mViewModel.setSavedInstanceState(savedInstanceState);
-        getLifecycle().addObserver(mViewModel);
 
         mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class);
-        mAutoCredentialViewModel.setCredentialModel(new CredentialModel(getIntent(),
-                SystemClock.elapsedRealtimeClock()));
-        getLifecycle().addObserver(mAutoCredentialViewModel);
-
-        mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult);
-        mAutoCredentialViewModel.getActionLiveData().observe(this, this::onCredentialAction);
+        mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
+        checkCredential();
 
         // Theme
         setTheme(mViewModel.getRequest().getTheme());
@@ -116,21 +107,35 @@
 
         // fragment
         setContentView(R.layout.biometric_enrollment_container);
-        final FragmentManager fragmentManager = getSupportFragmentManager();
-        fragmentManager.setFragmentFactory(
-                new BiometricsFragmentFactory(getApplication(), viewModelProvider));
 
-        final FingerprintEnrollIntroViewModel fingerprintEnrollIntroViewModel =
+        final FingerprintEnrollIntroViewModel introViewModel =
                 viewModelProvider.get(FingerprintEnrollIntroViewModel.class);
-        fingerprintEnrollIntroViewModel.setEnrollmentRequest(mViewModel.getRequest());
-        fingerprintEnrollIntroViewModel.setUserId(mAutoCredentialViewModel.getUserId());
-        fingerprintEnrollIntroViewModel.getActionLiveData().observe(
-                this, this::observeIntroAction);
-        final String tag = "FingerprintEnrollIntroFragment";
-        fragmentManager.beginTransaction()
-                .setReorderingAllowed(true)
-                .add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null, tag)
-                .commit();
+        introViewModel.setEnrollmentRequest(mViewModel.getRequest());
+        introViewModel.setUserId(mAutoCredentialViewModel.getUserId());
+        if (savedInstanceState == null) {
+            final String tag = "FingerprintEnrollIntroFragment";
+            getSupportFragmentManager().beginTransaction()
+                    .setReorderingAllowed(true)
+                    .add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null,
+                            tag)
+                    .commit();
+        }
+
+        // observe LiveData
+        getLifecycle().addObserver(mViewModel);
+        mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult);
+
+        mAutoCredentialViewModel.getGenerateChallengeFailedLiveData().observe(this,
+                this::onGenerateChallengeFailed);
+
+        // Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
+        // recreate, like press 'I agree' then press 'back' in FingerprintEnrollFindSensor activity.
+        introViewModel.clearActionLiveData();
+        introViewModel.getActionLiveData().observe(this, this::observeIntroAction);
+    }
+
+    private void onGenerateChallengeFailed(@NonNull Boolean ignoredBoolean) {
+        onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
     }
 
     private void onSetActivityResult(@NonNull ActivityResult result) {
@@ -141,10 +146,10 @@
         finish();
     }
 
-    private void onCredentialAction(@NonNull Integer action) {
-        switch (action) {
+    private void checkCredential() {
+        switch (mAutoCredentialViewModel.checkCredential()) {
             case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: {
-                final Intent intent = mAutoCredentialViewModel.getChooseLockIntent(this,
+                final Intent intent = mAutoCredentialViewModel.createChooseLockIntent(this,
                         mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras());
                 if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
                     Log.w(TAG, "chooseLock, fail to set isWaiting flag to true");
@@ -153,7 +158,7 @@
                 return;
             }
             case CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK: {
-                final boolean launched = mAutoCredentialViewModel.getConfirmLockLauncher(
+                final boolean launched = mAutoCredentialViewModel.createConfirmLockLauncher(
                         this,
                         LAUNCH_CONFIRM_LOCK_ACTIVITY,
                         getString(R.string.security_settings_fingerprint_preference_title)
@@ -168,12 +173,9 @@
                 }
                 return;
             }
-            case CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE: {
-                Log.w(TAG, "observeCredentialLiveData, finish with action:" + action);
-                if (mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()) {
-                    setResult(Activity.RESULT_CANCELED);
-                }
-                finish();
+            case CREDENTIAL_VALID:
+            case CREDENTIAL_IS_GENERATING_CHALLENGE: {
+                // Do nothing
             }
         }
     }
@@ -186,10 +188,15 @@
         if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult(
                 isChooseLock, activityResult)) {
             overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
+        } else {
+            onSetActivityResult(activityResult);
         }
     }
 
-    private void observeIntroAction(@NonNull Integer action) {
+    private void observeIntroAction(@Nullable Integer action) {
+        if (action == null) {
+            return;
+        }
         switch (action) {
             case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
                 onSetActivityResult(
@@ -207,9 +214,9 @@
                     Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
                 }
                 final Intent intent = new Intent(this, isSuw
-                        ? SetupFingerprintEnrollEnrolling.class
+                        ? SetupFingerprintEnrollFindSensor.class
                         : FingerprintEnrollFindSensor.class);
-                intent.putExtras(mAutoCredentialViewModel.getCredentialBundle());
+                intent.putExtras(mAutoCredentialViewModel.createCredentialIntentExtra());
                 intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
                 mNextActivityLauncher.launch(intent);
             }
@@ -272,5 +279,6 @@
     protected void onSaveInstanceState(@NonNull Bundle outState) {
         super.onSaveInstanceState(outState);
         mViewModel.onSaveInstanceState(outState);
+        mAutoCredentialViewModel.onSaveInstanceState(outState);
     }
 }
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java
index b1a7f90..3b7a436 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java
@@ -30,20 +30,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.util.Log;
 
 import androidx.activity.result.ActivityResult;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.settings.biometrics.BiometricUtils;
+import com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
 import com.android.settings.biometrics2.data.repository.FingerprintRepository;
 import com.android.settings.biometrics2.ui.model.CredentialModel;
 import com.android.settings.password.ChooseLockGeneric;
@@ -57,31 +58,40 @@
  * AutoCredentialViewModel which uses CredentialModel to determine next actions for activity, like
  * start ChooseLockActivity, start ConfirmLockActivity, GenerateCredential, or do nothing.
  */
-public class AutoCredentialViewModel extends AndroidViewModel implements DefaultLifecycleObserver {
+public class AutoCredentialViewModel extends AndroidViewModel {
 
     private static final String TAG = "AutoCredentialViewModel";
-    private static final boolean DEBUG = true;
+
+    @VisibleForTesting
+    static final String KEY_CREDENTIAL_MODEL = "credential_model";
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * Valid credential, activity does nothing.
+     */
+    public static final int CREDENTIAL_VALID = 0;
+
+    /**
+     * This credential looks good, but still need to run generateChallenge().
+     */
+    public static final int CREDENTIAL_IS_GENERATING_CHALLENGE = 1;
 
     /**
      * Need activity to run choose lock
      */
-    public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 1;
+    public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 2;
 
     /**
      * Need activity to run confirm lock
      */
-    public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 2;
-
-    /**
-     * Fail to use challenge from hardware generateChallenge(), shall finish activity with proper
-     * error code
-     */
-    public static final int CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE = 3;
+    public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 3;
 
     @IntDef(prefix = { "CREDENTIAL_" }, value = {
+            CREDENTIAL_VALID,
+            CREDENTIAL_IS_GENERATING_CHALLENGE,
             CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK,
-            CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK,
-            CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE
+            CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CredentialAction {}
@@ -157,11 +167,10 @@
         }
     }
 
-
     @NonNull private final LockPatternUtils mLockPatternUtils;
     @NonNull private final ChallengeGenerator mChallengeGenerator;
     private CredentialModel mCredentialModel = null;
-    @NonNull private final MutableLiveData<Integer> mActionLiveData =
+    @NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailedLiveData =
             new MutableLiveData<>();
 
     public AutoCredentialViewModel(
@@ -173,51 +182,65 @@
         mChallengeGenerator = challengeGenerator;
     }
 
-    public void setCredentialModel(@NonNull CredentialModel credentialModel) {
-        mCredentialModel = credentialModel;
+    /**
+     * Set CredentialModel, the source is coming from savedInstanceState or activity intent
+     */
+    public void setCredentialModel(@Nullable Bundle savedInstanceState, @NonNull Intent intent) {
+        final Bundle bundle;
+        if (savedInstanceState != null) {
+            bundle = savedInstanceState.getBundle(KEY_CREDENTIAL_MODEL);
+        } else {
+            bundle = intent.getExtras();
+        }
+        mCredentialModel = new CredentialModel(bundle, SystemClock.elapsedRealtimeClock());
+
+        if (DEBUG) {
+            Log.d(TAG, "setCredentialModel " + mCredentialModel + ", savedInstanceState exist:"
+                    + (savedInstanceState != null));
+        }
     }
 
     /**
-     * Observe ActionLiveData for actions about choosing lock, confirming lock, or finishing
-     * activity
+     * Handle onSaveInstanceState from activity
      */
-    @NonNull
-    public LiveData<Integer> getActionLiveData() {
-        return mActionLiveData;
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle());
     }
 
-    @Override
-    public void onCreate(@NonNull LifecycleOwner owner) {
-        checkCredential();
+    @NonNull
+    public LiveData<Boolean> getGenerateChallengeFailedLiveData() {
+        return mGenerateChallengeFailedLiveData;
     }
 
     /**
      * Check credential status for biometric enrollment.
      */
-    private void checkCredential() {
+    @CredentialAction
+    public int checkCredential() {
         if (isValidCredential()) {
-            return;
+            return CREDENTIAL_VALID;
         }
         final long gkPwHandle = mCredentialModel.getGkPwHandle();
         if (isUnspecifiedPassword()) {
-            mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
+            return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
         } else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
             generateChallenge(gkPwHandle);
+            return CREDENTIAL_IS_GENERATING_CHALLENGE;
         } else {
-            mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
+            return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
         }
     }
 
     private void generateChallenge(long gkPwHandle) {
         mChallengeGenerator.setCallback((sensorId, userId, challenge) -> {
-            mCredentialModel.setSensorId(sensorId);
-            mCredentialModel.setChallenge(challenge);
             try {
                 final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId);
+                mCredentialModel.setSensorId(sensorId);
+                mCredentialModel.setChallenge(challenge);
                 mCredentialModel.setToken(newToken);
             } catch (IllegalStateException e) {
                 Log.e(TAG, "generateChallenge, IllegalStateException", e);
-                mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE);
+                mGenerateChallengeFailedLiveData.postValue(true);
                 return;
             }
 
@@ -231,7 +254,7 @@
             // Check credential again
             if (!isValidCredential()) {
                 Log.w(TAG, "generateChallenge, invalid Credential");
-                mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE);
+                mGenerateChallengeFailedLiveData.postValue(true);
             }
         });
         mChallengeGenerator.generateChallenge(getUserId());
@@ -282,16 +305,16 @@
         final VerifyCredentialResponse response = mLockPatternUtils
                 .verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId);
         if (!response.isMatched()) {
-            throw new IllegalStateException("Unable to request Gatekeeper HAT");
+            throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT");
         }
         return response.getGatekeeperHAT();
     }
 
     /**
-     * Get Credential bundle which will be used to launch next activity.
+     * Get Credential intent extra which will be used to launch next activity.
      */
     @NonNull
-    public Bundle getCredentialBundle() {
+    public Bundle createCredentialIntentExtra() {
         final Bundle retBundle = new Bundle();
         final long gkPwHandle = mCredentialModel.getGkPwHandle();
         if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
@@ -311,10 +334,10 @@
     }
 
     /**
-     * Get Intent for choosing lock
+     * Create Intent for choosing lock
      */
     @NonNull
-    public Intent getChooseLockIntent(@NonNull Context context, boolean isSuw,
+    public Intent createChooseLockIntent(@NonNull Context context, boolean isSuw,
             @NonNull Bundle suwExtras) {
         final Intent intent = BiometricUtils.getChooseLockIntent(context, isSuw,
                 suwExtras);
@@ -331,10 +354,10 @@
     }
 
     /**
-     * Get ConfirmLockLauncher
+     * Create ConfirmLockLauncher
      */
     @NonNull
-    public ChooseLockSettingsHelper getConfirmLockLauncher(@NonNull Activity activity,
+    public ChooseLockSettingsHelper createConfirmLockLauncher(@NonNull Activity activity,
             int requestCode, @NonNull String title) {
         final ChooseLockSettingsHelper.Builder builder =
                 new ChooseLockSettingsHelper.Builder(activity);
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModel.java
index 252a508..72611a7 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModel.java
@@ -145,6 +145,13 @@
     }
 
     /**
+     * Clear user's action live data
+     */
+    public void clearActionLiveData() {
+        mActionLiveData.setValue(null);
+    }
+
+    /**
      * Get user's action live data (like clicking Agree, Skip, or Done)
      */
     public LiveData<Integer> getActionLiveData() {
@@ -161,8 +168,8 @@
     /**
      * Update onboarding intro page has scrolled to bottom
      */
-    public void setHasScrolledToBottom() {
-        mHasScrolledToBottomLiveData.postValue(true);
+    public void setHasScrolledToBottom(boolean value) {
+        mHasScrolledToBottomLiveData.postValue(value);
     }
 
     /**
diff --git a/src/com/android/settings/development/RebootWithMteDialog.java b/src/com/android/settings/development/RebootWithMteDialog.java
index b6522a9..858cd8f 100644
--- a/src/com/android/settings/development/RebootWithMteDialog.java
+++ b/src/com/android/settings/development/RebootWithMteDialog.java
@@ -52,7 +52,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.REBOOT_WITH_MTE;
+        return SettingsEnums.REBOOT_WITH_MTE_DIALOG;
     }
 
     @Override
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeForget.java b/src/com/android/settings/deviceinfo/PrivateVolumeForget.java
index bd0772c..759c7d8 100644
--- a/src/com/android/settings/deviceinfo/PrivateVolumeForget.java
+++ b/src/com/android/settings/deviceinfo/PrivateVolumeForget.java
@@ -48,7 +48,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.DEVICEINFO_STORAGE;
+        return SettingsEnums.PRIVATE_VOLUME_FORGET;
     }
 
     @Override
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeFormat.java b/src/com/android/settings/deviceinfo/PrivateVolumeFormat.java
index dadff3c..3820eb6 100644
--- a/src/com/android/settings/deviceinfo/PrivateVolumeFormat.java
+++ b/src/com/android/settings/deviceinfo/PrivateVolumeFormat.java
@@ -44,7 +44,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.DEVICEINFO_STORAGE;
+        return SettingsEnums.PRIVATE_VOLUME_FORMAT;
     }
 
     @Override
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeUnmount.java b/src/com/android/settings/deviceinfo/PrivateVolumeUnmount.java
index 738c3ca..dccb12d 100644
--- a/src/com/android/settings/deviceinfo/PrivateVolumeUnmount.java
+++ b/src/com/android/settings/deviceinfo/PrivateVolumeUnmount.java
@@ -40,7 +40,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.DEVICEINFO_STORAGE;
+        return SettingsEnums.PRIVATE_VOLUME_UNMOUNT;
     }
 
     @Override
diff --git a/src/com/android/settings/deviceinfo/PublicVolumeSettings.java b/src/com/android/settings/deviceinfo/PublicVolumeSettings.java
index d43b254..4fd63af 100644
--- a/src/com/android/settings/deviceinfo/PublicVolumeSettings.java
+++ b/src/com/android/settings/deviceinfo/PublicVolumeSettings.java
@@ -95,7 +95,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.DEVICEINFO_STORAGE;
+        return SettingsEnums.PUBLIC_VOLUME_SETTINGS;
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index 7a33a62..2faacb7 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -20,10 +20,8 @@
 import android.content.Intent;
 import android.util.SparseIntArray;
 
-import com.android.settings.fuelgauge.batteryusage.BatteryHistEntry;
 import com.android.settingslib.fuelgauge.Estimate;
 
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -104,12 +102,6 @@
     String getOldEstimateDebugString(String timeRemaining);
 
     /**
-     * Returns the string to show in the advanced usage battery page when enhanced estimates are
-     * enabled. This string notifies users that the estimate is using enhanced prediction.
-     */
-    String getAdvancedUsageScreenInfoString();
-
-    /**
      * Returns a signal to indicate if the device will need to warn the user they may not make it
      * to their next charging time.
      *
@@ -144,11 +136,6 @@
     Intent getResumeChargeIntent();
 
     /**
-     * Returns battery history data with corresponding timestamp key.
-     */
-    Map<Long, Map<String, BatteryHistEntry>> getBatteryHistory(Context context);
-
-    /**
      * Returns {@link Set} for hidding applications background usage time.
      */
     Set<CharSequence> getHideBackgroundUsageTimeSet(Context context);
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index 3fe4275..ff1edec 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -24,10 +24,8 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.settings.fuelgauge.batteryusage.BatteryHistEntry;
 import com.android.settingslib.fuelgauge.Estimate;
 
-import java.util.Map;
 import java.util.Set;
 
 /** Implementation of {@code PowerUsageFeatureProvider} */
@@ -123,11 +121,6 @@
     }
 
     @Override
-    public String getAdvancedUsageScreenInfoString() {
-        return null;
-    }
-
-    @Override
     public boolean getEarlyWarningSignal(Context context, String id) {
         return false;
     }
@@ -159,11 +152,6 @@
     }
 
     @Override
-    public Map<Long, Map<String, BatteryHistEntry>> getBatteryHistory(Context context) {
-        return null;
-    }
-
-    @Override
     public Set<CharSequence> getHideBackgroundUsageTimeSet(Context context) {
         return new ArraySet<>();
     }
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
index 7382299..0eca3a6 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
@@ -167,7 +167,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return 0;
+        return SettingsEnums.FUELGAUGE_BATTERY_SAVER_SCHEDULE;
     }
 
     private void logPowerSaver() {
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
index d4c00a4..5fd3905 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -43,7 +43,6 @@
 import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
 import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
 
-import java.text.NumberFormat;
 import java.util.List;
 
 /**
@@ -142,29 +141,6 @@
                         .setPositiveButton(R.string.battery_tip_unrestrict_app_dialog_ok, this)
                         .setNegativeButton(R.string.battery_tip_unrestrict_app_dialog_cancel, null)
                         .create();
-            case BatteryTip.TipType.BATTERY_DEFENDER:
-                mMetricsFeatureProvider.action(context,
-                        SettingsEnums.ACTION_TIP_BATTERY_DEFENDER, mMetricsKey);
-                final double chargeLimitLevel = 0.8f;
-                final String percentage =
-                        NumberFormat.getPercentInstance().format(chargeLimitLevel);
-                final String message = context.getString(
-                        R.string.battery_tip_limited_temporarily_dialog_msg, percentage);
-                final boolean isPluggedIn = isPluggedIn();
-                final AlertDialog.Builder dialogBuilder =
-                        new AlertDialog.Builder(context)
-                                .setTitle(R.string.battery_tip_limited_temporarily_title)
-                                .setMessage(message);
-                if (isPluggedIn) {
-                    dialogBuilder
-                            .setPositiveButton(
-                                    R.string.battery_tip_limited_temporarily_dialog_resume_charge,
-                                    this)
-                            .setNegativeButton(R.string.okay, null);
-                } else {
-                    dialogBuilder.setPositiveButton(R.string.okay, null);
-                }
-                return dialogBuilder.create();
             default:
                 throw new IllegalArgumentException("unknown type " + mBatteryTip.getType());
         }
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java
deleted file mode 100644
index 824b6be..0000000
--- a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2020 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.fuelgauge.batterytip.actions;
-
-import android.content.Intent;
-
-import com.android.settings.SettingsActivity;
-import com.android.settings.overlay.FeatureFactory;
-
-/**
- * Action to open the Support Center article
- */
-public class BatteryDefenderAction extends BatteryTipAction {
-    private SettingsActivity mSettingsActivity;
-
-    public BatteryDefenderAction(SettingsActivity settingsActivity) {
-        super(settingsActivity.getApplicationContext());
-        mSettingsActivity = settingsActivity;
-    }
-
-    @Override
-    public void handlePositiveAction(int metricsKey) {
-        final Intent intent = FeatureFactory.getFactory(mContext)
-                .getPowerUsageFeatureProvider(mContext).getResumeChargeIntent();
-        if (intent != null) {
-            mContext.sendBroadcast(intent);
-        }
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
index af67610..cb870f4 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
@@ -70,9 +70,6 @@
         }
     }
 
-    public static final int MSG_UPDATE_NAME_ICON = 1;
-    public static final int MSG_REPORT_FULLY_DRAWN = 2;
-
     private static final String TAG = "BatteryEntry";
     private static final String PACKAGE_SYSTEM = "android";
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
index e376d85..2dba3c2 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
@@ -52,7 +52,6 @@
     private static final String KEY_REFRESH_TYPE = "refresh_type";
     private static final String KEY_BATTERY_GRAPH = "battery_graph";
     private static final String KEY_APP_LIST = "app_list";
-    private static final int LOADER_BATTERY_USAGE_STATS = 2;
 
     @VisibleForTesting
     BatteryHistoryPreference mHistPref;
@@ -165,7 +164,7 @@
         bundle.putInt(KEY_REFRESH_TYPE, refreshType);
         if (!mIsChartDataLoaded) {
             mIsChartDataLoaded = true;
-            getLoaderManager().restartLoader(LOADER_BATTERY_USAGE_STATS, bundle,
+            restartLoader(LoaderIndex.BATTERY_HISTORY_LOADER, bundle,
                     mBatteryHistoryLoaderCallbacks);
         }
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBase.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBase.java
index ccefdf2..ed3a921 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBase.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBase.java
@@ -24,6 +24,7 @@
 import android.os.UserManager;
 import android.util.Log;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.loader.app.LoaderManager;
@@ -33,17 +34,19 @@
 import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
 import com.android.settings.fuelgauge.BatteryUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Common base class for things that need to show the battery usage graph.
  */
 public abstract class PowerUsageBase extends DashboardFragment {
-
     private static final String TAG = "PowerUsageBase";
-    private static final String KEY_REFRESH_TYPE = "refresh_type";
-    private static final String KEY_INCLUDE_HISTORY = "include_history";
 
-    private static final int LOADER_BATTERY_USAGE_STATS = 1;
-
+    @VisibleForTesting
+    static final String KEY_REFRESH_TYPE = "refresh_type";
+    @VisibleForTesting
+    static final String KEY_INCLUDE_HISTORY = "include_history";
     @VisibleForTesting
     BatteryUsageStats mBatteryUsageStats;
 
@@ -55,6 +58,21 @@
     final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks =
             new BatteryUsageStatsLoaderCallbacks();
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            LoaderIndex.BATTERY_USAGE_STATS_LOADER,
+            LoaderIndex.BATTERY_INFO_LOADER,
+            LoaderIndex.BATTERY_TIP_LOADER,
+            LoaderIndex.BATTERY_HISTORY_LOADER
+
+    })
+    public @interface LoaderIndex {
+        int BATTERY_USAGE_STATS_LOADER = 0;
+        int BATTERY_INFO_LOADER = 1;
+        int BATTERY_TIP_LOADER = 2;
+        int BATTERY_HISTORY_LOADER = 3;
+    }
+
     @Override
     public void onAttach(Activity activity) {
         super.onAttach(activity);
@@ -91,10 +109,28 @@
         final Bundle bundle = new Bundle();
         bundle.putInt(KEY_REFRESH_TYPE, refreshType);
         bundle.putBoolean(KEY_INCLUDE_HISTORY, isBatteryHistoryNeeded());
-        getLoaderManager().restartLoader(LOADER_BATTERY_USAGE_STATS, bundle,
+        restartLoader(LoaderIndex.BATTERY_USAGE_STATS_LOADER, bundle,
                 mBatteryUsageStatsLoaderCallbacks);
     }
 
+    protected LoaderManager getLoaderManagerForCurrentFragment() {
+        return LoaderManager.getInstance(this);
+    }
+
+    protected void restartLoader(int loaderId, Bundle bundle,
+            LoaderManager.LoaderCallbacks<?> loaderCallbacks) {
+        LoaderManager loaderManager = getLoaderManagerForCurrentFragment();
+        Loader<?> loader = loaderManager.getLoader(
+                loaderId);
+        if (loader != null && !loader.isReset()) {
+            loaderManager.restartLoader(loaderId, bundle,
+                    loaderCallbacks);
+        } else {
+            loaderManager.initLoader(loaderId, bundle,
+                    loaderCallbacks);
+        }
+    }
+
     protected void onLoadFinished(@BatteryUpdateType int refreshType) {
         refreshUi(refreshType);
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummary.java
index bca32a7..f266492 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummary.java
@@ -65,11 +65,6 @@
     static final String KEY_BATTERY_USAGE = "battery_usage_summary";
 
     @VisibleForTesting
-    static final int BATTERY_INFO_LOADER = 1;
-    @VisibleForTesting
-    static final int BATTERY_TIP_LOADER = 2;
-
-    @VisibleForTesting
     PowerUsageFeatureProvider mPowerFeatureProvider;
     @VisibleForTesting
     BatteryUtils mBatteryUtils;
@@ -241,7 +236,7 @@
 
     @VisibleForTesting
     void restartBatteryTipLoader() {
-        getLoaderManager().restartLoader(BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks);
+        restartLoader(LoaderIndex.BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks);
     }
 
     @VisibleForTesting
@@ -274,8 +269,7 @@
         if (!mIsBatteryPresent) {
             return;
         }
-        getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
-                mBatteryInfoLoaderCallbacks);
+        restartLoader(LoaderIndex.BATTERY_INFO_LOADER, Bundle.EMPTY, mBatteryInfoLoaderCallbacks);
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/network/InternetResetHelper.java b/src/com/android/settings/network/InternetResetHelper.java
index 086ef1b..7920cca 100644
--- a/src/com/android/settings/network/InternetResetHelper.java
+++ b/src/com/android/settings/network/InternetResetHelper.java
@@ -21,14 +21,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.wifi.WifiManager;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.annotation.UiThread;
 import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.OnLifecycleEvent;
@@ -38,14 +33,14 @@
 import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager;
 import com.android.settingslib.utils.HandlerInjector;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Helper class to restart connectivity for all requested subsystems.
  */
-public class InternetResetHelper implements LifecycleObserver,
-        ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback {
+public class InternetResetHelper implements LifecycleObserver {
 
     protected static final String TAG = "InternetResetHelper";
     public static final long RESTART_TIMEOUT_MS = 15_000; // 15 seconds
@@ -61,41 +56,40 @@
     protected final IntentFilter mWifiStateFilter;
     protected final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
         @Override
-        @WorkerThread
         public void onReceive(Context context, Intent intent) {
-            if (intent != null && TextUtils.equals(intent.getAction(),
-                    WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                updateWifiStateChange();
-            }
+            updateWifiStateChange();
         }
     };
 
-    protected ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager;
-    protected HandlerThread mWorkerThread;
-    protected boolean mIsRecoveryReady;
-    protected boolean mIsWifiReady;
+    protected RecoveryWorker mRecoveryWorker;
+    protected boolean mIsWifiReady = true;
     protected HandlerInjector mHandlerInjector;
-    protected final Runnable mResumeRunnable = () -> {
-        resumePreferences();
-    };
     protected final Runnable mTimeoutRunnable = () -> {
-        mIsRecoveryReady = true;
+        Log.w(TAG, "Resume preferences due to connectivity subsystems recovery timed out.");
+        mRecoveryWorker.clearRecovering();
         mIsWifiReady = true;
         resumePreferences();
     };
 
-    public InternetResetHelper(Context context, Lifecycle lifecycle) {
+    public InternetResetHelper(Context context, Lifecycle lifecycle,
+            NetworkMobileProviderController mobileNetworkController,
+            Preference wifiTogglePreferences,
+            PreferenceCategory connectedWifiEntryPreferenceCategory,
+            PreferenceCategory firstWifiEntryPreferenceCategory,
+            PreferenceCategory wifiEntryPreferenceCategory,
+            Preference resettingPreference) {
         mContext = context;
+        mMobileNetworkController = mobileNetworkController;
+        mWifiTogglePreferences = wifiTogglePreferences;
+        mWifiNetworkPreferences.add(connectedWifiEntryPreferenceCategory);
+        mWifiNetworkPreferences.add(firstWifiEntryPreferenceCategory);
+        mWifiNetworkPreferences.add(wifiEntryPreferenceCategory);
+        mResettingPreference = resettingPreference;
+
         mHandlerInjector = new HandlerInjector(context.getMainThreadHandler());
         mWifiManager = mContext.getSystemService(WifiManager.class);
         mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-
-        mWorkerThread = new HandlerThread(TAG
-                + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
-                Process.THREAD_PRIORITY_BACKGROUND);
-        mWorkerThread.start();
-        mConnectivitySubsystemsRecoveryManager = new ConnectivitySubsystemsRecoveryManager(
-                mContext, mWorkerThread.getThreadHandler());
+        mRecoveryWorker = RecoveryWorker.getInstance(mContext, this);
 
         if (lifecycle != null) {
             lifecycle.addObserver(this);
@@ -118,72 +112,18 @@
     /** @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) */
     @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
     public void onDestroy() {
-        mHandlerInjector.removeCallbacks(mResumeRunnable);
         mHandlerInjector.removeCallbacks(mTimeoutRunnable);
-        mWorkerThread.quit();
-    }
-
-    @Override
-    @WorkerThread
-    public void onSubsystemRestartOperationBegin() {
-        Log.d(TAG, "The connectivity subsystem is starting for recovery.");
-    }
-
-    @Override
-    @WorkerThread
-    public void onSubsystemRestartOperationEnd() {
-        Log.d(TAG, "The connectivity subsystem is done for recovery.");
-        if (!mIsRecoveryReady) {
-            mIsRecoveryReady = true;
-            mHandlerInjector.postDelayed(mResumeRunnable, 0 /* delayMillis */);
-        }
     }
 
     @VisibleForTesting
-    @WorkerThread
     protected void updateWifiStateChange() {
         if (!mIsWifiReady && mWifiManager.isWifiEnabled()) {
             Log.d(TAG, "The Wi-Fi subsystem is done for recovery.");
             mIsWifiReady = true;
-            mHandlerInjector.postDelayed(mResumeRunnable, 0 /* delayMillis */);
+            resumePreferences();
         }
     }
 
-    /**
-     * Sets the resetting preference.
-     */
-    @UiThread
-    public void setResettingPreference(Preference preference) {
-        mResettingPreference = preference;
-    }
-
-    /**
-     * Sets the mobile network controller.
-     */
-    @UiThread
-    public void setMobileNetworkController(NetworkMobileProviderController controller) {
-        mMobileNetworkController = controller;
-    }
-
-    /**
-     * Sets the Wi-Fi toggle preference.
-     */
-    @UiThread
-    public void setWifiTogglePreference(Preference preference) {
-        mWifiTogglePreferences = preference;
-    }
-
-    /**
-     * Adds the Wi-Fi network preference.
-     */
-    @UiThread
-    public void addWifiNetworkPreference(PreferenceCategory preference) {
-        if (preference != null) {
-            mWifiNetworkPreferences.add(preference);
-        }
-    }
-
-    @UiThread
     protected void suspendPreferences() {
         Log.d(TAG, "Suspend the subsystem preferences");
         if (mMobileNetworkController != null) {
@@ -201,9 +141,9 @@
         }
     }
 
-    @UiThread
     protected void resumePreferences() {
-        if (mIsRecoveryReady && mMobileNetworkController != null) {
+        boolean isRecoveryReady = !mRecoveryWorker.isRecovering();
+        if (isRecoveryReady && mMobileNetworkController != null) {
             Log.d(TAG, "Resume the Mobile Network controller");
             mMobileNetworkController.hidePreference(false /* hide */, true /* immediately */);
         }
@@ -214,7 +154,7 @@
                 pref.setVisible(true);
             }
         }
-        if (mIsRecoveryReady && mIsWifiReady) {
+        if (isRecoveryReady && mIsWifiReady) {
             mHandlerInjector.removeCallbacks(mTimeoutRunnable);
             if (mResettingPreference != null) {
                 Log.d(TAG, "Resume the Resetting preference");
@@ -223,21 +163,99 @@
         }
     }
 
-    /**
-     * Restart connectivity for all requested subsystems.
-     */
-    @UiThread
+    protected void showResettingAndSendTimeoutChecks() {
+        suspendPreferences();
+        mHandlerInjector.postDelayed(mTimeoutRunnable, RESTART_TIMEOUT_MS);
+    }
+
+    /** Restart connectivity for all requested subsystems. */
     public void restart() {
-        if (!mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()) {
+        if (!mRecoveryWorker.isRecoveryAvailable()) {
             Log.e(TAG, "The connectivity subsystem is not available to restart.");
             return;
         }
-
-        Log.d(TAG, "The connectivity subsystem is restarting for recovery.");
-        suspendPreferences();
-        mIsRecoveryReady = false;
+        showResettingAndSendTimeoutChecks();
         mIsWifiReady = !mWifiManager.isWifiEnabled();
-        mHandlerInjector.postDelayed(mTimeoutRunnable, RESTART_TIMEOUT_MS);
-        mConnectivitySubsystemsRecoveryManager.triggerSubsystemRestart(null /* reason */, this);
+        mRecoveryWorker.triggerRestart();
+    }
+
+    /** Check if the connectivity subsystem is under recovering. */
+    public void checkRecovering() {
+        if (!mRecoveryWorker.isRecovering()) return;
+        mIsWifiReady = false;
+        showResettingAndSendTimeoutChecks();
+    }
+
+    /**
+     * This is a singleton class for ConnectivitySubsystemsRecoveryManager worker.
+     */
+    @VisibleForTesting
+    public static class RecoveryWorker implements
+            ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback {
+        private static final String TAG = "RecoveryWorker";
+        private static RecoveryWorker sInstance;
+        private static WeakReference<InternetResetHelper> sCallback;
+        private static ConnectivitySubsystemsRecoveryManager sRecoveryManager;
+        private static boolean sIsRecovering;
+
+        /**
+         * Create a singleton class for ConnectivitySubsystemsRecoveryManager.
+         *
+         * @param context  The context to use for the content resolver.
+         * @param callback The callback of {@link InternetResetHelper} object.
+         * @return an instance of {@link RecoveryWorker} object.
+         */
+        public static RecoveryWorker getInstance(Context context, InternetResetHelper callback) {
+            sCallback = new WeakReference<>(callback);
+            if (sInstance != null) return sInstance;
+
+            sInstance = new RecoveryWorker();
+            Context appContext = context.getApplicationContext();
+            sRecoveryManager = new ConnectivitySubsystemsRecoveryManager(appContext,
+                    appContext.getMainThreadHandler());
+            return sInstance;
+        }
+
+        /** Returns true, If the subsystem service is recovering. */
+        public boolean isRecovering() {
+            return sIsRecovering;
+        }
+
+        /** Clear the recovering flag. */
+        public void clearRecovering() {
+            sIsRecovering = false;
+        }
+
+        /** Returns true, If the subsystem service is recovery available. */
+        public boolean isRecoveryAvailable() {
+            return sRecoveryManager.isRecoveryAvailable();
+        }
+
+        /** Trigger connectivity recovery for all requested technologies. */
+        public boolean triggerRestart() {
+            if (!isRecoveryAvailable()) {
+                Log.e(TAG, "The connectivity subsystem is not available to restart.");
+                return false;
+            }
+            sIsRecovering = true;
+            sRecoveryManager.triggerSubsystemRestart(null /* reason */, sInstance);
+            Log.d(TAG, "The connectivity subsystem is restarting for recovery.");
+            return true;
+        }
+
+        @Override
+        public void onSubsystemRestartOperationBegin() {
+            Log.d(TAG, "The connectivity subsystem is starting for recovery.");
+            sIsRecovering = true;
+        }
+
+        @Override
+        public void onSubsystemRestartOperationEnd() {
+            Log.d(TAG, "The connectivity subsystem is done for recovery.");
+            sIsRecovering = false;
+            InternetResetHelper callback = sCallback.get();
+            if (callback == null) return;
+            callback.resumePreferences();
+        }
     }
 }
diff --git a/src/com/android/settings/network/MobileNetworkRepository.java b/src/com/android/settings/network/MobileNetworkRepository.java
index f688138..5de4c72 100644
--- a/src/com/android/settings/network/MobileNetworkRepository.java
+++ b/src/com/android/settings/network/MobileNetworkRepository.java
@@ -88,6 +88,7 @@
     private Uri mAirplaneModeSettingUri;
     private MetricsFeatureProvider mMetricsFeatureProvider;
     private IntentFilter mFilter = new IntentFilter();
+    private MobileDataContentObserver mDataContentObserver;
 
     private int mPhysicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
     private int mLogicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
@@ -125,6 +126,13 @@
         mMobileNetworkInfoDao = mMobileNetworkDatabase.mMobileNetworkInfoDao();
         mAirplaneModeObserver = new AirplaneModeObserver(new Handler(Looper.getMainLooper()));
         mAirplaneModeSettingUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
+        mDataContentObserver = new MobileDataContentObserver(
+                new Handler(Looper.getMainLooper()));
+        mDataContentObserver.setOnMobileDataChangedListener(() -> {
+            mExecutor.execute(() -> {
+                insertMobileNetworkInfo(context);
+            });
+        });
         mFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
         mFilter.addAction(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
         mFilter.addAction(ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
@@ -163,6 +171,9 @@
     public void addRegister(LifecycleOwner lifecycleOwner) {
         mSubscriptionManager.addOnSubscriptionsChangedListener(mContext.getMainExecutor(), this);
         mAirplaneModeObserver.register(mContext);
+        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            mDataContentObserver.register(mContext, mSubId);
+        }
         mContext.registerReceiver(mDataSubscriptionChangedReceiver, mFilter);
         observeAllSubInfo(lifecycleOwner);
         observeAllUiccInfo(lifecycleOwner);
@@ -170,6 +181,9 @@
     }
 
     public void removeRegister() {
+        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            mDataContentObserver.unRegister(mContext);
+        }
         mAirplaneModeObserver.unRegister(mContext);
         if (mDataSubscriptionChangedReceiver != null) {
             mContext.unregisterReceiver(mDataSubscriptionChangedReceiver);
@@ -404,7 +418,7 @@
         return new MobileNetworkInfoEntity(String.valueOf(mSubId),
                 MobileNetworkUtils.isContactDiscoveryEnabled(context, mSubId),
                 MobileNetworkUtils.isContactDiscoveryVisible(context, mSubId),
-                MobileNetworkUtils.isMobileDataEnabled(context),
+                mTelephonyManager.isDataEnabled(),
                 MobileNetworkUtils.isCdmaOptions(context, mSubId),
                 MobileNetworkUtils.isGsmOptions(context, mSubId),
                 MobileNetworkUtils.isWorldMode(context, mSubId),
diff --git a/src/com/android/settings/network/NetworkProviderDownloadedSimListController.java b/src/com/android/settings/network/NetworkProviderDownloadedSimListController.java
index abde7c0..421a854 100644
--- a/src/com/android/settings/network/NetworkProviderDownloadedSimListController.java
+++ b/src/com/android/settings/network/NetworkProviderDownloadedSimListController.java
@@ -41,6 +41,7 @@
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.mobile.dataservice.DataServiceUtils;
 import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
@@ -181,10 +182,7 @@
 
     @Override
     public void onAvailableSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
-        if ((mSubInfoEntityList != null &&
-                (subInfoEntityList.isEmpty() || !subInfoEntityList.equals(mSubInfoEntityList)))
-                || (!subInfoEntityList.isEmpty() && mSubInfoEntityList == null)) {
-            Log.d(TAG, "subInfo list from framework is changed, update the subInfo entity list.");
+        if (DataServiceUtils.shouldUpdateEntityList(mSubInfoEntityList, subInfoEntityList)) {
             mSubInfoEntityList = subInfoEntityList;
             mPreferenceCategory.setVisible(isAvailable());
             update();
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index b622a3c..7b7cdca 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -337,6 +337,8 @@
         addConnectedEthernetNetworkController();
         addWifiSwitchPreferenceController();
         mWifiStatusMessagePreference = findPreference(PREF_KEY_WIFI_STATUS_MESSAGE);
+
+        checkConnectivityRecovering();
     }
 
     private void updateAirplaneModeMsgPreference(boolean visible) {
@@ -382,6 +384,17 @@
         mWifiSwitchPreferenceController.displayPreference(getPreferenceScreen());
     }
 
+    private void checkConnectivityRecovering() {
+        mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle(),
+                mNetworkMobileProviderController,
+                findPreference(WifiSwitchPreferenceController.KEY),
+                mConnectedWifiEntryPreferenceCategory,
+                mFirstWifiEntryPreferenceCategory,
+                mWifiEntryPreferenceCategory,
+                mResetInternetPreference);
+        mInternetResetHelper.checkRecovering();
+    }
+
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
@@ -1455,16 +1468,6 @@
             EventLog.writeEvent(0x534e4554, "252995826", UserHandle.myUserId(), "User is a guest");
             return;
         }
-        if (mInternetResetHelper == null) {
-            mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle());
-            mInternetResetHelper.setResettingPreference(mResetInternetPreference);
-            mInternetResetHelper.setMobileNetworkController(mNetworkMobileProviderController);
-            mInternetResetHelper.setWifiTogglePreference(
-                    findPreference(WifiSwitchPreferenceController.KEY));
-            mInternetResetHelper.addWifiNetworkPreference(mConnectedWifiEntryPreferenceCategory);
-            mInternetResetHelper.addWifiNetworkPreference(mFirstWifiEntryPreferenceCategory);
-            mInternetResetHelper.addWifiNetworkPreference(mWifiEntryPreferenceCategory);
-        }
         mInternetResetHelper.restart();
     }
 
diff --git a/src/com/android/settings/network/ResetNetworkOperationBuilder.java b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
index 3b5c9bc..3583d06 100644
--- a/src/com/android/settings/network/ResetNetworkOperationBuilder.java
+++ b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
@@ -28,8 +28,10 @@
 import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Looper;
 import android.os.RecoverySystem;
+import android.os.SystemClock;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.util.Log;
 
 import com.android.settings.network.apn.ApnSettings;
 
@@ -43,6 +45,10 @@
  */
 public class ResetNetworkOperationBuilder {
 
+    private static final String TAG = "ResetNetworkOpBuilder";
+
+    private static final boolean DRY_RUN = false;
+
     private Context mContext;
     private List<Runnable> mResetSequence = new ArrayList<Runnable>();
 
@@ -127,10 +133,17 @@
     public ResetNetworkOperationBuilder resetEsim(String callerPackage,
             Consumer<Boolean> resultCallback) {
         Runnable runnable = () -> {
-            Boolean wipped = RecoverySystem.wipeEuiccData(mContext, callerPackage);
-            if (resultCallback != null) {
-                resultCallback.accept(wipped);
+            long startTime = SystemClock.elapsedRealtime();
+
+            if (!DRY_RUN) {
+                Boolean wipped = RecoverySystem.wipeEuiccData(mContext, callerPackage);
+                if (resultCallback != null) {
+                    resultCallback.accept(wipped);
+                }
             }
+
+            long endTime = SystemClock.elapsedRealtime();
+            Log.i(TAG, "Reset eSIM, takes " + (endTime - startTime) + " ms");
         };
         mResetSequence.add(runnable);
         return this;
@@ -179,14 +192,21 @@
      */
     public ResetNetworkOperationBuilder resetApn(int subscriptionId) {
         Runnable runnable = () -> {
+            long startTime = SystemClock.elapsedRealtime();
+
             Uri uri = Uri.parse(ApnSettings.RESTORE_CARRIERS_URI);
 
             if (SubscriptionManager.isUsableSubscriptionId(subscriptionId)) {
                 uri = Uri.withAppendedPath(uri, "subId/" + String.valueOf(subscriptionId));
             }
 
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.delete(uri, null, null);
+            if (!DRY_RUN) {
+                ContentResolver resolver = mContext.getContentResolver();
+                resolver.delete(uri, null, null);
+            }
+
+            long endTime = SystemClock.elapsedRealtime();
+            Log.i(TAG, "Reset " + uri + ", takes " + (endTime - startTime) + " ms");
         };
         mResetSequence.add(runnable);
         return this;
@@ -205,7 +225,14 @@
         if (service == null) {
             return;
         }
-        Runnable runnable = () -> serviceAccess.accept(service);
+        Runnable runnable = () -> {
+            long startTime = SystemClock.elapsedRealtime();
+            if (!DRY_RUN) {
+                serviceAccess.accept(service);
+            }
+            long endTime = SystemClock.elapsedRealtime();
+            Log.i(TAG, "Reset " + serviceName + ", takes " + (endTime - startTime) + " ms");
+        };
         mResetSequence.add(runnable);
     }
 }
diff --git a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
index 69951bf..8543770 100644
--- a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
+++ b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
@@ -16,14 +16,19 @@
 
 package com.android.settings.network.telephony;
 
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
-import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.OnLifecycleEvent;
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.FragmentManager;
 import androidx.preference.Preference;
@@ -32,16 +37,22 @@
 
 import com.android.settings.R;
 import com.android.settings.network.MobileDataContentObserver;
+import com.android.settings.network.MobileNetworkRepository;
 import com.android.settings.wifi.WifiPickerTrackerHelper;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.mobile.dataservice.DataServiceUtils;
+import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
+import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
+import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Preference controller for "Mobile data"
  */
 public class MobileDataPreferenceController extends TelephonyTogglePreferenceController
-        implements LifecycleObserver, OnStart, OnStop {
+        implements LifecycleObserver, MobileNetworkRepository.MobileNetworkCallback {
 
     private static final String DIALOG_TAG = "MobileDataDialog";
 
@@ -56,12 +67,26 @@
     boolean mNeedDialog;
 
     private WifiPickerTrackerHelper mWifiPickerTrackerHelper;
+    protected MobileNetworkRepository mMobileNetworkRepository;
+    protected LifecycleOwner mLifecycleOwner;
+    private List<SubscriptionInfoEntity> mSubscriptionInfoEntityList = new ArrayList<>();
+    private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
+    private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    SubscriptionInfoEntity mSubscriptionInfoEntity;
+    MobileNetworkInfoEntity mMobileNetworkInfoEntity;
 
-    public MobileDataPreferenceController(Context context, String key) {
+    public MobileDataPreferenceController(Context context, String key, Lifecycle lifecycle,
+            LifecycleOwner lifecycleOwner, int subId) {
         super(context, key);
+        mSubId = subId;
         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
         mDataContentObserver = new MobileDataContentObserver(new Handler(Looper.getMainLooper()));
         mDataContentObserver.setOnMobileDataChangedListener(() -> updateState(mPreference));
+        mMobileNetworkRepository = MobileNetworkRepository.createBySubId(context, this, mSubId);
+        mLifecycleOwner = lifecycleOwner;
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
     }
 
     @Override
@@ -77,18 +102,14 @@
         mPreference = screen.findPreference(getPreferenceKey());
     }
 
-    @Override
+    @OnLifecycleEvent(ON_START)
     public void onStart() {
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            mDataContentObserver.register(mContext, mSubId);
-        }
+        mMobileNetworkRepository.addRegister(mLifecycleOwner);
     }
 
-    @Override
+    @OnLifecycleEvent(ON_STOP)
     public void onStop() {
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            mDataContentObserver.unRegister(mContext);
-        }
+        mMobileNetworkRepository.removeRegister();
     }
 
     @Override
@@ -122,39 +143,47 @@
 
     @Override
     public boolean isChecked() {
-        mTelephonyManager = getTelephonyManager();
-        return mTelephonyManager.isDataEnabled();
+        return mMobileNetworkInfoEntity == null ? false
+                : mMobileNetworkInfoEntity.isMobileDataEnabled;
     }
 
     @Override
     public void updateState(Preference preference) {
         super.updateState(preference);
-        if (isOpportunistic()) {
-            preference.setEnabled(false);
-            preference.setSummary(R.string.mobile_data_settings_summary_auto_switch);
-        } else {
-            preference.setEnabled(true);
-            preference.setSummary(R.string.mobile_data_settings_summary);
+        mPreference = (SwitchPreference) preference;
+        update();
+    }
+
+    private void update() {
+
+        if (mSubscriptionInfoEntity == null || mPreference == null) {
+            return;
         }
 
-        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            preference.setSelectable(false);
-            preference.setSummary(R.string.mobile_data_settings_summary_unavailable);
+        mPreference.setChecked(isChecked());
+        if (mSubscriptionInfoEntity.isOpportunistic) {
+            mPreference.setEnabled(false);
+            mPreference.setSummary(R.string.mobile_data_settings_summary_auto_switch);
         } else {
-            preference.setSelectable(true);
+            mPreference.setEnabled(true);
+            mPreference.setSummary(R.string.mobile_data_settings_summary);
+        }
+        if (!mSubscriptionInfoEntity.isValidSubscription) {
+            mPreference.setSelectable(false);
+            mPreference.setSummary(R.string.mobile_data_settings_summary_unavailable);
+        } else {
+            mPreference.setSelectable(true);
         }
     }
 
-    private boolean isOpportunistic() {
-        SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
-        return info != null && info.isOpportunistic();
-    }
-
-    public void init(FragmentManager fragmentManager, int subId) {
+    public void init(FragmentManager fragmentManager, int subId,
+            SubscriptionInfoEntity subInfoEntity, MobileNetworkInfoEntity networkInfoEntity) {
         mFragmentManager = fragmentManager;
         mSubId = subId;
         mTelephonyManager = null;
         mTelephonyManager = getTelephonyManager();
+        mSubscriptionInfoEntity = subInfoEntity;
+        mMobileNetworkInfoEntity = networkInfoEntity;
     }
 
     private TelephonyManager getTelephonyManager() {
@@ -179,9 +208,8 @@
         final boolean enableData = !isChecked();
         mTelephonyManager = getTelephonyManager();
         final boolean isMultiSim = (mTelephonyManager.getActiveModemCount() > 1);
-        final int defaultSubId = mSubscriptionManager.getDefaultDataSubscriptionId();
-        final boolean needToDisableOthers = mSubscriptionManager
-                .isActiveSubscriptionId(defaultSubId) && defaultSubId != mSubId;
+        final boolean needToDisableOthers = mDefaultSubId != mSubId;
+
         if (enableData && isMultiSim && needToDisableOthers) {
             mDialogType = MobileDataDialogFragment.TYPE_MULTI_SIM_DIALOG;
             return true;
@@ -194,4 +222,62 @@
                 mSubId);
         dialogFragment.show(mFragmentManager, DIALOG_TAG);
     }
+
+    @VisibleForTesting
+    public void setSubscriptionInfoEntity(SubscriptionInfoEntity subscriptionInfoEntity) {
+        mSubscriptionInfoEntity = subscriptionInfoEntity;
+    }
+
+    @VisibleForTesting
+    public void setMobileNetworkInfoEntity(MobileNetworkInfoEntity mobileNetworkInfoEntity) {
+        mMobileNetworkInfoEntity = mobileNetworkInfoEntity;
+    }
+
+    @Override
+    public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
+    }
+
+    @Override
+    public void onAvailableSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
+    }
+
+    @Override
+    public void onActiveSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
+        if (DataServiceUtils.shouldUpdateEntityList(mSubscriptionInfoEntityList,
+                subInfoEntityList)) {
+            mSubscriptionInfoEntityList = subInfoEntityList;
+            mSubscriptionInfoEntityList.forEach(entity -> {
+                if (Integer.parseInt(entity.subId) == mSubId) {
+                    mSubscriptionInfoEntity = entity;
+                }
+            });
+            if (mSubscriptionInfoEntity != null
+                    && mSubscriptionInfoEntity.isDefaultDataSubscription) {
+                mDefaultSubId = Integer.parseInt(mSubscriptionInfoEntity.subId);
+            }
+            update();
+            refreshSummary(mPreference);
+        }
+    }
+
+    @Override
+    public void onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList) {
+    }
+
+    @Override
+    public void onAllMobileNetworkInfoChanged(
+            List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList) {
+        if (DataServiceUtils.shouldUpdateEntityList(mMobileNetworkInfoEntityList,
+                mobileNetworkInfoEntityList)) {
+            mMobileNetworkInfoEntityList = mobileNetworkInfoEntityList;
+            mMobileNetworkInfoEntityList.forEach(entity -> {
+                if (Integer.parseInt(entity.subId) == mSubId) {
+                    mMobileNetworkInfoEntity = entity;
+                    update();
+                    refreshSummary(mPreference);
+                    return;
+                }
+            });
+        }
+    }
 }
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index df7fd83..88c3656 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -79,6 +79,7 @@
     private static final String KEY_ROAMING_PREF = "button_roaming_key";
     private static final String KEY_CALLS_PREF = "calls_preference";
     private static final String KEY_SMS_PREF = "sms_preference";
+    private static final String KEY_MOBILE_DATA_PREF = "mobile_data_enable";
 
     //String keys for preference lookup
     private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key";
@@ -175,7 +176,9 @@
                 new CallsDefaultSubscriptionController(context, KEY_CALLS_PREF,
                         getSettingsLifecycle(), this),
                 new SmsDefaultSubscriptionController(context, KEY_SMS_PREF, getSettingsLifecycle(),
-                        this));
+                        this),
+                new MobileDataPreferenceController(context, KEY_MOBILE_DATA_PREF,
+                        getSettingsLifecycle(), this, mSubId));
     }
 
     @Override
@@ -231,10 +234,16 @@
                 REQUEST_CODE_DELETE_SUBSCRIPTION);
         use(DisableSimFooterPreferenceController.class).init(mSubId);
         use(NrDisabledInDsdsFooterPreferenceController.class).init(mSubId);
-        use(MobileDataPreferenceController.class).init(getFragmentManager(), mSubId);
-        use(MobileDataPreferenceController.class).setWifiPickerTrackerHelper(
-                new WifiPickerTrackerHelper(getSettingsLifecycle(), context,
-                        null /* WifiPickerTrackerCallback */));
+
+        final MobileDataPreferenceController mobileDataPreferenceController =
+                use(MobileDataPreferenceController.class);
+        if (mobileDataPreferenceController != null) {
+            mobileDataPreferenceController.init(getFragmentManager(), mSubId,
+                    mSubscriptionInfoEntity, mMobileNetworkInfoEntity);
+            mobileDataPreferenceController.setWifiPickerTrackerHelper(
+                    new WifiPickerTrackerHelper(getSettingsLifecycle(), context,
+                            null /* WifiPickerTrackerCallback */));
+        }
 
         final RoamingPreferenceController roamingPreferenceController =
                 use(RoamingPreferenceController.class);
diff --git a/src/com/android/settings/notification/RedactionInterstitial.java b/src/com/android/settings/notification/RedactionInterstitial.java
index f243250..d6fdaf8 100644
--- a/src/com/android/settings/notification/RedactionInterstitial.java
+++ b/src/com/android/settings/notification/RedactionInterstitial.java
@@ -189,13 +189,16 @@
         }
 
         private void loadFromSettings() {
+            final boolean showUnRedactedDefault = getContext().getResources().getBoolean(
+                    R.bool.default_allow_sensitive_lockscreen_content);
             final boolean managedProfile = UserManager.get(getContext()).isManagedProfile(mUserId);
             // Hiding all notifications is device-wide setting, managed profiles can only set
             // whether their notifications are show in full or redacted.
             final boolean showNotifications = managedProfile || Settings.Secure.getIntForUser(
                     getContentResolver(), LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mUserId) != 0;
             final boolean showUnredacted = Settings.Secure.getIntForUser(
-                    getContentResolver(), LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mUserId) != 0;
+                    getContentResolver(), LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                    showUnRedactedDefault ? 1 : 0, mUserId) != 0;
 
             int checkedButtonId = R.id.hide_all;
             if (showNotifications) {
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index e4d52ba..85c203d 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -28,6 +28,7 @@
 import android.os.UserManager;
 import android.util.Log;
 
+import androidx.activity.result.ActivityResultLauncher;
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.Fragment;
 
@@ -109,19 +110,23 @@
     @VisibleForTesting @NonNull LockPatternUtils mLockPatternUtils;
     @NonNull private final Activity mActivity;
     @Nullable private final Fragment mFragment;
+    @Nullable private final ActivityResultLauncher mActivityResultLauncher;
     @NonNull private final Builder mBuilder;
 
     private ChooseLockSettingsHelper(@NonNull Builder builder, @NonNull Activity activity,
-            @Nullable Fragment fragment) {
+            @Nullable Fragment fragment,
+            @Nullable ActivityResultLauncher activityResultLauncher) {
         mBuilder = builder;
         mActivity = activity;
         mFragment = fragment;
+        mActivityResultLauncher = activityResultLauncher;
         mLockPatternUtils = new LockPatternUtils(activity);
     }
 
     public static class Builder {
         @NonNull private final Activity mActivity;
         @Nullable private Fragment mFragment;
+        @Nullable private ActivityResultLauncher mActivityResultLauncher;
 
         private int mRequestCode;
         @Nullable private CharSequence mTitle;
@@ -265,6 +270,18 @@
             return this;
         }
 
+        /**
+         * Support of ActivityResultLauncher.
+         *
+         * Which allowing the launch operation be controlled externally.
+         * @param activityResultLauncher a launcher previously prepared.
+         */
+        @NonNull public Builder setActivityResultLauncher(
+                ActivityResultLauncher activityResultLauncher) {
+            mActivityResultLauncher = activityResultLauncher;
+            return this;
+        }
+
         @NonNull public ChooseLockSettingsHelper build() {
             if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP) {
                 Utils.enforceSameOwner(mActivity, mUserId);
@@ -282,7 +299,8 @@
                         + " ReturnCredentials. Are you sure this is what you want?");
             }
 
-            return new ChooseLockSettingsHelper(this, mActivity, mFragment);
+            return new ChooseLockSettingsHelper(this, mActivity, mFragment,
+                    mActivityResultLauncher);
         }
 
         public boolean show() {
@@ -369,13 +387,17 @@
         if (external) {
             intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
             copyOptionalExtras(inIntent, intent);
-            if (mFragment != null) {
+            if (mActivityResultLauncher != null) {
+                mActivityResultLauncher.launch(intent);
+            } else if (mFragment != null) {
                 mFragment.startActivity(intent);
             } else {
                 mActivity.startActivity(intent);
             }
         } else {
-            if (mFragment != null) {
+            if (mActivityResultLauncher != null) {
+                mActivityResultLauncher.launch(intent);
+            } else if (mFragment != null) {
                 mFragment.startActivityForResult(intent, request);
             } else {
                 mActivity.startActivityForResult(intent, request);
diff --git a/tests/componenttests/Android.bp b/tests/componenttests/Android.bp
index 77ee164..5c03aa9 100644
--- a/tests/componenttests/Android.bp
+++ b/tests/componenttests/Android.bp
@@ -26,6 +26,7 @@
         "androidx.test.runner",
         "androidx.test.rules",
         "androidx.test.ext.junit",
+        "mockito-target",
     ],
 
     test_suites: ["device-tests"],
diff --git a/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java b/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java
index 78a2c92..df3dd2e 100644
--- a/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java
+++ b/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java
@@ -20,6 +20,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
+
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.Intent;
@@ -31,6 +33,7 @@
 
 import androidx.fragment.app.FragmentActivity;
 import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -40,6 +43,9 @@
 import com.android.settings.Settings;
 import com.android.settings.testutils.CommonUtils;
 import com.android.settings.testutils.UiUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
+import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 
 import org.junit.After;
 import org.junit.Assume;
@@ -47,6 +53,8 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.io.IOException;
 import java.net.URL;
@@ -54,6 +62,12 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MobileDataPreferenceControllerComponentTest {
+
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private LifecycleOwner mLifecycleOwner;
+
     public static final int TIMEOUT = 2000;
     private static int sSubscriptionId = 2;
     public final String TAG = this.getClass().getName();
@@ -116,9 +130,11 @@
             try {
                 URL url = new URL("https://www.google.net");
                 MobileDataPreferenceController controller = new MobileDataPreferenceController(
-                        mInstrumentation.getTargetContext(), "mobile_data");
+                        mInstrumentation.getTargetContext(), "mobile_data", mLifecycle,
+                        mLifecycleOwner, sSubscriptionId);
                 FragmentManager manager = ((FragmentActivity) activity).getSupportFragmentManager();
-                controller.init(manager, sSubscriptionId);
+                controller.init(manager, sSubscriptionId, mock(SubscriptionInfoEntity.class), mock(
+                        MobileNetworkInfoEntity.class));
 
                 // Make sure mobile network can connect at first.
                 assertThat(UiUtils.waitUntilCondition(1000,
diff --git a/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
index 4f870d3..5dad40d 100644
--- a/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
+++ b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
@@ -31,6 +31,9 @@
 
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settings.testutils.shadow.ShadowRecoverySystem;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -43,8 +46,11 @@
 import org.robolectric.annotation.Config;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowRecoverySystem.class, ShadowBluetoothAdapter.class})
 public class ResetNetworkConfirmTest {
 
+    private static final String TEST_PACKAGE = "com.android.settings";
+
     private FragmentActivity mActivity;
 
     @Mock
@@ -59,9 +65,28 @@
         mResetNetworkConfirm.mActivity = mActivity;
     }
 
+    @After
+    public void tearDown() {
+        ShadowRecoverySystem.reset();
+    }
+
+    @Test
+    public void testResetNetworkData_notResetEsim() {
+        mResetNetworkConfirm.mResetNetworkRequest =
+                new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE);
+
+        mResetNetworkConfirm.mFinalClickListener.onClick(null /* View */);
+        Robolectric.getBackgroundThreadScheduler().advanceToLastPostedRunnable();
+
+        assertThat(ShadowRecoverySystem.getWipeEuiccCalledCount()).isEqualTo(0);
+    }
+
     @Test
     public void setSubtitle_eraseEsim() {
-        mResetNetworkConfirm.mEraseEsim = true;
+        mResetNetworkConfirm.mResetNetworkRequest =
+                new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE);
+        mResetNetworkConfirm.mResetNetworkRequest.setResetEsim(TEST_PACKAGE);
+
         mResetNetworkConfirm.mContentView =
                 LayoutInflater.from(mActivity).inflate(R.layout.reset_network_confirm, null);
 
@@ -74,7 +99,9 @@
 
     @Test
     public void setSubtitle_notEraseEsim() {
-        mResetNetworkConfirm.mEraseEsim = false;
+        mResetNetworkConfirm.mResetNetworkRequest =
+                new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE);
+
         mResetNetworkConfirm.mContentView =
                 LayoutInflater.from(mActivity).inflate(R.layout.reset_network_confirm, null);
 
diff --git a/tests/robotests/src/com/android/settings/ResetNetworkTest.java b/tests/robotests/src/com/android/settings/ResetNetworkTest.java
index d703279..0c2c7e8 100644
--- a/tests/robotests/src/com/android/settings/ResetNetworkTest.java
+++ b/tests/robotests/src/com/android/settings/ResetNetworkTest.java
@@ -28,6 +28,7 @@
 import android.widget.CheckBox;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
@@ -48,6 +49,7 @@
     }
 
     @Test
+    @Ignore
     public void showFinalConfirmation_checkboxVisible_eraseEsimChecked() {
         mResetNetwork.mEsimContainer.setVisibility(View.VISIBLE);
         mResetNetwork.mEsimCheckbox.setChecked(true);
@@ -55,8 +57,8 @@
         mResetNetwork.showFinalConfirmation();
 
         Intent intent = shadowOf(mActivity).getNextStartedActivity();
-        assertThat(intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
-                .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)).isTrue();
+        assertThat(intent.getStringExtra(ResetNetworkRequest.KEY_ESIM_PACKAGE))
+                .isNotNull();
     }
 
     @Test
@@ -67,8 +69,8 @@
         mResetNetwork.showFinalConfirmation();
 
         Intent intent = shadowOf(mActivity).getNextStartedActivity();
-        assertThat(intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
-                .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)).isFalse();
+        assertThat(intent.getStringExtra(ResetNetworkRequest.KEY_ESIM_PACKAGE))
+                .isNull();
     }
 
     @Test
@@ -79,8 +81,8 @@
         mResetNetwork.showFinalConfirmation();
 
         Intent intent = shadowOf(mActivity).getNextStartedActivity();
-        assertThat(intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
-                .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)).isFalse();
+        assertThat(intent.getStringExtra(ResetNetworkRequest.KEY_ESIM_PACKAGE))
+                .isNull();
     }
 
     @Test
@@ -91,7 +93,7 @@
         mResetNetwork.showFinalConfirmation();
 
         Intent intent = shadowOf(mActivity).getNextStartedActivity();
-        assertThat(intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
-                .getBoolean(MainClear.ERASE_ESIMS_EXTRA, false)).isFalse();
+        assertThat(intent.getStringExtra(ResetNetworkRequest.KEY_ESIM_PACKAGE))
+                .isNull();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettingsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettingsTest.java
index 270a625..dc9cac4 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettingsTest.java
@@ -1,5 +1,6 @@
 package com.android.settings.fuelgauge.batterysaver;
 
+import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -82,6 +83,12 @@
         verifySchedule("key_battery_saver_percentage", expectedPercentage);
     }
 
+    @Test
+    public void getMetricsCategory_returnExpectedResult() {
+        assertThat(mBatterySaverScheduleSettings.getMetricsCategory())
+                .isEqualTo(SettingsEnums.FUELGAUGE_BATTERY_SAVER_SCHEDULE);
+    }
+
     private void setSchedule(int scheduleType, int schedulePercentage) {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.AUTOMATIC_POWER_SAVE_MODE, scheduleType);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
index db1159e..c5d66a6 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
@@ -244,20 +244,4 @@
         assertThat(shadowDialog.getMessage()).isEqualTo(
                 mContext.getText(R.string.battery_tip_dialog_summary_message));
     }
-
-    @Test
-    public void testOnCreateDialog_defenderTip_fireDialog() {
-        mDialogFragment = BatteryTipDialogFragment.newInstance(mDefenderTip, METRICS_KEY);
-
-        FragmentController.setupFragment(mDialogFragment, FragmentActivity.class,
-                0 /* containerViewId */, null /* bundle */);
-
-        final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
-
-        assertThat(shadowDialog.getTitle()).isEqualTo(
-                mContext.getString(R.string.battery_tip_limited_temporarily_title));
-        assertThat(shadowDialog.getMessage()).isEqualTo(
-                mContext.getString(R.string.battery_tip_limited_temporarily_dialog_msg, "80%"));
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBaseTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBaseTest.java
index 2700930..6ed10cd 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBaseTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageBaseTest.java
@@ -15,18 +15,26 @@
  */
 package com.android.settings.fuelgauge.batteryusage;
 
+import static com.android.settings.fuelgauge.batteryusage.PowerUsageBase.KEY_INCLUDE_HISTORY;
+import static com.android.settings.fuelgauge.batteryusage.PowerUsageBase.KEY_REFRESH_TYPE;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
+import android.os.BatteryUsageStats;
 import android.os.Bundle;
 
 import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
 
+import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
 import com.android.settings.testutils.shadow.ShadowDashboardFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
 
@@ -46,14 +54,15 @@
 
     @Mock
     private LoaderManager mLoaderManager;
+    @Mock
+    private Loader<BatteryUsageStats> mBatteryUsageStatsLoader;
     private TestFragment mFragment;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mFragment = spy(new TestFragment());
-        doReturn(mLoaderManager).when(mFragment).getLoaderManager();
+        mFragment = spy(new TestFragment(mLoaderManager));
     }
 
     @Test
@@ -63,7 +72,62 @@
         verify(mLoaderManager, never()).initLoader(anyInt(), any(Bundle.class), any());
     }
 
-    public static class TestFragment extends PowerUsageBase {
+    @Test
+    public void restartBatteryInfoLoader() {
+        final Bundle bundle = new Bundle();
+        bundle.putInt(KEY_REFRESH_TYPE, BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_STATUS);
+        bundle.putBoolean(KEY_INCLUDE_HISTORY, false);
+        doReturn(mBatteryUsageStatsLoader).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_USAGE_STATS_LOADER);
+        doReturn(false).when(mBatteryUsageStatsLoader).isReset();
+
+        mFragment.restartBatteryStatsLoader(
+                BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_STATUS);
+
+        verify(mLoaderManager)
+                .restartLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_USAGE_STATS_LOADER),
+                        refEq(bundle), any());
+    }
+
+    @Test
+    public void restartBatteryInfoLoader_loaderReset_initLoader() {
+        final Bundle bundle = new Bundle();
+        bundle.putInt(KEY_REFRESH_TYPE, BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_STATUS);
+        bundle.putBoolean(KEY_INCLUDE_HISTORY, false);
+        doReturn(mBatteryUsageStatsLoader).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_USAGE_STATS_LOADER);
+        doReturn(true).when(mBatteryUsageStatsLoader).isReset();
+
+        mFragment.restartBatteryStatsLoader(
+                BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_STATUS);
+
+        verify(mLoaderManager)
+                .initLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_USAGE_STATS_LOADER),
+                        refEq(bundle), any());
+    }
+
+    @Test
+    public void restartBatteryInfoLoader_nullLoader_initLoader() {
+        final Bundle bundle = new Bundle();
+        bundle.putInt(KEY_REFRESH_TYPE, BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_STATUS);
+        bundle.putBoolean(KEY_INCLUDE_HISTORY, false);
+        doReturn(null).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_USAGE_STATS_LOADER);
+
+        mFragment.restartBatteryStatsLoader(
+                BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_STATUS);
+
+        verify(mLoaderManager).initLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_USAGE_STATS_LOADER),
+                refEq(bundle), any());
+    }
+
+    private static class TestFragment extends PowerUsageBase {
+
+        private LoaderManager mLoaderManager;
+
+        TestFragment(LoaderManager loaderManager) {
+            mLoaderManager = loaderManager;
+        }
 
         @Override
         public int getMetricsCategory() {
@@ -94,5 +158,10 @@
         protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
             return null;
         }
+
+        @Override
+        protected LoaderManager getLoaderManagerForCurrentFragment() {
+            return mLoaderManager;
+        }
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java
index e134490..e44d92c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java
@@ -15,7 +15,6 @@
  */
 package com.android.settings.fuelgauge.batteryusage;
 
-import static com.android.settings.fuelgauge.batteryusage.PowerUsageSummary.BATTERY_INFO_LOADER;
 import static com.android.settings.fuelgauge.batteryusage.PowerUsageSummary.KEY_BATTERY_ERROR;
 import static com.android.settings.fuelgauge.batteryusage.PowerUsageSummary.KEY_BATTERY_USAGE;
 
@@ -39,14 +38,17 @@
 import android.provider.Settings;
 
 import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
+import com.android.settings.fuelgauge.BatteryInfo;
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.XmlTestUtils;
 import com.android.settings.testutils.shadow.ShadowUtils;
@@ -69,8 +71,6 @@
 // TODO: Improve this test class so that it starts up the real activity and fragment.
 @RunWith(RobolectricTestRunner.class)
 public class PowerUsageSummaryTest {
-
-    private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
     private static Intent sAdditionalBatteryInfoIntent;
 
     @BeforeClass
@@ -83,6 +83,10 @@
     @Mock
     private LoaderManager mLoaderManager;
     @Mock
+    private Loader<BatteryTip> mBatteryTipLoader;
+    @Mock
+    private Loader<BatteryInfo> mBatteryInfoLoader;
+    @Mock
     private ContentResolver mContentResolver;
     @Mock
     private BatteryBroadcastReceiver mBatteryBroadcastReceiver;
@@ -105,11 +109,9 @@
 
         mRealContext = spy(RuntimeEnvironment.application);
         mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mFragment = spy(new TestFragment(mRealContext));
+        mFragment = spy(new TestFragment(mRealContext, mLoaderManager));
         mFragment.initFeatureProvider();
         doNothing().when(mFragment).restartBatteryStatsLoader(anyInt());
-        doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager();
-
         when(mFragment.getActivity()).thenReturn(mSettingsActivity);
         when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
                 .thenReturn(sAdditionalBatteryInfoIntent);
@@ -153,12 +155,58 @@
     @Test
     public void restartBatteryTipLoader() {
         //TODO: add policy logic here when BatteryTipPolicy is implemented
-        doReturn(mLoaderManager).when(mFragment).getLoaderManager();
+        doReturn(mBatteryTipLoader).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_TIP_LOADER);
+        doReturn(false).when(mBatteryTipLoader).isReset();
 
         mFragment.restartBatteryTipLoader();
 
-        verify(mLoaderManager)
-                .restartLoader(eq(PowerUsageSummary.BATTERY_TIP_LOADER), eq(Bundle.EMPTY), any());
+        verify(mLoaderManager).restartLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_TIP_LOADER),
+                eq(Bundle.EMPTY), any());
+    }
+
+    @Test
+    public void restartBatteryTipLoader_nullLoader_initLoader() {
+        doReturn(null).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_TIP_LOADER);
+
+        mFragment.restartBatteryTipLoader();
+
+        verify(mLoaderManager).initLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_TIP_LOADER),
+                eq(Bundle.EMPTY), any());
+    }
+
+    @Test
+    public void restartBatteryTipLoader_loaderReset_initLoader() {
+        doReturn(mBatteryTipLoader).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_TIP_LOADER);
+        doReturn(true).when(mBatteryTipLoader).isReset();
+
+        mFragment.restartBatteryTipLoader();
+
+
+        verify(mLoaderManager).initLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_TIP_LOADER),
+                eq(Bundle.EMPTY), any());
+    }
+
+
+    @Test
+    public void refreshUi_contextNull_doNothing() {
+        doReturn(null).when(mFragment).getContext();
+
+        mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL);
+
+        verify(mFragment, never()).restartBatteryTipLoader();
+        verify(mFragment, never()).restartBatteryInfoLoader();
+    }
+
+    @Test
+    public void refreshUi_batteryNotPresent_doNothing() {
+        mFragment.setIsBatteryPresent(false);
+        mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL);
+
+        verify(mFragment, never()).restartBatteryTipLoader();
+        verify(mFragment, never()).restartBatteryInfoLoader();
     }
 
     @Test
@@ -166,10 +214,12 @@
         mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class);
         when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(false);
         mFragment.updateBatteryTipFlag(new Bundle());
+        doNothing().when(mFragment).restartBatteryInfoLoader();
 
         mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL);
 
         verify(mFragment, never()).restartBatteryTipLoader();
+        verify(mFragment).restartBatteryInfoLoader();
     }
 
     @Test
@@ -177,10 +227,12 @@
         mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class);
         when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(true);
         mFragment.updateBatteryTipFlag(new Bundle());
+        doNothing().when(mFragment).restartBatteryInfoLoader();
 
         mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_LEVEL);
 
         verify(mFragment, never()).restartBatteryTipLoader();
+        verify(mFragment).restartBatteryInfoLoader();
     }
 
     @Test
@@ -188,10 +240,13 @@
         mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class);
         when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(true);
         mFragment.updateBatteryTipFlag(new Bundle());
+        doNothing().when(mFragment).restartBatteryInfoLoader();
+        doNothing().when(mFragment).restartBatteryTipLoader();
 
         mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL);
 
         verify(mFragment).restartBatteryTipLoader();
+        verify(mFragment).restartBatteryInfoLoader();
     }
 
     @Test
@@ -215,19 +270,68 @@
     @Test
     public void restartBatteryInfoLoader_contextNull_doNothing() {
         when(mFragment.getContext()).thenReturn(null);
-        when(mFragment.getLoaderManager()).thenReturn(mLoaderManager);
 
         mFragment.restartBatteryInfoLoader();
 
-        verify(mLoaderManager, never()).restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
+        verify(mLoaderManager, never()).restartLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER, Bundle.EMPTY,
                 mFragment.mBatteryInfoLoaderCallbacks);
     }
 
-    public static class TestFragment extends PowerUsageSummary {
-        private Context mContext;
+    @Test
+    public void restartBatteryInfoLoader_batteryIsNotPresent_doNothing() {
+        mFragment.setIsBatteryPresent(false);
 
-        public TestFragment(Context context) {
+        mFragment.restartBatteryInfoLoader();
+
+        verify(mLoaderManager, never()).restartLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER, Bundle.EMPTY,
+                mFragment.mBatteryInfoLoaderCallbacks);
+    }
+
+    @Test
+    public void restartBatteryInfoLoader() {
+        doReturn(mBatteryInfoLoader).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER);
+        doReturn(false).when(mBatteryTipLoader).isReset();
+
+        mFragment.restartBatteryInfoLoader();
+
+        verify(mLoaderManager).restartLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER),
+                eq(Bundle.EMPTY), any());
+    }
+
+    @Test
+    public void restartBatteryInfoLoader_nullLoader_initLoader() {
+        doReturn(null).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER);
+
+        mFragment.restartBatteryInfoLoader();
+
+        verify(mLoaderManager).initLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER),
+                eq(Bundle.EMPTY), any());
+    }
+
+    @Test
+    public void restartBatteryInfoLoader_loaderReset_initLoader() {
+        mFragment.setIsBatteryPresent(true);
+        doReturn(mBatteryInfoLoader).when(mLoaderManager).getLoader(
+                PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER);
+        doReturn(true).when(mBatteryInfoLoader).isReset();
+
+        mFragment.restartBatteryInfoLoader();
+
+        verify(mLoaderManager).initLoader(eq(PowerUsageBase.LoaderIndex.BATTERY_INFO_LOADER),
+                eq(Bundle.EMPTY), any());
+    }
+
+    private static class TestFragment extends PowerUsageSummary {
+        private Context mContext;
+        private LoaderManager mLoaderManager;
+
+        TestFragment(Context context, LoaderManager loaderManager) {
             mContext = context;
+            mLoaderManager = loaderManager;
         }
 
         @Override
@@ -240,5 +344,15 @@
             // Override it so we can access this method in test
             return super.getContentResolver();
         }
+
+        public void setIsBatteryPresent(boolean isBatteryPresent) {
+            mIsBatteryPresent = isBatteryPresent;
+        }
+
+        @Override
+        protected LoaderManager getLoaderManagerForCurrentFragment() {
+            return mLoaderManager;
+        }
     }
+
 }
diff --git a/tests/robotests/src/com/android/settings/notification/RedactionInterstitialTest.java b/tests/robotests/src/com/android/settings/notification/RedactionInterstitialTest.java
index 5c6da49..9d475b8 100644
--- a/tests/robotests/src/com/android/settings/notification/RedactionInterstitialTest.java
+++ b/tests/robotests/src/com/android/settings/notification/RedactionInterstitialTest.java
@@ -21,6 +21,7 @@
 import com.android.settings.R;
 import com.android.settings.RestrictedRadioButton;
 import com.android.settings.notification.RedactionInterstitial.RedactionInterstitialFragment;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
@@ -38,6 +39,7 @@
 @Config(shadows = {
         ShadowUtils.class,
         ShadowRestrictedLockUtilsInternal.class,
+        SettingsShadowResources.class,
 })
 public class RedactionInterstitialTest {
     private RedactionInterstitial mActivity;
@@ -134,6 +136,28 @@
         assertSelectedButton(R.id.redact_sensitive);
     }
 
+    @Test
+    public void defaultShowSensitiveContent_configDeny() {
+        final ContentResolver resolver = RuntimeEnvironment.application.getContentResolver();
+        Settings.Secure.putIntForUser(resolver,
+                LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, UserHandle.myUserId());
+        setupConfig(false);
+        setupActivity();
+
+        assertSelectedButton(R.id.redact_sensitive);
+    }
+
+    @Test
+    public void defaultShowSensitiveContent_configAllow() {
+        final ContentResolver resolver = RuntimeEnvironment.application.getContentResolver();
+        Settings.Secure.putIntForUser(resolver,
+                LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, UserHandle.myUserId());
+        setupConfig(true);
+        setupActivity();
+
+        assertSelectedButton(R.id.show_all);
+    }
+
     private void setupActivity() {
         mActivity = buildActivity(RedactionInterstitial.class, new Intent()).setup().get();
         mFragment = (RedactionInterstitialFragment)
@@ -142,6 +166,11 @@
         assertThat(mFragment).isNotNull();
     }
 
+    private void setupConfig(boolean allowSensitiveContent) {
+        SettingsShadowResources.overrideResource(
+                R.bool.default_allow_sensitive_lockscreen_content, allowSensitiveContent);
+    }
+
     private void setupSettings(int show, int showUnredacted) {
         final ContentResolver resolver = RuntimeEnvironment.application.getContentResolver();
         Settings.Secure.putIntForUser(resolver,
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppAllServicesPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppAllServicesPreferenceTest.kt
index 9846e3f..bd73d8b 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppAllServicesPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppAllServicesPreferenceTest.kt
@@ -30,6 +30,7 @@
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
@@ -38,6 +39,8 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
+import com.android.settingslib.spa.testutils.delay
+import com.android.settingslib.spa.testutils.waitUntilExists
 import com.android.settingslib.spaprivileged.model.app.userHandle
 import com.android.settingslib.spaprivileged.model.app.userId
 import com.google.common.truth.Truth.assertThat
@@ -140,7 +143,7 @@
 
         setContent()
 
-        composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+        composeTestRule.waitUntilExists(hasText(SUMMARY))
     }
 
     @Test
@@ -149,6 +152,7 @@
 
         setContent()
         composeTestRule.onRoot().performClick()
+        composeTestRule.delay()
 
         val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
         verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt
index 60c4f79..39524df 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
@@ -36,6 +37,8 @@
 import com.android.settings.applications.AppLocaleUtil
 import com.android.settings.applications.appinfo.AppLocaleDetails
 import com.android.settings.localepicker.AppLocalePickerActivity
+import com.android.settingslib.spa.testutils.delay
+import com.android.settingslib.spa.testutils.waitUntilExists
 import com.android.settingslib.spaprivileged.model.app.userHandle
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
@@ -103,15 +106,16 @@
 
         composeTestRule.onNodeWithText(context.getString(R.string.app_locale_preference_title))
             .assertIsDisplayed()
-        composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+        composeTestRule.waitUntilExists(hasText(SUMMARY))
     }
 
     @Test
-    fun whenCanDisplayLocalUi_click_startsActivity() {
+    fun whenCanDisplayLocalUi_click_startActivity() {
         doNothing().`when`(context).startActivityAsUser(any(), any())
 
         setContent()
         composeTestRule.onRoot().performClick()
+        composeTestRule.delay()
 
         val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
         verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppSettingsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppSettingsPreferenceTest.kt
index a1fb367..35811e2 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppSettingsPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppSettingsPreferenceTest.kt
@@ -36,6 +36,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
+import com.android.settingslib.spa.testutils.delay
 import com.android.settingslib.spaprivileged.model.app.userHandle
 import com.android.settingslib.spaprivileged.model.app.userId
 import com.google.common.truth.Truth.assertThat
@@ -129,6 +130,7 @@
 
         setContent()
         composeTestRule.onRoot().performClick()
+        composeTestRule.delay()
 
         val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
         verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
index aeccb07..f4489c6 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
@@ -34,6 +35,8 @@
 import com.android.settings.R
 import com.android.settings.applications.appinfo.AppInfoDashboardFragment
 import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetails
+import com.android.settingslib.spa.testutils.delay
+import com.android.settingslib.spa.testutils.waitUntilExists
 import com.android.settingslib.spaprivileged.framework.common.crossProfileApps
 import org.junit.After
 import org.junit.Before
@@ -110,7 +113,7 @@
 
         setContent()
 
-        composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+        composeTestRule.waitUntilExists(hasText(SUMMARY))
     }
 
     @Test
@@ -119,6 +122,7 @@
 
         setContent()
         composeTestRule.onRoot().performClick()
+        composeTestRule.delay()
 
         ExtendedMockito.verify {
             AppInfoDashboardFragment.startAppInfoFragment(
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/model/CredentialModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/model/CredentialModelTest.java
new file mode 100644
index 0000000..1fac8b5
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/model/CredentialModelTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 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.biometrics2.ui.model;
+
+import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
+import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_SENSOR_ID;
+import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_CHALLENGE;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemClock;
+
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.password.ChooseLockSettingsHelper;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.util.Arrays;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class CredentialModelTest {
+
+    private final Clock mClock = SystemClock.elapsedRealtimeClock();
+
+    public static Bundle newCredentialModelIntentExtras(int userId, long challenge, int sensorId,
+            @Nullable byte[] token, long gkPwHandle) {
+        final Bundle bundle = new Bundle();
+        bundle.putInt(Intent.EXTRA_USER_ID, userId);
+        bundle.putInt(EXTRA_KEY_SENSOR_ID, sensorId);
+        bundle.putLong(EXTRA_KEY_CHALLENGE, challenge);
+        bundle.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+        bundle.putLong(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
+        return bundle;
+    }
+
+    public static Bundle newValidTokenCredentialIntentExtras(int userId) {
+        return newCredentialModelIntentExtras(userId, 1L, 1, new byte[] { 0 }, 0L);
+    }
+
+    public static Bundle newInvalidChallengeCredentialIntentExtras(int userId) {
+        return newCredentialModelIntentExtras(userId, INVALID_CHALLENGE, 1, null, 0L);
+    }
+
+    public static Bundle newGkPwHandleCredentialIntentExtras(int userId, long gkPwHandle) {
+        return newCredentialModelIntentExtras(userId, INVALID_CHALLENGE, 1, null, gkPwHandle);
+    }
+
+    private static void checkBundleLongValue(@NonNull Bundle bundle1, @NonNull Bundle bundle2,
+            @NonNull String key) {
+        if (!bundle1.containsKey(key)) {
+            return;
+        }
+        final int value1 = bundle1.getInt(key);
+        final int value2 = bundle2.getInt(key);
+        assertWithMessage("bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
+                + value2).that(value1).isEqualTo(value2);
+    }
+
+    private static void checkBundleIntValue(@NonNull Bundle bundle1, @NonNull Bundle bundle2,
+            @NonNull String key) {
+        if (!bundle1.containsKey(key)) {
+            return;
+        }
+        final long value1 = bundle1.getLong(key);
+        final long value2 = bundle2.getLong(key);
+        assertWithMessage("bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
+                + value2).that(value1).isEqualTo(value2);
+    }
+
+    private static void checkBundleByteArrayValue(@NonNull Bundle bundle1, @NonNull Bundle bundle2,
+            @NonNull String key) {
+        if (!bundle1.containsKey(key)) {
+            return;
+        }
+        final byte[] value1 = bundle1.getByteArray(key);
+        final byte[] value2 = bundle2.getByteArray(key);
+        final String errMsg = "bundle not match, key:" + key + ", value1:" + Arrays.toString(value1)
+                + ", value2:" + Arrays.toString(value2);
+        if (value1 == null) {
+            assertWithMessage(errMsg).that(value2).isNull();
+        } else {
+            assertWithMessage(errMsg).that(value1.length).isEqualTo(value2.length);
+            for (int i = 0; i < value1.length; ++i) {
+                assertWithMessage(errMsg).that(value1[i]).isEqualTo(value2[i]);
+            }
+        }
+    }
+
+    public static void verifySameCredentialModels(@NonNull CredentialModel model1,
+            @NonNull CredentialModel model2) {
+
+        assertThat(model1.getUserId()).isEqualTo(model2.getUserId());
+        assertThat(model1.getSensorId()).isEqualTo(model2.getSensorId());
+        assertThat(model1.getChallenge()).isEqualTo(model2.getChallenge());
+        assertThat(model1.getGkPwHandle()).isEqualTo(model2.getGkPwHandle());
+
+        final byte[] token1 = model1.getToken();
+        final byte[] token2 = model2.getToken();
+        if (token1 == null) {
+            assertThat(token2).isNull();
+        } else {
+            assertThat(token2).isNotNull();
+            assertThat(token1.length).isEqualTo(token2.length);
+            for (int i = 0; i < token1.length; ++i) {
+                assertThat(token1[i]).isEqualTo(token2[i]);
+            }
+        }
+
+        final Bundle bundle1 = model1.getBundle();
+        final Bundle bundle2 = model2.getBundle();
+        final Set<String> keySet1 = bundle1.keySet();
+        assertThat(keySet1.equals(bundle2.keySet())).isTrue();
+        checkBundleIntValue(bundle1, bundle2, Intent.EXTRA_USER_ID);
+        checkBundleIntValue(bundle1, bundle2, EXTRA_KEY_SENSOR_ID);
+        checkBundleLongValue(bundle1, bundle2, EXTRA_KEY_CHALLENGE);
+        checkBundleByteArrayValue(bundle1, bundle2, EXTRA_KEY_CHALLENGE);
+        checkBundleLongValue(bundle1, bundle2, EXTRA_KEY_GK_PW_HANDLE);
+    }
+
+    @Test
+    public void sameValueFromBundle() {
+        final Bundle bundle = newCredentialModelIntentExtras(1234, 6677L, 1,
+                new byte[] { 33, 44, 55 }, 987654321);
+
+        final CredentialModel model1 = new CredentialModel(bundle, mClock);
+        final CredentialModel model2 = new CredentialModel(model1.getBundle(), mClock);
+
+        verifySameCredentialModels(model1, model2);
+    }
+
+    @Test
+    public void sameValueFromBundle_nullToken() {
+        final Bundle bundle = newCredentialModelIntentExtras(22, 33L, 1, null, 21L);
+
+        final CredentialModel model1 = new CredentialModel(bundle, mClock);
+        final CredentialModel model2 = new CredentialModel(model1.getBundle(), mClock);
+
+        verifySameCredentialModels(model1, model2);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.java
index 7a13875..3abbca6 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.java
@@ -24,12 +24,24 @@
 import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
 import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_SENSOR_ID;
 import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_CHALLENGE;
+import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_GK_PW_HANDLE;
 import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_SENSOR_ID;
+import static com.android.settings.biometrics2.ui.model.CredentialModel.isValidGkPwHandle;
+import static com.android.settings.biometrics2.ui.model.CredentialModel.isValidToken;
+import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newCredentialModelIntentExtras;
+import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newGkPwHandleCredentialIntentExtras;
+import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newInvalidChallengeCredentialIntentExtras;
+import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newValidTokenCredentialIntentExtras;
 import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
 import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
+import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
+import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
 import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator;
 import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CredentialAction;
 import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.GenerateChallengeCallback;
+import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_CREDENTIAL_MODEL;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -38,20 +50,17 @@
 import android.annotation.NonNull;
 import android.app.Activity;
 import android.content.Intent;
-import android.os.SystemClock;
+import android.os.Bundle;
 import android.os.UserHandle;
 
 import androidx.activity.result.ActivityResult;
 import androidx.annotation.Nullable;
-import androidx.lifecycle.LifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.settings.biometrics2.ui.model.CredentialModel;
 import com.android.settings.password.ChooseLockPattern;
-import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.testutils.InstantTaskExecutorRule;
 
 import org.junit.Before;
@@ -68,248 +77,326 @@
     @Rule public final MockitoRule mockito = MockitoJUnit.rule();
     @Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
 
-    @Mock private LifecycleOwner mLifecycleOwner;
     @Mock private LockPatternUtils mLockPatternUtils;
     private TestChallengeGenerator mChallengeGenerator = null;
-    private AutoCredentialViewModel mAutoCredentialViewModel;
+    private AutoCredentialViewModel mViewModel;
 
     @Before
     public void setUp() {
         mChallengeGenerator = new TestChallengeGenerator();
-        mAutoCredentialViewModel = new AutoCredentialViewModel(
+        mViewModel = new AutoCredentialViewModel(
                 ApplicationProvider.getApplicationContext(),
                 mLockPatternUtils,
                 mChallengeGenerator);
     }
 
-    private CredentialModel newCredentialModel(int userId, long challenge,
-            @Nullable byte[] token, long gkPwHandle) {
-        final Intent intent = new Intent();
-        intent.putExtra(Intent.EXTRA_USER_ID, userId);
-        intent.putExtra(EXTRA_KEY_SENSOR_ID, 1);
-        intent.putExtra(EXTRA_KEY_CHALLENGE, challenge);
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
-        return new CredentialModel(intent, SystemClock.elapsedRealtimeClock());
-    }
-
-    private CredentialModel newValidTokenCredentialModel(int userId) {
-        return newCredentialModel(userId, 1L, new byte[] { 0 }, 0L);
-    }
-
-    private CredentialModel newInvalidChallengeCredentialModel(int userId) {
-        return newCredentialModel(userId, INVALID_CHALLENGE, null, 0L);
-    }
-
-    private CredentialModel newGkPwHandleCredentialModel(int userId, long gkPwHandle) {
-        return newCredentialModel(userId, INVALID_CHALLENGE, null, gkPwHandle);
-    }
-
-    private void verifyNothingHappen() {
-        assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
-    }
-
-    private void verifyOnlyActionLiveData(@CredentialAction int action) {
-        final Integer value = mAutoCredentialViewModel.getActionLiveData().getValue();
-        assertThat(value).isEqualTo(action);
-    }
-
-    private void setupGenerateTokenFlow(long gkPwHandle, int userId, int newSensorId,
-            long newChallenge) {
+    private void setupGenerateChallenge(int userId, int newSensorId, long newChallenge) {
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_SOMETHING);
         mChallengeGenerator.mUserId = userId;
         mChallengeGenerator.mSensorId = newSensorId;
         mChallengeGenerator.mChallenge = newChallenge;
-        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
-                .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
     }
 
     @Test
-    public void checkCredential_validCredentialCase() {
+    public void testSetCredentialModel_sameResultFromSavedInstanceOrIntent() {
+        final Bundle extras = newCredentialModelIntentExtras(12, 33, 1, new byte[] { 2, 3 }, 3L);
+
+        AutoCredentialViewModel viewModel2 = new AutoCredentialViewModel(
+                ApplicationProvider.getApplicationContext(),
+                mLockPatternUtils,
+                mChallengeGenerator);
+
+        mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
+        final Bundle savedInstance = new Bundle();
+        savedInstance.putBundle(KEY_CREDENTIAL_MODEL, extras);
+        viewModel2.setCredentialModel(savedInstance, new Intent());
+
+        final Bundle bundle1 = mViewModel.createCredentialIntentExtra();
+        final Bundle bundle2 = viewModel2.createCredentialIntentExtra();
+        assertThat(bundle1.getLong(EXTRA_KEY_GK_PW_HANDLE))
+                .isEqualTo(bundle2.getLong(EXTRA_KEY_GK_PW_HANDLE));
+        assertThat(bundle1.getLong(Intent.EXTRA_USER_ID))
+                .isEqualTo(bundle2.getLong(Intent.EXTRA_USER_ID));
+        assertThat(bundle1.getLong(EXTRA_KEY_CHALLENGE))
+                .isEqualTo(bundle2.getLong(EXTRA_KEY_CHALLENGE));
+        assertThat(bundle1.getInt(EXTRA_KEY_SENSOR_ID))
+                .isEqualTo(bundle2.getInt(EXTRA_KEY_SENSOR_ID));
+        final byte[] token1 = bundle1.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
+        final byte[] token2 = bundle2.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
+        assertThat(token1).isNotNull();
+        assertThat(token2).isNotNull();
+        assertThat(token1.length).isEqualTo(token2.length);
+        for (int i = 0; i < token2.length; ++i) {
+            assertThat(token1[i]).isEqualTo(token2[i]);
+        }
+    }
+
+    @Test
+    public void testSetCredentialModel_sameResultFromSavedInstanceOrIntent_invalidValues() {
+        final Bundle extras = newCredentialModelIntentExtras(UserHandle.USER_NULL,
+                INVALID_CHALLENGE, INVALID_SENSOR_ID, null, INVALID_GK_PW_HANDLE);
+
+        AutoCredentialViewModel viewModel2 = new AutoCredentialViewModel(
+                ApplicationProvider.getApplicationContext(),
+                mLockPatternUtils,
+                mChallengeGenerator);
+
+        mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
+        final Bundle savedInstance = new Bundle();
+        savedInstance.putBundle(KEY_CREDENTIAL_MODEL, extras);
+        viewModel2.setCredentialModel(savedInstance, new Intent());
+
+        final Bundle bundle1 = mViewModel.createCredentialIntentExtra();
+        final Bundle bundle2 = viewModel2.createCredentialIntentExtra();
+        assertThat(bundle1.containsKey(EXTRA_KEY_GK_PW_HANDLE)).isFalse();
+        assertThat(bundle2.containsKey(EXTRA_KEY_GK_PW_HANDLE)).isFalse();
+        assertThat(bundle1.containsKey(EXTRA_KEY_CHALLENGE_TOKEN)).isFalse();
+        assertThat(bundle2.containsKey(EXTRA_KEY_CHALLENGE_TOKEN)).isFalse();
+        assertThat(bundle1.containsKey(EXTRA_KEY_SENSOR_ID)).isTrue();
+        assertThat(bundle2.containsKey(EXTRA_KEY_SENSOR_ID)).isTrue();
+        assertThat(bundle1.containsKey(Intent.EXTRA_USER_ID)).isFalse();
+        assertThat(bundle2.containsKey(Intent.EXTRA_USER_ID)).isFalse();
+    }
+
+    @Test
+    public void testCheckCredential_validCredentialCase() {
         final int userId = 99;
-        mAutoCredentialViewModel.setCredentialModel(newValidTokenCredentialModel(userId));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newValidTokenCredentialIntentExtras(userId)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_SOMETHING);
 
         // Run credential check
-        mAutoCredentialViewModel.onCreate(mLifecycleOwner);
+        @CredentialAction final int action = mViewModel.checkCredential();
 
-        verifyNothingHappen();
+        assertThat(action).isEqualTo(CREDENTIAL_VALID);
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
-    public void checkCredential_needToChooseLock() {
+    public void testCheckCredential_needToChooseLock() {
         final int userId = 100;
-        mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_UNSPECIFIED);
 
         // Run credential check
-        mAutoCredentialViewModel.onCreate(mLifecycleOwner);
+        @CredentialAction final int action = mViewModel.checkCredential();
 
-        verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
+        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
-    public void checkCredential_needToConfirmLockFoSomething() {
+    public void testCheckCredential_needToConfirmLockFoSomething() {
         final int userId = 101;
-        mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_SOMETHING);
 
         // Run credential check
-        mAutoCredentialViewModel.onCreate(mLifecycleOwner);
+        @CredentialAction final int action = mViewModel.checkCredential();
 
-        verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
+        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
-    public void checkCredential_needToConfirmLockForNumeric() {
+    public void testCheckCredential_needToConfirmLockForNumeric() {
         final int userId = 102;
-        mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_NUMERIC);
 
         // Run credential check
-        mAutoCredentialViewModel.onCreate(mLifecycleOwner);
+        @CredentialAction final int action = mViewModel.checkCredential();
 
-        verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
+        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
-    public void checkCredential_needToConfirmLockForAlphabetic() {
+    public void testCheckCredential_needToConfirmLockForAlphabetic() {
         final int userId = 103;
-        mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_ALPHABETIC);
 
         // Run credential check
-        mAutoCredentialViewModel.onCreate(mLifecycleOwner);
+        @CredentialAction final int action = mViewModel.checkCredential();
 
-        verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
+        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
-    public void checkCredential_generateChallenge() {
+    public void testCheckCredential_generateChallenge() {
         final int userId = 104;
         final long gkPwHandle = 1111L;
-        final CredentialModel credentialModel = newGkPwHandleCredentialModel(userId, gkPwHandle);
-        mAutoCredentialViewModel.setCredentialModel(credentialModel);
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_SOMETHING);
 
         final int newSensorId = 10;
         final long newChallenge = 20L;
-        setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge);
+        setupGenerateChallenge(userId, newSensorId, newChallenge);
+        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
+                .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
 
         // Run credential check
-        mAutoCredentialViewModel.onCreate(mLifecycleOwner);
+        @CredentialAction final int action = mViewModel.checkCredential();
 
-        assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
-        assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId);
-        assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge);
-        assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue();
-        assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse();
+        assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
+        final Bundle extras = mViewModel.createCredentialIntentExtra();
+        assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
+        assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
+        assertThat(isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN))).isTrue();
+        assertThat(isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE))).isFalse();
         assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
     }
 
     @Test
-    public void testGetUserId() {
+    public void testCheckCredential_generateChallengeFail() {
+        final int userId = 104;
+        final long gkPwHandle = 1111L;
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
+        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+                PASSWORD_QUALITY_SOMETHING);
+
+        final int newSensorId = 10;
+        final long newChallenge = 20L;
+        setupGenerateChallenge(userId, newSensorId, newChallenge);
+        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
+                .thenReturn(newBadCredential(0));
+
+        // Run credential check
+        @CredentialAction final int action = mViewModel.checkCredential();
+
+        assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isTrue();
+        assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
+    }
+
+    @Test
+    public void testGetUserId_fromIntent() {
         final int userId = 106;
-        mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
 
         // Get userId
-        assertThat(mAutoCredentialViewModel.getUserId()).isEqualTo(userId);
+        assertThat(mViewModel.getUserId()).isEqualTo(userId);
+    }
+
+    @Test
+    public void testGetUserId_fromSavedInstance() {
+        final int userId = 106;
+        final Bundle savedInstance = new Bundle();
+        savedInstance.putBundle(KEY_CREDENTIAL_MODEL,
+                newInvalidChallengeCredentialIntentExtras(userId));
+        mViewModel.setCredentialModel(savedInstance, new Intent());
+
+        // Get userId
+        assertThat(mViewModel.getUserId()).isEqualTo(userId);
     }
 
     @Test
     public void testCheckNewCredentialFromActivityResult_invalidChooseLock() {
         final int userId = 107;
         final long gkPwHandle = 3333L;
-        mAutoCredentialViewModel.setCredentialModel(
-                newGkPwHandleCredentialModel(userId, gkPwHandle));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
         final Intent intent = new Intent();
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
+        intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
 
         // run checkNewCredentialFromActivityResult()
-        final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true,
+        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
                 new ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent));
 
         assertThat(ret).isFalse();
-        verifyNothingHappen();
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
     public void testCheckNewCredentialFromActivityResult_invalidConfirmLock() {
         final int userId = 107;
         final long gkPwHandle = 3333L;
-        mAutoCredentialViewModel.setCredentialModel(
-                newGkPwHandleCredentialModel(userId, gkPwHandle));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
         final Intent intent = new Intent();
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
+        intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
 
         // run checkNewCredentialFromActivityResult()
-        final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false,
+        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
                 new ActivityResult(Activity.RESULT_OK + 1, intent));
 
         assertThat(ret).isFalse();
-        verifyNothingHappen();
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
     public void testCheckNewCredentialFromActivityResult_nullDataChooseLock() {
         final int userId = 108;
         final long gkPwHandle = 4444L;
-        mAutoCredentialViewModel.setCredentialModel(
-                newGkPwHandleCredentialModel(userId, gkPwHandle));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
 
         // run checkNewCredentialFromActivityResult()
-        final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true,
+        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
                 new ActivityResult(ChooseLockPattern.RESULT_FINISHED, null));
 
         assertThat(ret).isFalse();
-        verifyNothingHappen();
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
     public void testCheckNewCredentialFromActivityResult_nullDataConfirmLock() {
         final int userId = 109;
-        mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
 
         // run checkNewCredentialFromActivityResult()
-        final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false,
+        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
                 new ActivityResult(Activity.RESULT_OK, null));
 
         assertThat(ret).isFalse();
-        verifyNothingHappen();
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
     }
 
     @Test
     public void testCheckNewCredentialFromActivityResult_validChooseLock() {
         final int userId = 108;
-        final CredentialModel credentialModel = newInvalidChallengeCredentialModel(userId);
-        mAutoCredentialViewModel.setCredentialModel(credentialModel);
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_SOMETHING);
 
         final long gkPwHandle = 6666L;
         final int newSensorId = 50;
         final long newChallenge = 60L;
-        setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge);
-        final Intent intent = new Intent();
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
+        setupGenerateChallenge(userId, newSensorId, newChallenge);
+        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
+                .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
 
         // Run checkNewCredentialFromActivityResult()
-        final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true,
+        final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
+        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
                 new ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent));
 
         assertThat(ret).isTrue();
-        assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
-        assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId);
-        assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge);
-        assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue();
-        assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse();
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
+        final Bundle extras = mViewModel.createCredentialIntentExtra();
+        assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
+        assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
+        assertThat(isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN))).isTrue();
+        assertThat(isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE))).isFalse();
         assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
     }
 
@@ -317,28 +404,30 @@
     @Test
     public void testCheckNewCredentialFromActivityResult_validConfirmLock() {
         final int userId = 109;
-        final CredentialModel credentialModel = newInvalidChallengeCredentialModel(userId);
-        mAutoCredentialViewModel.setCredentialModel(credentialModel);
+        mViewModel.setCredentialModel(null,
+                new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
         when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
                 PASSWORD_QUALITY_SOMETHING);
 
         final long gkPwHandle = 5555L;
         final int newSensorId = 80;
         final long newChallenge = 90L;
-        setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge);
-        final Intent intent = new Intent();
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
+        setupGenerateChallenge(userId, newSensorId, newChallenge);
+        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
+                .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
 
         // Run checkNewCredentialFromActivityResult()
-        final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false,
+        final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
+        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
                 new ActivityResult(Activity.RESULT_OK, intent));
 
         assertThat(ret).isTrue();
-        assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
-        assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId);
-        assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge);
-        assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue();
-        assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse();
+        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
+        final Bundle extras = mViewModel.createCredentialIntentExtra();
+        assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
+        assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
+        assertThat(isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN))).isTrue();
+        assertThat(isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE))).isFalse();
         assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
     }
 
@@ -377,4 +466,12 @@
                 .setGatekeeperHAT(hat)
                 .build();
     }
+
+    private VerifyCredentialResponse newBadCredential(int timeout) {
+        if (timeout > 0) {
+            return VerifyCredentialResponse.fromTimeout(timeout);
+        } else {
+            return VerifyCredentialResponse.fromError();
+        }
+    }
 }
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModelTest.java
index 5069ea1..a9536cf 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollIntroViewModelTest.java
@@ -46,6 +46,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.MutableLiveData;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -93,6 +94,18 @@
     }
 
     @Test
+    public void testClearActionLiveData() {
+        final MutableLiveData<Integer> actionLiveData =
+                (MutableLiveData<Integer>) mViewModel.getActionLiveData();
+        actionLiveData.postValue(1);
+        assertThat(actionLiveData.getValue()).isEqualTo(1);
+
+        mViewModel.clearActionLiveData();
+
+        assertThat(actionLiveData.getValue()).isNull();
+    }
+
+    @Test
     public void testGetEnrollmentRequest() {
         final EnrollmentRequest request = newAllFalseRequest(mApplication);
 
@@ -202,11 +215,13 @@
 
     @Test
     public void testSetHasScrolledToBottom() {
-        mViewModel.setHasScrolledToBottom();
-
+        mViewModel.setHasScrolledToBottom(true);
         FingerprintEnrollIntroStatus status = mViewModel.getPageStatusLiveData().getValue();
-
         assertThat(status.hasScrollToBottom()).isEqualTo(true);
+
+        mViewModel.setHasScrolledToBottom(false);
+        status = mViewModel.getPageStatusLiveData().getValue();
+        assertThat(status.hasScrollToBottom()).isEqualTo(false);
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/network/InternetResetHelperTest.java b/tests/unit/src/com/android/settings/network/InternetResetHelperTest.java
index e6df8a0..3fe6882 100644
--- a/tests/unit/src/com/android/settings/network/InternetResetHelperTest.java
+++ b/tests/unit/src/com/android/settings/network/InternetResetHelperTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.network;
 
+import static com.android.settings.network.InternetResetHelper.RESTART_TIMEOUT_MS;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -29,8 +31,6 @@
 import android.content.Context;
 import android.content.IntentFilter;
 import android.net.wifi.WifiManager;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
 
 import androidx.lifecycle.Lifecycle;
@@ -39,7 +39,6 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager;
 import com.android.settingslib.utils.HandlerInjector;
 
 import org.junit.Before;
@@ -47,6 +46,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -55,66 +55,46 @@
 
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Spy
+    private final Context mContext = ApplicationProvider.getApplicationContext();
     @Mock
     private WifiManager mWifiManager;
     @Mock
-    public HandlerThread mWorkerThread;
+    InternetResetHelper.RecoveryWorker mRecoveryWorker;
     @Mock
-    public ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager;
+    HandlerInjector mHandlerInjector;
     @Mock
     public NetworkMobileProviderController mMobileNetworkController;
 
-    private Context mContext;
     private InternetResetHelper mInternetResetHelper;
     private Preference mResettingPreference;
     private Preference mWifiTogglePreferences;
     private PreferenceCategory mConnectedWifiEntryPreferences;
+    private PreferenceCategory mFirstWifiEntryPreference;
     private PreferenceCategory mWifiEntryPreferences;
 
-    private FakeHandlerInjector mFakeHandlerInjector;
-
-    private static class FakeHandlerInjector extends HandlerInjector {
-
-        private Runnable mRunnable;
-
-        FakeHandlerInjector(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void postDelayed(Runnable runnable, long delayMillis) {
-            mRunnable = runnable;
-        }
-
-        public Runnable getRunnable() {
-            return mRunnable;
-        }
-    }
-
     @Before
     public void setUp() {
-        mContext = spy(ApplicationProvider.getApplicationContext());
         when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
+        when(mRecoveryWorker.isRecovering()).thenReturn(false);
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
-        mResettingPreference = new Preference(mContext);
+        mResettingPreference = spy(new Preference(mContext));
         mWifiTogglePreferences = new Preference(mContext);
         mConnectedWifiEntryPreferences = spy(new PreferenceCategory(mContext));
+        mFirstWifiEntryPreference = spy(new PreferenceCategory(mContext));
         mWifiEntryPreferences = spy(new PreferenceCategory(mContext));
 
-        final Lifecycle lifecycle = mock(Lifecycle.class);
-        mInternetResetHelper = new InternetResetHelper(mContext, lifecycle);
-        mInternetResetHelper.mWorkerThread = mWorkerThread;
-        mFakeHandlerInjector = new FakeHandlerInjector(mContext.getMainThreadHandler());
-        mInternetResetHelper.mHandlerInjector = mFakeHandlerInjector;
-        mInternetResetHelper.mConnectivitySubsystemsRecoveryManager =
-                mConnectivitySubsystemsRecoveryManager;
-        mInternetResetHelper.setResettingPreference(mResettingPreference);
-        mInternetResetHelper.setMobileNetworkController(mMobileNetworkController);
-        mInternetResetHelper.setWifiTogglePreference(mWifiTogglePreferences);
-        mInternetResetHelper.addWifiNetworkPreference(mConnectedWifiEntryPreferences);
-        mInternetResetHelper.addWifiNetworkPreference(mWifiEntryPreferences);
+        mInternetResetHelper = new InternetResetHelper(mContext, mock(Lifecycle.class),
+                mMobileNetworkController,
+                mWifiTogglePreferences,
+                mConnectedWifiEntryPreferences,
+                mFirstWifiEntryPreference,
+                mWifiEntryPreferences,
+                mResettingPreference);
+        mInternetResetHelper.mHandlerInjector = mHandlerInjector;
+        mInternetResetHelper.mRecoveryWorker = mRecoveryWorker;
     }
 
     @Test
@@ -135,30 +115,10 @@
     }
 
     @Test
-    public void onDestroy_quitWorkerThread() {
+    public void onDestroy_removeCallbacks() {
         mInternetResetHelper.onDestroy();
 
-        verify(mWorkerThread).quit();
-    }
-
-    @Test
-    public void onSubsystemRestartOperationEnd_recoveryIsNotReady_postResumeRunnable() {
-        mInternetResetHelper.mIsRecoveryReady = false;
-
-        mInternetResetHelper.onSubsystemRestartOperationEnd();
-
-        assertThat(mInternetResetHelper.mIsRecoveryReady).isTrue();
-        assertThat(mFakeHandlerInjector.getRunnable())
-                .isEqualTo(mInternetResetHelper.mResumeRunnable);
-    }
-
-    @Test
-    public void onSubsystemRestartOperationEnd_recoveryIsReady_doNothing() {
-        mInternetResetHelper.mIsRecoveryReady = true;
-
-        mInternetResetHelper.onSubsystemRestartOperationEnd();
-
-        assertThat(mFakeHandlerInjector.getRunnable()).isNull();
+        verify(mHandlerInjector).removeCallbacks(any());
     }
 
     @Test
@@ -169,19 +129,16 @@
         mInternetResetHelper.updateWifiStateChange();
 
         assertThat(mInternetResetHelper.mIsWifiReady).isFalse();
-        assertThat(mFakeHandlerInjector.getRunnable()).isNull();
     }
 
     @Test
-    public void updateWifiStateChange_wifiIsNotReadyAndWifiEnabled_postResumeRunnable() {
+    public void updateWifiStateChange_wifiIsNotReadyAndWifiEnabled_updateWifiIsReady() {
         mInternetResetHelper.mIsWifiReady = false;
         when(mWifiManager.isWifiEnabled()).thenReturn(true);
 
         mInternetResetHelper.updateWifiStateChange();
 
         assertThat(mInternetResetHelper.mIsWifiReady).isTrue();
-        assertThat(mFakeHandlerInjector.getRunnable())
-                .isEqualTo(mInternetResetHelper.mResumeRunnable);
     }
 
     @Test
@@ -191,7 +148,6 @@
         mInternetResetHelper.updateWifiStateChange();
 
         assertThat(mInternetResetHelper.mIsWifiReady).isTrue();
-        assertThat(mFakeHandlerInjector.getRunnable()).isNull();
     }
 
     @Test
@@ -203,16 +159,15 @@
         // Hide subsystem preferences
         verify(mMobileNetworkController).hidePreference(true /* hide */, true /* immediately*/);
         assertThat(mWifiTogglePreferences.isVisible()).isFalse();
-        verify(mConnectedWifiEntryPreferences).removeAll();
         assertThat(mConnectedWifiEntryPreferences.isVisible()).isFalse();
-        verify(mWifiEntryPreferences).removeAll();
+        assertThat(mFirstWifiEntryPreference.isVisible()).isFalse();
         assertThat(mWifiEntryPreferences.isVisible()).isFalse();
     }
 
     @Test
     public void resumePreferences_onlyRecoveryReady_shouldShowSubSysHideResetting() {
         mInternetResetHelper.suspendPreferences();
-        mInternetResetHelper.mIsRecoveryReady = true;
+        when(mRecoveryWorker.isRecovering()).thenReturn(false);
         mInternetResetHelper.mIsWifiReady = false;
 
         mInternetResetHelper.resumePreferences();
@@ -224,13 +179,14 @@
         // Hide Wi-Fi preferences
         assertThat(mWifiTogglePreferences.isVisible()).isFalse();
         assertThat(mConnectedWifiEntryPreferences.isVisible()).isFalse();
+        assertThat(mFirstWifiEntryPreference.isVisible()).isFalse();
         assertThat(mWifiEntryPreferences.isVisible()).isFalse();
     }
 
     @Test
     public void resumePreferences_onlyWifiReady_shouldShowSubSysHideResetting() {
         mInternetResetHelper.suspendPreferences();
-        mInternetResetHelper.mIsRecoveryReady = false;
+        when(mRecoveryWorker.isRecovering()).thenReturn(true);
         mInternetResetHelper.mIsWifiReady = true;
 
         mInternetResetHelper.resumePreferences();
@@ -240,6 +196,7 @@
         // Show Wi-Fi preferences
         assertThat(mWifiTogglePreferences.isVisible()).isTrue();
         assertThat(mConnectedWifiEntryPreferences.isVisible()).isTrue();
+        assertThat(mFirstWifiEntryPreference.isVisible()).isTrue();
         assertThat(mWifiEntryPreferences.isVisible()).isTrue();
         // Hide Mobile Network controller
         verify(mMobileNetworkController, never())
@@ -249,14 +206,16 @@
     @Test
     public void resumePreferences_allReady_shouldShowSubSysHideResetting() {
         mInternetResetHelper.suspendPreferences();
-        mInternetResetHelper.mIsRecoveryReady = true;
+        when(mRecoveryWorker.isRecovering()).thenReturn(false);
         mInternetResetHelper.mIsWifiReady = true;
+
         mInternetResetHelper.resumePreferences();
 
         // Show subsystem preferences
         verify(mMobileNetworkController).hidePreference(false, true);
         assertThat(mWifiTogglePreferences.isVisible()).isTrue();
         assertThat(mConnectedWifiEntryPreferences.isVisible()).isTrue();
+        assertThat(mFirstWifiEntryPreference.isVisible()).isTrue();
         assertThat(mWifiEntryPreferences.isVisible()).isTrue();
         // Hide resetting preference
         assertThat(mResettingPreference.isVisible()).isFalse();
@@ -264,22 +223,39 @@
 
     @Test
     public void restart_recoveryNotAvailable_shouldDoTriggerSubsystemRestart() {
-        when(mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()).thenReturn(false);
+        when(mRecoveryWorker.isRecoveryAvailable()).thenReturn(false);
 
         mInternetResetHelper.restart();
 
-        verify(mConnectivitySubsystemsRecoveryManager, never())
-                .triggerSubsystemRestart(any(), any());
+        verify(mRecoveryWorker, never()).triggerRestart();
     }
 
     @Test
     public void restart_recoveryAvailable_triggerSubsystemRestart() {
-        when(mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()).thenReturn(true);
+        when(mRecoveryWorker.isRecoveryAvailable()).thenReturn(true);
 
         mInternetResetHelper.restart();
 
-        assertThat(mFakeHandlerInjector.getRunnable())
-                .isEqualTo(mInternetResetHelper.mTimeoutRunnable);
-        verify(mConnectivitySubsystemsRecoveryManager).triggerSubsystemRestart(any(), any());
+        verify(mHandlerInjector)
+                .postDelayed(mInternetResetHelper.mTimeoutRunnable, RESTART_TIMEOUT_MS);
+        verify(mRecoveryWorker).triggerRestart();
+    }
+
+    @Test
+    public void checkRecovering_isRecovering_showResetting() {
+        when(mRecoveryWorker.isRecovering()).thenReturn(true);
+
+        mInternetResetHelper.checkRecovering();
+
+        verify(mResettingPreference).setVisible(true);
+    }
+
+    @Test
+    public void checkRecovering_isNotRecovering_doNotShowResetting() {
+        when(mRecoveryWorker.isRecovering()).thenReturn(false);
+
+        mInternetResetHelper.checkRecovering();
+
+        verify(mResettingPreference, never()).setVisible(true);
     }
 }
diff --git a/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
index 45f3693..16e3963 100644
--- a/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
@@ -28,18 +28,24 @@
 
 import android.app.Instrumentation;
 import android.content.Context;
+import android.os.Looper;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
 import androidx.fragment.app.FragmentManager;
 import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
+import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,6 +55,10 @@
 
 @RunWith(AndroidJUnit4.class)
 public class MobileDataPreferenceControllerTest {
+    private static final String SUB_ID_1 = "1";
+    private static final String SUB_ID_2 = "2";
+    private static final String DISPLAY_NAME_1 = "Sub 1";
+    private static final String DISPLAY_NAME_2 = "Sub 2";
     private static final int SUB_ID = 2;
     private static final int SUB_ID_OTHER = 3;
 
@@ -64,7 +74,15 @@
     private SubscriptionInfo mSubscriptionInfo;
     @Mock
     private FragmentTransaction mFragmentTransaction;
-
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private LifecycleOwner mLifecycleOwner;
+    private SubscriptionInfoEntity mSubInfo1;
+    private SubscriptionInfoEntity mSubInfo2;
+    private MobileNetworkInfoEntity mNetworkInfo1;
+    private MobileNetworkInfoEntity mNetworkInfo2;
+    private LifecycleRegistry mLifecycleRegistry;
     private MobileDataPreferenceController mController;
     private SwitchPreference mPreference;
     private Context mContext;
@@ -73,6 +91,10 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
         mContext = spy(ApplicationProvider.getApplicationContext());
         doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
 
@@ -83,22 +105,45 @@
         doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction();
 
         mPreference = new SwitchPreference(mContext);
-        mController = new MobileDataPreferenceController(mContext, "mobile_data");
-        mController.init(mFragmentManager, SUB_ID);
+        mController = new MobileDataPreferenceController(mContext, "mobile_data", mLifecycle,
+                mLifecycleOwner, SUB_ID);
+        mController.init(mFragmentManager, SUB_ID, mSubInfo1, mNetworkInfo1);
         mPreference.setKey(mController.getPreferenceKey());
+        mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
+        when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry);
+    }
+
+    private SubscriptionInfoEntity setupSubscriptionInfoEntity(String subId, String displayName,
+            boolean isOpportunistic, boolean isValid, boolean isActive, boolean isAvailable,
+            boolean isDefaultData) {
+        int id = Integer.parseInt(subId);
+        return new SubscriptionInfoEntity(subId, id, id,
+                displayName, displayName, 0, "mcc", "mnc", "countryIso", false, id,
+                TelephonyManager.DEFAULT_PORT_INDEX, isOpportunistic, null,
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, displayName, false,
+                "1234567890", true, "default", false, isValid, true, isActive, isAvailable, false,
+                false, isDefaultData, false, false);
+    }
+
+    private MobileNetworkInfoEntity setupMobileNetworkInfoEntity(String subId,
+            boolean isDatEnabled) {
+        return new MobileNetworkInfoEntity(subId, false, false, isDatEnabled, false, false, false,
+                false, false, false, false, false);
     }
 
     @Test
     public void getAvailabilityStatus_invalidSubscription_returnAvailableUnsearchable() {
-        mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID, mSubInfo1,
+                mNetworkInfo1);
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
     }
 
     @Test
     public void isDialogNeeded_disableSingleSim_returnFalse() {
-        doReturn(true).when(mTelephonyManager).isDataEnabled();
-        doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+        mSubInfo1 = setupSubscriptionInfoEntity(SUB_ID_1, DISPLAY_NAME_1, false, true, true, true,
+                true);
+        mNetworkInfo1 = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
         doReturn(1).when(mTelephonyManager).getActiveModemCount();
 
         assertThat(mController.isDialogNeeded()).isFalse();
@@ -106,12 +151,15 @@
 
     @Test
     public void isDialogNeeded_enableNonDefaultSimInMultiSimMode_returnTrue() {
-        doReturn(false).when(mTelephonyManager).isDataEnabled();
-        doReturn(mSubscriptionInfo).when(mSubscriptionManager)
-                .getActiveSubscriptionInfo(SUB_ID);
+        mSubInfo1 = setupSubscriptionInfoEntity(SUB_ID_1, DISPLAY_NAME_1, false, true, true, true,
+                false);
+        mNetworkInfo1 = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false);
+        doReturn(1).when(mTelephonyManager).getActiveModemCount();
         // Ideally, it would be better if we could set the default data subscription to
         // SUB_ID_OTHER, and set that as an active subscription id.
-        when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true);
+        mSubInfo2 = setupSubscriptionInfoEntity(SUB_ID_2, DISPLAY_NAME_2, false, true, true, true,
+                true);
+        mNetworkInfo1 = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
 
         assertThat(mController.isDialogNeeded()).isTrue();
@@ -132,8 +180,11 @@
 
     @Test
     public void onPreferenceChange_singleSim_On_shouldEnableData() {
-        doReturn(true).when(mTelephonyManager).isDataEnabled();
-        doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+        mSubInfo1 = setupSubscriptionInfoEntity(SUB_ID_1, DISPLAY_NAME_1, true, true, true, true,
+                true);
+        mNetworkInfo1 = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
+        mController.setSubscriptionInfoEntity(mSubInfo1);
+        mController.setMobileNetworkInfoEntity(mNetworkInfo1);
         doReturn(1).when(mTelephonyManager).getActiveModemCount();
 
         mController.onPreferenceChange(mPreference, true);
@@ -143,8 +194,11 @@
 
     @Test
     public void onPreferenceChange_multiSim_On_shouldEnableData() {
-        doReturn(true).when(mTelephonyManager).isDataEnabled();
-        doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+        mSubInfo1 = setupSubscriptionInfoEntity(SUB_ID_1, DISPLAY_NAME_1, true, true, true, true,
+                true);
+        mNetworkInfo1 = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
+        mController.setSubscriptionInfoEntity(mSubInfo1);
+        mController.setMobileNetworkInfoEntity(mNetworkInfo1);
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
 
         mController.onPreferenceChange(mPreference, true);
@@ -154,18 +208,20 @@
 
     @Test
     public void isChecked_returnUserDataEnabled() {
-        mController.init(mFragmentManager, SUB_ID);
+        mNetworkInfo1 = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false);
+        mController.init(mFragmentManager, SUB_ID, mSubInfo1, mNetworkInfo1);
         assertThat(mController.isChecked()).isFalse();
 
-        doReturn(true).when(mTelephonyManager).isDataEnabled();
+        mNetworkInfo1 = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
+        mController.setMobileNetworkInfoEntity(mNetworkInfo1);
         assertThat(mController.isChecked()).isTrue();
     }
 
     @Test
     public void updateState_opportunistic_disabled() {
-        doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
-        mController.init(mFragmentManager, SUB_ID);
-        doReturn(true).when(mSubscriptionInfo).isOpportunistic();
+        mSubInfo1 = setupSubscriptionInfoEntity(SUB_ID_1, DISPLAY_NAME_1, true, true, true, true,
+                true);
+        mController.init(mFragmentManager, SUB_ID, mSubInfo1, mNetworkInfo1);
         mController.updateState(mPreference);
 
         assertThat(mPreference.isEnabled()).isFalse();
@@ -176,9 +232,9 @@
 
     @Test
     public void updateState_notOpportunistic_enabled() {
-        doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
-        mController.init(mFragmentManager, SUB_ID);
-        doReturn(false).when(mSubscriptionInfo).isOpportunistic();
+        mSubInfo1 = setupSubscriptionInfoEntity(SUB_ID_1, DISPLAY_NAME_1, false, true, true, true,
+                true);
+        mController.init(mFragmentManager, SUB_ID, mSubInfo1, mNetworkInfo1);
         mController.updateState(mPreference);
 
         assertThat(mPreference.isEnabled()).isTrue();