Merge "Use SettingsAlertDialog for app button dialogs"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 17b9414..b44274a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2524,6 +2524,8 @@
             <meta-data android:name="com.android.settings.icon_tintable" android:value="true" />
         </activity-alias>
 
+        <activity android:name=".biometrics.activeunlock.ActiveUnlockRequireBiometricSetup" android:exported="false"/>
+
         <!-- Note this must not be exported since it returns the password in the intent -->
         <activity android:name=".password.ConfirmLockPattern$InternalActivity"
             android:exported="false"
diff --git a/res/layout/activeunlock_require_biometric_setup.xml b/res/layout/activeunlock_require_biometric_setup.xml
new file mode 100644
index 0000000..db374ff
--- /dev/null
+++ b/res/layout/activeunlock_require_biometric_setup.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.google.android.setupdesign.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    style="?attr/fingerprint_layout_theme"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</com.google.android.setupdesign.GlifLayout>
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index e6dd3ed..1b4aeb0 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -22,7 +22,7 @@
     <Preference
         android:key="manage_external_storage"
         android:title="@string/manage_external_storage_title"
-        android:order="100"
+        android:order="-1900"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
         <extra
             android:name="classname"
@@ -32,7 +32,7 @@
     <Preference
         android:key="interact_across_profiles"
         android:title="@string/interact_across_profiles_title"
-        android:order="200"
+        android:order="-1800"
         android:fragment="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesSettings"
         settings:keywords="@string/interact_across_profiles_keywords"
         settings:controller="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesController" />
@@ -40,7 +40,7 @@
     <Preference
         android:key="device_administrators"
         android:title="@string/manage_device_admin"
-        android:order="300"
+        android:order="-1700"
         android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings"
         android:summary="@string/summary_placeholder"
         settings:searchable="false"
@@ -49,7 +49,7 @@
     <Preference
         android:key="system_alert_window_app_list"
         android:title="@string/system_alert_window_settings"
-        android:order="400"
+        android:order="-1600"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_draw_overlay"
         settings:controller="com.android.settings.applications.specialaccess.SystemAlertWindowPreferenceController">
@@ -61,14 +61,14 @@
     <Preference
         android:key="zen_access"
         android:title="@string/manage_zen_access_title"
-        android:order="500"
+        android:order="-1500"
         android:fragment="com.android.settings.notification.zen.ZenAccessSettings"
         settings:controller="com.android.settings.applications.specialaccess.zenaccess.ZenAccessController" />
 
     <Preference
         android:key="media_management_apps"
         android:title="@string/media_management_apps_title"
-        android:order="600"
+        android:order="-1400"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_media_management_apps">
         <extra
@@ -79,7 +79,7 @@
     <Preference
         android:key="write_settings_apps"
         android:title="@string/write_settings"
-        android:order="700"
+        android:order="-1300"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_write_settings">
         <extra
@@ -90,14 +90,14 @@
     <Preference
         android:key="notification_access"
         android:title="@string/manage_notification_access_title"
-        android:order="800"
+        android:order="-1200"
         android:fragment="com.android.settings.notification.NotificationAccessSettings"
         settings:controller="com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessController" />
 
     <Preference
         android:key="picture_in_picture"
         android:title="@string/picture_in_picture_title"
-        android:order="900"
+        android:order="-1100"
         android:fragment="com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureSettings"
         settings:keywords="@string/picture_in_picture_keywords"
         settings:controller="com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureController" />
@@ -105,7 +105,7 @@
     <Preference
         android:key="premium_sms"
         android:title="@string/premium_sms_access"
-        android:order="1000"
+        android:order="-1000"
         android:fragment="com.android.settings.applications.specialaccess.premiumsms.PremiumSmsAccess"
         settings:isPreferenceVisible="@bool/config_show_premium_sms"
         settings:controller="com.android.settings.applications.specialaccess.premiumsms.PremiumSmsController" />
@@ -113,7 +113,7 @@
     <Preference
         android:key="data_saver"
         android:title="@string/unrestricted_data_saver"
-        android:order="1100"
+        android:order="-900"
         android:fragment="com.android.settings.datausage.UnrestrictedDataAccess"
         settings:isPreferenceVisible="@bool/config_show_data_saver"
         settings:controller="com.android.settings.applications.specialaccess.DataSaverController" />
@@ -121,7 +121,7 @@
     <Preference
         android:key="long_background_tasks"
         android:title="@string/long_background_tasks_title"
-        android:order="1200"
+        android:order="-800"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_long_background_tasks"
         settings:controller="com.android.settings.applications.specialaccess.applications.LongBackgroundTaskController">
@@ -133,7 +133,7 @@
     <Preference
         android:key="manage_external_sources"
         android:title="@string/install_other_apps"
-        android:order="1300"
+        android:order="-700"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_install_other_apps">
         <extra
@@ -144,7 +144,7 @@
     <Preference
         android:key="alarms_and_reminders"
         android:title="@string/alarms_and_reminders_title"
-        android:order="1400"
+        android:order="-600"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_alarms_and_reminders">
         <extra
@@ -155,7 +155,7 @@
     <Preference
         android:key="special_app_usage_access"
         android:title="@string/usage_access"
-        android:order="1500"
+        android:order="-500"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_write_settings">
         <extra
@@ -166,7 +166,7 @@
     <Preference
         android:key="enabled_vr_listeners"
         android:title="@string/vr_listeners_title"
-        android:order="1600"
+        android:order="-400"
         android:fragment="com.android.settings.applications.specialaccess.vrlistener.VrListenerSettings"
         settings:keywords="@string/keywords_vr_listener"
         settings:controller="com.android.settings.applications.specialaccess.vrlistener.EnabledVrListenersController">
@@ -178,7 +178,7 @@
     <Preference
         android:key="change_wifi_state"
         android:title="@string/change_wifi_state_title"
-        android:order="1700"
+        android:order="-300"
         android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
         settings:keywords="@string/keywords_change_wifi_state">
         <extra
@@ -189,12 +189,12 @@
     <Preference
         android:key="turn_screen_on"
         android:title="@string/turn_screen_on_title"
-        android:order="1800"
+        android:order="-200"
         android:fragment="com.android.settings.applications.specialaccess.turnscreenon.TurnScreenOnSettings" />
 
     <Preference
         android:key="special_access_more"
         android:title="@string/special_access_more"
-        android:order="1900"
+        android:order="-100"
         settings:controller="com.android.settings.applications.specialaccess.MoreSpecialAccessPreferenceController" />
 </PreferenceScreen>
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index 4f9b0fb..1e2a0c9 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -36,6 +36,7 @@
 import com.android.settings.Settings;
 import com.android.settings.SettingsActivity;
 import com.android.settings.SubSettings;
+import com.android.settings.biometrics.face.FaceEnrollIntroduction;
 import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
 import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
 import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
@@ -233,7 +234,7 @@
         addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
         addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
         addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
-        addActivityFilter(activityFilters, Settings.FaceSettingsInternalActivity.class);
+        addActivityFilter(activityFilters, FaceEnrollIntroduction.class);
         addActivityFilter(activityFilters, AvatarPickerActivity.class);
         addActivityFilter(activityFilters, ChooseLockPattern.class);
         mSplitController.registerRule(new ActivityRule(activityFilters, true /* alwaysExpand */));
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index b4f8967..e63a754 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -495,7 +495,13 @@
     @Override
     public void finish() {
         if (mGkPwHandle != null) {
-            BiometricUtils.removeGatekeeperPasswordHandle(this, mGkPwHandle);
+            // When launched as InternalActivity, the mGkPwHandle was gotten from intent extra
+            // instead of requesting from the user. Do not remove the mGkPwHandle in service side
+            // for this case because the caller activity may still need it and will be responsible
+            // for removing it.
+            if (!(this instanceof InternalActivity)) {
+                BiometricUtils.removeGatekeeperPasswordHandle(this, mGkPwHandle);
+            }
         }
         super.finish();
     }
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
index c2a8f39..8cc7d6a 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
@@ -87,10 +87,10 @@
 
     }
 
-    /** Starts listening for updates from the ContentProvider, and fetches the current value. */
-    public synchronized void subscribe() {
-        if (mSubscribed && mUri != null) {
-            return;
+    /** Returns true if start listening for updates from the ContentProvider, false otherwise. */
+    public synchronized boolean subscribe() {
+        if (mSubscribed || mUri == null) {
+            return false;
         }
         mSubscribed = true;
         mContext.getContentResolver().registerContentObserver(
@@ -99,15 +99,17 @@
                 () -> {
                     getContentFromUri();
                 });
+        return true;
     }
 
-    /** Stops listening for updates from the ContentProvider. */
-    public synchronized void unsubscribe() {
-        if (!mSubscribed && mUri != null) {
-            return;
+    /** Returns true if stops listening for updates from the ContentProvider, false otherewise. */
+    public synchronized boolean unsubscribe() {
+        if (!mSubscribed || mUri == null) {
+            return false;
         }
         mSubscribed = false;
         mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+        return true;
     }
 
     /** Retrieves the most recently fetched value from the ContentProvider. */
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java
new file mode 100644
index 0000000..1f30e56
--- /dev/null
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.activeunlock;
+
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
+import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
+import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
+
+import android.app.settings.SettingsEnums;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.biometrics.BiometricEnrollActivity;
+import com.android.settings.biometrics.BiometricEnrollBase;
+import com.android.settings.biometrics.combination.CombinedBiometricStatusUtils;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+
+/**
+ * Activity which instructs the user to set up face or fingerprint unlock before setting the watch
+ * unlock.
+ */
+public class ActiveUnlockRequireBiometricSetup extends BiometricEnrollBase {
+    private static final String TAG = "ActiveUnlockRequireBiometricSetup";
+
+    @VisibleForTesting
+    static final int BIOMETRIC_ENROLL_REQUEST = 1001;
+    private static final int ACTIVE_UNLOCK_REQUEST = 1002;
+    private long mGkPwHandle;
+    private boolean mNextClicked;
+    private ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activeunlock_require_biometric_setup);
+
+        mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(this);
+        mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
+        Log.i(TAG, "mUserId = " + mUserId);
+        mGkPwHandle = getIntent().getLongExtra(EXTRA_KEY_GK_PW_HANDLE, 0L);
+
+        final PackageManager pm = getApplicationContext().getPackageManager();
+        boolean hasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+        boolean hasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
+        if (hasFeatureFace && hasFeatureFingerprint) {
+            setHeaderText(
+                    R.string.security_settings_activeunlock_require_face_fingerprint_setup_title);
+            setDescriptionText(
+                    R.string.security_settings_activeunlock_require_face_fingerprint_setup_message);
+        } else if (hasFeatureFingerprint) {
+            setHeaderText(R.string.security_settings_activeunlock_require_fingerprint_setup_title);
+            setDescriptionText(
+                    R.string.security_settings_activeunlock_require_fingerprint_setup_message);
+        } else if (hasFeatureFace) {
+            setHeaderText(R.string.security_settings_activeunlock_require_face_setup_title);
+            setDescriptionText(
+                    R.string.security_settings_activeunlock_require_face_setup_message);
+        }
+
+        mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
+        mFooterBarMixin.setSecondaryButton(
+                new FooterButton.Builder(this)
+                        .setText(R.string.cancel)
+                        .setListener(this::onCancelClick)
+                        .setButtonType(FooterButton.ButtonType.CANCEL)
+                        .setTheme(R.style.SudGlifButton_Secondary)
+                        .build()
+        );
+
+        mFooterBarMixin.setPrimaryButton(
+                new FooterButton.Builder(this)
+                        .setText(R.string.security_settings_activeunlock_biometric_setup)
+                        .setListener(this::onNextButtonClick)
+                        .setButtonType(FooterButton.ButtonType.NEXT)
+                        .setTheme(R.style.SudGlifButton_Primary)
+                        .build()
+        );
+    }
+
+    @Override
+    public void onBackPressed() {
+        finish();
+    }
+
+    private void onCancelClick(View view) {
+        finish();
+    }
+
+    @Override
+    protected boolean shouldFinishWhenBackgrounded() {
+        return super.shouldFinishWhenBackgrounded() && !mNextClicked;
+    }
+
+    @Override
+    protected void onNextButtonClick(View view) {
+        mNextClicked = true;
+        Intent intent = new Intent(this, BiometricEnrollActivity.InternalActivity.class);
+        intent.setAction(ACTION_BIOMETRIC_ENROLL);
+        intent.putExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG);
+        intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+        intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle);
+        startActivityForResult(intent, BIOMETRIC_ENROLL_REQUEST);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == BIOMETRIC_ENROLL_REQUEST && resultCode != RESULT_CANCELED) {
+            CombinedBiometricStatusUtils combinedBiometricStatusUtils =
+                    new CombinedBiometricStatusUtils(this, mUserId);
+            if (combinedBiometricStatusUtils.hasEnrolled()) {
+                Intent activeUnlockIntent = mActiveUnlockStatusUtils.getIntent();
+                if (activeUnlockIntent != null) {
+                    startActivityForResult(activeUnlockIntent, ACTIVE_UNLOCK_REQUEST);
+                }
+            }
+        }
+        mNextClicked = false;
+        finish();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.ACTIVE_UNLOCK_REQUIRE_BIOMETRIC_SETUP;
+    }
+}
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java
index 05d4acb..ff835f8 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java
@@ -28,6 +28,7 @@
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricStatusPreferenceController;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener;
+import com.android.settings.biometrics.combination.CombinedBiometricStatusUtils;
 import com.android.settingslib.RestrictedPreference;
 
 /**
@@ -35,8 +36,7 @@
  * controls the ability to unlock the phone with watch authentication.
  */
 public class ActiveUnlockStatusPreferenceController
-        extends BiometricStatusPreferenceController
-        implements LifecycleObserver, OnContentChangedListener {
+        extends BiometricStatusPreferenceController implements LifecycleObserver {
     /**
      * Preference key.
      *
@@ -47,7 +47,10 @@
     @Nullable private PreferenceScreen mPreferenceScreen;
     @Nullable private String mSummary;
     private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
+    private final CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
     private final ActiveUnlockSummaryListener mActiveUnlockSummaryListener;
+    private final ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener;
+    private final boolean mIsAvailable;
 
     public ActiveUnlockStatusPreferenceController(@NonNull Context context) {
         this(context, KEY_ACTIVE_UNLOCK_SETTINGS);
@@ -57,14 +60,42 @@
             @NonNull Context context, @NonNull String key) {
         super(context, key);
         mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(context);
-        mActiveUnlockSummaryListener = new ActiveUnlockSummaryListener(context, this);
+        mIsAvailable = mActiveUnlockStatusUtils.isAvailable();
+        mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(context, getUserId());
+        OnContentChangedListener onSummaryChangedListener = new OnContentChangedListener() {
+            @Override
+            public void onContentChanged(String newContent) {
+                mSummary = newContent;
+                if (mPreference != null) {
+                    mPreference.setSummary(getSummaryText());
+                }
+            }
+        };
+        OnContentChangedListener onDeviceNameChangedListener =
+                new OnContentChangedListener() {
+
+            @Override
+            public void onContentChanged(String newContent) {
+                if (mPreference != null) {
+                    mPreference.setSummary(getSummaryText());
+                }
+            }
+
+        };
+        mActiveUnlockSummaryListener =
+                new ActiveUnlockSummaryListener(context, onSummaryChangedListener);
+        mActiveUnlockDeviceNameListener =
+                new ActiveUnlockDeviceNameListener(context, onDeviceNameChangedListener);
     }
 
 
     /** Subscribes to update preference summary dynamically. */
     @OnLifecycleEvent(Lifecycle.Event.ON_START)
     public void onStart() {
-        mActiveUnlockSummaryListener.subscribe();
+        if (mIsAvailable) {
+            mActiveUnlockSummaryListener.subscribe();
+            mActiveUnlockDeviceNameListener.subscribe();
+        }
     }
 
     /** Resets the preference reference on resume. */
@@ -78,14 +109,9 @@
     /** Unsubscribes to prevent leaked listener. */
     @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
     public void onStop() {
-        mActiveUnlockSummaryListener.unsubscribe();
-    }
-
-    @Override
-    public void onContentChanged(String newContent) {
-        mSummary = newContent;
-        if (mPreference != null) {
-            mPreference.setSummary(getSummaryText());
+        if (mIsAvailable) {
+            mActiveUnlockSummaryListener.unsubscribe();
+            mActiveUnlockDeviceNameListener.unsubscribe();
         }
     }
 
@@ -107,7 +133,7 @@
         // This should never be called, as getAvailabilityStatus() will return the exact value.
         // However, this is an abstract method in BiometricStatusPreferenceController, and so
         // needs to be overridden.
-        return mActiveUnlockStatusUtils.isAvailable();
+        return mIsAvailable;
     }
 
     @Override
@@ -120,6 +146,15 @@
 
     @Override
     protected String getSummaryText() {
+        if (mActiveUnlockStatusUtils.useBiometricFailureLayout()
+                && !mActiveUnlockDeviceNameListener.hasEnrolled()
+                && !mCombinedBiometricStatusUtils.hasEnrolled()) {
+            @Nullable final String setupString =
+                    mActiveUnlockStatusUtils.getSummaryWhenBiometricSetupRequired();
+            if (setupString != null) {
+                return setupString;
+            }
+        }
         if (mSummary == null) {
             // return non-empty string to prevent re-sizing of the tile
             return " ";
@@ -129,7 +164,6 @@
 
     @Override
     protected String getSettingsClassName() {
-        // TODO(b/264813445): direct user to face & fingerprint setup
-        return null;
+        return ActiveUnlockRequireBiometricSetup.class.getName();
     }
 }
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
index 439f176..4ff2b87 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
@@ -242,6 +242,32 @@
         }
     }
 
+    /**
+     * Returns the summary of the active unlock preference when biometrics are needed to set up the
+     * feature.
+     */
+    @Nullable
+    public String getSummaryWhenBiometricSetupRequired() {
+        final boolean faceAllowed = Utils.hasFaceHardware(mContext);
+        final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
+
+        int summaryRes = getSetupBiometricRes(faceAllowed, fingerprintAllowed);
+        return summaryRes == 0 ? null : mContext.getString(summaryRes);
+    }
+
+    @StringRes
+    private static int getSetupBiometricRes(boolean faceAllowed, boolean fingerprintAllowed) {
+        if (faceAllowed && fingerprintAllowed) {
+            return R.string.security_settings_activeunlock_require_face_fingerprint_setup_title;
+        } else if (faceAllowed) {
+            return R.string.security_settings_activeunlock_require_face_setup_title;
+        } else if (fingerprintAllowed) {
+            return R.string.security_settings_activeunlock_require_fingerprint_setup_title;
+        } else {
+            return 0;
+        }
+    }
+
     private static String getFlagState() {
         return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME);
     }
diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
index 0a1d29d..1f91a46 100644
--- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -55,6 +55,7 @@
     @VisibleForTesting
     static final int CONFIRM_REQUEST = 2001;
     private static final int CHOOSE_LOCK_REQUEST = 2002;
+    protected static final int ACTIVE_UNLOCK_REQUEST = 2003;
 
     private static final String SAVE_STATE_CONFIRM_CREDETIAL = "confirm_credential";
     private static final String DO_NOT_FINISH_ACTIVITY = "do_not_finish_activity";
@@ -68,8 +69,9 @@
     private boolean mConfirmCredential;
     @Nullable private FaceManager mFaceManager;
     @Nullable private FingerprintManager mFingerprintManager;
-    // Do not finish() if choosing/confirming credential, or showing fp/face settings
-    private boolean mDoNotFinishActivity;
+    // Do not finish() if choosing/confirming credential, showing fp/face settings, or launching
+    // active unlock
+    protected boolean mDoNotFinishActivity;
     @Nullable private String mRetryPreferenceKey = null;
     @Nullable private Bundle mRetryPreferenceExtra = null;
 
@@ -132,7 +134,7 @@
         }
     }
 
-    private boolean onRetryPreferenceTreeClick(Preference preference, final boolean retry) {
+    protected boolean onRetryPreferenceTreeClick(Preference preference, final boolean retry) {
         final String key = preference.getKey();
         final Context context = requireActivity().getApplicationContext();
 
@@ -323,6 +325,14 @@
         return resId == 0 ? "" : getString(resId);
     }
 
+    protected int getUserId() {
+        return mUserId;
+    }
+
+    protected long getGkPwHandle() {
+        return mGkPwHandle;
+    }
+
     @NonNull
     private String getUseClass2BiometricSummary() {
         boolean isFaceAllowed = false;
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
index a352e5a..d0e986f 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
@@ -15,9 +15,14 @@
  */
 package com.android.settings.biometrics.combination;
 
+import static com.android.settings.biometrics.activeunlock.ActiveUnlockStatusPreferenceController.KEY_ACTIVE_UNLOCK_SETTINGS;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
+
 import android.app.settings.SettingsEnums;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
+import android.os.UserHandle;
 
 import androidx.annotation.Nullable;
 import androidx.preference.Preference;
@@ -25,6 +30,7 @@
 import com.android.settings.R;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockDeviceNameListener;
+import com.android.settings.biometrics.activeunlock.ActiveUnlockRequireBiometricSetup;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.search.SearchIndexable;
@@ -42,6 +48,7 @@
     private static final String KEY_INTRO_PREFERENCE = "biometric_intro";
 
     private ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
+    private CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
     @Nullable private ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener;
 
     @Override
@@ -55,6 +62,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(getActivity());
+        mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(getActivity(), mUserId);
         if (mActiveUnlockStatusUtils.isAvailable()) {
             updateUiForActiveUnlock();
         }
@@ -122,6 +130,35 @@
     }
 
     @Override
+    protected boolean onRetryPreferenceTreeClick(Preference preference, final boolean retry) {
+        if (!mActiveUnlockStatusUtils.isAvailable()
+                || !KEY_ACTIVE_UNLOCK_SETTINGS.equals(preference.getKey())) {
+            return super.onRetryPreferenceTreeClick(preference, retry);
+        }
+        mDoNotFinishActivity = true;
+        Intent intent;
+        if (mActiveUnlockStatusUtils.useBiometricFailureLayout()
+                && mActiveUnlockDeviceNameListener != null
+                && !mActiveUnlockDeviceNameListener.hasEnrolled()
+                && !mCombinedBiometricStatusUtils.hasEnrolled()) {
+            intent = new Intent(getActivity(), ActiveUnlockRequireBiometricSetup.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            int userId = mUserId;
+            if (mUserId != UserHandle.USER_NULL) {
+                intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+            }
+            intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGkPwHandle());
+        } else {
+            intent = mActiveUnlockStatusUtils.getIntent();
+        }
+        if (intent != null) {
+            startActivityForResult(intent, ACTIVE_UNLOCK_REQUEST);
+        }
+        return true;
+
+    }
+
+    @Override
     protected String getUseAnyBiometricSummary() {
         // either Active Unlock is not enabled or no device is enrolled.
         if (mActiveUnlockDeviceNameListener == null
diff --git a/src/com/android/settings/datetime/LocationProviderStatusPreferenceController.java b/src/com/android/settings/datetime/LocationProviderStatusPreferenceController.java
index 9380f13..a64273b 100644
--- a/src/com/android/settings/datetime/LocationProviderStatusPreferenceController.java
+++ b/src/com/android/settings/datetime/LocationProviderStatusPreferenceController.java
@@ -19,10 +19,12 @@
 import android.app.time.LocationTimeZoneAlgorithmStatus;
 import android.app.time.TelephonyTimeZoneAlgorithmStatus;
 import android.app.time.TimeManager;
+import android.app.time.TimeZoneCapabilities;
+import android.app.time.TimeZoneCapabilitiesAndConfig;
 import android.app.time.TimeZoneDetectorStatus;
 import android.content.Context;
-import android.location.LocationManager;
 import android.service.timezone.TimeZoneProviderStatus;
+import android.service.timezone.TimeZoneProviderStatus.DependencyStatus;
 import android.text.TextUtils;
 
 import androidx.annotation.Nullable;
@@ -43,14 +45,12 @@
 public class LocationProviderStatusPreferenceController
         extends BasePreferenceController implements TimeManager.TimeZoneDetectorListener {
     private final TimeManager mTimeManager;
-    private final LocationManager mLocationManager;
 
     private BannerMessagePreference mPreference = null;
 
     public LocationProviderStatusPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mTimeManager = context.getSystemService(TimeManager.class);
-        mLocationManager = context.getSystemService(LocationManager.class);
 
         Executor mainExecutor = context.getMainExecutor();
         mTimeManager.addTimeZoneDetectorListener(mainExecutor, this);
@@ -123,11 +123,15 @@
 
     @Override
     public CharSequence getSummary() {
-        boolean locationEnabled = mLocationManager.isLocationEnabled();
+        final TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig =
+                mTimeManager.getTimeZoneCapabilitiesAndConfig();
         final TimeZoneDetectorStatus detectorStatus =
-                mTimeManager.getTimeZoneCapabilitiesAndConfig().getDetectorStatus();
+                timeZoneCapabilitiesAndConfig.getDetectorStatus();
+        final TimeZoneCapabilities timeZoneCapabilities =
+                timeZoneCapabilitiesAndConfig.getCapabilities();
 
-        if (!locationEnabled && hasLocationTimeZoneNoTelephonyFallback(detectorStatus)) {
+        if (!timeZoneCapabilities.isUseLocationEnabled()
+                && hasLocationTimeZoneNoTelephonyFallback(detectorStatus)) {
             return mContext.getResources().getString(
                     R.string.location_time_zone_detection_status_summary_blocked_by_settings);
         }
@@ -137,7 +141,7 @@
             return "";
         }
 
-        int status = ltzpStatus.getLocationDetectionDependencyStatus();
+        @DependencyStatus int status = ltzpStatus.getLocationDetectionDependencyStatus();
 
         if (status == TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT) {
             return mContext.getResources().getString(
diff --git a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
index 36cbc9e..d567466 100644
--- a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
+++ b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
@@ -25,7 +25,6 @@
 import android.app.time.TimeZoneCapabilitiesAndConfig;
 import android.app.time.TimeZoneConfiguration;
 import android.content.Context;
-import android.location.LocationManager;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -50,7 +49,6 @@
     private static final String TAG = "location_time_zone_detection";
 
     private final TimeManager mTimeManager;
-    private final LocationManager mLocationManager;
     private TimeZoneCapabilitiesAndConfig mTimeZoneCapabilitiesAndConfig;
     private InstrumentedPreferenceFragment mFragment;
     private Preference mPreference;
@@ -58,7 +56,6 @@
     public LocationTimeZoneDetectionPreferenceController(Context context) {
         super(context, TAG);
         mTimeManager = context.getSystemService(TimeManager.class);
-        mLocationManager = context.getSystemService(LocationManager.class);
     }
 
     void setFragment(InstrumentedPreferenceFragment fragment) {
@@ -68,14 +65,18 @@
     @Override
     public boolean isChecked() {
         TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
-                mTimeManager.getTimeZoneCapabilitiesAndConfig();
+                getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/false);
         TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
         return configuration.isGeoDetectionEnabled();
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        if (isChecked && !mLocationManager.isLocationEnabled()) {
+        TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig =
+                getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/false);
+        boolean isLocationEnabled =
+                timeZoneCapabilitiesAndConfig.getCapabilities().isUseLocationEnabled();
+        if (isChecked && !isLocationEnabled) {
             new LocationToggleDisabledDialogFragment(mContext)
                     .show(mFragment.getFragmentManager(), TAG);
             // Toggle status is not updated.
@@ -157,11 +158,13 @@
             // The preference should not be visible, but text is referenced in case this changes.
             summaryResId = R.string.location_time_zone_detection_not_allowed;
         } else if (configureGeoDetectionEnabledCapability == CAPABILITY_NOT_APPLICABLE) {
-            // The TimeZoneCapabilities deliberately doesn't provide information about why the user
-            // doesn't have the capability, but the user's "location enabled" being off and the
-            // global automatic detection setting will always be considered overriding reasons why
-            // location time zone detection cannot be used.
-            if (!mLocationManager.isLocationEnabled()) {
+            boolean isLocationEnabled =
+                    timeZoneCapabilitiesAndConfig.getCapabilities().isUseLocationEnabled();
+            // The TimeZoneCapabilities cannot provide implementation-specific information about why
+            // the user doesn't have the capability, but the user's "location enabled" being off and
+            // the global automatic detection setting will always be considered overriding reasons
+            // why location time zone detection cannot be used.
+            if (!isLocationEnabled) {
                 summaryResId = R.string.location_app_permission_summary_location_off;
             } else if (!configuration.isAutoDetectionEnabled()) {
                 summaryResId = R.string.location_time_zone_detection_auto_is_off;
@@ -184,6 +187,10 @@
         return mContext.getString(summaryResId);
     }
 
+    /**
+     * Implementation of {@link TimeManager.TimeZoneDetectorListener#onChange()}. Called by the
+     * system server after a change that affects {@link TimeZoneCapabilitiesAndConfig}.
+     */
     @Override
     public void onChange() {
         refreshUi();
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusPreferenceController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusPreferenceController.java
index 00819b5..ae002f3 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusPreferenceController.java
@@ -109,12 +109,18 @@
 
     @Override
     public void updateState(Preference preference) {
+        if (mFragment == null) {
+            return;
+        }
         final int simSlot = getSimSlotIndex();
         if (mSimChangeObserver == null) {
             mSimChangeObserver = x -> updateStateBySlot(preference, simSlot);
-            mSlotSimStatus.observe(mFragment.getViewLifecycleOwner(), mSimChangeObserver);
+            mFragment.getViewLifecycleOwnerLiveData().observeForever(lifecycleOwner -> {
+                mSlotSimStatus.observe(lifecycleOwner, mSimChangeObserver);
+            });
+        } else {
+            updateStateBySlot(preference, simSlot);
         }
-        updateStateBySlot(preference, simSlot);
     }
 
     protected void updateStateBySlot(Preference preference, int simSlot) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index a307619..34a37e3 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -163,6 +163,10 @@
                             mHandler.post(() -> {
                                 removeAndCacheAllPreferences();
                                 addAllPreferences();
+                                mMetricsFeatureProvider.action(
+                                        mPrefContext,
+                                        SettingsEnums.ACTION_BATTERY_USAGE_SPINNER,
+                                        mSpinnerPosition);
                             });
                         }
                     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index 183ca81..7888cff 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -99,10 +99,10 @@
     static boolean sDebug = false;
 
     @VisibleForTesting
-    static long sFakeCurrentTimeMillis = 0;
+    static long sTestCurrentTimeMillis = 0;
 
     @VisibleForTesting
-    static Set<String> sFakeSystemAppsSet;
+    static Set<String> sTestSystemAppsSet;
 
     @VisibleForTesting
     static IUsageStatsManager sUsageStatsManager =
@@ -539,7 +539,7 @@
         }
         while (nextDay < endTime) {
             dailyTimestampList.add(nextDay);
-            nextDay += DateUtils.DAY_IN_MILLIS;
+            nextDay = getTimestampOfNextDay(nextDay);
         }
         final long lastDailyTimestamp = dailyTimestampList.get(dailyTimestampList.size() - 1);
         // Only if the timestamp diff in the last day is bigger than MIN_TIME_SLOT, add the
@@ -1900,12 +1900,12 @@
     }
 
     private static Set<String> getSystemAppsSet(Context context) {
-        return sFakeSystemAppsSet != null ? sFakeSystemAppsSet
+        return sTestSystemAppsSet != null ? sTestSystemAppsSet
                 : AppListRepositoryUtil.getSystemPackageNames(context, context.getUserId(), false);
     }
 
     private static long getCurrentTimeMillis() {
-        return sFakeCurrentTimeMillis > 0 ? sFakeCurrentTimeMillis : System.currentTimeMillis();
+        return sTestCurrentTimeMillis > 0 ? sTestCurrentTimeMillis : System.currentTimeMillis();
     }
 
     private static void log(Context context, final String content, final long timestamp,
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java b/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java
index 2e60583..64642d7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java
@@ -30,6 +30,7 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.utils.StringUtil;
 
+import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -39,6 +40,7 @@
     private static final String ROOT_PREFERENCE_KEY = "screen_on_time_category";
     private static final String SCREEN_ON_TIME_TEXT_PREFERENCE_KEY = "screen_on_time_text";
     private static final Pattern NUMBER_PATTERN = Pattern.compile("[\\d]*[\\.,]?[\\d]+");
+    private static final Locale IW_LOCALE = new Locale("iw");
 
     @VisibleForTesting
     Context mPrefContext;
@@ -89,16 +91,22 @@
         final CharSequence timeSequence =
                 StringUtil.formatElapsedTime(mPrefContext, (double) screenOnTime,
                         /*withSeconds=*/ false, /*collapseTimeUnit=*/ false);
-        mScreenOnTimeTextPreference.setText(enlargeFontOfNumber(removeCommas(timeSequence)));
+        mScreenOnTimeTextPreference.setText(
+                enlargeFontOfNumberIfNeeded(mPrefContext, removeCommas(timeSequence)));
         mScreenOnTimeTextPreference.setVisible(true);
     }
 
     @VisibleForTesting
-    static CharSequence enlargeFontOfNumber(CharSequence text) {
+    static CharSequence enlargeFontOfNumberIfNeeded(Context context, CharSequence text) {
         if (TextUtils.isEmpty(text)) {
             return "";
         }
 
+        final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
+        if (locale != null && IW_LOCALE.getLanguage().equals(locale.getLanguage())) {
+            return text;
+        }
+
         final SpannableString spannableText =  new SpannableString(text);
         final Matcher matcher = NUMBER_PATTERN.matcher(text);
         while (matcher.find()) {
diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java
index cb0c942..4da8151 100644
--- a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 import static org.robolectric.shadows.ShadowLooper.idleMainLooper;
@@ -43,6 +44,8 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import java.util.ArrayList;
+
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowDeviceConfig.class})
 public class ActiveUnlockContentListenerTest {
@@ -136,6 +139,26 @@
         assertThat(mUpdateCount).isEqualTo(1);
     }
 
+    @Test
+    public void noProvider_subscribeDoesntRegisterObserver() {
+        when(mPackageManager.getInstalledPackages(any()))
+                .thenReturn(new ArrayList<>());
+        OnContentChangedListener listener = new OnContentChangedListener() {
+            @Override
+            public void onContentChanged(String newValue) {}
+        };
+
+        ActiveUnlockContentListener contentListener =
+                new ActiveUnlockContentListener(
+                        mContext,
+                        listener,
+                        "logTag",
+                        FakeContentProvider.METHOD_SUMMARY,
+                        FakeContentProvider.KEY_SUMMARY);
+
+        assertThat(contentListener.subscribe()).isFalse();
+    }
+
     private void updateContent(String content) {
         FakeContentProvider.setTileSummary(content);
         mContext.getContentResolver().notifyChange(
diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java
new file mode 100644
index 0000000..d3bbf24
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.activeunlock;
+
+import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED;
+import static com.android.settings.biometrics.activeunlock.ActiveUnlockRequireBiometricSetup.BIOMETRIC_ENROLL_REQUEST;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
+
+import com.android.settings.R;
+import com.android.settings.biometrics.BiometricEnrollActivity;
+
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupcompat.template.FooterBarMixin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowActivity;
+
+@RunWith(RobolectricTestRunner.class)
+public class ActiveUnlockRequireBiometricSetupTest {
+
+    private ActiveUnlockRequireBiometricSetup mActivity;
+    private PartnerCustomizationLayout mLayout;
+
+    @Before
+    public void setUp() {
+        mActivity = Robolectric.buildActivity(
+                ActiveUnlockRequireBiometricSetup.class).setup().get();
+        mLayout = mActivity.findViewById(R.id.setup_wizard_layout);
+    }
+
+    @Test
+    public void onBackPressed_shouldFinish() {
+        mActivity.onBackPressed();
+
+        assertThat(mActivity.isFinishing()).isTrue();
+    }
+
+    @Test
+    public void clickCancel_shouldFinish() {
+        mLayout.getMixin(FooterBarMixin.class).getSecondaryButtonView().performClick();
+
+        assertThat(mActivity.isFinishing()).isTrue();
+    }
+
+    @Test
+    public void clickNext_shouldLaunchBiometricSetup() {
+        final ComponentName expectedComponent = new ComponentName(application,
+                BiometricEnrollActivity.InternalActivity.class);
+
+        mLayout.getMixin(FooterBarMixin.class).getPrimaryButtonView().performClick();
+
+        ShadowActivity.IntentForResult startedActivity = Shadows.shadowOf(
+                mActivity).getNextStartedActivityForResult();
+        assertWithMessage("Next activity").that(startedActivity).isNotNull();
+        assertThat(startedActivity.intent.getComponent()).isEqualTo(expectedComponent);
+    }
+
+    @Test
+    public void onActivityResult_shouldFinish() {
+        mActivity.onActivityResult(BIOMETRIC_ENROLL_REQUEST, RESULT_FINISHED, null);
+
+        assertThat(mActivity.isFinishing()).isTrue();
+    }
+
+    @Test
+    public void getMetricsCategory_returnsCorrectCategory() {
+        assertThat(mActivity.getMetricsCategory()).isEqualTo(
+                SettingsEnums.ACTIVE_UNLOCK_REQUIRE_BIOMETRIC_SETUP);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java
index bf60d01..5219a3a 100644
--- a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java
@@ -32,6 +32,7 @@
 
 import androidx.preference.PreferenceScreen;
 
+import com.android.settings.R;
 import com.android.settings.testutils.ActiveUnlockTestUtils;
 import com.android.settings.testutils.shadow.ShadowDeviceConfig;
 import com.android.settingslib.RestrictedPreference;
@@ -162,9 +163,57 @@
         assertThat(mPreference.getSummary().toString()).isEqualTo(summary);
     }
 
+    @Test
+    public void biometricsNotSetUp_deviceNameIsNotSet_setupBiometricStringShown() {
+        ActiveUnlockTestUtils.enable(mContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT);
+        updateSummary("newSummary");
+        mController.displayPreference(mPreferenceScreen);
+
+        mController.onStart();
+        idleMainLooper();
+
+        assertThat(mPreference.getSummary()).isEqualTo(mContext.getString(
+                R.string.security_settings_activeunlock_require_face_fingerprint_setup_title));
+    }
+
+    @Test
+    public void biometricNotSetUp_deviceNameIsSet_summaryShown() {
+        ActiveUnlockTestUtils.enable(mContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT);
+        String summary = "newSummary";
+        updateSummary(summary);
+        updateDeviceName("deviceName");
+        mController.displayPreference(mPreferenceScreen);
+
+        mController.onStart();
+        idleMainLooper();
+
+        assertThat(mPreference.getSummary()).isEqualTo(summary);
+    }
+
+    @Test
+    public void biometricSetUp_summaryShown() {
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+        ActiveUnlockTestUtils.enable(mContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT);
+        String summary = "newSummary";
+        updateSummary(summary);
+        mController.displayPreference(mPreferenceScreen);
+
+        mController.onStart();
+        idleMainLooper();
+
+        assertThat(mPreference.getSummary()).isEqualTo(summary);
+    }
+
     private void updateSummary(String summary) {
         FakeContentProvider.setTileSummary(summary);
         mContext.getContentResolver().notifyChange(FakeContentProvider.URI, null /* observer */);
         idleMainLooper();
     }
+
+    private void updateDeviceName(String deviceName) {
+        FakeContentProvider.setDeviceName(deviceName);
+        mContext.getContentResolver().notifyChange(FakeContentProvider.URI, null /* observer */);
+        idleMainLooper();
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
index 41aca50..7685a5b 100644
--- a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
@@ -37,7 +37,6 @@
 import android.app.time.TimeZoneConfiguration;
 import android.app.time.TimeZoneDetectorStatus;
 import android.content.Context;
-import android.location.LocationManager;
 import android.os.UserHandle;
 
 import androidx.preference.Preference;
@@ -62,8 +61,6 @@
     private Preference mPreference;
     @Mock
     private TimeManager mTimeManager;
-    @Mock
-    private LocationManager mLocationManager;
 
     @Before
     public void setUp() {
@@ -73,9 +70,6 @@
         mPreference = new Preference(mContext);
 
         when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager);
-        when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
-
-        when(mLocationManager.isLocationEnabled()).thenReturn(true);
     }
 
     @Test
@@ -267,6 +261,7 @@
                         : Capabilities.CAPABILITY_NOT_SUPPORTED;
         TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(UserHandle.SYSTEM)
                 .setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability)
+                .setUseLocationEnabled(true)
                 .setConfigureGeoDetectionEnabledCapability(Capabilities.CAPABILITY_NOT_SUPPORTED)
                 .setSetManualTimeZoneCapability(Capabilities.CAPABILITY_POSSESSED)
                 .build();
diff --git a/tests/robotests/src/com/android/settings/datetime/LocationProviderStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/LocationProviderStatusPreferenceControllerTest.java
index 4f9f1cc..b838355 100644
--- a/tests/robotests/src/com/android/settings/datetime/LocationProviderStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/LocationProviderStatusPreferenceControllerTest.java
@@ -29,7 +29,9 @@
 import static org.mockito.Mockito.when;
 
 import android.app.time.Capabilities;
+import android.app.time.Capabilities.CapabilityState;
 import android.app.time.LocationTimeZoneAlgorithmStatus;
+import android.app.time.LocationTimeZoneAlgorithmStatus.ProviderStatus;
 import android.app.time.TelephonyTimeZoneAlgorithmStatus;
 import android.app.time.TimeManager;
 import android.app.time.TimeZoneCapabilities;
@@ -37,9 +39,9 @@
 import android.app.time.TimeZoneConfiguration;
 import android.app.time.TimeZoneDetectorStatus;
 import android.content.Context;
-import android.location.LocationManager;
 import android.os.UserHandle;
 import android.service.timezone.TimeZoneProviderStatus;
+import android.service.timezone.TimeZoneProviderStatus.DependencyStatus;
 
 import androidx.annotation.Nullable;
 
@@ -60,8 +62,6 @@
     private Context mContext;
     @Mock
     private TimeManager mTimeManager;
-    @Mock
-    private LocationManager mLocationManager;
 
     @Before
     public void setUp() {
@@ -69,8 +69,6 @@
         mContext = spy(RuntimeEnvironment.application);
 
         when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager);
-        when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
-        when(mLocationManager.isLocationEnabled()).thenReturn(true);
         when(mContext.getString(
                 R.string.location_time_zone_detection_status_summary_blocked_by_settings))
                 .thenReturn("BBS");
@@ -78,11 +76,12 @@
 
     @Test
     public void testCapabilityStatus() {
+        LocationProviderStatusPreferenceController controller =
+                new LocationProviderStatusPreferenceController(mContext, "LPSPC");
+
         TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(false,
                 DEPENDENCY_STATUS_OK, DEPENDENCY_STATUS_OK);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
-        LocationProviderStatusPreferenceController controller =
-                new LocationProviderStatusPreferenceController(mContext, "LPSPC");
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
@@ -95,14 +94,16 @@
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
 
         capabilitiesAndConfig = createCapabilitiesAndConfig(false,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS, DEPENDENCY_STATUS_OK);
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.AVAILABLE_UNSEARCHABLE);
 
         capabilitiesAndConfig = createCapabilitiesAndConfig(true,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS, DEPENDENCY_STATUS_OK);
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
@@ -111,31 +112,36 @@
 
     @Test
     public void testProviderStatus_primaryCertain() {
-        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(false,
-                DEPENDENCY_STATUS_OK, DEPENDENCY_STATUS_OK);
-        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
         LocationProviderStatusPreferenceController controller =
                 new LocationProviderStatusPreferenceController(mContext, "LPSPC");
 
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK);
+        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
+
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
 
         capabilitiesAndConfig = createCapabilitiesAndConfig(false,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS, DEPENDENCY_STATUS_OK);
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.AVAILABLE_UNSEARCHABLE);
 
-        capabilitiesAndConfig = createCapabilitiesAndConfig(false, DEPENDENCY_STATUS_OK,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
+        capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
 
-        capabilitiesAndConfig = createCapabilitiesAndConfig(false, DEPENDENCY_STATUS_OK,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
+        capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
@@ -144,32 +150,36 @@
 
     @Test
     public void testProviderStatus_primaryUncertain() {
-        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(false,
-                DEPENDENCY_STATUS_OK, DEPENDENCY_STATUS_OK, PROVIDER_STATUS_IS_CERTAIN);
-        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
         LocationProviderStatusPreferenceController controller =
                 new LocationProviderStatusPreferenceController(mContext, "LPSPC");
 
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK);
+        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
+
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
 
         capabilitiesAndConfig = createCapabilitiesAndConfig(false,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS, DEPENDENCY_STATUS_OK,
-                PROVIDER_STATUS_IS_CERTAIN);
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.AVAILABLE_UNSEARCHABLE);
 
-        capabilitiesAndConfig = createCapabilitiesAndConfig(false, DEPENDENCY_STATUS_OK,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS, PROVIDER_STATUS_IS_UNCERTAIN);
+        capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_UNCERTAIN, DEPENDENCY_STATUS_OK,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.AVAILABLE_UNSEARCHABLE);
 
-        capabilitiesAndConfig = createCapabilitiesAndConfig(false, DEPENDENCY_STATUS_OK,
-                DEPENDENCY_STATUS_OK, PROVIDER_STATUS_IS_UNCERTAIN);
+        capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_UNCERTAIN, DEPENDENCY_STATUS_OK,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_OK);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
@@ -177,18 +187,21 @@
     }
 
     @Test
-    public void testProviderStatus_nullProviders() {
-        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(false,
-                null, null);
-        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
+    public void testProviderStatus_nullProviderStatuses() {
         LocationProviderStatusPreferenceController controller =
                 new LocationProviderStatusPreferenceController(mContext, "LPSPC");
 
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_CERTAIN, null,
+                PROVIDER_STATUS_IS_CERTAIN, null);
+        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
+
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
 
         capabilitiesAndConfig = createCapabilitiesAndConfig(false,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS, null);
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS,
+                PROVIDER_STATUS_IS_CERTAIN, null);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
@@ -200,39 +213,63 @@
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
 
-        capabilitiesAndConfig = createCapabilitiesAndConfig(false, null,
-                DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
+        capabilitiesAndConfig = createCapabilitiesAndConfig(false,
+                PROVIDER_STATUS_IS_CERTAIN, null,
+                PROVIDER_STATUS_IS_CERTAIN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(controller.getAvailabilityStatus()).isEqualTo(
                 BasePreferenceController.AVAILABLE_UNSEARCHABLE);
     }
 
-    private TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(boolean capabilitySupported,
-            @Nullable Integer primary, @Nullable Integer secondary) {
-        return createCapabilitiesAndConfig(capabilitySupported, primary, secondary,
-                PROVIDER_STATUS_IS_CERTAIN);
+    private static TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(
+            boolean userCanConfigureGeoDetection,
+            @Nullable @DependencyStatus Integer primaryProviderLocationStatus,
+            @Nullable @DependencyStatus Integer secondaryProviderLocationStatus) {
+        return createCapabilitiesAndConfig(userCanConfigureGeoDetection,
+                PROVIDER_STATUS_IS_CERTAIN, primaryProviderLocationStatus,
+                PROVIDER_STATUS_IS_CERTAIN, secondaryProviderLocationStatus);
     }
 
-    private TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(boolean capabilitySupported,
-            @Nullable Integer primary, @Nullable Integer secondary, int primaryProviderStatus) {
-        TimeZoneDetectorStatus status = new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING,
-                new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING),
-                new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
-                        primaryProviderStatus, primary != null
-                        ? new TimeZoneProviderStatus.Builder().setLocationDetectionDependencyStatus(
-                        primary).build() : null, PROVIDER_STATUS_IS_CERTAIN, secondary != null
-                        ? new TimeZoneProviderStatus.Builder().setLocationDetectionDependencyStatus(
-                        secondary).build() : null));
+    private static TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(
+            boolean userCanConfigureGeoDetection,
+            @ProviderStatus int primaryProviderStatus,
+            @Nullable @DependencyStatus Integer primaryProviderLocationStatus,
+            @ProviderStatus int secondaryProviderStatus,
+            @Nullable @DependencyStatus Integer secondaryProviderLocationStatus) {
+        TelephonyTimeZoneAlgorithmStatus telephonyTimeZoneAlgorithmStatus =
+                new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING);
 
-        TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(
-                UserHandle.SYSTEM).setConfigureAutoDetectionEnabledCapability(
-                Capabilities.CAPABILITY_POSSESSED).setConfigureGeoDetectionEnabledCapability(
-                capabilitySupported ? Capabilities.CAPABILITY_POSSESSED
-                        : Capabilities.CAPABILITY_NOT_SUPPORTED).setSetManualTimeZoneCapability(
-                Capabilities.CAPABILITY_POSSESSED).build();
+        LocationTimeZoneAlgorithmStatus locationTimeZoneAlgorithmStatus =
+                new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
+                        primaryProviderStatus,
+                        createTimeZoneProviderStatusOrNull(primaryProviderLocationStatus),
+                        secondaryProviderStatus,
+                        createTimeZoneProviderStatusOrNull(secondaryProviderLocationStatus));
+
+        TimeZoneDetectorStatus status = new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING,
+                telephonyTimeZoneAlgorithmStatus, locationTimeZoneAlgorithmStatus);
+
+        @CapabilityState int configureGeoDetectionEnabledCapability = userCanConfigureGeoDetection
+                ? Capabilities.CAPABILITY_POSSESSED : Capabilities.CAPABILITY_NOT_SUPPORTED;
+        TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(UserHandle.SYSTEM)
+                .setConfigureAutoDetectionEnabledCapability(Capabilities.CAPABILITY_POSSESSED)
+                .setUseLocationEnabled(true)
+                .setConfigureGeoDetectionEnabledCapability(configureGeoDetectionEnabledCapability)
+                .setSetManualTimeZoneCapability(Capabilities.CAPABILITY_POSSESSED)
+                .build();
 
         return new TimeZoneCapabilitiesAndConfig(status, capabilities,
                 new TimeZoneConfiguration.Builder().build());
     }
+
+    private static TimeZoneProviderStatus createTimeZoneProviderStatusOrNull(
+            @Nullable @DependencyStatus Integer locationDependencyStatusOrNull) {
+        if (locationDependencyStatusOrNull == null) {
+            return null;
+        }
+        return new TimeZoneProviderStatus.Builder()
+                .setLocationDetectionDependencyStatus(locationDependencyStatusOrNull)
+                .build();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
index ea83366..baef01c 100644
--- a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
@@ -27,9 +27,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.time.Capabilities.CapabilityState;
@@ -41,7 +42,6 @@
 import android.app.time.TimeZoneConfiguration;
 import android.app.time.TimeZoneDetectorStatus;
 import android.content.Context;
-import android.location.LocationManager;
 import android.os.UserHandle;
 
 import com.android.settings.R;
@@ -60,8 +60,6 @@
 public class LocationTimeZoneDetectionPreferenceControllerTest {
     @Mock
     private TimeManager mTimeManager;
-    @Mock
-    private LocationManager mLocationManager;
     private Context mContext;
     private LocationTimeZoneDetectionPreferenceController mController;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -72,14 +70,16 @@
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
         when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager);
-        when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
         mController = new LocationTimeZoneDetectionPreferenceController(mContext);
         mController.setFragment(mFragment);
     }
 
     @Test
     public void setChecked_withTrue_shouldUpdateSetting_whenLocationIsEnabled() {
-        when(mLocationManager.isLocationEnabled()).thenReturn(true);
+        boolean useLocationEnabled = true;
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+                createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED);
+        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         // Simulate the UI being clicked.
         mController.setChecked(true);
@@ -98,17 +98,25 @@
 
     @Test
     public void setChecked_withTrue_shouldDoNothing_whenLocationIsDisabled() {
-        when(mLocationManager.isLocationEnabled()).thenReturn(false);
+        boolean useLocationEnabled = false;
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+                createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED);
+        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         // Simulate the UI being clicked.
         mController.setChecked(true);
 
-        // Verify the TimeManager was not called.
-        verifyNoInteractions(mTimeManager);
+        // Verify the TimeManager was not updated.
+        verify(mTimeManager, never()).updateTimeZoneConfiguration(any());
     }
 
     @Test
     public void setChecked_withFalse_shouldUpdateSetting() {
+        boolean useLocationEnabled = false;
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+                createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED);
+        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
+
         // Simulate the UI being clicked.
         mController.setChecked(false);
 
@@ -121,8 +129,9 @@
 
     @Test
     public void testLocationTimeZoneDetection_supported_shouldBeShown() {
+        boolean useLocationEnabled = false;
         TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
-                createTimeZoneCapabilitiesAndConfig(CAPABILITY_POSSESSED);
+                createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(mController.isAvailable()).isTrue();
@@ -130,8 +139,9 @@
 
     @Test
     public void testLocationTimeZoneDetection_unsupported_shouldNotBeShown() {
-        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
-                createTimeZoneCapabilitiesAndConfig(CAPABILITY_NOT_SUPPORTED);
+        boolean useLocationEnabled = false;
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig(
+                useLocationEnabled, CAPABILITY_NOT_SUPPORTED);
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
 
         assertThat(mController.isAvailable()).isFalse();
@@ -142,8 +152,9 @@
      */
     @Test
     public void testLocationTimeZoneDetection_summary_geoDetectionEnabled() {
+        boolean useLocationEnabled = false;
         TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
-                createTimeZoneCapabilitiesAndConfig(CAPABILITY_POSSESSED);
+                createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED);
 
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
         assertThat(mController.getSummary()).isEqualTo(
@@ -152,11 +163,11 @@
 
     @Test
     public void testLocationTimeZoneDetection_toggleIsOn_whenGeoDetectionEnabledAnsMlsIsOff() {
-        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
-                createTimeZoneCapabilitiesAndConfig(CAPABILITY_NOT_APPLICABLE);
+        boolean useLocationEnabled = false;
+        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig(
+                useLocationEnabled, CAPABILITY_NOT_APPLICABLE);
 
         when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
-        when(mLocationManager.isLocationEnabled()).thenReturn(false);
 
         assertThat(mController.isChecked()).isTrue();
         assertThat(mController.getSummary()).isEqualTo(
@@ -164,6 +175,7 @@
     }
 
     private static TimeZoneCapabilitiesAndConfig createTimeZoneCapabilitiesAndConfig(
+            boolean useLocationEnabled,
             @CapabilityState int configureGeoDetectionEnabledCapability) {
 
         // Create a status that matches the user's capability state.
@@ -191,6 +203,7 @@
         UserHandle arbitraryUserHandle = UserHandle.of(123);
         TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(arbitraryUserHandle)
                 .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
+                .setUseLocationEnabled(useLocationEnabled)
                 .setConfigureGeoDetectionEnabledCapability(configureGeoDetectionEnabledCapability)
                 .setSetManualTimeZoneCapability(CAPABILITY_NOT_APPLICABLE)
                 .build();
diff --git a/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java
index 77ab9a2..10a5e5d 100644
--- a/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java
@@ -112,8 +112,10 @@
                         PROVIDER_STATUS_NOT_PRESENT, null));
         int suggestManualCapability = suggestManualAllowed ? Capabilities.CAPABILITY_POSSESSED
                 : Capabilities.CAPABILITY_NOT_SUPPORTED;
+        boolean useLocationEnabled = true;
         TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(UserHandle.SYSTEM)
                 .setConfigureAutoDetectionEnabledCapability(Capabilities.CAPABILITY_POSSESSED)
+                .setUseLocationEnabled(useLocationEnabled)
                 .setConfigureGeoDetectionEnabledCapability(Capabilities.CAPABILITY_NOT_SUPPORTED)
                 .setSetManualTimeZoneCapability(suggestManualCapability)
                 .build();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
index 9c922bb..58c69b1 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
@@ -88,7 +88,7 @@
         Locale.setDefault(new Locale("en_US"));
         org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
-        DataProcessor.sFakeSystemAppsSet = Set.of();
+        DataProcessor.sTestSystemAppsSet = Set.of();
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mContext = spy(RuntimeEnvironment.application);
         doReturn(mContext).when(mContext).getApplicationContext();
@@ -405,7 +405,7 @@
             entryMap.put("fake_entry_key" + index, entry);
             batteryHistoryMap.put(generateTimestamp(index), entryMap);
         }
-        DataProcessor.sFakeCurrentTimeMillis =
+        DataProcessor.sTestCurrentTimeMillis =
                 generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS;
         return batteryHistoryMap;
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
index 409c803..67c972f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -308,7 +308,7 @@
         final int[] levels = {100, 99, 98};
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
                 createHistoryMap(timestamps, levels);
-        DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
 
         assertThat(DataProcessManager.getBatteryLevelData(
                 mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
@@ -322,7 +322,7 @@
         final int[] levels = {100, 99};
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
                 createHistoryMap(timestamps, levels);
-        DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
 
         final BatteryLevelData resultData =
                 DataProcessManager.getBatteryLevelData(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
index 764b3a8..0ecf50e 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -18,20 +18,23 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isA;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
+import android.content.BroadcastReceiver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.UserInfo;
 import android.os.BatteryConsumer;
 import android.os.BatteryManager;
@@ -90,9 +93,10 @@
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
 
-        DataProcessor.sFakeSystemAppsSet = Set.of();
+        DataProcessor.sTestSystemAppsSet = Set.of();
         DataProcessor.sUsageStatsManager = mUsageStatsManager;
-        doReturn(mIntent).when(mContext).registerReceiver(any(), any());
+        doReturn(mIntent).when(mContext).registerReceiver(
+                isA(BroadcastReceiver.class), isA(IntentFilter.class));
         doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt());
         doReturn(66).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_LEVEL), anyInt());
         doReturn(mContext).when(mContext).getApplicationContext();
@@ -109,11 +113,11 @@
         doReturn(true).when(mUserManager).isUserUnlocked(userInfo.id);
         doReturn(mUsageEvents1)
                 .when(mUsageStatsManager)
-                .queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
+                .queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString());
 
         final Map<Long, UsageEvents> resultMap = DataProcessor.getAppUsageEvents(mContext);
 
-        assertThat(resultMap.size()).isEqualTo(1);
+        assertThat(resultMap).hasSize(1);
         assertThat(resultMap.get(Long.valueOf(userInfo.id))).isEqualTo(mUsageEvents1);
     }
 
@@ -138,8 +142,8 @@
         userInfoList.add(userInfo);
         doReturn(userInfoList).when(mUserManager).getAliveUsers();
         doReturn(true).when(mUserManager).isUserUnlocked(userInfo.id);
-        doReturn(null)
-                .when(mUsageStatsManager).queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
+        doReturn(null).when(mUsageStatsManager)
+                .queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString());
 
         final Map<Long, UsageEvents> resultMap = DataProcessor.getAppUsageEvents(mContext);
 
@@ -152,7 +156,7 @@
         doReturn(true).when(mUserManager).isUserUnlocked(userId);
         doReturn(mUsageEvents1)
                 .when(mUsageStatsManager)
-                .queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
+                .queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString());
 
         assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId, 0))
                 .isEqualTo(mUsageEvents1);
@@ -171,8 +175,8 @@
     public void getAppUsageEventsForUser_nullUsageEvents_returnNull() throws RemoteException {
         final int userId = 1;
         doReturn(true).when(mUserManager).isUserUnlocked(userId);
-        doReturn(null)
-                .when(mUsageStatsManager).queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
+        doReturn(null).when(mUsageStatsManager)
+                .queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString());
 
         assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId, 0)).isNull();
     }
@@ -248,31 +252,31 @@
                 DataProcessor.generateAppUsagePeriodMap(
                         hourlyBatteryLevelsPerDay, appUsageEventList);
 
-        assertThat(periodMap.size()).isEqualTo(3);
+        assertThat(periodMap).hasSize(3);
         // Day 1
-        assertThat(periodMap.get(0).size()).isEqualTo(2);
+        assertThat(periodMap.get(0)).hasSize(2);
         Map<Long, Map<String, List<AppUsagePeriod>>> hourlyMap = periodMap.get(0).get(0);
-        assertThat(hourlyMap.size()).isEqualTo(2);
+        assertThat(hourlyMap).hasSize(2);
         Map<String, List<AppUsagePeriod>> userMap = hourlyMap.get(1L);
-        assertThat(userMap.size()).isEqualTo(1);
-        assertThat(userMap.get(packageName).size()).isEqualTo(1);
+        assertThat(userMap).hasSize(1);
+        assertThat(userMap.get(packageName)).hasSize(1);
         assertAppUsagePeriod(userMap.get(packageName).get(0), 17200000L, 17800000L);
         userMap = hourlyMap.get(2L);
-        assertThat(userMap.size()).isEqualTo(1);
-        assertThat(userMap.get(packageName).size()).isEqualTo(2);
+        assertThat(userMap).hasSize(1);
+        assertThat(userMap.get(packageName)).hasSize(2);
         assertAppUsagePeriod(userMap.get(packageName).get(0), 14400000L, 15600000L);
         assertAppUsagePeriod(userMap.get(packageName).get(1), 16200000L, 18000000L);
         hourlyMap = periodMap.get(0).get(1);
         assertThat(hourlyMap).isNull();
         // Day 2
-        assertThat(periodMap.get(1).size()).isEqualTo(0);
+        assertThat(periodMap.get(1)).hasSize(0);
         // Day 3
-        assertThat(periodMap.get(2).size()).isEqualTo(1);
+        assertThat(periodMap.get(2)).hasSize(1);
         hourlyMap = periodMap.get(2).get(0);
-        assertThat(hourlyMap.size()).isEqualTo(1);
+        assertThat(hourlyMap).hasSize(1);
         userMap = hourlyMap.get(1L);
-        assertThat(userMap.size()).isEqualTo(1);
-        assertThat(userMap.get(packageName).size()).isEqualTo(2);
+        assertThat(userMap).hasSize(1);
+        assertThat(userMap.get(packageName)).hasSize(2);
         assertAppUsagePeriod(userMap.get(packageName).get(0), 45970000L, 46000000L);
         assertAppUsagePeriod(userMap.get(packageName).get(1), 47800000L, 48800000L);
     }
@@ -313,7 +317,7 @@
         final List<AppUsageEvent> appUsageEventList =
                 DataProcessor.generateAppUsageEventListFromUsageEvents(mContext, appUsageEvents);
 
-        assertThat(appUsageEventList.size()).isEqualTo(3);
+        assertThat(appUsageEventList).hasSize(3);
         assertAppUsageEvent(
                 appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 2);
         assertAppUsageEvent(
@@ -398,7 +402,7 @@
         final int[] levels = {100, 94, 90, 82, 50};
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
                 createHistoryMap(timestamps, levels);
-        DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
 
         final Map<Long, Map<String, BatteryHistEntry>> resultMap =
                 DataProcessor.getHistoryMapWithExpectedTimestamps(mContext, batteryHistoryMap);
@@ -431,7 +435,7 @@
         final int[] levels = {100};
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
                 createHistoryMap(timestamps, levels);
-        DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
 
         assertThat(
                 DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap))
@@ -451,7 +455,7 @@
         final int[] levels = {100, 94, 90, 82, 50};
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
                 createHistoryMap(timestamps, levels);
-        DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
 
         final BatteryLevelData resultData =
                 DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap);
@@ -484,7 +488,7 @@
         final int[] levels = {100, 94, 90, 82};
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
                 createHistoryMap(timestamps, levels);
-        DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
 
         final BatteryLevelData resultData =
                 DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap);
@@ -535,6 +539,194 @@
     }
 
     @Test
+    public void getLevelDataThroughProcessedHistoryMap_daylightSaving25Hour_returnExpectedResult() {
+        // Timezone PST 2022-11-06 has an extra 01:00:00 - 01:59:59 for daylight saving.
+        TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+        final long[] timestamps = {
+                1667667600000L, // 2022-11-05 10:00:00
+                1667829600000L  // 2022-11-07 06:00:00
+        };
+        final int[] levels = {100, 88};
+        final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
+                createHistoryMap(timestamps, levels);
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
+
+        final BatteryLevelData resultData =
+                DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap);
+
+        final List<Long> expectedDailyTimestamps = List.of(
+                1667667600000L, // 2022-11-05 10:00:00
+                1667718000000L, // 2022-11-06 00:00:00
+                1667808000000L, // 2022-11-07 00:00:00
+                1667829600000L  // 2022-11-07 06:00:00
+        );
+        final List<Integer> expectedDailyLevels = new ArrayList<>();
+        expectedDailyLevels.add(100);
+        expectedDailyLevels.add(null);
+        expectedDailyLevels.add(null);
+        expectedDailyLevels.add(88);
+        final List<List<Long>> expectedHourlyTimestamps = List.of(
+                List.of(
+                        1667667600000L, // 2022-11-05 10:00:00
+                        1667674800000L, // 2022-11-05 12:00:00
+                        1667682000000L, // 2022-11-05 14:00:00
+                        1667689200000L, // 2022-11-05 16:00:00
+                        1667696400000L, // 2022-11-05 18:00:00
+                        1667703600000L, // 2022-11-05 20:00:00
+                        1667710800000L, // 2022-11-05 22:00:00
+                        1667718000000L  // 2022-11-06 00:00:00
+                ),
+                List.of(
+                        1667718000000L, // 2022-11-06 00:00:00
+                        1667725200000L, // 2022-11-06 01:00:00  after daylight saving change
+                        1667732400000L, // 2022-11-06 03:00:00
+                        1667739600000L, // 2022-11-06 05:00:00
+                        1667746800000L, // 2022-11-06 07:00:00
+                        1667754000000L, // 2022-11-06 09:00:00
+                        1667761200000L, // 2022-11-06 11:00:00
+                        1667768400000L, // 2022-11-06 13:00:00
+                        1667775600000L, // 2022-11-06 15:00:00
+                        1667782800000L, // 2022-11-06 17:00:00
+                        1667790000000L, // 2022-11-06 19:00:00
+                        1667797200000L, // 2022-11-06 21:00:00
+                        1667804400000L  // 2022-11-06 23:00:00
+                ),
+                List.of(
+                        1667808000000L, // 2022-11-07 00:00:00
+                        1667815200000L, // 2022-11-07 02:00:00
+                        1667822400000L, // 2022-11-07 04:00:00
+                        1667829600000L  // 2022-11-07 06:00:00
+                )
+        );
+        final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
+        expectedHourlyLevels1.add(100);
+        expectedHourlyLevels1.add(null);
+        expectedHourlyLevels1.add(null);
+        expectedHourlyLevels1.add(null);
+        expectedHourlyLevels1.add(null);
+        expectedHourlyLevels1.add(null);
+        expectedHourlyLevels1.add(null);
+        expectedHourlyLevels1.add(null);
+        final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
+        expectedHourlyLevels3.add(null);
+        expectedHourlyLevels3.add(null);
+        expectedHourlyLevels3.add(null);
+        expectedHourlyLevels3.add(88);
+        final List<List<Integer>> expectedHourlyLevels = List.of(
+                expectedHourlyLevels1,
+                expectedHourlyLevels2,
+                expectedHourlyLevels3
+        );
+        verifyExpectedBatteryLevelData(
+                resultData,
+                expectedDailyTimestamps,
+                expectedDailyLevels,
+                expectedHourlyTimestamps,
+                expectedHourlyLevels);
+    }
+
+    @Test
+    public void getLevelDataThroughProcessedHistoryMap_daylightSaving23Hour_returnExpectedResult() {
+        // Timezone PST 2022-03-13 has no 02:00:00 - 02:59:59 for daylight saving.
+        TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+        final long[] timestamps = {
+                1647151200000L, // 2022-03-12 22:00:00
+                1647262800000L  // 2022-03-14 06:00:00
+        };
+        final int[] levels = {100, 88};
+        final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
+                createHistoryMap(timestamps, levels);
+        DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
+
+        final BatteryLevelData resultData =
+                DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap);
+
+        final List<Long> expectedDailyTimestamps = List.of(
+                1647151200000L, // 2022-03-12 22:00:00
+                1647158400000L, // 2022-03-13 00:00:00
+                1647241200000L, // 2022-03-14 00:00:00
+                1647262800000L  // 2022-03-14 06:00:00
+        );
+        final List<Integer> expectedDailyLevels = new ArrayList<>();
+        expectedDailyLevels.add(100);
+        expectedDailyLevels.add(null);
+        expectedDailyLevels.add(null);
+        expectedDailyLevels.add(88);
+        final List<List<Long>> expectedHourlyTimestamps = List.of(
+                List.of(
+                        1647151200000L, // 2022-03-12 22:00:00
+                        1647158400000L  // 2022-03-13 00:00:00
+                ),
+                List.of(
+                        1647158400000L, // 2022-03-13 00:00:00
+                        1647165600000L, // 2022-03-13 03:00:00  after daylight saving change
+                        1647172800000L, // 2022-03-13 05:00:00
+                        1647180000000L, // 2022-03-13 07:00:00
+                        1647187200000L, // 2022-03-13 09:00:00
+                        1647194400000L, // 2022-03-13 11:00:00
+                        1647201600000L, // 2022-03-13 13:00:00
+                        1647208800000L, // 2022-03-13 15:00:00
+                        1647216000000L, // 2022-03-13 17:00:00
+                        1647223200000L, // 2022-03-13 19:00:00
+                        1647230400000L, // 2022-03-13 21:00:00
+                        1647237600000L  // 2022-03-13 23:00:00
+                ),
+                List.of(
+                        1647241200000L, // 2022-03-14 00:00:00
+                        1647248400000L, // 2022-03-14 02:00:00
+                        1647255600000L, // 2022-03-14 04:00:00
+                        1647262800000L  // 2022-03-14 06:00:00
+                )
+        );
+        final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
+        expectedHourlyLevels1.add(100);
+        expectedHourlyLevels1.add(null);
+        final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        expectedHourlyLevels2.add(null);
+        final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
+        expectedHourlyLevels3.add(null);
+        expectedHourlyLevels3.add(null);
+        expectedHourlyLevels3.add(null);
+        expectedHourlyLevels3.add(88);
+        final List<List<Integer>> expectedHourlyLevels = List.of(
+                expectedHourlyLevels1,
+                expectedHourlyLevels2,
+                expectedHourlyLevels3
+        );
+        verifyExpectedBatteryLevelData(
+                resultData,
+                expectedDailyTimestamps,
+                expectedDailyLevels,
+                expectedHourlyTimestamps,
+                expectedHourlyLevels);
+    }
+
+    @Test
     public void getTimestampSlots_emptyRawList_returnEmptyList() {
         final List<Long> resultList = DataProcessor.getTimestampSlots(
                 new ArrayList<>(), 1641038400000L); // 2022-01-01 20:00:00
@@ -1479,21 +1671,21 @@
         final Map<Long, Map<String, List<AppUsagePeriod>>> appUsagePeriodMap =
                 DataProcessor.buildAppUsagePeriodList(appUsageEvents, 0, 5);
 
-        assertThat(appUsagePeriodMap.size()).isEqualTo(2);
+        assertThat(appUsagePeriodMap).hasSize(2);
         final Map<String, List<AppUsagePeriod>> userMap1 = appUsagePeriodMap.get(1L);
-        assertThat(userMap1.size()).isEqualTo(2);
+        assertThat(userMap1).hasSize(2);
         List<AppUsagePeriod> appUsagePeriodList = userMap1.get(packageName1);
-        assertThat(appUsagePeriodList.size()).isEqualTo(3);
+        assertThat(appUsagePeriodList).hasSize(3);
         assertAppUsagePeriod(appUsagePeriodList.get(0), 1, 2);
         assertAppUsagePeriod(appUsagePeriodList.get(1), 2, 4);
         assertAppUsagePeriod(appUsagePeriodList.get(2), 3, 4);
         appUsagePeriodList = userMap1.get(packageName2);
-        assertThat(appUsagePeriodList.size()).isEqualTo(1);
+        assertThat(appUsagePeriodList).hasSize(1);
         assertAppUsagePeriod(appUsagePeriodList.get(0), 2, 4);
         final Map<String, List<AppUsagePeriod>> userMap2 = appUsagePeriodMap.get(2L);
-        assertThat(userMap2.size()).isEqualTo(1);
+        assertThat(userMap2).hasSize(1);
         appUsagePeriodList = userMap2.get(packageName2);
-        assertThat(appUsagePeriodList.size()).isEqualTo(2);
+        assertThat(appUsagePeriodList).hasSize(2);
         assertAppUsagePeriod(appUsagePeriodList.get(0), 1, 2);
         assertAppUsagePeriod(appUsagePeriodList.get(1), 3, 4);
     }
@@ -1563,7 +1755,7 @@
         final List<AppUsagePeriod> appUsagePeriodList =
                 DataProcessor.buildAppUsagePeriodListPerInstance(appUsageEvents, 100000, 1100000);
 
-        assertThat(appUsagePeriodList.size()).isEqualTo(7);
+        assertThat(appUsagePeriodList).hasSize(7);
         assertAppUsagePeriod(appUsagePeriodList.get(0), 100000, 120000);
         assertAppUsagePeriod(appUsagePeriodList.get(1), 150000, 200000);
         assertAppUsagePeriod(appUsagePeriodList.get(2), 300000, 500000);