Merge "Replaces getBatteryHistoryUri and getBatteryHistorySinceLastFullCharge with the new functions in DatabaseUtils."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d89f05c..5d713b0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2978,7 +2978,7 @@
         <receiver android:name=".fuelgauge.batteryusage.BatteryUsageBroadcastReceiver"
                   android:exported="true">
             <intent-filter>
-                <action android:name="com.android.settings.battery.action.FETCH_BATTERY_USAGE_DATA"/>
+                <action android:name="android.intent.action.BATTERY_LEVEL_CHANGED"/>
                 <action android:name="com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"/>
             </intent-filter>
         </receiver>
diff --git a/res/layout/sfps_enroll_finish_base.xml b/res/layout/sfps_enroll_finish_base.xml
index 6e468c6..8d062d9 100644
--- a/res/layout/sfps_enroll_finish_base.xml
+++ b/res/layout/sfps_enroll_finish_base.xml
@@ -55,16 +55,20 @@
                     android:src="@drawable/sfps_enroll_finish" />
             </com.google.android.setupdesign.view.FillContentLayout>
 
-            <!-- Added to align elements with fingerprint_enroll_enrolling_base -->
-            <TextView
-                style="@style/TextAppearance.ErrorText"
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <com.android.settings.biometrics.fingerprint.FingerprintRequireScreenOnToAuthToggle
+                style="@style/SudSwitchStyle"
+                android:id="@+id/require_screen_on_to_auth_toggle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_horizontal|bottom"
-                android:visibility="invisible" />
+                android:layout_gravity="center_horizontal|bottom" />
 
         </LinearLayout>
 
     </LinearLayout>
 
-</com.google.android.setupdesign.GlifLayout>
+</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/sfps_require_screen_on_to_auth_toggle.xml b/res/layout/sfps_require_screen_on_to_auth_toggle.xml
new file mode 100644
index 0000000..929b64b
--- /dev/null
+++ b/res/layout/sfps_require_screen_on_to_auth_toggle.xml
@@ -0,0 +1,79 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    style="?attr/fingerprint_layout_theme">
+
+    <!-- Top divider -->
+    <View
+        android:layout_alignParentTop="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/listDivider" />
+
+    <!-- Title -->
+    <com.google.android.setupdesign.view.RichTextView
+        android:id="@+id/title"
+        android:paddingHorizontal="8dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="4dp"
+        android:gravity="start"
+        android:layout_alignParentStart="true"
+        android:layout_toStartOf="@+id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/security_settings_require_screen_on_to_auth_title"
+        android:textColor="?android:attr/textColorPrimary"
+        android:textSize="@dimen/sud_description_text_size"
+    />
+
+    <!-- Subtitle -->
+    <TextView
+        android:id="@+id/subtitle"
+        android:paddingHorizontal="8dp"
+        android:paddingBottom="8dp"
+        android:layout_alignParentStart="true"
+        android:layout_toStartOf="@+id/toggle"
+        android:layout_below="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/security_settings_require_screen_on_to_auth_description"
+        android:textColor="?android:attr/textColorSecondary"/>
+
+    <!-- Vertical divider -->
+    <View
+        android:layout_centerVertical="true"
+        android:layout_alignTop="@+id/toggle"
+        android:layout_alignBottom="@+id/toggle"
+        android:layout_toStartOf="@+id/toggle"
+        android:layout_width="1dp"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/listDivider" />
+
+    <!-- Toggle -->
+    <Switch
+        android:layout_alignParentEnd="true"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/toggle"
+        android:layout_centerVertical="true"
+        android:checked="false"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a186989..e2acc38 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -783,6 +783,8 @@
     <!-- Note: Update FingerprintEnrollParentalConsent.CONSENT_STRING_RESOURCES when any _consent_ strings are added or removed. -->
     <!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
     <string name="security_settings_fingerprint_preference_title">Fingerprint</string>
+    <!-- Title for a category shown for the fingerprint settings page, followed by items that the user can toggle on/off to require/disable. [CHAR LIMIT=50] -->
+    <string name="security_settings_fingerprint_settings_preferences_category">When using Fingerprint Unlock</string>
     <!-- Title shown for work menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
     <string name="security_settings_work_fingerprint_preference_title">Fingerprint for work</string>
     <!-- Preference to add another fingerprint -->
@@ -1036,6 +1038,12 @@
     <string name="security_settings_fingerprint_enroll_finish_v2_message" product="device">Now you can use your fingerprint to unlock your device or verify it\u2019s you, like when you sign in to apps</string>
     <!-- Message shown in fingerprint enrollment dialog once enrollment is completed (default) [CHAR LIMIT=NONE] -->
     <string name="security_settings_fingerprint_enroll_finish_v2_message" product="default">Now you can use your fingerprint to unlock your phone or verify it\u2019s you, like when you sign in to apps</string>
+    <!-- Title for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_require_screen_on_to_auth_title">Unlock only when screen is on</string>
+    <!-- Description for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_require_screen_on_to_auth_description">The screen must be on before you can unlock with your fingerprint. This makes accidental unlocking less likely.</string>
+    <!-- Description for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
+    <string name="security_settings_require_screen_on_to_auth_keywords">Screen, Unlock</string>
     <!-- Button text to skip enrollment of fingerprint [CHAR LIMIT=40] -->
     <string name="security_settings_fingerprint_enroll_enrolling_skip">Do it later</string>
     <!-- Accessibility message for fingerprint enrollment asking the user to place the tip of their finger on the fingerprint sensor [CHAR LIMIT=NONE] -->
diff --git a/res/xml/security_settings_fingerprint.xml b/res/xml/security_settings_fingerprint.xml
index 804ef88..a4ce545 100644
--- a/res/xml/security_settings_fingerprint.xml
+++ b/res/xml/security_settings_fingerprint.xml
@@ -16,5 +16,20 @@
 
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:title="@string/security_settings_fingerprint_preference_title"/>
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/security_settings_fingerprint_preference_title">
 
+    <PreferenceCategory
+        android:key="security_settings_fingerprint_unlock_category"
+        android:title="@string/security_settings_fingerprint_settings_preferences_category"
+        settings:controller="com.android.settings.biometrics.fingerprint.FingerprintUnlockCategoryPreferenceController">
+
+        <com.android.settingslib.RestrictedSwitchPreference
+            android:key="security_settings_require_screen_on_to_auth"
+            android:title="@string/security_settings_require_screen_on_to_auth_title"
+            android:summary="@string/security_settings_require_screen_on_to_auth_description"
+            settings:keywords="@string/security_settings_require_screen_on_to_auth_keywords"
+            settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController" />
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java
index ca3d123..4149e23 100644
--- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java
+++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java
@@ -402,8 +402,7 @@
      * @return the summary for the current state of whether the app associated with the given
      * {@code packageName} is allowed to interact across profiles.
      */
-    public static CharSequence getPreferenceSummary(
-            Context context, String packageName) {
+    public static String getPreferenceSummary(Context context, String packageName) {
         return context.getString(isInteractAcrossProfilesEnabled(context, packageName)
                 ? R.string.interact_across_profiles_summary_allowed
                 : R.string.interact_across_profiles_summary_not_allowed);
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
index 0c7ef98..a28ea8e 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
+import android.widget.CompoundButton;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -44,16 +45,24 @@
 public class FingerprintEnrollFinish extends BiometricEnrollBase {
 
     private static final String TAG = "FingerprintEnrollFinish";
+    private static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH = "require_screen_on_to_auth_toggle";
     private static final String ACTION_FINGERPRINT_SETTINGS =
             "android.settings.FINGERPRINT_SETTINGS";
     @VisibleForTesting
     static final String FINGERPRINT_SUGGESTION_ACTIVITY =
             "com.android.settings.SetupFingerprintSuggestionActivity";
+
     private FingerprintManager mFingerprintManager;
+
+    private FingerprintSettingsRequireScreenOnToAuthPreferenceController
+            mRequireScreenOnToAuthPreferenceController;
+    private FingerprintRequireScreenOnToAuthToggle mRequireScreenOnToAuthToggle;
     private boolean mCanAssumeSfps;
 
     private boolean mIsAddAnotherOrFinish;
 
+    private CompoundButton.OnCheckedChangeListener mRequireScreenOnToAuthToggleListener;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -63,6 +72,11 @@
         mCanAssumeSfps = props != null && props.size() == 1 && props.get(0).isAnySidefpsType();
         if (mCanAssumeSfps) {
             setContentView(R.layout.sfps_enroll_finish);
+            mRequireScreenOnToAuthPreferenceController =
+                    new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
+                            getApplicationContext(),
+                            KEY_REQUIRE_SCREEN_ON_TO_AUTH
+                    );
         } else {
             setContentView(R.layout.fingerprint_enroll_finish);
         }
@@ -90,6 +104,20 @@
                         .setTheme(R.style.SudGlifButton_Primary)
                         .build()
         );
+
+        if (mCanAssumeSfps) {
+            mRequireScreenOnToAuthToggleListener =
+                (buttonView, isChecked) -> {
+                    mRequireScreenOnToAuthPreferenceController.setChecked(isChecked);
+                };
+            mRequireScreenOnToAuthToggle = findViewById(R.id.require_screen_on_to_auth_toggle);
+            mRequireScreenOnToAuthToggle.setChecked(
+            mRequireScreenOnToAuthPreferenceController.isChecked());
+            mRequireScreenOnToAuthToggle.setListener(mRequireScreenOnToAuthToggleListener);
+            mRequireScreenOnToAuthToggle.setOnClickListener(v -> {
+                mRequireScreenOnToAuthToggle.getSwitch().toggle();
+            });
+        }
     }
 
     @Override
@@ -103,6 +131,12 @@
     @Override
     protected void onResume() {
         super.onResume();
+        if (mCanAssumeSfps) {
+            mRequireScreenOnToAuthToggleListener.onCheckedChanged(
+                    mRequireScreenOnToAuthToggle.getSwitch(),
+                    mRequireScreenOnToAuthToggle.isChecked()
+            );
+        }
 
         FooterButton addButton = mFooterBarMixin.getSecondaryButton();
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintRequireScreenOnToAuthToggle.java b/src/com/android/settings/biometrics/fingerprint/FingerprintRequireScreenOnToAuthToggle.java
new file mode 100644
index 0000000..f88c9aa
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintRequireScreenOnToAuthToggle.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+
+import com.android.settings.R;
+
+/**
+ * A layout that contains a start-justified title, and an end-justified switch.
+ */
+public class FingerprintRequireScreenOnToAuthToggle extends LinearLayout {
+    private Switch mSwitch;
+
+    public FingerprintRequireScreenOnToAuthToggle(Context context) {
+        this(context, null /* attrs */);
+    }
+
+    public FingerprintRequireScreenOnToAuthToggle(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FingerprintRequireScreenOnToAuthToggle(
+            Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        LayoutInflater.from(context).inflate(R.layout.sfps_require_screen_on_to_auth_toggle,
+                this, true /* attachToRoot */);
+
+        mSwitch = findViewById(R.id.toggle);
+        mSwitch.setClickable(true);
+        mSwitch.setFocusable(false);
+    }
+
+    public boolean isChecked() {
+        return mSwitch.isChecked();
+    }
+
+    /**
+     *
+     * @param checked
+     */
+    public void setChecked(boolean checked) {
+        mSwitch.setChecked(checked);
+    }
+
+    /**
+     *
+     * @param listener
+     */
+    public void setListener(CompoundButton.OnCheckedChangeListener listener) {
+        mSwitch.setOnCheckedChangeListener(listener);
+    }
+
+    public Switch getSwitch() {
+        return mSwitch;
+    }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index c031fe6..1c7c891 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -56,21 +56,24 @@
 import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceScreen;
 import androidx.preference.PreferenceViewHolder;
+import androidx.preference.SwitchPreference;
 
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.SubSettings;
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.biometrics.BiometricUtils;
 import com.android.settings.core.SettingsBaseActivity;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.password.ChooseLockGeneric;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settingslib.HelpUtils;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.transition.SettingsTransitionHelper;
 import com.android.settingslib.widget.FooterPreference;
 import com.android.settingslib.widget.TwoTargetPreference;
@@ -115,7 +118,26 @@
         setTitle(msg);
     }
 
-    public static class FingerprintSettingsFragment extends SettingsPreferenceFragment
+    /**
+     * @param context
+     * @return true if the Fingerprint hardware is detected.
+     */
+    public static boolean isFingerprintHardwareDetected(Context context) {
+        FingerprintManager manager = Utils.getFingerprintManagerOrNull(context);
+        boolean isHardwareDetected = false;
+        if (manager == null) {
+            Log.d(TAG, "FingerprintManager is null");
+        } else {
+            isHardwareDetected = manager.isHardwareDetected();
+            Log.d(TAG, "FingerprintManager is not null. Hardware detected: " + isHardwareDetected);
+        }
+        return manager != null && isHardwareDetected;
+    }
+
+    /**
+     *
+     */
+    public static class FingerprintSettingsFragment extends DashboardFragment
             implements OnPreferenceChangeListener, FingerprintPreference.OnDeleteClickListener {
 
         private static class FooterColumn {
@@ -134,6 +156,8 @@
         private static final String KEY_LAUNCHED_CONFIRM = "launched_confirm";
         private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled";
         private static final String KEY_IS_ENROLLING = "is_enrolled";
+        private static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH =
+                "security_settings_require_screen_on_to_auth";
 
         private static final int MSG_REFRESH_FINGERPRINT_TEMPLATES = 1000;
         private static final int MSG_FINGER_AUTH_SUCCESS = 1001;
@@ -149,6 +173,11 @@
 
         protected static final boolean DEBUG = false;
 
+        private List<AbstractPreferenceController> mControllers;
+        private FingerprintSettingsRequireScreenOnToAuthPreferenceController
+                mRequireScreenOnToAuthPreferenceController;
+        private RestrictedSwitchPreference mRequireScreenOnToAuthPreference;
+
         private FingerprintManager mFingerprintManager;
         private FingerprintUpdater mFingerprintUpdater;
         private List<FingerprintSensorPropertiesInternal> mSensorProperties;
@@ -214,6 +243,7 @@
                     }
 
                     private void updateDialog() {
+                        setRequireScreenOnToAuthVisibility();
                         RenameDialog renameDialog = (RenameDialog) getFragmentManager().
                                 findFragmentByTag(RenameDialog.class.getName());
                         if (renameDialog != null) {
@@ -448,13 +478,36 @@
             if (root != null) {
                 root.removeAll();
             }
-            addPreferencesFromResource(R.xml.security_settings_fingerprint);
             root = getPreferenceScreen();
             addFingerprintItemPreferences(root);
+            addPreferencesFromResource(getPreferenceScreenResId());
+            mRequireScreenOnToAuthPreference = findPreference(KEY_REQUIRE_SCREEN_ON_TO_AUTH);
+            for (AbstractPreferenceController controller : mControllers) {
+                ((FingerprintSettingsPreferenceController) controller).setUserId(mUserId);
+            }
+            mRequireScreenOnToAuthPreference.setChecked(
+                    mRequireScreenOnToAuthPreferenceController.isChecked());
+            mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
+                    (preference, newValue) -> {
+                        boolean isChecked = ((SwitchPreference) preference).isChecked();
+                        mRequireScreenOnToAuthPreferenceController.setChecked(!isChecked);
+                        return true;
+                    });
             setPreferenceScreen(root);
             return root;
         }
 
+        private void setRequireScreenOnToAuthVisibility() {
+            int fingerprintsEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size();
+            final boolean removalInProgress = mRemovalSidecar.inProgress();
+            // Removing last remaining fingerprint
+            if (fingerprintsEnrolled == 0 && removalInProgress) {
+                mRequireScreenOnToAuthPreference.setVisible(false);
+            } else {
+                mRequireScreenOnToAuthPreference.setVisible(true);
+            }
+        }
+
         private void addFingerprintItemPreferences(PreferenceGroup root) {
             root.removeAll();
             final List<Fingerprint> items = mFingerprintManager.getEnrolledFingerprints(mUserId);
@@ -477,6 +530,7 @@
                 root.addPreference(pref);
                 pref.setOnPreferenceChangeListener(this);
             }
+
             Preference addPreference = new Preference(root.getContext());
             addPreference.setKey(KEY_FINGERPRINT_ADD);
             addPreference.setTitle(R.string.fingerprint_add_title);
@@ -569,6 +623,16 @@
         }
 
         @Override
+        protected int getPreferenceScreenResId() {
+            return R.xml.security_settings_fingerprint;
+        }
+
+        @Override
+        protected String getLogTag() {
+            return TAG;
+        }
+
+        @Override
         public void onSaveInstanceState(final Bundle outState) {
             outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
                     mToken);
@@ -663,6 +727,27 @@
         }
 
         @Override
+        protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+            if (!isFingerprintHardwareDetected(context)) {
+                return null;
+            }
+
+            mControllers = buildPreferenceControllers(context);
+            return mControllers;
+        }
+
+        private List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
+            final List<AbstractPreferenceController> controllers = new ArrayList<>();
+            mRequireScreenOnToAuthPreferenceController =
+                    new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
+                            context,
+                            KEY_REQUIRE_SCREEN_ON_TO_AUTH
+                    );
+            controllers.add(mRequireScreenOnToAuthPreferenceController);
+            return controllers;
+        }
+
+        @Override
         public void onActivityResult(int requestCode, int resultCode, Intent data) {
             super.onActivityResult(requestCode, resultCode, data);
             if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsPreferenceController.java
new file mode 100644
index 0000000..2ca5da8
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsPreferenceController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+
+/**
+ * Abstract base class for all fingerprint settings toggles.
+ */
+public abstract class FingerprintSettingsPreferenceController extends TogglePreferenceController {
+
+    private int mUserId;
+
+    public FingerprintSettingsPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    public void setUserId(int userId) {
+        mUserId = userId;
+    }
+
+    protected int getUserId() {
+        return mUserId;
+    }
+
+    protected EnforcedAdmin getRestrictingAdmin() {
+        return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
+                mContext, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId);
+    }
+
+    @Override
+    public final boolean isSliceable() {
+        return false;
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        // not needed since it's not sliceable
+        return NO_RES;
+    }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
new file mode 100644
index 0000000..5b183c1
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.Utils;
+
+/**
+ * Preference controller that controls whether a SFPS device is required to be interactive for
+ * fingerprint authentication to unlock the device.
+ */
+public class FingerprintSettingsRequireScreenOnToAuthPreferenceController
+        extends FingerprintSettingsPreferenceController {
+    private static final String TAG =
+            "FingerprintSettingsRequireScreenOnToAuthPreferenceController";
+
+    @VisibleForTesting
+    protected FingerprintManager mFingerprintManager;
+
+    public FingerprintSettingsRequireScreenOnToAuthPreferenceController(
+            Context context, String prefKey) {
+        super(context, prefKey);
+        mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+    }
+
+    @Override
+    public boolean isChecked() {
+        if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+            return false;
+        } else if (getRestrictingAdmin() != null) {
+            return false;
+        }
+        int defaultValue = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_requireScreenOnToAuthEnabled) ? 1 : 0;
+
+        return Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
+                defaultValue,
+                getUserHandle()) != 0;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
+                isChecked ? 1 : 0,
+                getUserHandle());
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+            preference.setEnabled(false);
+        } else if (!mFingerprintManager.hasEnrolledTemplates(getUserId())) {
+            preference.setEnabled(false);
+        } else {
+            preference.setEnabled(true);
+        }
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (mFingerprintManager != null
+                && mFingerprintManager.isHardwareDetected()
+                && mFingerprintManager.isPowerbuttonFps()) {
+            return mFingerprintManager.hasEnrolledTemplates(getUserId())
+                    ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+        } else {
+            return UNSUPPORTED_ON_DEVICE;
+        }
+    }
+
+    private int getUserHandle() {
+        return UserHandle.of(getUserId()).getIdentifier();
+    }
+
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
index 5d8757d..a9e3c1d 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
@@ -20,21 +20,25 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
+import android.os.SystemClock;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.settingslib.fuelgauge.BatteryStatus;
+
+import java.time.Duration;
+
 /** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */
 public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
     private static final String TAG = "BatteryUsageBroadcastReceiver";
-    /** An intent action to request Settings to fetch usage data. */
-    public static final String ACTION_FETCH_BATTERY_USAGE_DATA =
-            "com.android.settings.battery.action.FETCH_BATTERY_USAGE_DATA";
     /** An intent action to request Settings to clear cache data. */
     public static final String ACTION_CLEAR_BATTERY_CACHE_DATA =
             "com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA";
 
     @VisibleForTesting
+    static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis();
+    @VisibleForTesting
     static boolean sIsDebugMode = Build.TYPE.equals("userdebug");
 
     @VisibleForTesting
@@ -47,9 +51,8 @@
         }
         Log.d(TAG, "onReceive:" + intent.getAction());
         switch (intent.getAction()) {
-            case ACTION_FETCH_BATTERY_USAGE_DATA:
-                mFetchBatteryUsageData = true;
-                BatteryUsageDataLoader.enqueueWork(context);
+            case Intent.ACTION_BATTERY_LEVEL_CHANGED:
+                tryToFetchUsageData(context);
                 break;
             case ACTION_CLEAR_BATTERY_CACHE_DATA:
                 if (sIsDebugMode) {
@@ -59,4 +62,23 @@
                 break;
         }
     }
+
+    private void tryToFetchUsageData(Context context) {
+        final Intent batteryIntent = DatabaseUtils.getBatteryIntent(context);
+        // Returns when battery is not fully charged.
+        if (!BatteryStatus.isCharged(batteryIntent)) {
+            return;
+        }
+
+        final long broadcastDelay = sBroadcastDelayFromBoot - SystemClock.elapsedRealtime();
+        // If current boot time is smaller than expected delay, cancel sending the broadcast.
+        if (broadcastDelay > 0) {
+            Log.d(TAG, "cancel sendBroadcastToFetchUsageData when broadcastDelay is"
+                    + broadcastDelay + "ms.");
+            return;
+        }
+
+        mFetchBatteryUsageData = true;
+        BatteryUsageDataLoader.enqueueWork(context);
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index 394c154..29e0b0e 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -141,6 +141,12 @@
         });
     }
 
+    /** Gets the latest sticky battery intent from framework. */
+    static Intent getBatteryIntent(Context context) {
+        return context.registerReceiver(
+                /*receiver=*/ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+    }
+
     static List<ContentValues> sendBatteryEntryData(
             Context context,
             List<BatteryEntry> batteryEntryList,
@@ -300,12 +306,6 @@
         return resultMap;
     }
 
-    /** Gets the latest sticky battery intent from framework. */
-    private static Intent getBatteryIntent(Context context) {
-        return context.registerReceiver(
-                /*receiver=*/ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-    }
-
     private static int getBatteryLevel(Intent intent) {
         final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
         final int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index da8b72a..e3d0805 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -115,7 +115,7 @@
             ModifySystemSettingsAppListProvider.InfoPageEntryItem(app)
             PictureInPictureListProvider.InfoPageEntryItem(app)
             InstallUnknownAppsListProvider.InfoPageEntryItem(app)
-            // TODO: interact_across_profiles
+            InteractAcrossProfilesDetailsPreference(app)
             AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app)
         }
 
diff --git a/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt b/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
new file mode 100644
index 0000000..15d0501
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment
+import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetails
+import com.android.settingslib.spa.framework.compose.collectAsStateWithLifecycle
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.framework.common.crossProfileApps
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+
+@Composable
+fun InteractAcrossProfilesDetailsPreference(app: ApplicationInfo) {
+    val context = LocalContext.current
+    val presenter = remember { InteractAcrossProfilesDetailsPresenter(context, app) }
+    if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+
+    Preference(object : PreferenceModel {
+        override val title = stringResource(R.string.interact_across_profiles_title)
+        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
+            initialValue = stringResource(R.string.summary_placeholder),
+        )
+        override val onClick = presenter::startActivity
+    })
+}
+
+private class InteractAcrossProfilesDetailsPresenter(
+    private val context: Context,
+    private val app: ApplicationInfo,
+) {
+    private val crossProfileApps = context.crossProfileApps
+
+    val isAvailableFlow = flow {
+        emit(crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(app.packageName))
+    }.flowOn(Dispatchers.IO)
+
+    val summaryFlow = flow {
+        emit(InteractAcrossProfilesDetails.getPreferenceSummary(context, app.packageName))
+    }.flowOn(Dispatchers.IO)
+
+    fun startActivity() {
+        AppInfoDashboardFragment.startAppInfoFragment(
+            InteractAcrossProfilesDetails::class.java,
+            app,
+            context,
+            AppInfoSettingsProvider.METRICS_CATEGORY,
+        )
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest.java
new file mode 100644
index 0000000..ff74d59
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.provider.Settings;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUtils.class})
+public class FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest {
+
+    @Mock
+    private FingerprintManager mFingerprintManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private RestrictedSwitchPreference mPreference;
+
+    private Context mContext;
+    private FingerprintSettingsRequireScreenOnToAuthPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getSystemService(eq(Context.FINGERPRINT_SERVICE))).thenReturn(
+                mFingerprintManager);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+        mController = spy(new FingerprintSettingsRequireScreenOnToAuthPreferenceController(mContext,
+                "test_key"));
+        ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowUtils.reset();
+    }
+
+    @Test
+    public void onPreferenceChange_settingIsUpdated() {
+        boolean state = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED, 1) != 0;
+
+        assertThat(mController.isChecked()).isFalse();
+        assertThat(mController.onPreferenceChange(mPreference, !state)).isTrue();
+        boolean newState = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED, 1) != 0;
+        assertThat(newState).isEqualTo(!state);
+    }
+
+    @Test
+    public void isAvailable_isEnabled_whenSfpsHardwareDetected_AndHasEnrolledFingerprints() {
+        assertThat(mController.isAvailable()).isEqualTo(false);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                true /* isPowerbuttonFps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isEqualTo(true);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void isAvailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() {
+        assertThat(mController.isAvailable()).isEqualTo(false);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                true /* isPowerbuttonFps */,
+                false /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isEqualTo(true);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+    }
+
+    @Test
+    public void isUnavailable_whenHardwareNotDetected() {
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+                false /* isHardwareDetected */,
+                true /* isPowerbuttonFps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void isUnavailable_onNonSfpsDevice() {
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+        configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+                true /* isHardwareDetected */,
+                false /* isPowerbuttonFps */,
+                true /* hasEnrolledTemplates */);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    private void configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+            boolean isHardwareDetected, boolean isPowerbuttonFps, boolean hasEnrolledTemplates) {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(isHardwareDetected);
+        when(mFingerprintManager.isPowerbuttonFps()).thenReturn(isPowerbuttonFps);
+        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(hasEnrolledTemplates);
+    }
+
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
index 51b2212..513dfdf 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
@@ -18,12 +18,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.BatteryManager;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -50,14 +54,6 @@
     }
 
     @Test
-    public void onReceive_fetchUsageDataIntent_startService() {
-        mBatteryUsageBroadcastReceiver.onReceive(mContext,
-                new Intent(BatteryUsageBroadcastReceiver.ACTION_FETCH_BATTERY_USAGE_DATA));
-
-        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isTrue();
-    }
-
-    @Test
     public void onReceive_invalidIntent_notStartService() {
         mBatteryUsageBroadcastReceiver.onReceive(mContext, new Intent("invalid intent"));
 
@@ -65,6 +61,47 @@
     }
 
     @Test
+    public void onReceive_actionBatteryLevelChanged_notFetchUsageData_notFullCharged() {
+        doReturn(getBatteryIntent(/*level=*/ 20, BatteryManager.BATTERY_STATUS_UNKNOWN))
+                .when(mContext).registerReceiver(any(), any());
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
+    public void onReceive_actionBatteryLevelChanged_cancelFetchUsageData() {
+        // Make sure isCharged returns true.
+        doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_FULL))
+                .when(mContext).registerReceiver(any(), any());
+        // Make sure broadcast will be sent with delay.
+        BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot =
+                SystemClock.elapsedRealtime() + 5 * DateUtils.MINUTE_IN_MILLIS;
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
+    public void onReceive_actionBatteryLevelChanged_notFetchUsageData() {
+        // Make sure isCharged returns true.
+        doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN))
+                .when(mContext).registerReceiver(any(), any());
+        BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot =
+                SystemClock.elapsedRealtime() - 5 * DateUtils.MINUTE_IN_MILLIS;
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isTrue();
+    }
+
+
+    @Test
     public void onReceive_clearCacheIntentInDebugMode_clearBatteryCacheData() {
         BatteryUsageBroadcastReceiver.sIsDebugMode = true;
         // Insert testing data first.
@@ -91,4 +128,11 @@
 
         assertThat(BatteryDiffEntry.sValidForRestriction).isNotEmpty();
     }
+
+    private static Intent getBatteryIntent(int level, int status) {
+        final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_LEVEL, level);
+        intent.putExtra(BatteryManager.EXTRA_STATUS, status);
+        return intent;
+    }
 }
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
new file mode 100644
index 0000000..aeccb07
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.CrossProfileApps
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.settings.R
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment
+import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetails
+import com.android.settingslib.spaprivileged.framework.common.crossProfileApps
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoSession
+import org.mockito.Spy
+import org.mockito.quality.Strictness
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class InteractAcrossProfilesDetailsPreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private lateinit var mockSession: MockitoSession
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Mock
+    private lateinit var crossProfileApps: CrossProfileApps
+
+    @Before
+    fun setUp() {
+        mockSession = ExtendedMockito.mockitoSession()
+            .initMocks(this)
+            .mockStatic(InteractAcrossProfilesDetails::class.java)
+            .mockStatic(AppInfoDashboardFragment::class.java)
+            .strictness(Strictness.LENIENT)
+            .startMocking()
+        whenever(context.crossProfileApps).thenReturn(crossProfileApps)
+        whenever(InteractAcrossProfilesDetails.getPreferenceSummary(context, PACKAGE_NAME))
+            .thenReturn("")
+    }
+
+    @After
+    fun tearDown() {
+        mockSession.finishMocking()
+    }
+
+    private fun mockCanConfig(canConfig: Boolean) {
+        whenever(crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(PACKAGE_NAME))
+            .thenReturn(canConfig)
+    }
+
+    @Test
+    fun cannotConfig_notDisplayed() {
+        mockCanConfig(false)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun canConfig_displayed() {
+        mockCanConfig(true)
+
+        setContent()
+
+        composeTestRule.onNodeWithText(context.getString(R.string.interact_across_profiles_title))
+            .assertIsDisplayed()
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun hasSummary() {
+        mockCanConfig(true)
+        whenever(InteractAcrossProfilesDetails.getPreferenceSummary(context, PACKAGE_NAME))
+            .thenReturn(SUMMARY)
+
+        setContent()
+
+        composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+    }
+
+    @Test
+    fun whenClick_startActivity() {
+        mockCanConfig(true)
+
+        setContent()
+        composeTestRule.onRoot().performClick()
+
+        ExtendedMockito.verify {
+            AppInfoDashboardFragment.startAppInfoFragment(
+                InteractAcrossProfilesDetails::class.java,
+                APP,
+                context,
+                AppInfoSettingsProvider.METRICS_CATEGORY,
+            )
+        }
+    }
+
+    private fun setContent() {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                InteractAcrossProfilesDetailsPreference(APP)
+            }
+        }
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "packageName"
+        const val UID = 123
+        const val SUMMARY = "summary"
+
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+        }
+    }
+}